Compare commits

...
138 Commits
Author SHA1 Message Date
Daniel Imms f30c38f773 Merge pull request #2515 from Tyriar/r420
v4.2.0
2019-10-25 05:50:45 -07:00
Daniel bfe11b7081 v4.2.0 2019-10-25 05:38:17 -07:00
Daniel Imms a800dd3aab Merge pull request #2514 from Tyriar/scroll
Apply scrollSensitivity on fast scroll too
2019-10-25 05:32:39 -07:00
Daniel a5a0779aa9 Apply scrollSensitivity on fast scroll too
Fixes #2513
2019-10-25 05:22:03 -07:00
Daniel Imms 4fe5a5a0bb Merge pull request #2512 from Tyriar/refine_range
Refine IViewportRange API to use 'cursor positions' for x value
2019-10-25 04:48:51 -07:00
Daniel Imms 974c721f66 Merge pull request #2511 from Tyriar/marker_doc
Clarify marker API docs
2019-10-25 04:45:06 -07:00
Daniel 851fb7ae38 Refine IViewportRange API to use 'cursor positions' for x value
Fixes #2484
2019-10-25 04:43:34 -07:00
jerch 1c72a66567 Merge pull request #2507 from jerch/remove_obsolete_mouseencodings
remove URXVT and UTF8 mouse encoding from codebase
2019-10-25 13:37:00 +02:00
jerch 6f8919ab93 Merge branch 'master' into remove_obsolete_mouseencodings 2019-10-25 13:20:20 +02:00
Daniel dac2da05bf Clarify marker API docs 2019-10-25 04:13:03 -07:00
Daniel Imms 7487d61fe2 Merge pull request #2510 from Tyriar/line_one
Set disposed marker line to -1
2019-10-25 04:08:45 -07:00
Daniel 67e5974eee Set disposed marker line to -1
Another indicator that it's disposed/invalid
2019-10-25 04:04:17 -07:00
jerch 21c3b3243d Merge branch 'master' into remove_obsolete_mouseencodings 2019-10-25 12:53:40 +02:00
Daniel Imms 4bd721c749 Merge pull request #2505 from Tyriar/lgtm_line
Fix lgtm suppression comment line
2019-10-25 03:49:48 -07:00
Jörg Breitbart ee2c1f71ef fix typo in debug msg 2019-10-25 12:43:34 +02:00
Jörg Breitbart a721e4884d Merge branch 'remove_obsolete_mouseencodings' of github.com:jerch/xterm.js into remove_obsolete_mouseencodings 2019-10-25 12:42:17 +02:00
Jörg Breitbart 31be1ebc53 remove DECSET 1005 and 1015 from InputHandler 2019-10-25 12:41:31 +02:00
jerch be59c74eae Merge branch 'master' into remove_obsolete_mouseencodings 2019-10-25 12:02:18 +02:00
jerch c1a0b26150 Merge pull request #2508 from jerch/fix_2423
limit parse buffer size
2019-10-25 12:01:51 +02:00
Jörg Breitbart 13ddaf8f98 limit parse buffer size 2019-10-24 22:32:10 +02:00
jerch 6dc6748876 Merge branch 'master' into remove_obsolete_mouseencodings 2019-10-24 22:07:41 +02:00
jerch 527379d343 Merge pull request #2360 from jerch/left_right_scroll
Support column sequences SL (scroll left), SR (scroll right), DECIC (insert columns) and DECDC (delete columns).
2019-10-24 22:03:30 +02:00
Jörg Breitbart 5ea016d537 remove URXVT and UTF8 encoding from codebase 2019-10-24 21:57:22 +02:00
jerch bb424bd86f Merge branch 'master' into left_right_scroll 2019-10-24 21:46:02 +02:00
Daniel Imms a6f4855f66 Merge pull request #2506 from Tyriar/mp3_sound
Use mp3 audio instead of wav
2019-10-24 12:05:39 -07:00
Daniel Imms fe08f34918 Use mp3 audio instead of wav
wav needs ffmpeg which vscode does not ship with, mp3 is best for compat
2019-10-24 12:04:45 -07:00
Jörg Breitbart 68d6d5576c force screen update for altered lines 2019-10-24 21:01:59 +02:00
Jörg Breitbart a4d561d3b9 xterm testfiles for SL/SR/DECIC/DECDC with DECSTBM 2019-10-24 20:36:01 +02:00
Jörg Breitbart c223524f77 respect DECSTBM 2019-10-24 19:27:35 +02:00
Daniel Imms f8fa962ec7 Fix lgtm suppression comment line 2019-10-24 09:35:12 -07:00
jerch 2a46f5f147 Merge branch 'master' into left_right_scroll 2019-10-24 18:31:51 +02:00
Daniel Imms 080188c468 Merge pull request #2504 from jerch/fix_2495
fix typo in TextDecoder
2019-10-24 09:29:42 -07:00
jerch 1392abb6eb Merge branch 'master' into left_right_scroll 2019-10-24 18:07:35 +02:00
Jörg Breitbart 30d3d46027 make linter happy 2019-10-24 18:04:44 +02:00
Jörg Breitbart 6d620166b4 fix typo in decoder and add a test case 2019-10-24 17:52:03 +02:00
Daniel Imms 276463e95e Merge pull request #2501 from xtermjs/Tyriar-patch-1
Remove double space in dts
2019-10-23 11:37:30 -07:00
Daniel Imms 84e4cf7e9c Remove double space in dts 2019-10-23 11:37:17 -07:00
Daniel Imms d9f58c0dbb Merge pull request #2491 from miggs125/issue#2456
Prevent search result from being deselected if it is the only result
2019-10-22 07:32:26 -07:00
Daniel Imms 3e753f1370 Merge branch 'master' into issue#2456 2019-10-22 07:17:47 -07:00
Daniel Imms 616d341e8c Avoid initializing to undefined 2019-10-22 07:17:37 -07:00
Daniel Imms 15392c6c87 Merge pull request #2493 from leomoty/forbid-only
Forbid only in mocha
2019-10-22 07:15:17 -07:00
Daniel Imms 5e2001f4b5 Space out if 2019-10-22 07:14:32 -07:00
Daniel Imms 5194098f12 Merge pull request #2497 from Tyriar/warn_not_throw
Warn don't throw when open is called on unattached element
2019-10-22 07:02:30 -07:00
Daniel Imms 3ce552cdec Warn don't throw when open is called on unattached element
Related microsoft/vscode#83016
2019-10-22 06:56:05 -07:00
Miguel R 720c8c4623 Merge branch 'master' into issue#2456 2019-10-22 04:14:14 +00:00
Miguel Roncancio 243874c644 change comment 2019-10-21 23:51:47 -04:00
Miguel Roncancio 5d547bffb0 change return value, import ISelectionPosition 2019-10-21 23:49:28 -04:00
Miguel Roncancio 495f97cf95 change return value, import ISelectionPosition 2019-10-21 23:39:01 -04:00
Leonardo Bressan Motyczka 5c2479944e Ensure test-unit fails fast if .only present 2019-10-22 03:00:40 +00:00
Leonardo Bressan Motyczka 51ef0d0680 Update test-unit to allow double dash args 2019-10-22 03:00:18 +00:00
Daniel Imms 2b39887a37 Merge pull request #2492 from jmbockhorst/publicViewportRange
Expose IViewportRange in the public API
2019-10-21 19:18:03 -07:00
Daniel Imms 7c82f9c7d4 Merge pull request #2496 from Tyriar/lgtm_alerts
Fix alerts on lgtm.com
2019-10-21 19:11:23 -07:00
Daniel Imms 1dbe482fcb Suppress server alerts 2019-10-21 19:03:53 -07:00
Daniel Imms cfd158cbff Remove unused imports 2019-10-21 18:48:49 -07:00
Daniel Imms 9826f3a14c Remove unneeded defensive check 2019-10-21 18:47:43 -07:00
Daniel Imms 6f7c91d0cd Remove useless local var assignment 2019-10-21 18:47:18 -07:00
Daniel Imms bcdebe7648 Remove duplicate switch case
Guessing this one was the result of a bad merge?
2019-10-21 18:46:26 -07:00
Daniel Imms 09b244ed2f Fix NaN comparison 2019-10-21 18:44:03 -07:00
Leonardo Bressan Motyczka 7576e8c202 test-api: Fail fast if .only is used 2019-10-21 22:27:25 -03:00
Jon Bockhorst 7b752be68a Expose IViewportRange in the public API 2019-10-21 20:24:03 -05:00
Daniel Imms d02086e5c6 Add test to cover search multiple times 2019-10-21 15:23:59 -07:00
Daniel Imms 5c172c74b2 Merge branch 'master' into issue#2456 2019-10-21 14:34:10 -07:00
Daniel Imms d29fc6d5de Merge pull request #2489 from Tyriar/test_fiix
Remove .only from fit test
2019-10-21 14:31:10 -07:00
miguel 449ede1d57 Prevent search result from being deselected if it is the only result
remove leading whitespace
prevent single search term deselection in findPrevious
2019-10-21 15:58:44 -04:00
Daniel Imms fc10816615 Remove .only from fit test 2019-10-21 11:27:01 -07:00
Daniel Imms 631107698e Merge pull request #2483 from leomoty/fit-tests
Introduce FitAddon tests
2019-10-18 12:13:34 -07:00
Daniel Imms 33d58ec3d2 Merge pull request #2485 from eamodio/feature/scroll-sensitivity
Adds scrollSensitivity option for scrolling speed
2019-10-18 11:42:44 -07:00
Eric Amodio f445bc763b Adds scrollSensitivity option for scrolling speed 2019-10-18 14:30:46 -04:00
Daniel Imms f8a62a23e5 Speed tests up by avoiding page loads 2019-10-18 11:23:11 -07:00
Leonardo Bressan Motyczka a92635b22c Introduce FitAddon tests 2019-10-18 14:53:09 -03:00
Leonardo Bressan Motyczka 44a4bd788f Adjust proposeDimensions to behave like resize 2019-10-18 14:48:56 -03:00
Daniel Imms eb252433ce Merge pull request #2470 from jmbockhorst/linkPosition
Support link position in hover tooltip callback
2019-10-18 08:47:16 -07:00
Daniel Imms 499d6c26f0 Merge branch 'master' into linkPosition 2019-10-17 12:38:14 -07:00
Daniel 8350804858 Adjust to use 1-based, note in API 2019-10-17 12:36:03 -07:00
Daniel Imms 9df547d746 Merge pull request #2482 from Tyriar/2452_website_update
Create a PR for website update after a release
2019-10-17 12:26:45 -07:00
Daniel Imms 7537adfb72 Merge branch 'master' into 2452_website_update 2019-10-17 12:00:13 -07:00
Daniel caf075dcb0 Create a PR for website update after a release 2019-10-17 11:54:32 -07:00
Jon Bockhorst 57581ca4ef Updated IViewportCellPosition wording 2019-10-17 12:40:32 -05:00
Daniel Imms a604389cc0 Merge pull request #2481 from Tyriar/2154_force_release_var
Allow builds to specify a force release var
2019-10-17 10:28:34 -07:00
Jon Bockhorst 08475eb495 Update API to use IViewportRange and IViewportCellPosition 2019-10-17 12:25:42 -05:00
Daniel Imms 91d1f97754 Merge pull request #2476 from Tyriar/2459_win_scroll
Fix scrollback going missing when increase size on Windows
2019-10-17 10:19:46 -07:00
Daniel Imms 990be060f2 Allow builds to specify a force release var
This will let us automate patch releases by queuing a manual build

