diff --git a/.travis.yml b/.travis.yml index 8297ec37f5..1878c3a4d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ before_install: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -qq update ; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sudo apt-get -qq install g++-4.8 ; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export CXX=g++-4.8 ; fi + - npm install -g npm@5.1.0 env: matrix: - NPM_COMMAND=lint diff --git a/gulpfile.js b/gulpfile.js index 9b0a6de73f..ef4e460998 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -14,7 +14,7 @@ const sorcery = require('sorcery'); const source = require('vinyl-source-stream'); const sourcemaps = require('gulp-sourcemaps'); const ts = require('gulp-typescript'); - +const util = require('gulp-util'); let buildDir = process.env.BUILD_DIR || 'build'; let tsProject = ts.createProject('tsconfig.json'); @@ -121,13 +121,26 @@ gulp.task('mocha', ['instrument-test'], function () { .pipe(istanbul.writeReports()); }); +/** + * Run single test file by file name(without file extension). Example of the command: + * gulp mocha-test --test InputHandler.test + */ +gulp.task('mocha-test', ['instrument-test'], function () { + let testName = util.env.test; + util.log("Run test by Name: " + testName); + return gulp.src([`${outDir}/${testName}.js`, `${outDir}/**/${testName}.js`], {read: false}) + .pipe(mocha()) + .once('error', () => process.exit(1)) + .pipe(istanbul.writeReports()); +}); + /** * Use `sorcery` to resolve the source map chain and point back to the TypeScript files. * (Without this task the source maps produced for the JavaScript bundle points into the * compiled JavaScript files in ${outDir}/). */ gulp.task('sorcery', ['browserify'], function () { - var chain = sorcery.loadSync(`${buildDir}/xterm.js`); + let chain = sorcery.loadSync(`${buildDir}/xterm.js`); chain.apply(); chain.writeSync(); }); diff --git a/package-lock.json b/package-lock.json index 73745a3265..98ea098961 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "xterm", - "version": "2.8.0", + "version": "2.8.1", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -86,6 +86,12 @@ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", "dev": true }, + "array-differ": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-1.0.0.tgz", + "integrity": "sha1-7/UuN1gknTO+QCuLuOVkuytdQDE=", + "dev": true + }, "array-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", @@ -103,6 +109,12 @@ "integrity": "sha1-5zA08A3MH0CHYAj9IP6ud71LfC8=", "dev": true }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, "asn1": { "version": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", @@ -137,6 +149,12 @@ "tweetnacl": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" } }, + "beeper": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/beeper/-/beeper-1.1.1.tgz", + "integrity": "sha1-5tXqjF2tABMEpwsiY4RH9pyy+Ak=", + "dev": true + }, "boom": { "version": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", @@ -1266,6 +1284,18 @@ "supports-color": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" } }, + "clone": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", + "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=", + "dev": true + }, + "clone-stats": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-0.0.1.tgz", + "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE=", + "dev": true + }, "combined-stream": { "version": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", @@ -1295,6 +1325,12 @@ "is-plain-object": "2.0.3" } }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, "cross-spawn-async": { "version": "2.2.5", "resolved": "https://registry.npmjs.org/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz", @@ -1374,6 +1410,12 @@ } } }, + "dateformat": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-2.0.0.tgz", + "integrity": "sha1-J0Pjq7XD/CRi5SfcpEXgTp9N7hc=", + "dev": true + }, "deep-is": { "version": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", @@ -1390,6 +1432,15 @@ "integrity": "sha1-BcOlDYMYmYFpnuDAdtOjlQ237AA=", "dev": true }, + "duplexer2": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.0.2.tgz", + "integrity": "sha1-xhTc9n4vsUmVqRcR5aYX6KYKMds=", + "dev": true, + "requires": { + "readable-stream": "1.1.14" + } + }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -1878,6 +1929,73 @@ "integrity": "sha1-4QgOBljjALBilJkMxw4VAiNf1VA=", "dev": true }, + "fancy-log": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.0.tgz", + "integrity": "sha1-Rb4X0Cu5kX1gzP/UmVyZnmyMmUg=", + "dev": true, + "requires": { + "chalk": "1.1.3", + "time-stamp": "1.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, "fast-levenshtein": { "version": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", @@ -2059,6 +2177,15 @@ } } }, + "glogg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.0.tgz", + "integrity": "sha1-f+DxmfV6yQbPUS/urY+Q7kooT8U=", + "dev": true, + "requires": { + "sparkles": "1.0.0" + } + }, "graceful-readlink": { "version": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", @@ -8629,6 +8756,98 @@ } } }, + "gulp-util": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/gulp-util/-/gulp-util-3.0.8.tgz", + "integrity": "sha1-AFTh50RQLifATBh8PsxQXdVLu08=", + "dev": true, + "requires": { + "array-differ": "1.0.0", + "array-uniq": "1.0.3", + "beeper": "1.1.1", + "chalk": "1.1.3", + "dateformat": "2.0.0", + "fancy-log": "1.3.0", + "gulplog": "1.0.0", + "has-gulplog": "0.1.0", + "lodash._reescape": "3.0.0", + "lodash._reevaluate": "3.0.0", + "lodash._reinterpolate": "3.0.0", + "lodash.template": "3.6.2", + "minimist": "1.2.0", + "multipipe": "0.1.2", + "object-assign": "3.0.0", + "replace-ext": "0.0.1", + "through2": "2.0.3", + "vinyl": "0.5.3" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "ansi-styles": "2.2.1", + "escape-string-regexp": "1.0.5", + "has-ansi": "2.0.0", + "strip-ansi": "3.0.1", + "supports-color": "2.0.0" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "2.1.1" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "gulplog": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", + "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U=", + "dev": true, + "requires": { + "glogg": "1.0.0" + } + }, "har-validator": { "version": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", @@ -8658,6 +8877,15 @@ "ansi-regex": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" } }, + "has-gulplog": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/has-gulplog/-/has-gulplog-0.1.0.tgz", + "integrity": "sha1-ZBTIKRNpfaUVkDl9r7EvIpZ4Ec4=", + "dev": true, + "requires": { + "sparkles": "1.0.0" + } + }, "hash-base": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-2.0.2.tgz", @@ -8755,6 +8983,12 @@ "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", "dev": true }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true + }, "is-my-json-valid": { "version": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.15.0.tgz", "integrity": "sha1-k27do8o8IR/ZjzstPgjaQ/eykVs=", @@ -8793,6 +9027,12 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, "isstream": { "version": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", @@ -9073,11 +9313,130 @@ "type-check": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz" } }, + "lodash._basecopy": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", + "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", + "dev": true + }, + "lodash._basetostring": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-3.0.1.tgz", + "integrity": "sha1-0YYdh3+CSlL2aYMtyvPuFVZqB9U=", + "dev": true + }, + "lodash._basevalues": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._basevalues/-/lodash._basevalues-3.0.0.tgz", + "integrity": "sha1-W3dXYoAr3j0yl1A+JjAIIP32Ybc=", + "dev": true + }, + "lodash._getnative": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", + "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", + "dev": true + }, + "lodash._isiterateecall": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", + "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", + "dev": true + }, + "lodash._reescape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reescape/-/lodash._reescape-3.0.0.tgz", + "integrity": "sha1-Kx1vXf4HyKNVdT5fJ/rH8c3hYWo=", + "dev": true + }, + "lodash._reevaluate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reevaluate/-/lodash._reevaluate-3.0.0.tgz", + "integrity": "sha1-WLx0xAZklTrgsSTYBpltrKQx4u0=", + "dev": true + }, + "lodash._reinterpolate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", + "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", + "dev": true + }, + "lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=", + "dev": true + }, + "lodash.escape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-3.2.0.tgz", + "integrity": "sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg=", + "dev": true, + "requires": { + "lodash._root": "3.0.1" + } + }, + "lodash.isarguments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", + "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", + "dev": true + }, + "lodash.isarray": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", + "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", + "dev": true + }, + "lodash.keys": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", + "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", + "dev": true, + "requires": { + "lodash._getnative": "3.9.1", + "lodash.isarguments": "3.1.0", + "lodash.isarray": "3.0.4" + } + }, + "lodash.restparam": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz", + "integrity": "sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU=", + "dev": true + }, "lodash.sortby": { "version": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", "dev": true }, + "lodash.template": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-3.6.2.tgz", + "integrity": "sha1-+M3sxhaaJVvpCYrosMU9N4kx0U8=", + "dev": true, + "requires": { + "lodash._basecopy": "3.0.1", + "lodash._basetostring": "3.0.1", + "lodash._basevalues": "3.0.0", + "lodash._isiterateecall": "3.0.9", + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0", + "lodash.keys": "3.1.2", + "lodash.restparam": "3.6.1", + "lodash.templatesettings": "3.1.1" + } + }, + "lodash.templatesettings": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-3.1.1.tgz", + "integrity": "sha1-+zB4RHU7Zrnxr6VOJix0UwfbqOU=", + "dev": true, + "requires": { + "lodash._reinterpolate": "3.0.0", + "lodash.escape": "3.2.0" + } + }, "make-dir": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.0.0.tgz", @@ -9171,6 +9530,12 @@ "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", "dev": true }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, "mocha": { "version": "3.4.2", "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.4.2.tgz", @@ -9438,6 +9803,15 @@ } } }, + "multipipe": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/multipipe/-/multipipe-0.1.2.tgz", + "integrity": "sha1-Ko8t33Du1WTf8tV/HhoTfZ8FB4s=", + "dev": true, + "requires": { + "duplexer2": "0.0.2" + } + }, "node-pty": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-0.4.1.tgz", @@ -10630,6 +11004,12 @@ "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", "dev": true }, + "object-assign": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-3.0.0.tgz", + "integrity": "sha1-m+3VygiXlJvKR+f/QIBi1Un1h/I=", + "dev": true + }, "object.defaults": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", @@ -10753,6 +11133,12 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", + "dev": true + }, "pseudomap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", @@ -10764,12 +11150,30 @@ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", "dev": true }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "0.0.1", + "string_decoder": "0.10.31" + } + }, "remove-trailing-separator": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.0.2.tgz", "integrity": "sha1-abBi2XhyetFNxrVrpKt3L9jXBRE=", "dev": true }, + "replace-ext": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-0.0.1.tgz", + "integrity": "sha1-KbvZIHinOfC8zitO5B6DeVNSKSQ=", + "dev": true + }, "request": { "version": "https://registry.npmjs.org/request/-/request-2.79.0.tgz", "integrity": "sha1-Tf5b9r6LjNw3/Pk+BLZVd3InEN4=", @@ -10848,23 +11252,6 @@ "integrity": "sha1-/YYxojvHgmvvXYcb24c3jJVkeCg=", "dev": true }, - "sleep": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/sleep/-/sleep-3.0.1.tgz", - "integrity": "sha1-vk0XxXk2DgfgTtgXK6KxCmkFTfM=", - "dev": true, - "requires": { - "nan": "2.6.2" - }, - "dependencies": { - "nan": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.6.2.tgz", - "integrity": "sha1-5P805slf37WuzAjeZZb0NgWn20U=", - "dev": true - } - } - }, "sntp": { "version": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", @@ -10964,6 +11351,12 @@ } } }, + "sparkles": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.0.tgz", + "integrity": "sha1-Gsu/tZJDbRC76PeFt8xvgoFQEsM=", + "dev": true + }, "sshpk": { "version": "https://registry.npmjs.org/sshpk/-/sshpk-1.10.2.tgz", "integrity": "sha1-1agEziJpVRVjjnmNviMnPeBwpfo=", @@ -10993,6 +11386,12 @@ "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", "dev": true }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, "stringstream": { "version": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=", @@ -11031,6 +11430,60 @@ "execa": "0.4.0" } }, + "through2": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz", + "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=", + "dev": true, + "requires": { + "readable-stream": "2.3.3", + "xtend": "4.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "readable-stream": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", + "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", + "dev": true, + "requires": { + "core-util-is": "1.0.2", + "inherits": "2.0.3", + "isarray": "1.0.0", + "process-nextick-args": "1.0.7", + "safe-buffer": "5.1.1", + "string_decoder": "1.0.3", + "util-deprecate": "1.0.2" + } + }, + "string_decoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", + "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", + "dev": true, + "requires": { + "safe-buffer": "5.1.1" + } + }, + "xtend": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", + "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", + "dev": true + } + } + }, + "time-stamp": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", + "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM=", + "dev": true + }, "tough-cookie": { "version": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.2.tgz", "integrity": "sha1-8IH3bkyFcg5sN6X6ztc3FQ2EByo=", @@ -11692,6 +12145,12 @@ "crypto-random-string": "1.0.0" } }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true + }, "verror": { "version": "https://registry.npmjs.org/verror/-/verror-1.3.6.tgz", "integrity": "sha1-z/XfEpRtKX0rqu+qJoniW+AcAFw=", @@ -11700,6 +12159,17 @@ "extsprintf": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.2.tgz" } }, + "vinyl": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-0.5.3.tgz", + "integrity": "sha1-sEVbOPxeDPMNQyUTLkYZcMIJHN4=", + "dev": true, + "requires": { + "clone": "1.0.2", + "clone-stats": "0.0.1", + "replace-ext": "0.0.1" + } + }, "vinyl-buffer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/vinyl-buffer/-/vinyl-buffer-1.0.0.tgz", diff --git a/package.json b/package.json index b23f4a2c50..917d35e4d9 100644 --- a/package.json +++ b/package.json @@ -46,13 +46,14 @@ "express-ws": "2.0.0-rc.1", "fs-extra": "^1.0.0", "glob": "^7.0.5", - "gulp": "^3.9.1", + "gulp": "3.9.1", "gulp-cli": "^1.2.2", "gulp-coveralls": "^0.1.4", "gulp-istanbul": "^1.1.1", "gulp-mocha": "^3.0.1", "gulp-sourcemaps": "1.9.1", "gulp-typescript": "^3.1.3", + "gulp-util": "3.0.8", "jsdoc": "3.4.3", "jsdom": "^11.1.0", "merge-stream": "^1.0.1", diff --git a/src/Buffer.test.ts b/src/Buffer.test.ts new file mode 100644 index 0000000000..f68baa82ac --- /dev/null +++ b/src/Buffer.test.ts @@ -0,0 +1,31 @@ +/** + * @license MIT + */ +import { assert } from 'chai'; +import { ITerminal } from './Interfaces'; +import { Buffer } from './Buffer'; +import { CircularList } from './utils/CircularList'; + +describe('Buffer', () => { + let terminal: ITerminal; + let buffer: Buffer; + + beforeEach(() => { + terminal = { + cols: 80, + rows: 24, + scrollback: 1000 + }; + buffer = new Buffer(terminal); + }); + + describe('constructor', () => { + it('should create a CircularList with max length equal to scrollback, for its lines', () => { + assert.instanceOf(buffer.lines, CircularList); + assert.equal(buffer.lines.maxLength, terminal.scrollback); + }); + it('should set the Buffer\'s scrollBottom value equal to the terminal\'s rows -1', () => { + assert.equal(buffer.scrollBottom, terminal.rows - 1); + }); + }); +}); diff --git a/src/Buffer.ts b/src/Buffer.ts new file mode 100644 index 0000000000..93af3c460f --- /dev/null +++ b/src/Buffer.ts @@ -0,0 +1,39 @@ +/** + * @license MIT + */ + +import { ITerminal } from './Interfaces'; +import { CircularList } from './utils/CircularList'; + +/** + * This class represents a terminal buffer (an internal state of the terminal), where the + * following information is stored (in high-level): + * - text content of this particular buffer + * - cursor position + * - scroll position + */ +export class Buffer { + public lines: CircularList<[number, string, number][]>; + + /** + * Create a new Buffer. + * @param {Terminal} terminal - The terminal the Buffer will belong to + * @param {number} ydisp - The scroll position of the Buffer in the viewport + * @param {number} ybase - The scroll position of the y cursor (ybase + y = the y position within the Buffer) + * @param {number} y - The cursor's y position after ybase + * @param {number} x - The cursor's x position after ybase + */ + constructor( + private terminal: ITerminal, + public ydisp: number = 0, + public ybase: number = 0, + public y: number = 0, + public x: number = 0, + public scrollBottom: number = 0, + public scrollTop: number = 0, + public tabs: any = {}, + ) { + this.lines = new CircularList<[number, string, number][]>(this.terminal.scrollback); + this.scrollBottom = this.terminal.rows - 1; + } +} diff --git a/src/BufferSet.test.ts b/src/BufferSet.test.ts new file mode 100644 index 0000000000..2101fbc1f4 --- /dev/null +++ b/src/BufferSet.test.ts @@ -0,0 +1,49 @@ +/** + * @license MIT + */ +import { assert } from 'chai'; +import { ITerminal } from './Interfaces'; +import { BufferSet } from './BufferSet'; +import { Buffer } from './Buffer'; + +describe('BufferSet', () => { + let terminal: ITerminal; + let bufferSet: BufferSet; + + beforeEach(() => { + terminal = { + cols: 80, + rows: 24, + scrollback: 1000 + }; + bufferSet = new BufferSet(terminal); + }); + + describe('constructor', () => { + it('should create two different buffers: alt and normal', () => { + assert.instanceOf(bufferSet.normal, Buffer); + assert.instanceOf(bufferSet.alt, Buffer); + assert.notEqual(bufferSet.normal, bufferSet.alt); + }); + }); + + describe('activateNormalBuffer', () => { + beforeEach(() => { + bufferSet.activateNormalBuffer(); + }); + + it('should set the normal buffer as the currently active buffer', () => { + assert.equal(bufferSet.active, bufferSet.normal); + }); + }); + + describe('activateAltBuffer', () => { + beforeEach(() => { + bufferSet.activateAltBuffer(); + }); + + it('should set the alt buffer as the currently active buffer', () => { + assert.equal(bufferSet.active, bufferSet.alt); + }); + }); +}); diff --git a/src/BufferSet.ts b/src/BufferSet.ts new file mode 100644 index 0000000000..e86c098f5f --- /dev/null +++ b/src/BufferSet.ts @@ -0,0 +1,68 @@ +/** + * @license MIT + */ + +import { ITerminal, IBufferSet } from './Interfaces'; +import { Buffer } from './Buffer'; +import { EventEmitter } from './EventEmitter'; + +/** + * The BufferSet represents the set of two buffers used by xterm terminals (normal and alt) and + * provides also utilities for working with them. + */ +export class BufferSet extends EventEmitter implements IBufferSet { + private _normal: Buffer; + private _alt: Buffer; + private _activeBuffer: Buffer; + + /** + * Create a new BufferSet for the given terminal. + * @param {Terminal} terminal - The terminal the BufferSet will belong to + */ + constructor(private _terminal: ITerminal) { + super(); + this._normal = new Buffer(this._terminal); + this._alt = new Buffer(this._terminal); + this._activeBuffer = this._normal; + } + + /** + * Returns the alt Buffer of the BufferSet + * @returns {Buffer} + */ + public get alt(): Buffer { + return this._alt; + } + + /** + * Returns the normal Buffer of the BufferSet + * @returns {Buffer} + */ + public get active(): Buffer { + return this._activeBuffer; + } + + /** + * Returns the currently active Buffer of the BufferSet + * @returns {Buffer} + */ + public get normal(): Buffer { + return this._normal; + } + + /** + * Sets the normal Buffer of the BufferSet as its currently active Buffer + */ + public activateNormalBuffer(): void { + this._activeBuffer = this._normal; + this.emit('activate', this._normal); + } + + /** + * Sets the alt Buffer of the BufferSet as its currently active Buffer + */ + public activateAltBuffer(): void { + this._activeBuffer = this._alt; + this.emit('activate', this._alt); + } +} diff --git a/src/EventEmitter.ts b/src/EventEmitter.ts index a4fd4542c3..00c1094186 100644 --- a/src/EventEmitter.ts +++ b/src/EventEmitter.ts @@ -2,12 +2,14 @@ * @license MIT */ +import { IEventEmitter } from './Interfaces'; + interface ListenerType { (): void; listener?: () => void; }; -export class EventEmitter { +export class EventEmitter implements IEventEmitter { private _events: {[type: string]: ListenerType[]}; constructor() { diff --git a/src/InputHandler.ts b/src/InputHandler.ts index 52e5b4a11c..f41297fca1 100644 --- a/src/InputHandler.ts +++ b/src/InputHandler.ts @@ -27,49 +27,48 @@ export class InputHandler implements IInputHandler { char = this._terminal.charset[char]; } - let row = this._terminal.y + this._terminal.ybase; + let row = this._terminal.buffer.y + this._terminal.buffer.ybase; // insert combining char in last cell // FIXME: needs handling after cursor jumps - if (!ch_width && this._terminal.x) { + if (!ch_width && this._terminal.buffer.x) { // dont overflow left - if (this._terminal.lines.get(row)[this._terminal.x - 1]) { - if (!this._terminal.lines.get(row)[this._terminal.x - 1][2]) { + if (this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1]) { + if (!this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][2]) { // found empty cell after fullwidth, need to go 2 cells back - if (this._terminal.lines.get(row)[this._terminal.x - 2]) - this._terminal.lines.get(row)[this._terminal.x - 2][1] += char; + if (this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2]) + this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 2][1] += char; } else { - this._terminal.lines.get(row)[this._terminal.x - 1][1] += char; + this._terminal.buffer.lines.get(row)[this._terminal.buffer.x - 1][1] += char; } - this._terminal.updateRange(this._terminal.y); + this._terminal.updateRange(this._terminal.buffer.y); } return; } // goto next line if ch would overflow // TODO: needs a global min terminal width of 2 - if (this._terminal.x + ch_width - 1 >= this._terminal.cols) { + if (this._terminal.buffer.x + ch_width - 1 >= this._terminal.cols) { // autowrap - DECAWM if (this._terminal.wraparoundMode) { - this._terminal.x = 0; - this._terminal.y++; - if (this._terminal.y > this._terminal.scrollBottom) { - // Insert a new line, scroll and mark as a wrapped line - this._terminal.y--; + this._terminal.buffer.x = 0; + this._terminal.buffer.y++; + if (this._terminal.buffer.y > this._terminal.buffer.scrollBottom) { + this._terminal.buffer.y--; this._terminal.scroll(true); } else { // The line already exists (eg. the initial viewport), mark it as a // wrapped line - this._terminal.lines.get(this._terminal.y).isWrapped = true; + this._terminal.buffer.lines.get(this._terminal.buffer.y).isWrapped = true; } } else { if (ch_width === 2) // FIXME: check for xterm behavior return; } } - row = this._terminal.y + this._terminal.ybase; + row = this._terminal.buffer.y + this._terminal.buffer.ybase; // insert mode: move characters to right if (this._terminal.insertMode) { @@ -77,26 +76,26 @@ export class InputHandler implements IInputHandler { for (let moves = 0; moves < ch_width; ++moves) { // remove last cell, if it's width is 0 // we have to adjust the second last cell as well - const removed = this._terminal.lines.get(this._terminal.y + this._terminal.ybase).pop(); + const removed = this._terminal.buffer.lines.get(this._terminal.buffer.y + this._terminal.buffer.ybase).pop(); if (removed[2] === 0 - && this._terminal.lines.get(row)[this._terminal.cols - 2] - && this._terminal.lines.get(row)[this._terminal.cols - 2][2] === 2) { - this._terminal.lines.get(row)[this._terminal.cols - 2] = [this._terminal.curAttr, ' ', 1]; + && this._terminal.buffer.lines.get(row)[this._terminal.cols - 2] + && this._terminal.buffer.lines.get(row)[this._terminal.cols - 2][2] === 2) { + this._terminal.buffer.lines.get(row)[this._terminal.cols - 2] = [this._terminal.curAttr, ' ', 1]; } // insert empty cell at cursor - this._terminal.lines.get(row).splice(this._terminal.x, 0, [this._terminal.curAttr, ' ', 1]); + this._terminal.buffer.lines.get(row).splice(this._terminal.buffer.x, 0, [this._terminal.curAttr, ' ', 1]); } } - this._terminal.lines.get(row)[this._terminal.x] = [this._terminal.curAttr, char, ch_width]; - this._terminal.x++; - this._terminal.updateRange(this._terminal.y); + this._terminal.buffer.lines.get(row)[this._terminal.buffer.x] = [this._terminal.curAttr, char, ch_width]; + this._terminal.buffer.x++; + this._terminal.updateRange(this._terminal.buffer.y); // fullwidth char - set next cell width to zero and advance cursor if (ch_width === 2) { - this._terminal.lines.get(row)[this._terminal.x] = [this._terminal.curAttr, '', 0]; - this._terminal.x++; + this._terminal.buffer.lines.get(row)[this._terminal.buffer.x] = [this._terminal.curAttr, '', 0]; + this._terminal.buffer.x++; } } } @@ -122,16 +121,16 @@ export class InputHandler implements IInputHandler { */ public lineFeed(): void { if (this._terminal.convertEol) { - this._terminal.x = 0; + this._terminal.buffer.x = 0; } - this._terminal.y++; - if (this._terminal.y > this._terminal.scrollBottom) { - this._terminal.y--; + this._terminal.buffer.y++; + if (this._terminal.buffer.y > this._terminal.buffer.scrollBottom) { + this._terminal.buffer.y--; this._terminal.scroll(); } // If the end of the line is hit, prevent this action from wrapping around to the next line. - if (this._terminal.x >= this._terminal.cols) { - this._terminal.x--; + if (this._terminal.buffer.x >= this._terminal.cols) { + this._terminal.buffer.x--; } } @@ -140,7 +139,7 @@ export class InputHandler implements IInputHandler { * Carriage Return (Ctrl-M). */ public carriageReturn(): void { - this._terminal.x = 0; + this._terminal.buffer.x = 0; } /** @@ -148,8 +147,8 @@ export class InputHandler implements IInputHandler { * Backspace (Ctrl-H). */ public backspace(): void { - if (this._terminal.x > 0) { - this._terminal.x--; + if (this._terminal.buffer.x > 0) { + this._terminal.buffer.x--; } } @@ -158,7 +157,7 @@ export class InputHandler implements IInputHandler { * Horizontal Tab (HT) (Ctrl-I). */ public tab(): void { - this._terminal.x = this._terminal.nextStop(); + this._terminal.buffer.x = this._terminal.nextStop(); } /** @@ -189,13 +188,13 @@ export class InputHandler implements IInputHandler { param = params[0]; if (param < 1) param = 1; - row = this._terminal.y + this._terminal.ybase; - j = this._terminal.x; + row = this._terminal.buffer.y + this._terminal.buffer.ybase; + j = this._terminal.buffer.x; ch = [this._terminal.eraseAttr(), ' ', 1]; // xterm while (param-- && j < this._terminal.cols) { - this._terminal.lines.get(row).splice(j++, 0, ch); - this._terminal.lines.get(row).pop(); + this._terminal.buffer.lines.get(row).splice(j++, 0, ch); + this._terminal.buffer.lines.get(row).pop(); } } @@ -208,9 +207,9 @@ export class InputHandler implements IInputHandler { if (param < 1) { param = 1; } - this._terminal.y -= param; - if (this._terminal.y < 0) { - this._terminal.y = 0; + this._terminal.buffer.y -= param; + if (this._terminal.buffer.y < 0) { + this._terminal.buffer.y = 0; } } @@ -223,13 +222,13 @@ export class InputHandler implements IInputHandler { if (param < 1) { param = 1; } - this._terminal.y += param; - if (this._terminal.y >= this._terminal.rows) { - this._terminal.y = this._terminal.rows - 1; + this._terminal.buffer.y += param; + if (this._terminal.buffer.y >= this._terminal.rows) { + this._terminal.buffer.y = this._terminal.rows - 1; } // If the end of the line is hit, prevent this action from wrapping around to the next line. - if (this._terminal.x >= this._terminal.cols) { - this._terminal.x--; + if (this._terminal.buffer.x >= this._terminal.cols) { + this._terminal.buffer.x--; } } @@ -242,9 +241,9 @@ export class InputHandler implements IInputHandler { if (param < 1) { param = 1; } - this._terminal.x += param; - if (this._terminal.x >= this._terminal.cols) { - this._terminal.x = this._terminal.cols - 1; + this._terminal.buffer.x += param; + if (this._terminal.buffer.x >= this._terminal.cols) { + this._terminal.buffer.x = this._terminal.cols - 1; } } @@ -258,12 +257,12 @@ export class InputHandler implements IInputHandler { param = 1; } // If the end of the line is hit, prevent this action from wrapping around to the next line. - if (this._terminal.x >= this._terminal.cols) { - this._terminal.x--; + if (this._terminal.buffer.x >= this._terminal.cols) { + this._terminal.buffer.x--; } - this._terminal.x -= param; - if (this._terminal.x < 0) { - this._terminal.x = 0; + this._terminal.buffer.x -= param; + if (this._terminal.buffer.x < 0) { + this._terminal.buffer.x = 0; } } @@ -277,11 +276,11 @@ export class InputHandler implements IInputHandler { if (param < 1) { param = 1; } - this._terminal.y += param; - if (this._terminal.y >= this._terminal.rows) { - this._terminal.y = this._terminal.rows - 1; + this._terminal.buffer.y += param; + if (this._terminal.buffer.y >= this._terminal.rows) { + this._terminal.buffer.y = this._terminal.rows - 1; } - this._terminal.x = 0; + this._terminal.buffer.x = 0; }; @@ -295,11 +294,11 @@ export class InputHandler implements IInputHandler { if (param < 1) { param = 1; } - this._terminal.y -= param; - if (this._terminal.y < 0) { - this._terminal.y = 0; + this._terminal.buffer.y -= param; + if (this._terminal.buffer.y < 0) { + this._terminal.buffer.y = 0; } - this._terminal.x = 0; + this._terminal.buffer.x = 0; }; @@ -312,7 +311,7 @@ export class InputHandler implements IInputHandler { if (param < 1) { param = 1; } - this._terminal.x = param - 1; + this._terminal.buffer.x = param - 1; } /** @@ -342,8 +341,8 @@ export class InputHandler implements IInputHandler { col = this._terminal.cols - 1; } - this._terminal.x = col; - this._terminal.y = row; + this._terminal.buffer.x = col; + this._terminal.buffer.y = row; } /** @@ -353,7 +352,7 @@ export class InputHandler implements IInputHandler { public cursorForwardTab(params: number[]): void { let param = params[0] || 1; while (param--) { - this._terminal.x = this._terminal.nextStop(); + this._terminal.buffer.x = this._terminal.nextStop(); } } @@ -373,15 +372,15 @@ export class InputHandler implements IInputHandler { let j; switch (params[0]) { case 0: - this._terminal.eraseRight(this._terminal.x, this._terminal.y); - j = this._terminal.y + 1; + this._terminal.eraseRight(this._terminal.buffer.x, this._terminal.buffer.y); + j = this._terminal.buffer.y + 1; for (; j < this._terminal.rows; j++) { this._terminal.eraseLine(j); } break; case 1: - this._terminal.eraseLeft(this._terminal.x, this._terminal.y); - j = this._terminal.y; + this._terminal.eraseLeft(this._terminal.buffer.x, this._terminal.buffer.y); + j = this._terminal.buffer.y; while (j--) { this._terminal.eraseLine(j); } @@ -392,11 +391,11 @@ export class InputHandler implements IInputHandler { break; case 3: // Clear scrollback (everything not in viewport) - const scrollBackSize = this._terminal.lines.length - this._terminal.rows; + const scrollBackSize = this._terminal.buffer.lines.length - this._terminal.rows; if (scrollBackSize > 0) { - this._terminal.lines.trimStart(scrollBackSize); - this._terminal.ybase = Math.max(this._terminal.ybase - scrollBackSize, 0); - this._terminal.ydisp = Math.max(this._terminal.ydisp - scrollBackSize, 0); + this._terminal.buffer.lines.trimStart(scrollBackSize); + this._terminal.buffer.ybase = Math.max(this._terminal.buffer.ybase - scrollBackSize, 0); + this._terminal.buffer.ydisp = Math.max(this._terminal.buffer.ydisp - scrollBackSize, 0); // Force a scroll event to refresh viewport this._terminal.emit('scroll', 0); } @@ -418,13 +417,13 @@ export class InputHandler implements IInputHandler { public eraseInLine(params: number[]): void { switch (params[0]) { case 0: - this._terminal.eraseRight(this._terminal.x, this._terminal.y); + this._terminal.eraseRight(this._terminal.buffer.x, this._terminal.buffer.y); break; case 1: - this._terminal.eraseLeft(this._terminal.x, this._terminal.y); + this._terminal.eraseLeft(this._terminal.buffer.x, this._terminal.buffer.y); break; case 2: - this._terminal.eraseLine(this._terminal.y); + this._terminal.eraseLine(this._terminal.buffer.y); break; } } @@ -440,29 +439,29 @@ export class InputHandler implements IInputHandler { if (param < 1) { param = 1; } - row = this._terminal.y + this._terminal.ybase; + row = this._terminal.buffer.y + this._terminal.buffer.ybase; - j = this._terminal.rows - 1 - this._terminal.scrollBottom; - j = this._terminal.rows - 1 + this._terminal.ybase - j + 1; + j = this._terminal.rows - 1 - this._terminal.buffer.scrollBottom; + j = this._terminal.rows - 1 + this._terminal.buffer.ybase - j + 1; while (param--) { - if (this._terminal.lines.length === this._terminal.lines.maxLength) { + if (this._terminal.buffer.lines.length === this._terminal.buffer.lines.maxLength) { // Trim the start of lines to make room for the new line - this._terminal.lines.trimStart(1); - this._terminal.ybase--; - this._terminal.ydisp--; + this._terminal.buffer.lines.trimStart(1); + this._terminal.buffer.ybase--; + this._terminal.buffer.ydisp--; row--; j--; } // test: echo -e '\e[44m\e[1L\e[0m' // blankLine(true) - xterm/linux behavior - this._terminal.lines.splice(row, 0, this._terminal.blankLine(true)); - this._terminal.lines.splice(j, 1); + this._terminal.buffer.lines.splice(row, 0, this._terminal.blankLine(true)); + this._terminal.buffer.lines.splice(j, 1); } // this.maxRange(); - this._terminal.updateRange(this._terminal.y); - this._terminal.updateRange(this._terminal.scrollBottom); + this._terminal.updateRange(this._terminal.buffer.y); + this._terminal.updateRange(this._terminal.buffer.scrollBottom); } /** @@ -476,27 +475,27 @@ export class InputHandler implements IInputHandler { if (param < 1) { param = 1; } - row = this._terminal.y + this._terminal.ybase; + row = this._terminal.buffer.y + this._terminal.buffer.ybase; - j = this._terminal.rows - 1 - this._terminal.scrollBottom; - j = this._terminal.rows - 1 + this._terminal.ybase - j; + j = this._terminal.rows - 1 - this._terminal.buffer.scrollBottom; + j = this._terminal.rows - 1 + this._terminal.buffer.ybase - j; while (param--) { - if (this._terminal.lines.length === this._terminal.lines.maxLength) { + if (this._terminal.buffer.lines.length === this._terminal.buffer.lines.maxLength) { // Trim the start of lines to make room for the new line - this._terminal.lines.trimStart(1); - this._terminal.ybase -= 1; - this._terminal.ydisp -= 1; + this._terminal.buffer.lines.trimStart(1); + this._terminal.buffer.ybase -= 1; + this._terminal.buffer.ydisp -= 1; } // test: echo -e '\e[44m\e[1M\e[0m' // blankLine(true) - xterm/linux behavior - this._terminal.lines.splice(j + 1, 0, this._terminal.blankLine(true)); - this._terminal.lines.splice(row, 1); + this._terminal.buffer.lines.splice(j + 1, 0, this._terminal.blankLine(true)); + this._terminal.buffer.lines.splice(row, 1); } // this.maxRange(); - this._terminal.updateRange(this._terminal.y); - this._terminal.updateRange(this._terminal.scrollBottom); + this._terminal.updateRange(this._terminal.buffer.y); + this._terminal.updateRange(this._terminal.buffer.scrollBottom); } /** @@ -511,12 +510,12 @@ export class InputHandler implements IInputHandler { param = 1; } - row = this._terminal.y + this._terminal.ybase; + row = this._terminal.buffer.y + this._terminal.buffer.ybase; ch = [this._terminal.eraseAttr(), ' ', 1]; // xterm while (param--) { - this._terminal.lines.get(row).splice(this._terminal.x, 1); - this._terminal.lines.get(row).push(ch); + this._terminal.buffer.lines.get(row).splice(this._terminal.buffer.x, 1); + this._terminal.buffer.lines.get(row).push(ch); } } @@ -526,12 +525,12 @@ export class InputHandler implements IInputHandler { public scrollUp(params: number[]): void { let param = params[0] || 1; while (param--) { - this._terminal.lines.splice(this._terminal.ybase + this._terminal.scrollTop, 1); - this._terminal.lines.splice(this._terminal.ybase + this._terminal.scrollBottom, 0, this._terminal.blankLine()); + this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollTop, 1); + this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollBottom, 0, this._terminal.blankLine()); } // this.maxRange(); - this._terminal.updateRange(this._terminal.scrollTop); - this._terminal.updateRange(this._terminal.scrollBottom); + this._terminal.updateRange(this._terminal.buffer.scrollTop); + this._terminal.updateRange(this._terminal.buffer.scrollBottom); } /** @@ -540,12 +539,12 @@ export class InputHandler implements IInputHandler { public scrollDown(params: number[]): void { let param = params[0] || 1; while (param--) { - this._terminal.lines.splice(this._terminal.ybase + this._terminal.scrollBottom, 1); - this._terminal.lines.splice(this._terminal.ybase + this._terminal.scrollTop, 0, this._terminal.blankLine()); + this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollBottom, 1); + this._terminal.buffer.lines.splice(this._terminal.buffer.ybase + this._terminal.buffer.scrollTop, 0, this._terminal.blankLine()); } // this.maxRange(); - this._terminal.updateRange(this._terminal.scrollTop); - this._terminal.updateRange(this._terminal.scrollBottom); + this._terminal.updateRange(this._terminal.buffer.scrollTop); + this._terminal.updateRange(this._terminal.buffer.scrollBottom); } /** @@ -560,12 +559,12 @@ export class InputHandler implements IInputHandler { param = 1; } - row = this._terminal.y + this._terminal.ybase; - j = this._terminal.x; + row = this._terminal.buffer.y + this._terminal.buffer.ybase; + j = this._terminal.buffer.x; ch = [this._terminal.eraseAttr(), ' ', 1]; // xterm while (param-- && j < this._terminal.cols) { - this._terminal.lines.get(row)[j++] = ch; + this._terminal.buffer.lines.get(row)[j++] = ch; } } @@ -575,7 +574,7 @@ export class InputHandler implements IInputHandler { public cursorBackwardTab(params: number[]): void { let param = params[0] || 1; while (param--) { - this._terminal.x = this._terminal.prevStop(); + this._terminal.buffer.x = this._terminal.prevStop(); } } @@ -588,9 +587,9 @@ export class InputHandler implements IInputHandler { if (param < 1) { param = 1; } - this._terminal.x = param - 1; - if (this._terminal.x >= this._terminal.cols) { - this._terminal.x = this._terminal.cols - 1; + this._terminal.buffer.x = param - 1; + if (this._terminal.buffer.x >= this._terminal.cols) { + this._terminal.buffer.x = this._terminal.cols - 1; } } @@ -604,9 +603,9 @@ export class InputHandler implements IInputHandler { if (param < 1) { param = 1; } - this._terminal.x += param; - if (this._terminal.x >= this._terminal.cols) { - this._terminal.x = this._terminal.cols - 1; + this._terminal.buffer.x += param; + if (this._terminal.buffer.x >= this._terminal.cols) { + this._terminal.buffer.x = this._terminal.cols - 1; } } @@ -615,11 +614,11 @@ export class InputHandler implements IInputHandler { */ public repeatPrecedingCharacter(params: number[]): void { let param = params[0] || 1 - , line = this._terminal.lines.get(this._terminal.ybase + this._terminal.y) - , ch = line[this._terminal.x - 1] || [this._terminal.defAttr, ' ', 1]; + , line = this._terminal.buffer.lines.get(this._terminal.buffer.ybase + this._terminal.buffer.y) + , ch = line[this._terminal.buffer.x - 1] || [this._terminal.defAttr, ' ', 1]; while (param--) { - line[this._terminal.x++] = ch; + line[this._terminal.buffer.x++] = ch; } } @@ -698,9 +697,9 @@ export class InputHandler implements IInputHandler { if (param < 1) { param = 1; } - this._terminal.y = param - 1; - if (this._terminal.y >= this._terminal.rows) { - this._terminal.y = this._terminal.rows - 1; + this._terminal.buffer.y = param - 1; + if (this._terminal.buffer.y >= this._terminal.rows) { + this._terminal.buffer.y = this._terminal.rows - 1; } } @@ -714,13 +713,13 @@ export class InputHandler implements IInputHandler { if (param < 1) { param = 1; } - this._terminal.y += param; - if (this._terminal.y >= this._terminal.rows) { - this._terminal.y = this._terminal.rows - 1; + this._terminal.buffer.y += param; + if (this._terminal.buffer.y >= this._terminal.rows) { + this._terminal.buffer.y = this._terminal.rows - 1; } // If the end of the line is hit, prevent this action from wrapping around to the next line. - if (this._terminal.x >= this._terminal.cols) { - this._terminal.x--; + if (this._terminal.buffer.x >= this._terminal.cols) { + this._terminal.buffer.x--; } } @@ -733,14 +732,14 @@ export class InputHandler implements IInputHandler { if (params[0] < 1) params[0] = 1; if (params[1] < 1) params[1] = 1; - this._terminal.y = params[0] - 1; - if (this._terminal.y >= this._terminal.rows) { - this._terminal.y = this._terminal.rows - 1; + this._terminal.buffer.y = params[0] - 1; + if (this._terminal.buffer.y >= this._terminal.rows) { + this._terminal.buffer.y = this._terminal.rows - 1; } - this._terminal.x = params[1] - 1; - if (this._terminal.x >= this._terminal.cols) { - this._terminal.x = this._terminal.cols - 1; + this._terminal.buffer.x = params[1] - 1; + if (this._terminal.buffer.x >= this._terminal.cols) { + this._terminal.buffer.x = this._terminal.cols - 1; } } @@ -755,9 +754,9 @@ export class InputHandler implements IInputHandler { public tabClear(params: number[]): void { let param = params[0]; if (param <= 0) { - delete this._terminal.tabs[this._terminal.x]; + delete this._terminal.buffer.tabs[this._terminal.buffer.x]; } else if (param === 3) { - this._terminal.tabs = {}; + this._terminal.buffer.tabs = {}; } } @@ -943,30 +942,14 @@ export class InputHandler implements IInputHandler { this._terminal.cursorHidden = false; break; case 1049: // alt screen buffer cursor - // this._terminal.saveCursor(); - ; // FALL-THROUGH + this.saveCursor(params); + // FALL-THROUGH case 47: // alt screen buffer case 1047: // alt screen buffer - if (!this._terminal.normal) { - let normal = { - lines: this._terminal.lines, - ybase: this._terminal.ybase, - ydisp: this._terminal.ydisp, - x: this._terminal.x, - y: this._terminal.y, - scrollTop: this._terminal.scrollTop, - scrollBottom: this._terminal.scrollBottom, - tabs: this._terminal.tabs - // XXX save charset(s) here? - // charset: this._terminal.charset, - // glevel: this._terminal.glevel, - // charsets: this._terminal.charsets - }; - this._terminal.reset(); - this._terminal.viewport.syncScrollArea(); - this._terminal.normal = normal; - this._terminal.showCursor(); - } + this._terminal.buffers.activateAltBuffer(); + this._terminal.reset(); + this._terminal.viewport.syncScrollArea(); + this._terminal.showCursor(); break; } } @@ -1127,26 +1110,15 @@ export class InputHandler implements IInputHandler { ; // FALL-THROUGH case 47: // normal screen buffer case 1047: // normal screen buffer - clearing it first - if (this._terminal.normal) { - this._terminal.lines = this._terminal.normal.lines; - this._terminal.ybase = this._terminal.normal.ybase; - this._terminal.ydisp = this._terminal.normal.ydisp; - this._terminal.x = this._terminal.normal.x; - this._terminal.y = this._terminal.normal.y; - this._terminal.scrollTop = this._terminal.normal.scrollTop; - this._terminal.scrollBottom = this._terminal.normal.scrollBottom; - this._terminal.tabs = this._terminal.normal.tabs; - this._terminal.normal = null; - // Ensure the selection manager has the correct buffer - this._terminal.selectionManager.setBuffer(this._terminal.lines); - // if (params === 1049) { - // this.x = this.savedX; - // this.y = this.savedY; - // } - this._terminal.refresh(0, this._terminal.rows - 1); - this._terminal.viewport.syncScrollArea(); - this._terminal.showCursor(); + // Ensure the selection manager has the correct buffer + this._terminal.buffers.activateNormalBuffer(); + if (params[0] === 1049) { + this.restoreCursor(params); } + this._terminal.selectionManager.setBuffer(this._terminal.buffer.lines); + this._terminal.refresh(0, this._terminal.rows - 1); + this._terminal.viewport.syncScrollArea(); + this._terminal.showCursor(); break; } } @@ -1366,9 +1338,9 @@ export class InputHandler implements IInputHandler { case 6: // cursor position this._terminal.send(C0.ESC + '[' - + (this._terminal.y + 1) + + (this._terminal.buffer.y + 1) + ';' - + (this._terminal.x + 1) + + (this._terminal.buffer.x + 1) + 'R'); break; } @@ -1379,9 +1351,9 @@ export class InputHandler implements IInputHandler { case 6: // cursor position this._terminal.send(C0.ESC + '[?' - + (this._terminal.y + 1) + + (this._terminal.buffer.y + 1) + ';' - + (this._terminal.x + 1) + + (this._terminal.buffer.x + 1) + 'R'); break; case 15: @@ -1416,10 +1388,10 @@ export class InputHandler implements IInputHandler { this._terminal.applicationKeypad = false; // ? this._terminal.viewport.syncScrollArea(); this._terminal.applicationCursor = false; - this._terminal.scrollTop = 0; - this._terminal.scrollBottom = this._terminal.rows - 1; + this._terminal.buffer.scrollTop = 0; + this._terminal.buffer.scrollBottom = this._terminal.rows - 1; this._terminal.curAttr = this._terminal.defAttr; - this._terminal.x = this._terminal.y = 0; // ? + this._terminal.buffer.x = this._terminal.buffer.y = 0; // ? this._terminal.charset = null; this._terminal.glevel = 0; // ?? this._terminal.charsets = [null]; // ?? @@ -1463,10 +1435,10 @@ export class InputHandler implements IInputHandler { */ public setScrollRegion(params: number[]): void { if (this._terminal.prefix) return; - this._terminal.scrollTop = (params[0] || 1) - 1; - this._terminal.scrollBottom = (params[1] && params[1] <= this._terminal.rows ? params[1] : this._terminal.rows) - 1; - this._terminal.x = 0; - this._terminal.y = 0; + this._terminal.buffer.scrollTop = (params[0] || 1) - 1; + this._terminal.buffer.scrollBottom = (params[1] && params[1] <= this._terminal.rows ? params[1] : this._terminal.rows) - 1; + this._terminal.buffer.x = 0; + this._terminal.buffer.y = 0; } @@ -1475,8 +1447,8 @@ export class InputHandler implements IInputHandler { * Save cursor (ANSI.SYS). */ public saveCursor(params: number[]): void { - this._terminal.savedX = this._terminal.x; - this._terminal.savedY = this._terminal.y; + this._terminal.buffers.active.x = this._terminal.buffer.x; + this._terminal.buffers.active.y = this._terminal.buffer.y; } @@ -1485,8 +1457,8 @@ export class InputHandler implements IInputHandler { * Restore cursor (ANSI.SYS). */ public restoreCursor(params: number[]): void { - this._terminal.x = this._terminal.savedX || 0; - this._terminal.y = this._terminal.savedY || 0; + this._terminal.buffer.x = this._terminal.buffers.active.x || 0; + this._terminal.buffer.y = this._terminal.buffers.active.y || 0; } } diff --git a/src/Interfaces.ts b/src/Interfaces.ts index 4de2f28582..f19a7f28f8 100644 --- a/src/Interfaces.ts +++ b/src/Interfaces.ts @@ -24,9 +24,6 @@ export interface ITerminal { selectionManager: ISelectionManager; charMeasure: ICharMeasure; textarea: HTMLTextAreaElement; - ybase: number; - ydisp: number; - lines: ICircularList; rows: number; cols: number; browser: IBrowser; @@ -34,9 +31,10 @@ export interface ITerminal { children: HTMLElement[]; cursorHidden: boolean; cursorState: number; - x: number; - y: number; defAttr: number; + scrollback: number; + buffers: IBufferSet; + buffer: IBuffer; /** * Emit the 'data' event and populate the given data. @@ -48,6 +46,26 @@ export interface ITerminal { cancel(ev: Event, force?: boolean); log(text: string): void; emit(event: string, data: any); + reset(): void; + showCursor(): void; +} + +export interface IBuffer { + lines: ICircularList<[number, string, number][]>; + ydisp: number; + ybase: number; + y: number; + x: number; + tabs: any; +} + +export interface IBufferSet { + alt: IBuffer; + normal: IBuffer; + active: IBuffer; + + activateNormalBuffer(): void; + activateAltBuffer(): void; } export interface ISelectionManager { @@ -71,7 +89,7 @@ export interface ILinkifier { deregisterLinkMatcher(matcherId: number): boolean; } -interface ICircularList { +export interface ICircularList extends IEventEmitter { length: number; maxLength: number; @@ -85,6 +103,11 @@ interface ICircularList { shiftElements(start: number, count: number, offset: number): void; } +export interface IEventEmitter { + on(type, listener): void; + off(type, listener): void; +} + export interface LinkMatcherOptions { /** * The index of the link from the regex.match(text) call. This defaults to 0 diff --git a/src/Parser.ts b/src/Parser.ts index 00d574eec8..6679e366d1 100644 --- a/src/Parser.ts +++ b/src/Parser.ts @@ -52,7 +52,7 @@ escapedStateHandler['c'] = (parser, terminal) => { }; escapedStateHandler['E'] = (parser, terminal) => { // ESC E Next Line ( NEL is 0x85). - terminal.x = 0; + terminal.buffer.x = 0; terminal.index(); parser.setState(ParserState.NORMAL); }; @@ -499,9 +499,9 @@ export class Parser { // DECSTBM case 'r': pt = '' - + (this._terminal.scrollTop + 1) + + (this._terminal.buffer.scrollTop + 1) + ';' - + (this._terminal.scrollBottom + 1) + + (this._terminal.buffer.scrollBottom + 1) + 'r'; break; diff --git a/src/Renderer.ts b/src/Renderer.ts index f057a912bb..db4d6a63b5 100644 --- a/src/Renderer.ts +++ b/src/Renderer.ts @@ -139,15 +139,15 @@ export class Renderer { } for (; y <= end; y++) { - let row = y + this._terminal.ydisp; + let row = y + this._terminal.buffer.ydisp; - let line = this._terminal.lines.get(row); + let line = this._terminal.buffer.lines.get(row); let x; - if (this._terminal.y === y - (this._terminal.ybase - this._terminal.ydisp) && + if (this._terminal.buffer.y === y - (this._terminal.buffer.ybase - this._terminal.buffer.ydisp) && this._terminal.cursorState && !this._terminal.cursorHidden) { - x = this._terminal.x; + x = this._terminal.buffer.x; } else { x = -1; } @@ -337,8 +337,8 @@ export class Renderer { } // Translate from buffer position to viewport position - const viewportStartRow = start[1] - this._terminal.ydisp; - const viewportEndRow = end[1] - this._terminal.ydisp; + const viewportStartRow = start[1] - this._terminal.buffer.ydisp; + const viewportEndRow = end[1] - this._terminal.buffer.ydisp; const viewportCappedStartRow = Math.max(viewportStartRow, 0); const viewportCappedEndRow = Math.min(viewportEndRow, this._terminal.rows - 1); diff --git a/src/SelectionManager.test.ts b/src/SelectionManager.test.ts index 71505a294b..668fd92eb5 100644 --- a/src/SelectionManager.test.ts +++ b/src/SelectionManager.test.ts @@ -3,16 +3,17 @@ */ import jsdom = require('jsdom'); import { assert } from 'chai'; -import { ITerminal } from './Interfaces'; +import { ITerminal, ICircularList } from './Interfaces'; import { CharMeasure } from './utils/CharMeasure'; import { CircularList } from './utils/CircularList'; import { SelectionManager } from './SelectionManager'; import { SelectionModel } from './SelectionModel'; +import { BufferSet } from './BufferSet'; class TestSelectionManager extends SelectionManager { constructor( terminal: ITerminal, - buffer: CircularList, + buffer: ICircularList<[number, string, number][]>, rowContainer: HTMLElement, charMeasure: CharMeasure ) { @@ -36,7 +37,7 @@ describe('SelectionManager', () => { let document: Document; let terminal: ITerminal; - let buffer: CircularList; + let bufferLines: ICircularList<[number, string, number][]>; let rowContainer: HTMLElement; let selectionManager: TestSelectionManager; @@ -44,9 +45,12 @@ describe('SelectionManager', () => { dom = new jsdom.JSDOM(''); window = dom.window; document = window.document; - buffer = new CircularList(100); terminal = { cols: 80, rows: 2 }; - selectionManager = new TestSelectionManager(terminal, buffer, rowContainer, null); + terminal.scrollback = 100; + terminal.buffers = new BufferSet(terminal); + terminal.buffer = terminal.buffers.active; + bufferLines = terminal.buffer.lines; + selectionManager = new TestSelectionManager(terminal, bufferLines, rowContainer, null); }); function stringToRow(text: string): [number, string, number][] { @@ -59,7 +63,7 @@ describe('SelectionManager', () => { describe('_selectWordAt', () => { it('should expand selection for normal width chars', () => { - buffer.push(stringToRow('foo bar')); + bufferLines.push(stringToRow('foo bar')); selectionManager.selectWordAt([0, 0]); assert.equal(selectionManager.selectionText, 'foo'); selectionManager.selectWordAt([1, 0]); @@ -76,7 +80,7 @@ describe('SelectionManager', () => { assert.equal(selectionManager.selectionText, 'bar'); }); it('should expand selection for whitespace', () => { - buffer.push(stringToRow('a b')); + bufferLines.push(stringToRow('a b')); selectionManager.selectWordAt([0, 0]); assert.equal(selectionManager.selectionText, 'a'); selectionManager.selectWordAt([1, 0]); @@ -90,7 +94,7 @@ describe('SelectionManager', () => { }); it('should expand selection for wide characters', () => { // Wide characters use a special format - buffer.push([ + bufferLines.push([ [null, '中', 2], [null, '', 0], [null, '文', 2], @@ -142,7 +146,7 @@ describe('SelectionManager', () => { assert.equal(selectionManager.selectionText, 'foo'); }); it('should select up to non-path characters that are commonly adjacent to paths', () => { - buffer.push(stringToRow('(cd)[ef]{gh}\'ij"')); + bufferLines.push(stringToRow('(cd)[ef]{gh}\'ij"')); selectionManager.selectWordAt([0, 0]); assert.equal(selectionManager.selectionText, '(cd'); selectionManager.selectWordAt([1, 0]); @@ -180,7 +184,7 @@ describe('SelectionManager', () => { describe('_selectLineAt', () => { it('should select the entire line', () => { - buffer.push(stringToRow('foo bar')); + bufferLines.push(stringToRow('foo bar')); selectionManager.selectLineAt(0); assert.equal(selectionManager.selectionText, 'foo bar', 'The selected text is correct'); assert.deepEqual(selectionManager.model.finalSelectionStart, [0, 0]); @@ -190,13 +194,13 @@ describe('SelectionManager', () => { describe('selectAll', () => { it('should select the entire buffer, beyond the viewport', () => { - buffer.push(stringToRow('1')); - buffer.push(stringToRow('2')); - buffer.push(stringToRow('3')); - buffer.push(stringToRow('4')); - buffer.push(stringToRow('5')); + bufferLines.push(stringToRow('1')); + bufferLines.push(stringToRow('2')); + bufferLines.push(stringToRow('3')); + bufferLines.push(stringToRow('4')); + bufferLines.push(stringToRow('5')); selectionManager.selectAll(); - terminal.ybase = buffer.length - terminal.rows; + terminal.buffer.ybase = bufferLines.length - terminal.rows; assert.equal(selectionManager.selectionText, '1\n2\n3\n4\n5'); }); }); diff --git a/src/SelectionManager.ts b/src/SelectionManager.ts index 31ee8c2c8f..5a86a2b9cf 100644 --- a/src/SelectionManager.ts +++ b/src/SelectionManager.ts @@ -7,7 +7,7 @@ import * as Browser from './utils/Browser'; import { CharMeasure } from './utils/CharMeasure'; import { CircularList } from './utils/CircularList'; import { EventEmitter } from './EventEmitter'; -import { ITerminal } from './Interfaces'; +import { ITerminal, ICircularList } from './Interfaces'; import { SelectionModel } from './SelectionModel'; import { translateBufferLineToString } from './utils/BufferLine'; @@ -98,7 +98,7 @@ export class SelectionManager extends EventEmitter { constructor( private _terminal: ITerminal, - private _buffer: CircularList, + private _buffer: ICircularList<[number, string, number][]>, private _rowContainer: HTMLElement, private _charMeasure: CharMeasure ) { @@ -147,7 +147,7 @@ export class SelectionManager extends EventEmitter { * switched in or out. * @param buffer The active buffer. */ - public setBuffer(buffer: CircularList): void { + public setBuffer(buffer: ICircularList<[number, string, number][]>): void { this._buffer = buffer; this.clearSelection(); } @@ -186,7 +186,7 @@ export class SelectionManager extends EventEmitter { for (let i = start[1] + 1; i <= end[1] - 1; i++) { const bufferLine = this._buffer.get(i); const lineText = translateBufferLineToString(bufferLine, true); - if (bufferLine.isWrapped) { + if ((bufferLine).isWrapped) { result[result.length - 1] += lineText; } else { result.push(lineText); @@ -197,7 +197,7 @@ export class SelectionManager extends EventEmitter { if (start[1] !== end[1]) { const bufferLine = this._buffer.get(end[1]); const lineText = translateBufferLineToString(bufferLine, true, 0, end[0]); - if (bufferLine.isWrapped) { + if ((bufferLine).isWrapped) { result[result.length - 1] += lineText; } else { result.push(lineText); @@ -281,7 +281,7 @@ export class SelectionManager extends EventEmitter { coords[0]--; coords[1]--; // Convert viewport coords to buffer coords - coords[1] += this._terminal.ydisp; + coords[1] += this._terminal.buffer.ydisp; return coords; } @@ -476,9 +476,9 @@ export class SelectionManager extends EventEmitter { this._terminal.scrollDisp(this._dragScrollAmount, false); // Re-evaluate selection if (this._dragScrollAmount > 0) { - this._model.selectionEnd = [this._terminal.cols - 1, this._terminal.ydisp + this._terminal.rows]; + this._model.selectionEnd = [this._terminal.cols - 1, this._terminal.buffer.ydisp + this._terminal.rows]; } else { - this._model.selectionEnd = [0, this._terminal.ydisp]; + this._model.selectionEnd = [0, this._terminal.buffer.ydisp]; } this.refresh(); } diff --git a/src/SelectionModel.test.ts b/src/SelectionModel.test.ts index ab22b77cc5..6da3874bd4 100644 --- a/src/SelectionModel.test.ts +++ b/src/SelectionModel.test.ts @@ -4,6 +4,7 @@ import { assert } from 'chai'; import { ITerminal } from './Interfaces'; import { SelectionModel } from './SelectionModel'; +import {BufferSet} from './BufferSet'; class TestSelectionModel extends SelectionModel { constructor( @@ -22,6 +23,10 @@ describe('SelectionManager', () => { beforeEach(() => { terminal = { cols: 80, rows: 2, ybase: 0 }; + terminal.scrollback = 10; + terminal.buffers = new BufferSet(terminal); + terminal.buffer = terminal.buffers.active; + model = new TestSelectionModel(terminal); }); diff --git a/src/SelectionModel.ts b/src/SelectionModel.ts index 410af3b3a6..8c599fd7ae 100644 --- a/src/SelectionModel.ts +++ b/src/SelectionModel.ts @@ -68,7 +68,7 @@ export class SelectionModel { */ public get finalSelectionEnd(): [number, number] { if (this.isSelectAllActive) { - return [this._terminal.cols, this._terminal.ybase + this._terminal.rows - 1]; + return [this._terminal.cols, this._terminal.buffer.ybase + this._terminal.rows - 1]; } if (!this.selectionStart) { diff --git a/src/Viewport.test.ts b/src/Viewport.test.ts index 193e7969fd..6d09ea86a2 100644 --- a/src/Viewport.test.ts +++ b/src/Viewport.test.ts @@ -1,10 +1,10 @@ import { assert } from 'chai'; import { Viewport } from './Viewport'; +import {BufferSet} from './BufferSet'; describe('Viewport', () => { let terminal; let viewportElement; - let selectionContainer; let charMeasure; let viewport; let scrollAreaElement; @@ -13,7 +13,6 @@ describe('Viewport', () => { beforeEach(() => { terminal = { - lines: [], rows: 0, ydisp: 0, on: () => {}, @@ -26,8 +25,11 @@ describe('Viewport', () => { style: { height: 0 } - } + }, + scrollback: 10 }; + terminal.buffers = new BufferSet(terminal); + terminal.buffer = terminal.buffers.active; viewportElement = { addEventListener: () => {}, style: { @@ -60,14 +62,14 @@ describe('Viewport', () => { }, 0); }); it('should set the height of the viewport when the line-height changed', () => { - terminal.lines.push(''); - terminal.lines.push(''); + terminal.buffer.lines.push(''); + terminal.buffer.lines.push(''); terminal.rows = 1; viewport.refresh(); assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px'); - charMeasure.height = 20; + charMeasure.height = 2 * CHARACTER_HEIGHT; viewport.refresh(); - assert.equal(viewportElement.style.height, 20 + 'px'); + assert.equal(viewportElement.style.height, 2 * CHARACTER_HEIGHT + 'px'); }); }); @@ -75,13 +77,13 @@ describe('Viewport', () => { it('should sync the scroll area', done => { // Allow CharMeasure to be initialized setTimeout(() => { - terminal.lines.push(''); + terminal.buffer.lines.push(''); terminal.rows = 1; assert.equal(scrollAreaElement.style.height, 0 * CHARACTER_HEIGHT + 'px'); viewport.syncScrollArea(); assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px'); assert.equal(scrollAreaElement.style.height, 1 * CHARACTER_HEIGHT + 'px'); - terminal.lines.push(''); + terminal.buffer.lines.push(''); viewport.syncScrollArea(); assert.equal(viewportElement.style.height, 1 * CHARACTER_HEIGHT + 'px'); assert.equal(scrollAreaElement.style.height, 2 * CHARACTER_HEIGHT + 'px'); diff --git a/src/Viewport.ts b/src/Viewport.ts index 5be6bffdfb..fe276935bb 100644 --- a/src/Viewport.ts +++ b/src/Viewport.ts @@ -20,7 +20,7 @@ export class Viewport { * @param terminal The terminal this viewport belongs to. * @param viewportElement The DOM element acting as the viewport. * @param scrollArea The DOM element acting as the scroll area. - * @param charMeasureElement A DOM element used to measure the character size of. the terminal. + * @param charMeasure A DOM element used to measure the character size of. the terminal. */ constructor( private terminal: ITerminal, @@ -43,8 +43,6 @@ export class Viewport { /** * Refreshes row height, setting line-height, viewport height and scroll area height if * necessary. - * @param charSize A character size measurement bounding rect object, if it doesn't exist it will - * be created. */ private refresh(): void { if (this.charMeasure.height > 0) { @@ -68,9 +66,9 @@ export class Viewport { * Updates dimensions and synchronizes the scroll area if necessary. */ public syncScrollArea(): void { - if (this.lastRecordedBufferLength !== this.terminal.lines.length) { + if (this.lastRecordedBufferLength !== this.terminal.buffer.lines.length) { // If buffer height changed - this.lastRecordedBufferLength = this.terminal.lines.length; + this.lastRecordedBufferLength = this.terminal.buffer.lines.length; this.refresh(); } else if (this.lastRecordedViewportHeight !== this.terminal.rows) { // If viewport height changed @@ -83,7 +81,7 @@ export class Viewport { } // Sync scrollTop - const scrollTop = this.terminal.ydisp * this.currentRowHeight; + const scrollTop = this.terminal.buffer.ydisp * this.currentRowHeight; if (this.viewportElement.scrollTop !== scrollTop) { this.viewportElement.scrollTop = scrollTop; } @@ -96,7 +94,7 @@ export class Viewport { */ private onScroll(ev: Event) { const newRow = Math.round(this.viewportElement.scrollTop / this.currentRowHeight); - const diff = newRow - this.terminal.ydisp; + const diff = newRow - this.terminal.buffer.ydisp; this.terminal.scrollDisp(diff, true); } diff --git a/src/test/escape-sequences-test.js b/src/test/escape-sequences-test.js index c3831580dc..b4a53a17c3 100644 --- a/src/test/escape-sequences-test.js +++ b/src/test/escape-sequences-test.js @@ -62,10 +62,10 @@ function formatError(in_, out_, expected) { function terminalToString(term) { var result = ''; var line_s = ''; - for (var line = term.ybase; line < term.ybase + term.rows; line++) { + for (var line = term.buffer.ybase; line < term.buffer.ybase + term.rows; line++) { line_s = ''; for (var cell=0; cell value) { - const amountToTrim = this.lines.length - value; - const needsRefresh = (this.ydisp - amountToTrim < 0); - this.lines.trimStart(amountToTrim); - this.ybase = Math.max(this.ybase - amountToTrim, 0); - this.ydisp = Math.max(this.ydisp - amountToTrim, 0); + if (this.buffer.lines.length > value) { + const amountToTrim = this.buffer.lines.length - value; + 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); } } - this.lines.maxLength = value; + this.buffer.lines.maxLength = value; this.viewport.syncScrollArea(); } break; @@ -513,7 +492,7 @@ Terminal.prototype.blur = function() { */ Terminal.bindBlur = function (term) { on(term.textarea, 'blur', function (ev) { - term.refresh(term.y, term.y); + term.refresh(term.buffer.y, term.buffer.y); if (term.sendFocus) { term.send(C0.ESC + '[O'); } @@ -725,7 +704,9 @@ Terminal.prototype.open = function(parent, focus) { this.viewport = new Viewport(this, this.viewportElement, this.viewportScrollArea, this.charMeasure); this.renderer = new Renderer(this); - this.selectionManager = new SelectionManager(this, this.lines, this.rowContainer, this.charMeasure); + this.selectionManager = new SelectionManager( + this, this.buffer.lines, this.rowContainer, this.charMeasure + ); this.selectionManager.on('refresh', data => { this.renderer.refreshSelection(data.start, data.end); }); @@ -1156,7 +1137,7 @@ Terminal.prototype.queueLinkification = function(start, end) { Terminal.prototype.showCursor = function() { if (!this.cursorState) { this.cursorState = 1; - this.refresh(this.y, this.y); + this.refresh(this.buffer.y, this.buffer.y); } }; @@ -1169,48 +1150,48 @@ Terminal.prototype.scroll = function(isWrapped) { var row; // Make room for the new row in lines - if (this.lines.length === this.lines.maxLength) { - this.lines.trimStart(1); - this.ybase--; - if (this.ydisp !== 0) { - this.ydisp--; + if (this.buffer.lines.length === this.buffer.lines.maxLength) { + this.buffer.lines.trimStart(1); + this.buffer.ybase--; + if (this.buffer.ydisp !== 0) { + this.buffer.ydisp--; } } - this.ybase++; + this.buffer.ybase++; // TODO: Why is this done twice? if (!this.userScrolling) { - this.ydisp = this.ybase; + this.buffer.ydisp = this.buffer.ybase; } // last line - row = this.ybase + this.rows - 1; + row = this.buffer.ybase + this.rows - 1; // subtract the bottom scroll region - row -= this.rows - 1 - this.scrollBottom; + row -= this.rows - 1 - this.buffer.scrollBottom; - if (row === this.lines.length) { + if (row === this.buffer.lines.length) { // Optimization: pushing is faster than splicing when they amount to the same behavior - this.lines.push(this.blankLine(undefined, isWrapped)); + this.buffer.lines.push(this.blankLine(undefined, isWrapped)); } else { // add our new line - this.lines.splice(row, 0, this.blankLine(undefined, isWrapped)); + this.buffer.lines.splice(row, 0, this.blankLine(undefined, isWrapped)); } - if (this.scrollTop !== 0) { - if (this.ybase !== 0) { - this.ybase--; + if (this.buffer.scrollTop !== 0) { + if (this.buffer.ybase !== 0) { + this.buffer.ybase--; if (!this.userScrolling) { - this.ydisp = this.ybase; + this.buffer.ydisp = this.buffer.ybase; } } - this.lines.splice(this.ybase + this.scrollTop, 1); + this.buffer.lines.splice(this.buffer.ybase + this.buffer.scrollTop, 1); } // this.maxRange(); - this.updateRange(this.scrollTop); - this.updateRange(this.scrollBottom); + this.updateRange(this.buffer.scrollTop); + this.updateRange(this.buffer.scrollBottom); /** * This event is emitted whenever the terminal is scrolled. @@ -1218,7 +1199,7 @@ Terminal.prototype.scroll = function(isWrapped) { * * @event scroll */ - this.emit('scroll', this.ydisp); + this.emit('scroll', this.buffer.ydisp); }; /** @@ -1230,24 +1211,24 @@ Terminal.prototype.scroll = function(isWrapped) { */ Terminal.prototype.scrollDisp = function(disp, suppressScrollEvent) { if (disp < 0) { - if (this.ydisp === 0) { + if (this.buffer.ydisp === 0) { return; } this.userScrolling = true; - } else if (disp + this.ydisp >= this.ybase) { + } else if (disp + this.buffer.ydisp >= this.buffer.ybase) { this.userScrolling = false; } - const oldYdisp = this.ydisp; - this.ydisp = Math.max(Math.min(this.ydisp + disp, this.ybase), 0); + const oldYdisp = this.buffer.ydisp; + this.buffer.ydisp = Math.max(Math.min(this.buffer.ydisp + disp, this.buffer.ybase), 0); // No change occurred, don't trigger scroll/refresh - if (oldYdisp === this.ydisp) { + if (oldYdisp === this.buffer.ydisp) { return; } if (!suppressScrollEvent) { - this.emit('scroll', this.ydisp); + this.emit('scroll', this.buffer.ydisp); } this.refresh(0, this.rows - 1); @@ -1265,14 +1246,14 @@ Terminal.prototype.scrollPages = function(pageCount) { * Scrolls the display of the terminal to the top. */ Terminal.prototype.scrollToTop = function() { - this.scrollDisp(-this.ydisp); + this.scrollDisp(-this.buffer.ydisp); }; /** * Scrolls the display of the terminal to the bottom. */ Terminal.prototype.scrollToBottom = function() { - this.scrollDisp(this.ybase - this.ydisp); + this.scrollDisp(this.buffer.ybase - this.buffer.ydisp); }; /** @@ -1316,8 +1297,8 @@ Terminal.prototype.innerWrite = function() { this.xoffSentToCatchUp = false; } - this.refreshStart = this.y; - this.refreshEnd = this.y; + this.refreshStart = this.buffer.y; + this.refreshEnd = this.buffer.y; // HACK: Set the parser state based on it's state at the time of return. // This works around the bug #662 which saw the parser state reset in the @@ -1327,7 +1308,7 @@ Terminal.prototype.innerWrite = function() { var state = this.parser.parse(data); this.parser.setState(state); - this.updateRange(this.y); + this.updateRange(this.buffer.y); this.refresh(this.refreshStart, this.refreshEnd); } if (this.writeBuffer.length > 0) { @@ -1480,7 +1461,7 @@ Terminal.prototype.keyDown = function(ev) { this.restartCursorBlinking(); if (!this.compositionHelper.keydown.bind(this.compositionHelper)(ev)) { - if (this.ybase !== this.ydisp) { + if (this.buffer.ybase !== this.buffer.ydisp) { this.scrollToBottom(); } return false; @@ -1958,10 +1939,10 @@ Terminal.prototype.resize = function(x, y) { j = this.cols; if (j < x) { ch = [this.defAttr, ' ', 1]; // does xterm use the default attr? - i = this.lines.length; + i = this.buffer.lines.length; while (i--) { - while (this.lines.get(i).length < x) { - this.lines.get(i).push(ch); + while (this.buffer.lines.get(i).length < x) { + this.buffer.lines.get(i).push(ch); } } } @@ -1975,21 +1956,21 @@ Terminal.prototype.resize = function(x, y) { if (j < y) { el = this.element; while (j++ < y) { - // y is rows, not this.y - if (this.lines.length < y + this.ybase) { - if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) { + // y is rows, not this.buffer.y + if (this.buffer.lines.length < y + this.buffer.ybase) { + if (this.buffer.ybase > 0 && this.buffer.lines.length <= this.buffer.ybase + this.buffer.y + addToY + 1) { // There is room above the buffer and there are no empty elements below the line, // scroll up - this.ybase--; + this.buffer.ybase--; addToY++; - if (this.ydisp > 0) { + if (this.buffer.ydisp > 0) { // Viewport is at the top of the buffer, must increase downwards - this.ydisp--; + this.buffer.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(this.blankLine()); + this.buffer.lines.push(this.blankLine()); } } if (this.children.length < y) { @@ -1998,14 +1979,14 @@ Terminal.prototype.resize = function(x, y) { } } else { // (j > y) while (j-- > y) { - if (this.lines.length > y + this.ybase) { - if (this.lines.length > this.ybase + this.y + 1) { + if (this.buffer.lines.length > y + this.buffer.ybase) { + if (this.buffer.lines.length > this.buffer.ybase + this.buffer.y + 1) { // The line is a blank line below the cursor, remove it - this.lines.pop(); + this.buffer.lines.pop(); } else { // The line is the cursor, scroll down - this.ybase++; - this.ydisp++; + this.buffer.ybase++; + this.buffer.ydisp++; } } if (this.children.length > y) { @@ -2018,26 +1999,24 @@ Terminal.prototype.resize = function(x, y) { this.rows = y; // Make sure that the cursor stays on screen - if (this.y >= y) { - this.y = y - 1; + if (this.buffer.y >= y) { + this.buffer.y = y - 1; } if (addToY) { - this.y += addToY; + this.buffer.y += addToY; } - if (this.x >= x) { - this.x = x - 1; + if (this.buffer.x >= x) { + this.buffer.x = x - 1; } - this.scrollTop = 0; - this.scrollBottom = y - 1; + this.buffer.scrollTop = 0; + this.buffer.scrollBottom = y - 1; this.charMeasure.measure(); this.refresh(0, this.rows - 1); - this.normal = null; - this.geometry = [this.cols, this.rows]; this.emit('resize', {terminal: this, cols: x, rows: y}); }; @@ -2073,16 +2052,16 @@ Terminal.prototype.maxRange = function() { */ Terminal.prototype.setupStops = function(i) { if (i != null) { - if (!this.tabs[i]) { + if (!this.buffer.tabs[i]) { i = this.prevStop(i); } } else { - this.tabs = {}; + this.buffer.tabs = {}; i = 0; } for (; i < this.cols; i += this.getOption('tabStopWidth')) { - this.tabs[i] = true; + this.buffer.tabs[i] = true; } }; @@ -2092,8 +2071,8 @@ Terminal.prototype.setupStops = function(i) { * @param {number} x The position to move the cursor to the previous tab stop. */ Terminal.prototype.prevStop = function(x) { - if (x == null) x = this.x; - while (!this.tabs[--x] && x > 0); + if (x == null) x = this.buffer.x; + while (!this.buffer.tabs[--x] && x > 0); return x >= this.cols ? this.cols - 1 : x < 0 ? 0 : x; @@ -2105,8 +2084,8 @@ Terminal.prototype.prevStop = function(x) { * @param {number} x The position to move the cursor one tab stop forward. */ Terminal.prototype.nextStop = function(x) { - if (x == null) x = this.x; - while (!this.tabs[++x] && x < this.cols); + if (x == null) x = this.buffer.x; + while (!this.buffer.tabs[++x] && x < this.cols); return x >= this.cols ? this.cols - 1 : x < 0 ? 0 : x; @@ -2119,7 +2098,7 @@ Terminal.prototype.nextStop = function(x) { * @param {number} y The line in which to operate. */ Terminal.prototype.eraseRight = function(x, y) { - var line = this.lines.get(this.ybase + y); + var line = this.buffer.lines.get(this.buffer.ybase + y); if (!line) { return; } @@ -2138,7 +2117,7 @@ Terminal.prototype.eraseRight = function(x, y) { * @param {number} y The line in which to operate. */ Terminal.prototype.eraseLeft = function(x, y) { - var line = this.lines.get(this.ybase + y); + var line = this.buffer.lines.get(this.buffer.ybase + y); if (!line) { return; } @@ -2154,20 +2133,20 @@ Terminal.prototype.eraseLeft = function(x, y) { * Clears the entire buffer, making the prompt line the new first line. */ Terminal.prototype.clear = function() { - if (this.ybase === 0 && this.y === 0) { + if (this.buffer.ybase === 0 && this.buffer.y === 0) { // Don't clear if it's already clear return; } - this.lines.set(0, this.lines.get(this.ybase + this.y)); - this.lines.length = 1; - this.ydisp = 0; - this.ybase = 0; - this.y = 0; + this.buffer.lines.set(0, this.buffer.lines.get(this.buffer.ybase + this.buffer.y)); + this.buffer.lines.length = 1; + this.buffer.ydisp = 0; + this.buffer.ybase = 0; + this.buffer.y = 0; for (var i = 1; i < this.rows; i++) { - this.lines.push(this.blankLine()); + this.buffer.lines.push(this.blankLine()); } this.refresh(0, this.rows - 1); - this.emit('scroll', this.ydisp); + this.emit('scroll', this.buffer.ydisp); }; /** @@ -2244,7 +2223,7 @@ Terminal.prototype.handler = function(data) { } // Input is being sent to the terminal, the terminal should focus the prompt. - if (this.ybase !== this.ydisp) { + if (this.buffer.ybase !== this.buffer.ydisp) { this.scrollToBottom(); } this.emit('data', data); @@ -2274,14 +2253,14 @@ Terminal.prototype.handleTitle = function(title) { * ESC D Index (IND is 0x84). */ Terminal.prototype.index = function() { - this.y++; - if (this.y > this.scrollBottom) { - this.y--; + this.buffer.y++; + if (this.buffer.y > this.buffer.scrollBottom) { + this.buffer.y--; this.scroll(); } // If the end of the line is hit, prevent this action from wrapping around to the next line. - if (this.x >= this.cols) { - this.x--; + if (this.buffer.x >= this.cols) { + this.buffer.x--; } }; @@ -2293,16 +2272,16 @@ Terminal.prototype.index = function() { */ Terminal.prototype.reverseIndex = function() { var j; - if (this.y === this.scrollTop) { + if (this.buffer.y === this.buffer.scrollTop) { // possibly move the code below to term.reverseScroll(); // test: echo -ne '\e[1;1H\e[44m\eM\e[0m' // blankLine(true) is xterm/linux behavior - this.lines.shiftElements(this.y + this.ybase, this.rows - 1, 1); - this.lines.set(this.y + this.ybase, this.blankLine(true)); - this.updateRange(this.scrollTop); - this.updateRange(this.scrollBottom); + this.buffer.lines.shiftElements(this.buffer.y + this.buffer.ybase, this.rows - 1, 1); + this.buffer.lines.set(this.buffer.y + this.buffer.ybase, this.blankLine(true)); + this.updateRange(this.buffer.scrollTop); + this.updateRange(this.buffer.scrollBottom); } else { - this.y--; + this.buffer.y--; } }; @@ -2315,9 +2294,13 @@ Terminal.prototype.reset = function() { this.options.cols = this.cols; var customKeyEventHandler = this.customKeyEventHandler; var cursorBlinkInterval = this.cursorBlinkInterval; + var inputHandler = this.inputHandler; + var buffers = this.buffers; Terminal.call(this, this.options); this.customKeyEventHandler = customKeyEventHandler; this.cursorBlinkInterval = cursorBlinkInterval; + this.inputHandler = inputHandler; + this.buffers = buffers; this.refresh(0, this.rows - 1); this.viewport.syncScrollArea(); }; @@ -2327,7 +2310,7 @@ Terminal.prototype.reset = function() { * ESC H Tab Set (HTS is 0x88). */ Terminal.prototype.tabSet = function() { - this.tabs[this.x] = true; + this.buffer.tabs[this.buffer.x] = true; }; /**