Fixes #2154
2019-10-17 10:00:45 -07:00
Daniel Imms 29e53c9c11 Merge pull request #2477 from leomoty/devcontainer-hotfix
Allows to run all integration tests inside devcontainer
2019-10-17 09:19:25 -07:00
Daniel Imms da42738881 Merge pull request #2474 from glneto/master
#2463 Add comma as a default word separator
2019-10-17 09:07:06 -07:00
Geraldo Neto 9753a7d425 Merge branch 'master' into master 2019-10-16 20:16:56 -03:00
Leonardo Bressan Motyczka 07308485a4 Update MouseTracking puppeteer window size
This allows all tests to successfully run in devcontainer
2019-10-16 20:08:57 -03:00
Leonardo Bressan Motyczka 3d500ba802 Launch puppeteer without sandbox 2019-10-16 20:07:56 -03:00
Leonardo Bressan Motyczka e8dc563ccf Include puppeteer deps on devcontainer 2019-10-16 20:05:45 -03:00
Daniel Imms ee245230c9 Merge branch 'master' into 2459_win_scroll 2019-10-16 15:44:19 -07:00
Daniel Imms 843ea1b610 Fix scrollback going missing when increase size on Windows
This also forces the scroll bar to sync after a resize early, this may fix
other bugs.

Fixes #2459
2019-10-16 15:41:47 -07:00
Geraldo Neto 9f80ce25c2 Add : and ; to the word separator 2019-10-16 19:23:50 -03:00
Daniel Imms bd57e305e1 Merge pull request #2475 from Tyriar/vsc_82309
Protect against exception when clicking on mouse zones
2019-10-16 11:08:16 -07:00
Daniel Imms 740d261d7a Protect against exception when clicking on mouse zones
Still not sure how this happens but seems harmless to just not use the row as
wrapped here.

See microsoft/vscode#82309
2019-10-16 11:07:22 -07:00
Geraldo Neto 1bc246323c Add comma as a default word separator 2019-10-15 22:42:12 -03:00
Daniel Imms 91cbeecf52 Merge pull request #2473 from Tyriar/2471
Mark element and textarea APIs as | undefined
2019-10-15 16:41:59 -07:00
Daniel Imms cfe8022867 Mark element and textarea APIs as | undefined
Didn't fix all the issues in Terminal.ts as they will be covered in layering
refactor

Fixes #2471
2019-10-15 16:29:13 -07:00
Daniel Imms 99db619a5c Merge pull request #2375 from csurquin/feat/fast-scrolling
Support fast scrolling using a modifier key
2019-10-15 16:23:17 -07:00
Daniel Imms 4f8cda9f3a Remove bad whitespace 2019-10-15 16:22:41 -07:00
Daniel Imms 9fdb674f20 Use min of 1 for fast scroll 2019-10-15 16:22:09 -07:00
Daniel Imms e76b8f86b5 Pull modifier code into helper, apply to lines scrolled 2019-10-15 16:21:20 -07:00
Daniel Imms 0083c23b2d Merge branch 'master' into feat/fast-scrolling 2019-10-15 16:10:44 -07:00
Jon Bockhorst 040f9f49d9 Support link position in hover tooltip callback 2019-10-14 14:25:02 -05:00
Daniel Imms cbb79a77f9 Merge pull request #2467 from Tyriar/2387_dont_do_ctrl_arrow_on_mac
Don't use ctrl+up/down hack on macOS
2019-10-14 11:53:27 -07:00
Daniel Imms a97fe2df1c Merge pull request #2469 from leomoty/dispose-test
Add integration test for terminal dispose
2019-10-14 10:58:07 -07:00
Leonardo Bressan Motyczka ff13f74858 Add integration test for terminal dispose 2019-10-14 13:20:18 -03:00
Daniel Imms 3d6815ca6f Add tests for alt+up/down 2019-10-11 16:05:54 -07:00
Daniel Imms da11e31fe5 Don't use ctrl+up/down hack on macOS
Fixes #2387
2019-10-11 16:01:28 -07:00
Daniel Imms e224b865fd Merge pull request #2465 from syrusakbary/patch-1
Added WebAssembly.sh
2019-10-10 13:54:25 -07:00
Syrus Akbary 821a7afc01 Added WebAssembly.sh 2019-10-10 13:05:28 -07:00
Daniel Imms 84b56b7691 Merge pull request #2453 from Tyriar/2445_reveal_search
Reveal search results in line just below viewport
2019-10-08 07:05:21 -07:00
Daniel Imms 8288a26b49 Merge pull request #2455 from JeffreyCA/unfocused-cursor-style
Update unfocused cursor to match current cursor style
2019-10-08 06:43:03 -07:00
Daniel Imms d87bae374b Merge pull request #2454 from saisandeepvaddi/master
add ten hands to real-world uses list
2019-10-08 06:36:55 -07:00
JeffreyCA 7c982fc0ce Fix lint warnings 2019-10-07 22:51:03 -07:00
JeffreyCA 1eb6e37187 Refresh terminal even if blinking is enabled so cursor changes are immediately visible 2019-10-07 22:36:37 -07:00
JeffreyCA efbdbd3f3b Render non-block cursors as-is when terminal is unfocused 2019-10-07 22:25:47 -07:00
Sai Sandeep Vaddi 7fc57da85a add ten hands to real-world uses list 2019-10-07 23:45:03 -04:00
Daniel Imms 6f5e5215f2 Reveal search results in line just below viewport
Fixes #2445
2019-10-07 14:27:29 -07:00
Daniel Imms cad9477eef Merge pull request #2451 from Tyriar/1416_fit_infinity
Generalize verify integers check
2019-10-07 10:46:07 -07:00
Daniel Imms 4fde8db060 Merge pull request #2450 from Tyriar/1158_throw_bad_open
Throw when open is called on element not on DOM
2019-10-07 10:45:38 -07:00
Daniel Imms 724bcc3766 Generalize verify integers check
Fixes #1416
2019-10-07 10:28:51 -07:00
Daniel Imms 52ae8fc14b Throw when open is called on element not on DOM
Fixes #1158
2019-10-07 10:21:34 -07:00
jerch 89ac2834f1 Merge branch 'master' into left_right_scroll 2019-10-04 20:12:34 +02:00
Daniel Imms 358c09a108 Merge branch 'master' into feat/fast-scrolling 2019-09-28 09:30:23 -07:00
Daniel Imms 5236d4aabf Merge branch 'master' into feat/fast-scrolling 2019-09-28 08:51:28 -07:00
jerch 19f0fc0aa9 Merge branch 'master' into left_right_scroll 2019-09-12 10:55:36 +02:00
Daniel Imms 5e1de3b062 Merge branch 'master' into feat/fast-scrolling 2019-09-07 13:49:42 -07:00
jerch 54739d625f Merge branch 'master' into left_right_scroll 2019-09-05 21:54:22 +02:00
jerch 97bf796c43 Merge branch 'master' into left_right_scroll 2019-08-23 20:37:13 +02:00
jerch 54ff71f982 Merge branch 'master' into left_right_scroll 2019-08-23 16:45:42 +02:00
Jörg Breitbart f7827c4df8 add basic tests for commands 2019-08-17 18:52:11 +02:00
Jörg Breitbart 4b5709c68d add DECIC/DECDC to interface 2019-08-17 18:13:56 +02:00
Jörg Breitbart 4fe78dfe34 Merge branch 'master' into left_right_scroll 2019-08-17 17:57:31 +02:00
Corentin Surquin a353a8cffe Allow to disable fast scrolling 2019-08-10 19:45:41 +02:00
Corentin Surquin 73a17cfacb Support fast scrolling using a modifier key 2019-08-10 19:45:41 +02:00
Jörg Breitbart 8bc0099434 linter nonsense 2019-08-04 15:37:47 +02:00
Jörg Breitbart 9a903ca1c5 add DECIC/DECDC 2019-08-04 15:32:32 +02:00
Jörg Breitbart 5483a052cc always reset wrapped flag 2019-08-04 15:15:59 +02:00
Jörg Breitbart bd132d1f74 implement left/right scroll 2019-08-04 15:04:40 +02:00
60 changed files with 916 additions and 1381 deletions
+9 -2
View File
@@ -8,12 +8,19 @@ RUN apt-get update \
# Verify git and process tools are installed
RUN apt-get install -y git procps
# Install yarn
# Install yarn, puppeteer deps
RUN apt-get install -y curl apt-transport-https lsb-release \
&& curl -sS https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/pubkey.gpg | apt-key add - 2>/dev/null \
&& echo "deb https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/ stable main" | tee /etc/apt/sources.list.d/yarn.list \
&& apt-get update \
&& apt-get -y install --no-install-recommends yarn
&& apt-get -y install --no-install-recommends \
yarn fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst ttf-freefont \
# https://github.com/Googlechrome/puppeteer/issues/290#issuecomment-322921352
gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 \
libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 \
libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 \
libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 \
ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
# Clean up
RUN apt-get autoremove -y \
+2
View File
@@ -154,6 +154,8 @@ Xterm.js is used in several world-class applications to provide great terminal e
- [**PHP App Server**](https://github.com/cubiclesoft/php-app-server/): Create lightweight, installable almost-native applications for desktop OSes. ExecTerminal (nicely wraps the xterm.js Terminal), TerminalManager, and RunProcessSDK are self-contained, reusable ES5+ compliant Javascript components.
- [**NgTerminal**](https://github.com/qwefgh90/ng-terminal): NgTerminal is a web terminal that leverages xterm.js on Angular 7+. You can easily add it into your application by adding `<ng-terminal></ng-terminal>` into your component.
- [**tty-share**](https://tty-share.com): Extremely simple terminal sharing over the Internet.
- [**Ten Hands**](https://github.com/saisandeepvaddi/ten-hands): One place to run your command-line tasks.
- [**WebAssembly.sh**](https://webassembly.sh): A WebAssembly WASI browser terminal
[And much more...](https://github.com/xtermjs/xterm.js/network/dependents)
@@ -21,7 +21,7 @@ describe('AttachAddon', () => {
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
args: [`--window-size=${width},${height}`]
args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "xterm-addon-fit",
"version": "0.2.1",
"version": "0.3.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
+119
View File
@@ -0,0 +1,119 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import * as puppeteer from 'puppeteer';
import { assert } from 'chai';
import { ITerminalOptions } from 'xterm';
const APP = 'http://127.0.0.1:3000/test';
let browser: puppeteer.Browser;
let page: puppeteer.Page;
const width = 1024;
const height = 768;
describe('FitAddon', () => {
before(async function(): Promise<any> {
this.timeout(20000);
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();
});
after(async () => {
await browser.close();
});
it('no terminal', async function(): Promise<any> {
await page.evaluate(`window.fit = new FitAddon();`);
assert.equal(await page.evaluate(`window.fit.proposeDimensions()`), undefined);
});
describe('proposeDimensions', () => {
afterEach(async () => {
return unloadFit();
});
it('default', async function(): Promise<any> {
await loadFit();
assert.deepEqual(await page.evaluate(`window.fit.proposeDimensions()`), {
cols: 87,
rows: 26
});
});
it('width', async function(): Promise<any> {
await loadFit(1008);
assert.deepEqual(await page.evaluate(`window.fit.proposeDimensions()`), {
cols: 110,
rows: 26
});
});
it('small', async function(): Promise<any> {
await loadFit(1, 1);
assert.deepEqual(await page.evaluate(`window.fit.proposeDimensions()`), {
cols: 2,
rows: 1
});
});
});
describe('fit', () => {
afterEach(async () => {
return unloadFit();
});
it('default', async function(): Promise<any> {
await loadFit();
await page.evaluate(`window.fit.fit()`);
assert.equal(await page.evaluate(`window.term.cols`), 87);
assert.equal(await page.evaluate(`window.term.rows`), 26);
});
it('width', async function(): Promise<any> {
await loadFit(1008);
await page.evaluate(`window.fit.fit()`);
assert.equal(await page.evaluate(`window.term.cols`), 110);
assert.equal(await page.evaluate(`window.term.rows`), 26);
});
it('small', async function(): Promise<any> {
await loadFit(1, 1);
await page.evaluate(`window.fit.fit()`);
assert.equal(await page.evaluate(`window.term.cols`), 2);
assert.equal(await page.evaluate(`window.term.rows`), 1);
});
});
});
async function loadFit(width: number = 800, height: number = 450): Promise<void> {
await page.evaluate(`
window.fit = new FitAddon();
window.term.loadAddon(window.fit);
document.querySelector('#terminal-container').style.width='${width}px';
document.querySelector('#terminal-container').style.height='${height}px';
`);
}
async function unloadFit(): Promise<void> {
await page.evaluate(`window.fit.dispose();`);
}
async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
await page.evaluate(`window.term = new Terminal(${JSON.stringify(options)})`);
await page.evaluate(`window.term.open(document.querySelector('#terminal-container'))`);
if (options.rendererType === 'dom') {
await page.waitForSelector('.xterm-rows');
} else {
await page.waitForSelector('.xterm-text-layer');
}
}
+6 -3
View File
@@ -17,6 +17,9 @@ interface ITerminalDimensions {
cols: number;
}
const MINIMUM_COLS = 2;
const MINIMUM_ROWS = 1;
export class FitAddon implements ITerminalAddon {
private _terminal: Terminal | undefined;
@@ -49,7 +52,7 @@ export class FitAddon implements ITerminalAddon {
return undefined;
}
if (!this._terminal.element.parentElement) {
if (!this._terminal.element || !this._terminal.element.parentElement) {
return undefined;
}
@@ -71,8 +74,8 @@ export class FitAddon implements ITerminalAddon {
const availableHeight = parentElementHeight - elementPaddingVer;
const availableWidth = parentElementWidth - elementPaddingHor - core.viewport.scrollBarWidth;
const geometry = {
cols: Math.floor(availableWidth / core._renderService.dimensions.actualCellWidth),
rows: Math.floor(availableHeight / core._renderService.dimensions.actualCellHeight)
cols: Math.max(MINIMUM_COLS, Math.floor(availableWidth / core._renderService.dimensions.actualCellWidth)),
rows: Math.max(MINIMUM_ROWS, Math.floor(availableHeight / core._renderService.dimensions.actualCellHeight))
};
return geometry;
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "xterm-addon-search",
"version": "0.2.1",
"version": "0.3.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
@@ -15,13 +15,13 @@ const width = 800;
const height = 600;
describe('Search Tests', function (): void {
this.timeout(200000);
this.timeout(20000);
before(async function (): Promise<any> {
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
args: [`--window-size=${width},${height}`]
args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
@@ -98,6 +98,14 @@ describe('Search Tests', function (): void {
await page.evaluate(`window.search.findNext('[A-Z]+', {regex: true, caseSensitive: true})`);
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'ABCD');
});
it('Search for single result twice should not unselect it', async () => {
await writeSync('abc def');
assert.deepEqual(await page.evaluate(`window.search.findNext('abc')`), true);
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc');
assert.deepEqual(await page.evaluate(`window.search.findNext('abc')`), true);
assert.deepEqual(await page.evaluate(`window.term.getSelection()`), 'abc');
});
});
async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
+13 -6
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { Terminal, IDisposable, ITerminalAddon } from 'xterm';
import { Terminal, IDisposable, ITerminalAddon, ISelectionPosition } from 'xterm';
export interface ISearchOptions {
regex?: boolean;
@@ -59,12 +59,12 @@ export class SearchAddon implements ITerminalAddon {
let startCol = 0;
let startRow = 0;
let currentSelection: ISelectionPosition | undefined;
if (this._terminal.hasSelection()) {
const incremental = searchOptions ? searchOptions.incremental : false;
// Start from the selection end if there is a selection
// For incremental search, use existing row
const currentSelection = this._terminal.getSelectionPosition()!;
currentSelection = this._terminal.getSelectionPosition()!;
startRow = incremental ? currentSelection.startRow : currentSelection.endRow;
startCol = incremental ? currentSelection.startColumn : currentSelection.endColumn;
}
@@ -97,6 +97,9 @@ export class SearchAddon implements ITerminalAddon {
}
}
// If there is only one result, return true.
if (!result && currentSelection) return true;
// Set selection and scroll if a result was found
return this._selectResult(result);
}
@@ -121,10 +124,11 @@ export class SearchAddon implements ITerminalAddon {
const isReverseSearch = true;
let startRow = this._terminal.buffer.baseY + this._terminal.rows;
let startCol = this._terminal.cols;
let result: ISearchResult | undefined = undefined;
let result: ISearchResult | undefined;
const incremental = searchOptions ? searchOptions.incremental : false;
let currentSelection: ISelectionPosition | undefined;
if (this._terminal.hasSelection()) {
const currentSelection = this._terminal.getSelectionPosition()!;
currentSelection = this._terminal.getSelectionPosition()!;
// Start from selection start if there is a selection
startRow = currentSelection.startRow;
startCol = currentSelection.startColumn;
@@ -161,6 +165,9 @@ export class SearchAddon implements ITerminalAddon {
}
}
// If there is only one result, return true.
if (!result && currentSelection) return true;
// Set selection and scroll if a result was found
return this._selectResult(result);
}
@@ -344,7 +351,7 @@ export class SearchAddon implements ITerminalAddon {
}
terminal.select(result.col, result.row, result.term.length);
// If it is not in the viewport then we scroll else it just gets selected
if (result.row > (terminal.buffer.viewportY + terminal.rows) || result.row < terminal.buffer.viewportY) {
if (result.row >= (terminal.buffer.viewportY + terminal.rows) || result.row < terminal.buffer.viewportY) {
let scroll = result.row - terminal.buffer.viewportY;
scroll = scroll - Math.floor(terminal.rows / 2);
terminal.scrollLines(scroll);
@@ -20,7 +20,7 @@ describe('WebLinksAddon', () => {
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
args: [`--window-size=${width},${height}`]
args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "xterm-addon-webgl",
"version": "0.2.1",
"version": "0.3.0",
"author": {
"name": "The xterm.js authors",
"url": "https://xtermjs.org/"
@@ -105,9 +105,6 @@ export class GlyphRenderer {
const gl = this._gl;
const program = throwIfFalsy(createProgram(gl, vertexShaderSource, fragmentShaderSource));
if (program === undefined) {
throw new Error('Could not create WebGL program');
}
this._program = program;
// Uniform locations
@@ -22,7 +22,7 @@ describe('WebGL Renderer Integration Tests', function(): void {
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
args: [`--window-size=${width},${height}`]
args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
@@ -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, CHAR_DATA_ATTR_INDEX, NULL_CELL_CODE } from 'common/buffer/Constants';
import { DEFAULT_COLOR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_CODE_INDEX, NULL_CELL_CODE } from 'common/buffer/Constants';
import { Terminal } from 'xterm';
import { getLuminance } from './ColorUtils';
import { IRenderLayer } from './renderLayer/Types';
@@ -92,10 +92,10 @@ export class CursorRenderLayer extends BaseRenderLayer {
if (this._cursorBlinkStateManager) {
this._cursorBlinkStateManager.dispose();
}
// 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);
}
// 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);
}
public onCursorMove(terminal: Terminal): void {
@@ -139,12 +139,17 @@ export class CursorRenderLayer extends BaseRenderLayer {
this._clearCursor();
this._ctx.save();
this._ctx.fillStyle = this._colors.cursor.css;
this._renderBlurCursor(terminal, terminal.buffer.cursorX, viewportRelativeCursorY, this._cell);
const cursorStyle = terminal.getOption('cursorStyle');
if (cursorStyle && cursorStyle !== 'block') {
this._cursorRenderers[cursorStyle](terminal, terminal.buffer.cursorX, viewportRelativeCursorY, this._cell);
} else {
this._renderBlurCursor(terminal, terminal.buffer.cursorX, viewportRelativeCursorY, this._cell);
}
this._ctx.restore();
this._state.x = terminal.buffer.cursorX;
this._state.y = viewportRelativeCursorY;
this._state.isFocused = false;
this._state.style = terminal.getOption('cursorStyle');
this._state.style = cursorStyle;
this._state.width = this._cell.getWidth();
return;
}
+6 -6
View File
@@ -20,7 +20,7 @@ jobs:
yarn
displayName: 'Install dependencies and build'
- script: |
yarn test-unit
yarn test-unit --forbid-only
displayName: 'Unit tests'
- script: |
yarn lint
@@ -38,7 +38,7 @@ jobs:
yarn
displayName: 'Install dependencies and build'
- script: |
yarn test-unit
yarn test-unit --forbid-only
displayName: 'Unit tests'
- script: |
yarn lint
@@ -56,7 +56,7 @@ jobs:
yarn
displayName: 'Install dependencies and build'
- script: |
yarn test-unit
yarn test-unit --forbid-only
displayName: 'Unit tests'
- script: |
yarn lint
@@ -80,7 +80,7 @@ jobs:
- script: |
yarn start &
sleep 10
yarn test-api --headless
yarn test-api --headless --forbid-only
displayName: 'Linux Integration tests'
- job: macOS_IntegrationTests
@@ -97,7 +97,7 @@ jobs:
- script: |
yarn start &
sleep 10
yarn test-api --headless
yarn test-api --headless --forbid-only
displayName: 'MacOS Integration tests'
- job: Release
@@ -107,7 +107,7 @@ jobs:
- Windows
- Linux_IntegrationTests
- macOS_IntegrationTests
condition: and(succeeded(), or(eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['Build.SourceBranch'], 'refs/heads/release/*')))
condition: and(succeeded(), or(eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['FORCE_RELEASE'], 'true')))
pool:
vmImage: 'ubuntu-16.04'
steps:
+19 -1
View File
@@ -5,6 +5,7 @@
const cp = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');
// Setup auth
@@ -18,8 +19,9 @@ if (isDryRun) {
const changedFiles = getChangedFilesInCommit('HEAD');
// Publish xterm if any files were changed outside of the addons directory
let isStableRelease = false;
if (changedFiles.some(e => e.search(/^addons\//) === -1)) {
checkAndPublishPackage(path.resolve(__dirname, '..'));
isStableRelease = checkAndPublishPackage(path.resolve(__dirname, '..'));
}
// Publish addons if any files were changed inside of the addon
@@ -39,6 +41,11 @@ addonPackageDirs.forEach(p => {
}
});
// Publish website if it's a stable release
if (isStableRelease) {
updateWebsite();
}
function checkAndPublishPackage(packageDir) {
const packageJson = require(path.join(packageDir, 'package.json'));
@@ -76,6 +83,8 @@ function checkAndPublishPackage(packageDir) {
}
console.groupEnd();
return isStableRelease;
}
function getNextBetaVersion(packageJson) {
@@ -115,3 +124,12 @@ function getChangedFilesInCommit(commit) {
const changedFiles = output.split('\n').filter(e => e.length > 0);
return changedFiles;
}
function updateWebsite() {
console.log('Updating website');
const cwd = fs.mkdtempSync(path.join(os.tmpdir(), 'website-'));
const packageJson = require(path.join(path.resolve(__dirname, '..'), 'package.json'));
if (!isDryRun) {
cp.spawnSync('sh', [path.join(__dirname, 'update-website.sh'), packageJson.version], { cwd, stdio: [process.stdin, process.stdout, process.stderr] });
}
}
+12 -5
View File
@@ -15,15 +15,22 @@ let testFiles = [
'./out/**/*test.js'
];
// ability to inject particular test files via
// yarn test [testFileA testFileB ...]
let flagArgs = [];
if (process.argv.length > 2) {
testFiles = process.argv.slice(2);
const args = process.argv.slice(2);
flagArgs = args.filter(e => e.startsWith('--'));
// ability to inject particular test files via
// yarn test [testFileA testFileB ...]
files = args.filter(e => !e.startsWith('--'));
if (files.length) {
testFiles = files;
}
}
const run = cp.spawnSync(
path.resolve(__dirname, '../node_modules/.bin/mocha'),
testFiles,
[...testFiles, ...flagArgs],
{
cwd: path.resolve(__dirname, '..'),
env,
@@ -31,4 +38,4 @@ const run = cp.spawnSync(
}
);
process.exit(run.status);
process.exit(run.status);
+41
View File
@@ -0,0 +1,41 @@
#!/bin/sh
# Name the arguments
VERSION=$1
# Clone docs repo and update the documentation
git clone https://github.com/xtermjs/xtermjs.org
cd xtermjs.org
yarn
./bin/update-docs
# Add changes to index and only proceed if there are changes to commit
touch test-file
git add .
if ! git diff-index --quiet HEAD --; then
# Delete the upstream branch if it exists for some reason
export BRANCH_NAME=update-$VERSION
git branch -D $BRANCH_NAME || true
git push origin :$BRANCH_NAME || true
# Create commit and push it to update-x.y.z
git checkout -b $BRANCH_NAME
git config --global user.name Daniel Imms
git config --global user.email tyriar@tyriar.com
git commit -m 'Update docs for v$VERSION'
git push --set-upstream origin update-4.2.0
git push -f
# Create a PR in the GitHub repo
curl \
-H "Authorization: token $GITHUB_TOKEN" \
-X POST \
-d "{\"title\":\"Update docs for v$VERSION\",\"base\":\"master\",\"head\":\"xtermjs:$BRANCH_NAME\"}" \
https://api.github.com/repos/xtermjs/xtermjs.org/pulls
else
echo "No changes to commit"
fi
+3 -2
View File
@@ -221,6 +221,7 @@ function initOptions(term: TerminalType): void {
bellSound: null,
bellStyle: ['none', 'sound'],
cursorStyle: ['block', 'underline', 'bar'],
fastScrollModifier: ['alt', 'ctrl', 'shift', undefined],
fontFamily: null,
fontWeight: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
@@ -253,7 +254,7 @@ function initOptions(term: TerminalType): void {
});
html += '</div><div class="option-group">';
numberOptions.forEach(o => {
html += `<div class="option"><label>${o} <input id="opt-${o}" type="number" value="${term.getOption(o)}" step="${o === 'lineHeight' ? '0.1' : '1'}"/></label></div>`;
html += `<div class="option"><label>${o} <input id="opt-${o}" type="number" value="${term.getOption(o)}" step="${o === 'lineHeight' || o === 'scrollSensitivity' ? '0.1' : '1'}"/></label></div>`;
});
html += '</div><div class="option-group">';
Object.keys(stringOptions).forEach(o => {
@@ -282,7 +283,7 @@ function initOptions(term: TerminalType): void {
console.log('change', o, input.value);
if (o === 'cols' || o === 'rows') {
updateTerminalSize();
} else if (o === 'lineHeight') {
} else if (o === 'lineHeight' || o === 'scrollSensitivity') {
term.setOption(o, parseFloat(input.value));
updateTerminalSize();
} else {
+8 -6
View File
@@ -14,24 +14,26 @@ function startServer() {
logs = {};
app.use('/xterm.css', express.static(__dirname + '/../css/xterm.css'));
app.get('/logo.png', (req, res) => res.sendFile(__dirname + '/logo.png'));
app.get('/logo.png', (req, res) => { // lgtm [js/missing-rate-limiting]
res.sendFile(__dirname + '/logo.png');
});
app.get('/', function(req, res){
app.get('/', (req, res) => { // lgtm [js/missing-rate-limiting]
res.sendFile(__dirname + '/index.html');
});
app.get('/test', function(req, res){
app.get('/test', (req, res) => { // lgtm [js/missing-rate-limiting]
res.sendFile(__dirname + '/test.html');
});
app.get('/style.css', function(req, res){
app.get('/style.css', (req, res) => { // lgtm [js/missing-rate-limiting]
res.sendFile(__dirname + '/style.css');
});
app.use('/dist', express.static(__dirname + '/dist'));
app.use('/src', express.static(__dirname + '/src'));
app.post('/terminals', function (req, res) {
app.post('/terminals', (req, res) => {
const env = Object.assign({}, process.env);
env['COLORTERM'] = 'truecolor';
var cols = parseInt(req.query.cols),
@@ -55,7 +57,7 @@ function startServer() {
res.end();
});
app.post('/terminals/:pid/size', function (req, res) {
app.post('/terminals/:pid/size', (req, res) => {
var pid = parseInt(req.params.pid),
cols = parseInt(req.query.cols),
rows = parseInt(req.query.rows),
@@ -0,0 +1,25 @@
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB[5 A[5 A[5 A
@@ -0,0 +1,25 @@
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
@@ -0,0 +1,25 @@
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB[5 @[5 @[5 @
@@ -0,0 +1,25 @@
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
@@ -0,0 +1,25 @@
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB[5'}[5'}[5'}
@@ -0,0 +1,25 @@
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
@@ -0,0 +1,25 @@
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB[5'~[5'~[5'~
@@ -0,0 +1,25 @@
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "xterm",
"description": "Full xterm terminal, in your browser",
"version": "4.1.0",
"version": "4.2.0",
"main": "lib/xterm.js",
"style": "css/xterm.css",
"types": "typings/xterm.d.ts",
+82 -7
View File
@@ -25,6 +25,14 @@ function getCursor(term: TestTerminal): number[] {
];
}
function getLines(term: TestTerminal, limit: number = term.rows): string[] {
const res: string[] = [];
for (let i = 0; i < limit; ++i) {
res.push(term.buffer.lines.get(i).translateToString(true));
}
return res;
}
describe('InputHandler', () => {
describe('save and restore cursor', () => {
const terminal = new MockInputHandlingTerminal();
@@ -1125,13 +1133,6 @@ describe('InputHandler', () => {
beforeEach(() => {
term = new TestTerminal({cols: 10, rows: 10});
});
function getLines(term: TestTerminal, limit: number = term.rows): string[] {
const res: string[] = [];
for (let i = 0; i < limit; ++i) {
res.push(term.buffer.lines.get(i).translateToString(true));
}
return res;
}
it('scrollUp', () => {
term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[2;4r\x1b[2Sm');
assert.deepEqual(getLines(term), ['m', '3', '', '', '4', '5', '6', '7', '8', '9']);
@@ -1191,4 +1192,78 @@ describe('InputHandler', () => {
assert.deepEqual(getLines(term), ['0', '1', 'n', 'm', '', '', '6', '7', '8', '9']);
});
});
describe('SL/SR/DECIC/DECDC', () => {
let term: TestTerminal;
beforeEach(() => {
term = new TestTerminal({cols: 5, rows: 5, scrollback: 1});
});
it('SL (scrollLeft)', () => {
term.writeSync('12345'.repeat(6));
term.writeSync('\x1b[ @');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '2345', '2345', '2345', '2345', '2345']);
term.writeSync('\x1b[0 @');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '345', '345', '345', '345', '345']);
term.writeSync('\x1b[2 @');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '5', '5', '5', '5', '5']);
});
it('SR (scrollRight)', () => {
term.writeSync('12345'.repeat(6));
term.writeSync('\x1b[ A');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 1234', ' 1234', ' 1234', ' 1234', ' 1234']);
term.writeSync('\x1b[0 A');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 123', ' 123', ' 123', ' 123', ' 123']);
term.writeSync('\x1b[2 A');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 1', ' 1', ' 1', ' 1', ' 1']);
});
it('insertColumns (DECIC)', () => {
term.writeSync('12345'.repeat(6));
term.writeSync('\x1b[3;3H');
term.writeSync('\x1b[\'}');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']);
term.reset();
term.writeSync('12345'.repeat(6));
term.writeSync('\x1b[3;3H');
term.writeSync('\x1b[1\'}');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']);
term.reset();
term.writeSync('12345'.repeat(6));
term.writeSync('\x1b[3;3H');
term.writeSync('\x1b[2\'}');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 3', '12 3', '12 3', '12 3', '12 3']);
});
it('deleteColumns (DECDC)', () => {
term.writeSync('12345'.repeat(6));
term.writeSync('\x1b[3;3H');
term.writeSync('\x1b[\'~');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '1245', '1245', '1245', '1245', '1245']);
term.reset();
term.writeSync('12345'.repeat(6));
term.writeSync('\x1b[3;3H');
term.writeSync('\x1b[1\'~');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '1245', '1245', '1245', '1245', '1245']);
term.reset();
term.writeSync('12345'.repeat(6));
term.writeSync('\x1b[3;3H');
term.writeSync('\x1b[2\'~');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '125', '125', '125', '125', '125']);
});
});
it('should parse big chunks in smaller subchunks', () => {
// max single chunk size is hardcoded as 131072
const calls: any[] = [];
const term = new TestTerminal({cols: 10, rows: 10});
(term as any)._inputHandler._parser.parse = (data: Uint32Array, length: number) => {
calls.push([data.length, length]);
};
term.writeSync('12345');
term.writeSync('a'.repeat(10000));
term.writeSync('a'.repeat(200000));
term.writeSync('a'.repeat(300000));
assert.deepEqual(calls, [
[4096, 5],
[10000, 10000],
[131072, 131072], [131072, 200000 - 131072],
[131072, 131072], [131072, 131072], [131072, 300000 - 131072 - 131072]
]);
});
});
+135 -24
View File
@@ -28,6 +28,11 @@ import { DcsHandler } from 'common/parser/DcsParser';
*/
const GLEVEL: {[key: string]: number} = {'(': 0, ')': 1, '*': 2, '+': 3, '-': 1, '.': 2};
/**
* Max length of the UTF32 input buffer. Real memory consumption is 4 times higher.
*/
const MAX_PARSEBUFFER_LENGTH = 131072;
/**
* DCS subparser implementations
@@ -175,7 +180,9 @@ export class InputHandler extends Disposable implements IInputHandler {
* CSI handler
*/
this._parser.setCsiHandler({final: '@'}, params => this.insertChars(params));
this._parser.setCsiHandler({intermediates: ' ', final: '@'}, params => this.scrollLeft(params));
this._parser.setCsiHandler({final: 'A'}, params => this.cursorUp(params));
this._parser.setCsiHandler({intermediates: ' ', final: 'A'}, params => this.scrollRight(params));
this._parser.setCsiHandler({final: 'B'}, params => this.cursorDown(params));
this._parser.setCsiHandler({final: 'C'}, params => this.cursorForward(params));
this._parser.setCsiHandler({final: 'D'}, params => this.cursorBackward(params));
@@ -216,6 +223,8 @@ export class InputHandler extends Disposable implements IInputHandler {
this._parser.setCsiHandler({final: 'r'}, params => this.setScrollRegion(params));
this._parser.setCsiHandler({final: 's'}, params => this.saveCursor(params));
this._parser.setCsiHandler({final: 'u'}, params => this.restoreCursor(params));
this._parser.setCsiHandler({intermediates: '\'', final: '}'}, params => this.insertColumns(params));
this._parser.setCsiHandler({intermediates: '\'', final: '~'}, params => this.deleteColumns(params));
/**
* execute handler
@@ -331,14 +340,28 @@ export class InputHandler extends Disposable implements IInputHandler {
this._logService.debug('parsing data', data);
// resize input buffer if needed
if (this._parseBuffer.length < data.length) {
this._parseBuffer = new Uint32Array(data.length);
if (this._parseBuffer.length < MAX_PARSEBUFFER_LENGTH) {
this._parseBuffer = new Uint32Array(Math.min(data.length, MAX_PARSEBUFFER_LENGTH));
}
}
this._parser.parse(this._parseBuffer,
(typeof data === 'string')
// process big data in smaller chunks
if (data.length > MAX_PARSEBUFFER_LENGTH) {
for (let i = 0; i < data.length; i += MAX_PARSEBUFFER_LENGTH) {
const end = i + MAX_PARSEBUFFER_LENGTH < data.length ? i + MAX_PARSEBUFFER_LENGTH : data.length;
const len = (typeof data === 'string')
? this._stringDecoder.decode(data.substring(i, end), this._parseBuffer)
: this._utf8Decoder.decode(data.subarray(i, end), this._parseBuffer);
this._parser.parse(this._parseBuffer, len);
}
} else {
const len = (typeof data === 'string')
? this._stringDecoder.decode(data, this._parseBuffer)
: this._utf8Decoder.decode(data, this._parseBuffer)
);
: this._utf8Decoder.decode(data, this._parseBuffer);
this._parser.parse(this._parseBuffer, len);
}
buffer = this._bufferService.buffer;
if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) {
@@ -1033,18 +1056,108 @@ export class InputHandler extends Disposable implements IInputHandler {
* CSI Ps T Scroll down Ps lines (default = 1) (SD).
*/
public scrollDown(params: IParams): void {
if (params.length < 2) {
let param = params.params[0] || 1;
let param = params.params[0] || 1;
// make buffer local for faster access
const buffer = this._bufferService.buffer;
// make buffer local for faster access
const buffer = this._bufferService.buffer;
while (param--) {
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1);
buffer.lines.splice(buffer.ybase + buffer.scrollTop, 0, buffer.getBlankLine(this._terminal.eraseAttrData()));
}
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
while (param--) {
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1);
buffer.lines.splice(buffer.ybase + buffer.scrollTop, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
}
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
}
/**
* CSI Ps SP @ Scroll left Ps columns (default = 1) (SL) ECMA-48
*
* Notation: (Pn)
* Representation: CSI Pn 02/00 04/00
* Parameter default value: Pn = 1
* SL causes the data in the presentation component to be moved by n character positions
* if the line orientation is horizontal, or by n line positions if the line orientation
* is vertical, such that the data appear to move to the left; where n equals the value of Pn.
* The active presentation position is not affected by this control function.
*
* Supported:
* - always left shift (no line orientation setting respected)
*/
public scrollLeft(params: IParams): void {
const buffer = this._bufferService.buffer;
if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
return;
}
const param = params.params[0] || 1;
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
const line = buffer.lines.get(buffer.ybase + y);
line.deleteCells(0, param, buffer.getNullCell(this._terminal.eraseAttrData()));
line.isWrapped = false;
}
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
}
/**
* CSI Ps SP A Scroll right Ps columns (default = 1) (SR) ECMA-48
*
* Notation: (Pn)
* Representation: CSI Pn 02/00 04/01
* Parameter default value: Pn = 1
* SR causes the data in the presentation component to be moved by n character positions
* if the line orientation is horizontal, or by n line positions if the line orientation
* is vertical, such that the data appear to move to the right; where n equals the value of Pn.
* The active presentation position is not affected by this control function.
*
* Supported:
* - always right shift (no line orientation setting respected)
*/
public scrollRight(params: IParams): void {
const buffer = this._bufferService.buffer;
if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
return;
}
const param = params.params[0] || 1;
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
const line = buffer.lines.get(buffer.ybase + y);
line.insertCells(0, param, buffer.getNullCell(this._terminal.eraseAttrData()));
line.isWrapped = false;
}
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
}
/**
* CSI Pm ' }
* Insert Ps Column(s) (default = 1) (DECIC), VT420 and up.
*/
public insertColumns(params: IParams): void {
const buffer = this._bufferService.buffer;
if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
return;
}
const param = params.params[0] || 1;
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
const line = this._bufferService.buffer.lines.get(buffer.ybase + y);
line.insertCells(buffer.x, param, buffer.getNullCell(this._terminal.eraseAttrData()));
line.isWrapped = false;
}
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
}
/**
* CSI Pm ' ~
* Delete Ps Column(s) (default = 1) (DECDC), VT420 and up.
*/
public deleteColumns(params: IParams): void {
const buffer = this._bufferService.buffer;
if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
return;
}
const param = params.params[0] || 1;
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
const line = buffer.lines.get(buffer.ybase + y);
line.deleteCells(buffer.x, param, buffer.getNullCell(this._terminal.eraseAttrData()));
line.isWrapped = false;
}
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
}
/**
@@ -1321,16 +1434,14 @@ export class InputHandler extends Disposable implements IInputHandler {
// focusout: ^[[O
this._terminal.sendFocus = true;
break;
case 1005: // utf8 ext mode mouse
// for wide terminals
// simply encodes large values as utf8 characters
this._coreMouseService.activeEncoding = 'UTF8';
case 1005: // utf8 ext mode mouse - removed in #2507
this._logService.debug('DECSET 1005 not supported (see #2507)');
break;
case 1006: // sgr ext mode mouse
this._coreMouseService.activeEncoding = 'SGR';
break;
case 1015: // urxvt ext mode mouse
this._coreMouseService.activeEncoding = 'URXVT';
case 1015: // urxvt ext mode mouse - removed in #2507
this._logService.debug('DECSET 1015 not supported (see #2507)');
break;
case 25: // show cursor
this._terminal.cursorHidden = false;
@@ -1494,14 +1605,14 @@ export class InputHandler extends Disposable implements IInputHandler {
case 1004: // send focusin/focusout events
this._terminal.sendFocus = false;
break;
case 1005: // utf8 ext mode mouse
this._coreMouseService.activeEncoding = 'DEFAULT';
case 1005: // utf8 ext mode mouse - removed in #2507
this._logService.debug('DECRST 1005 not supported (see #2507)');
break;
case 1006: // sgr ext mode mouse
this._coreMouseService.activeEncoding = 'DEFAULT';
break;
case 1015: // urxvt ext mode mouse
this._coreMouseService.activeEncoding = 'DEFAULT';
case 1015: // urxvt ext mode mouse - removed in #2507
this._logService.debug('DECRST 1015 not supported (see #2507)');
break;
case 25: // hide cursor
this._terminal.cursorHidden = true;
+9 -13
View File
@@ -76,7 +76,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
/**
* The HTMLElement that the terminal is created in, set by Terminal.open.
*/
private _parent: HTMLElement;
private _parent: HTMLElement | null;
private _document: Document;
private _viewportScrollArea: HTMLElement;
private _viewportElement: HTMLElement;
@@ -383,18 +383,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
case 'theme':
this._setTheme(this.optionsService.options.theme);
break;
case 'scrollback':
const newBufferLength = this.rows + this.optionsService.options.scrollback;
if (this.buffer.lines.length > newBufferLength) {
const amountToTrim = this.buffer.lines.length - newBufferLength;
const needsRefresh = (this.buffer.ydisp - amountToTrim < 0);
this.buffer.lines.trimStart(amountToTrim);
this.buffer.ybase = Math.max(this.buffer.ybase - amountToTrim, 0);
this.buffer.ydisp = Math.max(this.buffer.ydisp - amountToTrim, 0);
if (needsRefresh) {
this.refresh(0, this.rows - 1);
}
}
case 'windowsMode':
if (this.optionsService.options.windowsMode) {
if (!this._windowsMode) {
@@ -520,6 +508,10 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
throw new Error('Terminal requires a parent element.');
}
if (!document.body.contains(parent)) {
this._logService.warn('Terminal.open was called on an element that was not attached to the DOM');
}
this._document = this._parent.ownerDocument;
// Create main element container
@@ -1469,6 +1461,10 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._charSizeService.measure();
}
// Sync the scroll area to make sure scroll events don't fire and scroll the viewport to an
// invalid location
this.viewport.syncScrollArea(true);
this.refresh(0, this.rows - 1);
this._onResize.fire({ cols: x, rows: y });
}
+6 -2
View File
@@ -85,7 +85,9 @@ export interface IInputHandler {
/** C0 SI */ shiftIn(): void;
/** CSI @ */ insertChars(params: IParams): void;
/** CSI SP @ */ scrollLeft(params: IParams): void;
/** CSI A */ cursorUp(params: IParams): void;
/** CSI SP A */ scrollRight(params: IParams): void;
/** CSI B */ cursorDown(params: IParams): void;
/** CSI C */ cursorForward(params: IParams): void;
/** CSI D */ cursorBackward(params: IParams): void;
@@ -121,6 +123,8 @@ export interface IInputHandler {
/** CSI r */ setScrollRegion(params: IParams, collect?: string): void;
/** CSI s */ saveCursor(params: IParams): void;
/** CSI u */ restoreCursor(params: IParams): void;
/** CSI ' } */ insertColumns(params: IParams): void;
/** CSI ' ~ */ deleteColumns(params: IParams): void;
/** OSC 0
OSC 2 */ setTitle(data: string): void;
/** ESC E */ nextLine(): void;
@@ -173,7 +177,7 @@ export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAcc
// Portions of the public API that are required by the internal Terminal
export interface IPublicTerminal extends IDisposable {
textarea: HTMLTextAreaElement;
textarea: HTMLTextAreaElement | undefined;
rows: number;
cols: number;
buffer: IBuffer;
@@ -226,7 +230,7 @@ export interface IBufferAccessor {
}
export interface IElementAccessor {
readonly element: HTMLElement;
readonly element: HTMLElement | undefined;
}
export interface ILinkifierAccessor {
+3 -1
View File
@@ -306,7 +306,9 @@ export class Linkifier implements ILinkifier {
e => {
this._onLinkTooltip.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg));
if (matcher.hoverTooltipCallback) {
matcher.hoverTooltipCallback(e, uri);
// Note that IViewportRange use 1-based coordinates to align with escape sequences such
// as CUP which use 1,1 as the default for row/col
matcher.hoverTooltipCallback(e, uri, { start: { x: x1, y: y1 }, end: { x: x2, y: y2 } });
}
},
() => {
+14 -3
View File
@@ -35,7 +35,7 @@ export interface IPartialColorSet {
export interface IViewport extends IDisposable {
scrollBarWidth: number;
syncScrollArea(): void;
syncScrollArea(immediate?: boolean): void;
getLinesScrolled(ev: WheelEvent): number;
onWheel(ev: WheelEvent): boolean;
onTouchStart(ev: TouchEvent): void;
@@ -43,14 +43,25 @@ export interface IViewport extends IDisposable {
onThemeChange(colors: IColorSet): void;
}
export interface IViewportRange {
start: IViewportRangePosition;
end: IViewportRangePosition;
}
export interface IViewportRangePosition {
x: number;
y: number;
}
export type LinkMatcherHandler = (event: MouseEvent, uri: string) => void;
export type LinkMatcherHoverTooltipCallback = (event: MouseEvent, uri: string, position: IViewportRange) => void;
export type LinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void;
export interface ILinkMatcher {
id: number;
regex: RegExp;
handler: LinkMatcherHandler;
hoverTooltipCallback?: LinkMatcherHandler;
hoverTooltipCallback?: LinkMatcherHoverTooltipCallback;
hoverLeaveCallback?: () => void;
matchIndex?: number;
validationCallback?: LinkMatcherValidationCallback;
@@ -96,7 +107,7 @@ export interface ILinkMatcherOptions {
/**
* A callback that fires when the mouse hovers over a link.
*/
tooltipCallback?: LinkMatcherHandler;
tooltipCallback?: LinkMatcherHoverTooltipCallback;
/**
* A callback that fires when the mouse leaves a link that was hovered.
*/
+30 -11
View File
@@ -7,7 +7,7 @@ import { Disposable } from 'common/Lifecycle';
import { addDisposableDomListener } from 'browser/Lifecycle';
import { IColorSet, IViewport } from 'browser/Types';
import { ICharSizeService, IRenderService } from 'browser/services/Services';
import { IBufferService } from 'common/services/Services';
import { IBufferService, IOptionsService } from 'common/services/Services';
const FALLBACK_SCROLL_BAR_WIDTH = 15;
@@ -37,6 +37,7 @@ export class Viewport extends Disposable implements IViewport {
private readonly _viewportElement: HTMLElement,
private readonly _scrollArea: HTMLElement,
@IBufferService private readonly _bufferService: IBufferService,
@IOptionsService private readonly _optionsService: IOptionsService,
@ICharSizeService private readonly _charSizeService: ICharSizeService,
@IRenderService private readonly _renderService: IRenderService
) {
@@ -60,7 +61,14 @@ export class Viewport extends Disposable implements IViewport {
* Refreshes row height, setting line-height, viewport height and scroll area height if
* necessary.
*/
private _refresh(): void {
private _refresh(immediate: boolean): void {
if (immediate) {
this._innerRefresh();
if (this._refreshAnimationFrame !== null) {
cancelAnimationFrame(this._refreshAnimationFrame);
}
return;
}
if (this._refreshAnimationFrame === null) {
this._refreshAnimationFrame = requestAnimationFrame(() => this._innerRefresh());
}
@@ -88,40 +96,39 @@ export class Viewport extends Disposable implements IViewport {
this._refreshAnimationFrame = null;
}
/**
* Updates dimensions and synchronizes the scroll area if necessary.
*/
public syncScrollArea(): void {
public syncScrollArea(immediate: boolean = false): void {
// If buffer height changed
if (this._lastRecordedBufferLength !== this._bufferService.buffer.lines.length) {
this._lastRecordedBufferLength = this._bufferService.buffer.lines.length;
this._refresh();
this._refresh(immediate);
return;
}
// If viewport height changed
if (this._lastRecordedViewportHeight !== this._renderService.dimensions.canvasHeight) {
this._refresh();
this._refresh(immediate);
return;
}
// If the buffer position doesn't match last scroll top
const newScrollTop = this._bufferService.buffer.ydisp * this._currentRowHeight;
if (this._lastScrollTop !== newScrollTop) {
this._refresh();
this._refresh(immediate);
return;
}
// If element's scroll top changed, this can happen when hiding the element
if (this._lastScrollTop !== this._viewportElement.scrollTop) {
this._refresh();
this._refresh(immediate);
return;
}
// If row height changed
if (this._renderService.dimensions.scaledCellHeight / window.devicePixelRatio !== this._currentRowHeight) {
this._refresh();
this._refresh(immediate);
return;
}
}
@@ -191,7 +198,7 @@ export class Viewport extends Disposable implements IViewport {
}
// Fallback to WheelEvent.DOM_DELTA_PIXEL
let amount = ev.deltaY;
let amount = this._applyScrollModifier(ev.deltaY, ev);
if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) {
amount *= this._currentRowHeight;
} else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
@@ -212,7 +219,7 @@ export class Viewport extends Disposable implements IViewport {
}
// Fallback to WheelEvent.DOM_DELTA_LINE
let amount = ev.deltaY;
let amount = this._applyScrollModifier(ev.deltaY, ev);
if (ev.deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
amount /= this._currentRowHeight + 0.0; // Prevent integer division
this._wheelPartialScroll += amount;
@@ -224,6 +231,18 @@ export class Viewport extends Disposable implements IViewport {
return amount;
}
private _applyScrollModifier(amount: number, ev: WheelEvent): number {
const modifier = this._optionsService.options.fastScrollModifier;
// Multiply the scroll speed when the modifier is down
if ((modifier === 'alt' && ev.altKey) ||
(modifier === 'ctrl' && ev.ctrlKey) ||
(modifier === 'shift' && ev.shiftKey)) {
return amount * this._optionsService.options.fastScrollSensitivity * this._optionsService.options.scrollSensitivity;
}
return amount * this._optionsService.options.scrollSensitivity;
}
/**
* Handles the touchstart event, recording the touch occurred.
* @param ev The touch event.
@@ -303,7 +303,6 @@ export class CharacterJoinerRegistry implements ICharacterJoinerRegistry {
// current range
ranges[i - 1][1] = Math.max(newRange[1], range[1]);
ranges.splice(i, 1);
inRange = false;
return ranges;
}
@@ -8,7 +8,7 @@ import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
import { AttributeData } from 'common/buffer/AttributeData';
import { NULL_CELL_CODE, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants';
import { CellData } from 'common/buffer/CellData';
import { ITerminalOptions, IOptionsService } from 'common/services/Services';
import { IOptionsService } from 'common/services/Services';
export const BOLD_CLASS = 'xterm-bold';
export const DIM_CLASS = 'xterm-dim';
+2 -2
View File
@@ -210,7 +210,7 @@ export class SelectionService implements ISelectionService {
for (let i = start[1] + 1; i <= end[1] - 1; i++) {
const bufferLine = buffer.lines.get(i);
const lineText = buffer.translateBufferLineToString(i, true);
if (bufferLine!.isWrapped) {
if (bufferLine && bufferLine.isWrapped) {
result[result.length - 1] += lineText;
} else {
result.push(lineText);
@@ -221,7 +221,7 @@ export class SelectionService implements ISelectionService {
if (start[1] !== end[1]) {
const bufferLine = buffer.lines.get(end[1]);
const lineText = buffer.translateBufferLineToString(end[1], true, 0, end[0]);
if (bufferLine!.isWrapped) {
if (bufferLine && bufferLine!.isWrapped) {
result[result.length - 1] += lineText;
} else {
result.push(lineText);
+18 -12
View File
@@ -167,19 +167,25 @@ export class Buffer implements IBuffer {
if (this._rows < newRows) {
for (let y = this._rows; y < newRows; y++) {
if (this.lines.length < newRows + this.ybase) {
if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) {
// There is room above the buffer and there are no empty elements below the line,
// scroll up
this.ybase--;
addToY++;
if (this.ydisp > 0) {
// Viewport is at the top of the buffer, must increase downwards
this.ydisp--;
}
} else {
// Add a blank line if there is no buffer left at the top to scroll to, or if there
// are blank lines after the cursor
if (this._optionsService.options.windowsMode) {
// Just add the new missing rows on Windows as conpty reprints the screen with it's
// view of the world. Once a line enters scrollback for conpty it remains there
this.lines.push(new BufferLine(newCols, nullCell));
} else {
if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) {
// There is room above the buffer and there are no empty elements below the line,
// scroll up
this.ybase--;
addToY++;
if (this.ydisp > 0) {
// Viewport is at the top of the buffer, must increase downwards
this.ydisp--;
}
} else {
// Add a blank line if there is no buffer left at the top to scroll to, or if there
// are blank lines after the cursor
this.lines.push(new BufferLine(newCols, nullCell));
}
}
}
}
+1 -1
View File
@@ -5,7 +5,7 @@
import { CharData, IBufferLine, ICellData } from 'common/Types';
import { stringFromCodePoint } from 'common/input/TextDecoder';
import { DEFAULT_COLOR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Content } from 'common/buffer/Constants';
import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Content } from 'common/buffer/Constants';
import { CellData } from 'common/buffer/CellData';
import { AttributeData } from 'common/buffer/AttributeData';
+1
View File
@@ -29,6 +29,7 @@ export class Marker extends Disposable implements IMarker {
return;
}
this.isDisposed = true;
this.line = -1;
// Emit before super.dispose such that dispose listeners get a change to react
this._onDispose.fire();
}
+12
View File
@@ -108,6 +108,12 @@ describe('Keyboard', () => {
it('should return \\x1b[5C for alt+right', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: false }).key, '\x1b[1;5C'); // CSI 5 C
});
it('should return \\x1b[5D for alt+up', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }, { isMac: false }).key, '\x1b[1;5A'); // CSI 5 D
});
it('should return \\x1b[5C for alt+down', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 40 }, { isMac: false }).key, '\x1b[1;5B'); // CSI 5 C
});
it('should return \\x1ba for alt+a', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: false }).key, '\x1ba');
});
@@ -120,6 +126,12 @@ describe('Keyboard', () => {
it('should return \\x1bf for alt+right', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: true }).key, '\x1bf'); // CSI 5 C
});
it('should return \\x1bb for alt+up', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }, { isMac: true }).key, '\x1b[1;3A'); // CSI 5 D
});
it('should return \\x1bf for alt+down', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 40 }, { isMac: true }).key, '\x1b[1;3B'); // CSI 5 C
});
it('should return undefined for alt+a', () => {
assert.strictEqual(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: true }).key, undefined), { isMac: true };
});
+6 -4
View File
@@ -122,7 +122,7 @@ export function evaluateKeyboardEvent(
// http://unix.stackexchange.com/a/108106
// macOS uses different escape sequences than linux
if (result.key === C0.ESC + '[1;3D') {
result.key = isMac ? C0.ESC + 'b' : C0.ESC + '[1;5D';
result.key = C0.ESC + (isMac ? 'b' : '[1;5D');
}
} else if (applicationCursorMode) {
result.key = C0.ESC + 'OD';
@@ -141,7 +141,7 @@ export function evaluateKeyboardEvent(
// http://unix.stackexchange.com/a/108106
// macOS uses different escape sequences than linux
if (result.key === C0.ESC + '[1;3C') {
result.key = isMac ? C0.ESC + 'f' : C0.ESC + '[1;5C';
result.key = C0.ESC + (isMac ? 'f' : '[1;5C');
}
} else if (applicationCursorMode) {
result.key = C0.ESC + 'OC';
@@ -158,7 +158,8 @@ export function evaluateKeyboardEvent(
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'A';
// HACK: Make Alt + up-arrow behave like Ctrl + up-arrow
// http://unix.stackexchange.com/a/108106
if (result.key === C0.ESC + '[1;3A') {
// macOS uses different escape sequences than linux
if (!isMac && result.key === C0.ESC + '[1;3A') {
result.key = C0.ESC + '[1;5A';
}
} else if (applicationCursorMode) {
@@ -176,7 +177,8 @@ export function evaluateKeyboardEvent(
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'B';
// HACK: Make Alt + down-arrow behave like Ctrl + down-arrow
// http://unix.stackexchange.com/a/108106
if (result.key === C0.ESC + '[1;3B') {
// macOS uses different escape sequences than linux
if (!isMac && result.key === C0.ESC + '[1;3B') {
result.key = C0.ESC + '[1;5B';
}
} else if (applicationCursorMode) {
+11
View File
@@ -7,6 +7,7 @@ import { assert } from 'chai';
import { StringToUtf32, stringFromCodePoint, Utf8ToUtf32, utf32ToString } from 'common/input/TextDecoder';
import { encode } from 'utf8';
// convert UTF32 codepoints to string
function toString(data: Uint32Array, length: number): string {
if ((String as any).fromCodePoint) {
@@ -214,6 +215,16 @@ describe('text encodings', () => {
}
assert(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€');
});
it('test break after 3 bytes - issue #2495', () => {
const decoder = new Utf8ToUtf32();
const target = new Uint32Array(5);
const utf8Data = fromByteString('\xf0\xa0\x9c\x8e');
let written = decoder.decode(utf8Data.slice(0, 3), target);
assert.equal(written, 0);
written = decoder.decode(utf8Data.slice(3), target);
assert.equal(written, 1);
assert(toString(target, written), '𠜎');
});
});
});
});
+1 -1
View File
@@ -194,7 +194,7 @@ export class Utf8ToUtf32 {
target[size++] = cp;
}
} else {
if (codepoint < 0x010000 || codepoint > 0x10FFFF) {
if (cp < 0x010000 || cp > 0x10FFFF) {
// illegal codepoint
} else {
target[size++] = cp;
+2 -18
View File
@@ -32,9 +32,9 @@ describe('CoreMouseService', () => {
const cms = new CoreMouseService(bufferService, coreService);
assert.deepEqual(Object.keys((cms as any)._protocols), ['NONE', 'X10', 'VT200', 'DRAG', 'ANY']);
});
it('default encodings - DEFAULT, UTF8, SGR, URXVT', () => {
it('default encodings - DEFAULT, SGR', () => {
const cms = new CoreMouseService(bufferService, coreService);
assert.deepEqual(Object.keys((cms as any)._encodings), ['DEFAULT', 'UTF8', 'SGR', 'URXVT']);
assert.deepEqual(Object.keys((cms as any)._encodings), ['DEFAULT', 'SGR']);
});
it('protocol/encoding setter, reset', () => {
const cms = new CoreMouseService(bufferService, coreService);
@@ -151,14 +151,6 @@ describe('CoreMouseService', () => {
}
}
});
it('UTF8 encoding', () => {
cms.activeProtocol = 'ANY';
cms.activeEncoding = 'UTF8';
for (let i = 0; i < bufferService.cols; ++i) {
assert.equal(cms.triggerMouseEvent({ col: i, row: 0, button: CoreMouseButton.LEFT, action: CoreMouseAction.DOWN }), true);
assert.deepEqual(toBytes(reports.pop()), [0x1b, 0x5b, 0x4d, 0x20, i + 33, 0x21]);
}
});
it('SGR encoding', () => {
cms.activeProtocol = 'ANY';
cms.activeEncoding = 'SGR';
@@ -167,14 +159,6 @@ describe('CoreMouseService', () => {
assert.deepEqual(reports.pop(), `\x1b[<0;${i + 1};1M`);
}
});
it('URXVT', () => {
cms.activeProtocol = 'ANY';
cms.activeEncoding = 'URXVT';
for (let i = 0; i < bufferService.cols; ++i) {
assert.equal(cms.triggerMouseEvent({ col: i, row: 0, button: CoreMouseButton.LEFT, action: CoreMouseAction.DOWN }), true);
assert.deepEqual(reports.pop(), `\x1b[32;${i + 1};1M`);
}
});
});
it('eventCodes with modifiers (DEFAULT encoding)', () => {
// TODO: implement AUX button tests
+1 -20
View File
@@ -132,17 +132,6 @@ const DEFAULT_ENCODINGS: {[key: string]: CoreMouseEncoding} = {
// FIXED: params = params.map(v => (v > 255) ? 0 : value);
return `\x1b[M${S(params[0])}${S(params[1])}${S(params[2])}`;
},
/**
* UTF8 - CSI M Pb Px Py
* Same as DEFAULT, but with optional 2-byte UTF8
* encoding for values > 223 (can encode up to 2015).
*/
UTF8: (e: ICoreMouseEvent) => {
let params = [eventCode(e, false) + 32, e.col + 32, e.row + 32];
// limit to 2-byte UTF8
params = params.map(v => (v > 2047) ? 0 : v);
return `\x1b[M${S(params[0])}${S(params[1])}${S(params[2])}`;
},
/**
* SGR - CSI < Pb ; Px ; Py M|m
* No encoding limitation.
@@ -151,14 +140,6 @@ const DEFAULT_ENCODINGS: {[key: string]: CoreMouseEncoding} = {
SGR: (e: ICoreMouseEvent) => {
const final = (e.action === CoreMouseAction.UP && e.button !== CoreMouseButton.WHEEL) ? 'm' : 'M';
return `\x1b[<${eventCode(e, true)};${e.col};${e.row}${final}`;
},
/**
* URXVT - CSI Pb ; Px ; Py M
* Same button encoding as default, decimal encoding for coords.
* Ambiguity with other sequences, should not be used.
*/
URXVT: (e: ICoreMouseEvent) => {
return `\x1b[${eventCode(e, false) + 32};${e.col};${e.row}M`;
}
};
@@ -167,7 +148,7 @@ const DEFAULT_ENCODINGS: {[key: string]: CoreMouseEncoding} = {
*
* Provides mouse tracking reports with different protocols and encodings.
* - protocols: NONE (default), X10, VT200, DRAG, ANY
* - encodings: DEFAULT, SGR, UTF8, URXVT
* - encodings: DEFAULT, SGR (UTF8, URXVT removed in #2507)
*
* Custom protocols/encodings can be added by `addProtocol` / `addEncoding`.
* To activate a protocol/encoding, set `activeProtocol` / `activeEncoding`.
+11 -2
View File
@@ -12,7 +12,7 @@ import { clone } from 'common/Clone';
// This sound is released under the Creative Commons Attribution 3.0 Unported
// (CC BY 3.0) license. It was created by 'altemark'. No modifications have been
// made, apart from the conversion to base64.
export const DEFAULT_BELL_SOUND = 'data:audio/wav;base64,UklGRigBAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQQBAADpAFgCwAMlBZoG/wdmCcoKRAypDQ8PbRDBEQQTOxRtFYcWlBePGIUZXhoiG88bcBz7HHIdzh0WHlMeZx51HmkeUx4WHs8dah0AHXwc3hs9G4saxRnyGBIYGBcQFv8U4RPAEoYRQBACD70NWwwHC6gJOwjWBloF7gOBAhABkf8b/qv8R/ve+Xf4Ife79W/0JfPZ8Z/wde9N7ijtE+wU6xvqM+lb6H7nw+YX5mrlxuQz5Mzje+Ma49fioeKD4nXiYeJy4pHitOL04j/jn+MN5IPkFOWs5U3mDefM55/ogOl36m7rdOyE7abuyu8D8Unyj/Pg9D/2qfcb+Yn6/vuK/Qj/lAAlAg==';
export const DEFAULT_BELL_SOUND = 'data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjMyLjEwNAAAAAAAAAAAAAAA//tQxAADB8AhSmxhIIEVCSiJrDCQBTcu3UrAIwUdkRgQbFAZC1CQEwTJ9mjRvBA4UOLD8nKVOWfh+UlK3z/177OXrfOdKl7pyn3Xf//WreyTRUoAWgBgkOAGbZHBgG1OF6zM82DWbZaUmMBptgQhGjsyYqc9ae9XFz280948NMBWInljyzsNRFLPWdnZGWrddDsjK1unuSrVN9jJsK8KuQtQCtMBjCEtImISdNKJOopIpBFpNSMbIHCSRpRR5iakjTiyzLhchUUBwCgyKiweBv/7UsQbg8isVNoMPMjAAAA0gAAABEVFGmgqK////9bP/6XCykxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq';
// TODO: Freeze?
export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
@@ -23,6 +23,8 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
bellSound: DEFAULT_BELL_SOUND,
bellStyle: 'none',
drawBoldTextInBrightColors: true,
fastScrollModifier: 'alt',
fastScrollSensitivity: 5,
fontFamily: 'courier-new, courier, monospace',
fontSize: 15,
fontWeight: 'normal',
@@ -31,6 +33,7 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
letterSpacing: 0,
logLevel: 'info',
scrollback: 1000,
scrollSensitivity: 1,
screenReaderMode: false,
macOptionIsMeta: false,
macOptionClickForcesSelection: false,
@@ -47,7 +50,7 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
screenKeys: false,
cancelEvents: false,
useFlowControl: false,
wordSeparator: ' ()[]{}\'"'
wordSeparator: ' ()[]{}\',:;"'
});
/**
@@ -119,6 +122,12 @@ export class OptionsService implements IOptionsService {
throw new Error(`${key} cannot be less than 0, value: ${value}`);
}
break;
case 'fastScrollSensitivity':
case 'scrollSensitivity':
if (value <= 0) {
throw new Error(`${key} cannot be less than or equal to 0, value: ${value}`);
}
break;
}
return value;
}
+6
View File
@@ -180,6 +180,8 @@ export interface IPartialTerminalOptions {
cursorStyle?: 'block' | 'underline' | 'bar';
disableStdin?: boolean;
drawBoldTextInBrightColors?: boolean;
fastScrollModifier?: 'alt' | 'ctrl' | 'shift';
fastScrollSensitivity?: number;
fontSize?: number;
fontFamily?: string;
fontWeight?: FontWeight;
@@ -194,6 +196,7 @@ export interface IPartialTerminalOptions {
rows?: number;
screenReaderMode?: boolean;
scrollback?: number;
scrollSensitivity?: number;
tabStopWidth?: number;
theme?: ITheme;
windowsMode?: boolean;
@@ -209,6 +212,8 @@ export interface ITerminalOptions {
cursorStyle: 'block' | 'underline' | 'bar';
disableStdin: boolean;
drawBoldTextInBrightColors: boolean;
fastScrollModifier: 'alt' | 'ctrl' | 'shift' | undefined;
fastScrollSensitivity: number;
fontSize: number;
fontFamily: string;
fontWeight: FontWeight;
@@ -223,6 +228,7 @@ export interface ITerminalOptions {
rows: number;
screenReaderMode: boolean;
scrollback: number;
scrollSensitivity: number;
tabStopWidth: number;
theme: ITheme;
windowsMode: boolean;
+4 -4
View File
@@ -33,14 +33,14 @@ export class Terminal implements ITerminalApi {
public get onRender(): IEvent<{ start: number, end: number }> { return this._core.onRender; }
public get onResize(): IEvent<{ cols: number, rows: number }> { return this._core.onResize; }
public get element(): HTMLElement { return this._core.element; }
public get element(): HTMLElement | undefined { return this._core.element; }
public get parser(): IParser {
if (!this._parser) {
this._parser = new ParserApi(this._core);
}
return this._parser;
}
public get textarea(): HTMLTextAreaElement { return this._core.textarea; }
public get textarea(): HTMLTextAreaElement | undefined { return this._core.textarea; }
public get rows(): number { return this._core.rows; }
public get cols(): number { return this._core.cols; }
public get buffer(): IBufferApi { return new BufferApiView(this._core.buffer); }
@@ -178,8 +178,8 @@ export class Terminal implements ITerminalApi {
private _verifyIntegers(...values: number[]): void {
values.forEach(value => {
if (value % 1 !== 0) {
throw new Error('This API does not accept floating point numbers');
if (value === Infinity || isNaN(value) || value % 1 !== 0) {
throw new Error('This API only accepts integers');
}
});
}
+10 -5
View File
@@ -103,10 +103,10 @@ export class CursorRenderLayer extends BaseRenderLayer {
this._cursorBlinkStateManager.dispose();
this._cursorBlinkStateManager = null;
}
// 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);
}
// 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);
}
public onCursorMove(): void {
@@ -148,12 +148,17 @@ export class CursorRenderLayer extends BaseRenderLayer {
this._clearCursor();
this._ctx.save();
this._ctx.fillStyle = this._colors.cursor.css;
this._renderBlurCursor(this._bufferService.buffer.x, viewportRelativeCursorY, this._cell);
const cursorStyle = this._optionsService.options.cursorStyle;
if (cursorStyle && cursorStyle !== 'block') {
this._cursorRenderers[cursorStyle](this._bufferService.buffer.x, viewportRelativeCursorY, this._cell);
} else {
this._renderBlurCursor(this._bufferService.buffer.x, viewportRelativeCursorY, this._cell);
}
this._ctx.restore();
this._state.x = this._bufferService.buffer.x;
this._state.y = viewportRelativeCursorY;
this._state.isFocused = false;
this._state.style = this._optionsService.options.cursorStyle;
this._state.style = cursorStyle;
this._state.width = this._cell.getWidth();
return;
}
+3 -3
View File
@@ -183,7 +183,7 @@ export class DomRenderer extends Disposable implements IRenderer {
`}`;
// Cursor
styles +=
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS} {` +
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +
` outline: 1px solid ${this._colors.cursor.css};` +
` outline-offset: -1px;` +
`}` +
@@ -197,10 +197,10 @@ export class DomRenderer extends Disposable implements IRenderer {
` background-color: ${this._colors.cursor.css};` +
` color: ${this._colors.cursorAccent.css};` +
`}` +
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` +
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` +
` box-shadow: 1px 0 0 ${this._colors.cursor.css} inset;` +
`}` +
`${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_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;` +
`}`;
// Selection
+1 -1
View File
@@ -21,7 +21,7 @@ describe('CharWidth Integration Tests', function(): void {
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
args: [`--window-size=${width},${height}`]
args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
+1 -1
View File
@@ -21,7 +21,7 @@ describe('InputHandler Integration Tests', function(): void {
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
args: [`--window-size=${width},${height}`]
args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -21,7 +21,7 @@ describe('Parser Integration Tests', function(): void {
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
args: [`--window-size=${width},${height}`]
args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
+15 -1
View File
@@ -21,7 +21,7 @@ describe('API Integration Tests', function(): void {
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
args: [`--window-size=${width},${height}`]
args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
@@ -510,6 +510,20 @@ describe('API Integration Tests', function(): void {
});
});
});
it('dispose', async function(): Promise<any> {
await page.evaluate(`
window.term = new Terminal();
window.term.dispose();
`);
assert.equal(await page.evaluate(`window.term._core._isDisposed`), true);
});
it('dispose (opened)', async function(): Promise<any> {
await openTerminal();
await page.evaluate(`window.term.dispose()`);
assert.equal(await page.evaluate(`window.term._core._isDisposed`), true);
});
});
async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
+58 -6
View File
@@ -82,6 +82,16 @@ declare module 'xterm' {
*/
drawBoldTextInBrightColors?: boolean;
/**
* The modifier key hold to multiply scroll speed.
*/
fastScrollModifier?: 'alt' | 'ctrl' | 'shift' | undefined;
/**
* The scroll speed multiplier used for fast scrolling.
*/
fastScrollSensitivity?: number;
/**
* The font size used to render text.
*/
@@ -173,6 +183,11 @@ declare module 'xterm' {
*/
scrollback?: number;
/**
* The scrolling speed multiplier used for adjusting normal scrolling speed.
*/
scrollSensitivity?: number;
/**
* The size of tab stops in the terminal.
*/
@@ -269,7 +284,7 @@ declare module 'xterm' {
/**
* A callback that fires when the mouse hovers over a link for a moment.
*/
tooltipCallback?: (event: MouseEvent, uri: string) => boolean | void;
tooltipCallback?: (event: MouseEvent, uri: string, location: IViewportRange) => boolean | void;
/**
* A callback that fires when the mouse leaves a link. Note that this can
@@ -310,7 +325,8 @@ declare module 'xterm' {
/**
* Represents a specific line in the terminal that is tracked when scrollback
* is trimmed and lines are added or removed.
* is trimmed and lines are added or removed. This is a single line that may
* be part of a larger wrapped line.
*/
export interface IMarker extends IDisposable {
/**
@@ -324,7 +340,8 @@ declare module 'xterm' {
readonly isDisposed: boolean;
/**
* The actual line index in the buffer at this point in time.
* The actual line index in the buffer at this point in time. This is set to
* -1 if the marker has been disposed.
*/
readonly line: number;
}
@@ -352,12 +369,12 @@ declare module 'xterm' {
/**
* The element containing the terminal.
*/
readonly element: HTMLElement;
readonly element: HTMLElement | undefined;
/**
* The textarea that accepts input for the terminal.
*/
readonly textarea: HTMLTextAreaElement;
readonly textarea: HTMLTextAreaElement | undefined;
/**
* The number of rows in the terminal's viewport. Use
@@ -434,7 +451,7 @@ declare module 'xterm' {
onLineFeed: IEvent<void>;
/**
* Adds an event listener for when a scroll occurs. The event value is the
* Adds an event listener for when a scroll occurs. The event value is the
* new position of the viewport.
* @returns an `IDisposable` to stop listening.
*/
@@ -842,6 +859,41 @@ declare module 'xterm' {
endRow: number;
}
/**
* An object representing a range within the viewport of the terminal.
*/
export interface IViewportRange {
/**
* The start of the range.
*/
start: IViewportRangePosition;
/**
* The end of the range.
*/
end: IViewportRangePosition;
}
/**
* An object representing a cell position within the viewport of the terminal.
*/
interface IViewportRangePosition {
/**
* The x position of the cell. This is a 0-based index that refers to the
* space in between columns, not the column itself. Index 0 refers to the
* left side of the viewport, index `Terminal.cols` refers to the right side
* of the viewport. This can be thought of as how a cursor is positioned in
* a text editor.
*/
x: number;
/**
* The y position of the cell. This is a 0-based index that refers to a
* specific row.
*/
y: number;
}
/**
* Represents a terminal buffer.
*/