]> git.proxmox.com Git - mirror_xterm.js.git/commitdiff
Merge branch 'master' into faster_wcwidth
authorDaniel Imms <tyriar@tyriar.com>
Mon, 17 Jul 2017 18:06:38 +0000 (11:06 -0700)
committerGitHub <noreply@github.com>
Mon, 17 Jul 2017 18:06:38 +0000 (11:06 -0700)
22 files changed:
.travis.yml
gulpfile.js
package-lock.json
package.json
src/Buffer.test.ts [new file with mode: 0644]
src/Buffer.ts [new file with mode: 0644]
src/BufferSet.test.ts [new file with mode: 0644]
src/BufferSet.ts [new file with mode: 0644]
src/EventEmitter.ts
src/InputHandler.ts
src/Interfaces.ts
src/Parser.ts
src/Renderer.ts
src/SelectionManager.test.ts
src/SelectionManager.ts
src/SelectionModel.test.ts
src/SelectionModel.ts
src/Viewport.test.ts
src/Viewport.ts
src/test/escape-sequences-test.js
src/test/test.js
src/xterm.js

index 8297ec37f5b74150f94c5c7d029484ee1505cc66..1878c3a4d864846959b7747bf671306049d10219 100644 (file)
@@ -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
index 9b0a6de73f25a775eba3602677e134a504b472ee..ef4e46099858a36e60b12b3ebb5678973c1b3960 100644 (file)
@@ -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();
 });
index 73745a32652d027dc899dc6493116f7cdaa23005..98ea09896108c638c496fcf3aacecec418e64fae 100644 (file)
@@ -1,6 +1,6 @@
 {
   "name": "xterm",
-  "version": "2.8.0",
+  "version": "2.8.1",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
       "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",
       "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=",
         "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=",
         "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=",
         "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",
         }
       }
     },
+    "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=",
       "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",
       "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=",
         }
       }
     },
+    "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=",
         }
       }
     },
+    "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=",
         "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",
       "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=",
       "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=",
         "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",
       "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",
         }
       }
     },
+    "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",
       "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",
       "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",
       "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=",
       "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=",
         }
       }
     },
+    "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=",
       "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=",
         "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=",
         "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=",
         "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",
index b23f4a2c50039dd4869e0793dc99f5243dc066a3..917d35e4d9d723792e86f0aaa680d75ac1b2d369 100644 (file)
     "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 (file)
index 0000000..f68baa8
--- /dev/null
@@ -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 = <any>{
+      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 (file)
index 0000000..93af3c4
--- /dev/null
@@ -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 (file)
index 0000000..2101fbc
--- /dev/null
@@ -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 = <any>{
+      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 (file)
index 0000000..e86c098
--- /dev/null
@@ -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);
+  }
+}
index a4fd4542c3c8a97ad4e2aa9b88696a407890ccc0..00c10941864e37634ee827115a6c14a8e5c26c8c 100644 (file)
@@ -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() {
index 85d0e8cf0cf557d88ab8b5bc280e1551b38db351..15d10c57f754d99cfb159c5266e95e017c2c695f 100644 (file)
@@ -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;
   }
 }
 
index 4de2f285822a07d3b7e3ff41821a4959294a2595..f19a7f28f8f4af5337dfed4c99861c3e1e6787fa 100644 (file)
@@ -24,9 +24,6 @@ export interface ITerminal {
   selectionManager: ISelectionManager;
   charMeasure: ICharMeasure;
   textarea: HTMLTextAreaElement;
-  ybase: number;
-  ydisp: number;
-  lines: ICircularList<string>;
   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<T> {
+export interface ICircularList<T> extends IEventEmitter {
   length: number;
   maxLength: number;
 
@@ -85,6 +103,11 @@ interface ICircularList<T> {
   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
index 00d574eec80871e9063feb26d0ebcec6ee7b19f2..6679e366d14af8d8c8762a364425405da5168650 100644 (file)
@@ -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;
 
index f057a912bb7fe86a35d1dc7a88605db0b2486a7e..db4d6a63b5cd4502fa6e8b226b95f15d8004e2ca 100644 (file)
@@ -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);
 
index 71505a294be64c4b3686d74fefabd9232f0cecc5..668fd92eb54435a25f9aed1b346a80f0de22f071 100644 (file)
@@ -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<any>,
+    buffer: ICircularList<[number, string, number][]>,
     rowContainer: HTMLElement,
     charMeasure: CharMeasure
   ) {
@@ -36,7 +37,7 @@ describe('SelectionManager', () => {
   let document: Document;
 
   let terminal: ITerminal;
-  let buffer: CircularList<any>;
+  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<any>(100);
     terminal = <any>{ 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');
     });
   });
index 31ee8c2c8f677e6b34ee8ba2bade1680b43abd3b..5a86a2b9cf6539d5777d8f3a52fcdc071ea85601 100644 (file)
@@ -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<any>,
+    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<any>): 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 ((<any>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 ((<any>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();
     }
index ab22b77cc50bb9fb6ccbf8a25df0528dc82de1a4..6da3874bd40ed0e3ade296f0bdf3079bc1add443 100644 (file)
@@ -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 = <any>{ cols: 80, rows: 2, ybase: 0 };
+    terminal.scrollback = 10;
+    terminal.buffers = new BufferSet(terminal);
+    terminal.buffer = terminal.buffers.active;
+
     model = new TestSelectionModel(terminal);
   });
 
index 410af3b3a681b96c5b9261b5b90ae144171dc60c..8c599fd7ae1177e66fe0ea0beda84f11707ea225 100644 (file)
@@ -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) {
index 193e7969fd6732e2e4c51c7fbdb2999825375a4a..6d09ea86a2b9d102b109ead0bc7746ff50f9f0d8 100644 (file)
@@ -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');
index 5be6bffdfbb411bec5c91ea05538fcc5d2903b5d..fe276935bb7b507f67f115598c038e53c12b398a 100644 (file)
@@ -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);
   }
 
index c3831580dc4913f22f40b5168c036457a6cd3df8..b4a53a17c36d8158bccfc8c76346e5bbfb0bb887 100644 (file)
@@ -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<term.cols; ++cell) {
-      line_s += term.lines.get(line)[cell][1];
+      line_s += term.buffer.lines.get(line)[cell][1];
     }
     // rtrim empty cells as xterm does
     line_s = line_s.replace(/\s+$/, '');
index 5e18397a9f7e43c38747a865ec12a990cfd0bdc4..c5130e2130dceb52e5befcf0115f8de98cc93a19 100644 (file)
@@ -64,15 +64,15 @@ describe('xterm.js', function() {
 
   describe('clear', function() {
     it('should clear a buffer equal to rows', function() {
-      var promptLine = xterm.lines.get(xterm.ybase + xterm.y);
+      var promptLine = xterm.buffer.lines.get(xterm.buffer.ybase + xterm.buffer.y);
       xterm.clear();
-      assert.equal(xterm.y, 0);
-      assert.equal(xterm.ybase, 0);
-      assert.equal(xterm.ydisp, 0);
-      assert.equal(xterm.lines.length, xterm.rows);
-      assert.deepEqual(xterm.lines.get(0), promptLine);
+      assert.equal(xterm.buffer.y, 0);
+      assert.equal(xterm.buffer.ybase, 0);
+      assert.equal(xterm.buffer.ydisp, 0);
+      assert.equal(xterm.buffer.lines.length, xterm.rows);
+      assert.deepEqual(xterm.buffer.lines.get(0), promptLine);
       for (var i = 1; i < xterm.rows; i++) {
-        assert.deepEqual(xterm.lines.get(i), xterm.blankLine());
+        assert.deepEqual(xterm.buffer.lines.get(i), xterm.blankLine());
       }
     });
     it('should clear a buffer larger than rows', function() {
@@ -81,28 +81,28 @@ describe('xterm.js', function() {
         xterm.write('test\n');
       }
 
-      var promptLine = xterm.lines.get(xterm.ybase + xterm.y);
+      var promptLine = xterm.buffer.lines.get(xterm.buffer.ybase + xterm.buffer.y);
       xterm.clear();
-      assert.equal(xterm.y, 0);
-      assert.equal(xterm.ybase, 0);
-      assert.equal(xterm.ydisp, 0);
-      assert.equal(xterm.lines.length, xterm.rows);
-      assert.deepEqual(xterm.lines.get(0), promptLine);
+      assert.equal(xterm.buffer.y, 0);
+      assert.equal(xterm.buffer.ybase, 0);
+      assert.equal(xterm.buffer.ydisp, 0);
+      assert.equal(xterm.buffer.lines.length, xterm.rows);
+      assert.deepEqual(xterm.buffer.lines.get(0), promptLine);
       for (var i = 1; i < xterm.rows; i++) {
-        assert.deepEqual(xterm.lines.get(i), xterm.blankLine());
+        assert.deepEqual(xterm.buffer.lines.get(i), xterm.blankLine());
       }
     });
     it('should not break the prompt when cleared twice', function() {
-      var promptLine = xterm.lines.get(xterm.ybase + xterm.y);
+      var promptLine = xterm.buffer.lines.get(xterm.buffer.ybase + xterm.buffer.y);
       xterm.clear();
       xterm.clear();
-      assert.equal(xterm.y, 0);
-      assert.equal(xterm.ybase, 0);
-      assert.equal(xterm.ydisp, 0);
-      assert.equal(xterm.lines.length, xterm.rows);
-      assert.deepEqual(xterm.lines.get(0), promptLine);
+      assert.equal(xterm.buffer.y, 0);
+      assert.equal(xterm.buffer.ybase, 0);
+      assert.equal(xterm.buffer.ydisp, 0);
+      assert.equal(xterm.buffer.lines.length, xterm.rows);
+      assert.deepEqual(xterm.buffer.lines.get(0), promptLine);
       for (var i = 1; i < xterm.rows; i++) {
-        assert.deepEqual(xterm.lines.get(i), xterm.blankLine());
+        assert.deepEqual(xterm.buffer.lines.get(i), xterm.blankLine());
       }
     });
   });
@@ -117,29 +117,29 @@ describe('xterm.js', function() {
         startYDisp = xterm.rows + 1;
       });
       it('should scroll a single line', function() {
-        assert.equal(xterm.ydisp, startYDisp);
+        assert.equal(xterm.buffer.ydisp, startYDisp);
         xterm.scrollDisp(-1);
-        assert.equal(xterm.ydisp, startYDisp - 1);
+        assert.equal(xterm.buffer.ydisp, startYDisp - 1);
         xterm.scrollDisp(1);
-        assert.equal(xterm.ydisp, startYDisp);
+        assert.equal(xterm.buffer.ydisp, startYDisp);
       });
       it('should scroll multiple lines', function() {
-        assert.equal(xterm.ydisp, startYDisp);
+        assert.equal(xterm.buffer.ydisp, startYDisp);
         xterm.scrollDisp(-5);
-        assert.equal(xterm.ydisp, startYDisp - 5);
+        assert.equal(xterm.buffer.ydisp, startYDisp - 5);
         xterm.scrollDisp(5);
-        assert.equal(xterm.ydisp, startYDisp);
+        assert.equal(xterm.buffer.ydisp, startYDisp);
       });
       it('should not scroll beyond the bounds of the buffer', function() {
-        assert.equal(xterm.ydisp, startYDisp);
+        assert.equal(xterm.buffer.ydisp, startYDisp);
         xterm.scrollDisp(1);
-        assert.equal(xterm.ydisp, startYDisp);
+        assert.equal(xterm.buffer.ydisp, startYDisp);
         for (var i = 0; i < startYDisp; i++) {
           xterm.scrollDisp(-1);
         }
-        assert.equal(xterm.ydisp, 0);
+        assert.equal(xterm.buffer.ydisp, 0);
         xterm.scrollDisp(-1);
-        assert.equal(xterm.ydisp, 0);
+        assert.equal(xterm.buffer.ydisp, 0);
       });
     });
 
@@ -152,18 +152,18 @@ describe('xterm.js', function() {
         startYDisp = (xterm.rows * 2) + 1;
       });
       it('should scroll a single page', function() {
-        assert.equal(xterm.ydisp, startYDisp);
+        assert.equal(xterm.buffer.ydisp, startYDisp);
         xterm.scrollPages(-1);
-        assert.equal(xterm.ydisp, startYDisp - (xterm.rows - 1));
+        assert.equal(xterm.buffer.ydisp, startYDisp - (xterm.rows - 1));
         xterm.scrollPages(1);
-        assert.equal(xterm.ydisp, startYDisp);
+        assert.equal(xterm.buffer.ydisp, startYDisp);
       });
       it('should scroll a multiple pages', function() {
-        assert.equal(xterm.ydisp, startYDisp);
+        assert.equal(xterm.buffer.ydisp, startYDisp);
         xterm.scrollPages(-2);
-        assert.equal(xterm.ydisp, startYDisp - (xterm.rows - 1) * 2);
+        assert.equal(xterm.buffer.ydisp, startYDisp - (xterm.rows - 1) * 2);
         xterm.scrollPages(2);
-        assert.equal(xterm.ydisp, startYDisp);
+        assert.equal(xterm.buffer.ydisp, startYDisp);
       });
     });
 
@@ -174,9 +174,9 @@ describe('xterm.js', function() {
         }
       });
       it('should scroll to the top', function() {
-        assert.notEqual(xterm.ydisp, 0);
+        assert.notEqual(xterm.buffer.ydisp, 0);
         xterm.scrollToTop();
-        assert.equal(xterm.ydisp, 0);
+        assert.equal(xterm.buffer.ydisp, 0);
       });
     });
 
@@ -191,13 +191,13 @@ describe('xterm.js', function() {
       it('should scroll to the bottom', function() {
         xterm.scrollDisp(-1);
         xterm.scrollToBottom();
-        assert.equal(xterm.ydisp, startYDisp);
+        assert.equal(xterm.buffer.ydisp, startYDisp);
         xterm.scrollPages(-1);
         xterm.scrollToBottom();
-        assert.equal(xterm.ydisp, startYDisp);
+        assert.equal(xterm.buffer.ydisp, startYDisp);
         xterm.scrollToTop();
         xterm.scrollToBottom();
-        assert.equal(xterm.ydisp, startYDisp);
+        assert.equal(xterm.buffer.ydisp, startYDisp);
       });
     });
 
@@ -214,13 +214,13 @@ describe('xterm.js', function() {
           stopPropagation: function(){}
         };
 
-        xterm.ydisp = 0;
-        xterm.ybase = 40;
-        assert.notEqual(xterm.ydisp, xterm.ybase);
+        xterm.buffer.ydisp = 0;
+        xterm.buffer.ybase = 40;
+        assert.notEqual(xterm.buffer.ydisp, xterm.buffer.ybase);
         xterm.keyDown(event);
 
         // Ensure that now the terminal is scrolled to bottom
-        assert.equal(xterm.ydisp, xterm.ybase);
+        assert.equal(xterm.buffer.ydisp, xterm.buffer.ybase);
       });
 
       it('should not scroll down, when a custom keydown handler prevents the event', function () {
@@ -233,11 +233,11 @@ describe('xterm.js', function() {
           return false;
         });
 
-        assert.equal(xterm.ydisp, startYDisp);
+        assert.equal(xterm.buffer.ydisp, startYDisp);
         xterm.scrollDisp(-1);
-        assert.equal(xterm.ydisp, startYDisp - 1);
+        assert.equal(xterm.buffer.ydisp, startYDisp - 1);
         xterm.keyDown({ keyCode: 0 });
-        assert.equal(xterm.ydisp, startYDisp - 1);
+        assert.equal(xterm.buffer.ydisp, startYDisp - 1);
       });
     });
   });
@@ -575,48 +575,48 @@ describe('xterm.js', function() {
       var high = String.fromCharCode(0xD800);
       for (var i=0xDC00; i<=0xDCFF; ++i) {
         xterm.write(high + String.fromCharCode(i));
-        var tchar = xterm.lines.get(0)[0];
+        var tchar = xterm.buffer.lines.get(0)[0];
         expect(tchar[1]).eql(high + String.fromCharCode(i));
         expect(tchar[1].length).eql(2);
         expect(tchar[2]).eql(1);
-        expect(xterm.lines.get(0)[1][1]).eql(' ');
+        expect(xterm.buffer.lines.get(0)[1][1]).eql(' ');
         xterm.reset();
       }
     });
     it('2 characters at last cell', function() {
       var high = String.fromCharCode(0xD800);
       for (var i=0xDC00; i<=0xDCFF; ++i) {
-        xterm.x = xterm.cols - 1;
+        xterm.buffer.x = xterm.cols - 1;
         xterm.write(high + String.fromCharCode(i));
-        expect(xterm.lines.get(0)[xterm.x-1][1]).eql(high + String.fromCharCode(i));
-        expect(xterm.lines.get(0)[xterm.x-1][1].length).eql(2);
-        expect(xterm.lines.get(1)[0][1]).eql(' ');
+        expect(xterm.buffer.lines.get(0)[xterm.buffer.x-1][1]).eql(high + String.fromCharCode(i));
+        expect(xterm.buffer.lines.get(0)[xterm.buffer.x-1][1].length).eql(2);
+        expect(xterm.buffer.lines.get(1)[0][1]).eql(' ');
         xterm.reset();
       }
     });
     it('2 characters per cell over line end with autowrap', function() {
       var high = String.fromCharCode(0xD800);
       for (var i=0xDC00; i<=0xDCFF; ++i) {
-        xterm.x = xterm.cols - 1;
+        xterm.buffer.x = xterm.cols - 1;
         xterm.wraparoundMode = true;
         xterm.write('a' + high + String.fromCharCode(i));
-        expect(xterm.lines.get(0)[xterm.cols-1][1]).eql('a');
-        expect(xterm.lines.get(1)[0][1]).eql(high + String.fromCharCode(i));
-        expect(xterm.lines.get(1)[0][1].length).eql(2);
-        expect(xterm.lines.get(1)[1][1]).eql(' ');
+        expect(xterm.buffer.lines.get(0)[xterm.cols-1][1]).eql('a');
+        expect(xterm.buffer.lines.get(1)[0][1]).eql(high + String.fromCharCode(i));
+        expect(xterm.buffer.lines.get(1)[0][1].length).eql(2);
+        expect(xterm.buffer.lines.get(1)[1][1]).eql(' ');
         xterm.reset();
       }
     });
     it('2 characters per cell over line end without autowrap', function() {
       var high = String.fromCharCode(0xD800);
       for (var i=0xDC00; i<=0xDCFF; ++i) {
-        xterm.x = xterm.cols - 1;
+        xterm.buffer.x = xterm.cols - 1;
         xterm.wraparoundMode = false;
         xterm.write('a' + high + String.fromCharCode(i));
         // auto wraparound mode should cut off the rest of the line
-        expect(xterm.lines.get(0)[xterm.cols-1][1]).eql('a');
-        expect(xterm.lines.get(0)[xterm.cols-1][1].length).eql(1);
-        expect(xterm.lines.get(1)[1][1]).eql(' ');
+        expect(xterm.buffer.lines.get(0)[xterm.cols-1][1]).eql('a');
+        expect(xterm.buffer.lines.get(0)[xterm.cols-1][1].length).eql(1);
+        expect(xterm.buffer.lines.get(1)[1][1]).eql(' ');
         xterm.reset();
       }
     });
@@ -625,11 +625,11 @@ describe('xterm.js', function() {
       for (var i=0xDC00; i<=0xDCFF; ++i) {
         xterm.write(high);
         xterm.write(String.fromCharCode(i));
-        var tchar = xterm.lines.get(0)[0];
+        var tchar = xterm.buffer.lines.get(0)[0];
         expect(tchar[1]).eql(high + String.fromCharCode(i));
         expect(tchar[1].length).eql(2);
         expect(tchar[2]).eql(1);
-        expect(xterm.lines.get(0)[1][1]).eql(' ');
+        expect(xterm.buffer.lines.get(0)[1][1]).eql(' ');
         xterm.reset();
       }
     });
@@ -638,30 +638,30 @@ describe('xterm.js', function() {
   describe('unicode - combining characters', function() {
     it('café', function () {
       xterm.write('cafe\u0301');
-      expect(xterm.lines.get(0)[3][1]).eql('e\u0301');
-      expect(xterm.lines.get(0)[3][1].length).eql(2);
-      expect(xterm.lines.get(0)[3][2]).eql(1);
+      expect(xterm.buffer.lines.get(0)[3][1]).eql('e\u0301');
+      expect(xterm.buffer.lines.get(0)[3][1].length).eql(2);
+      expect(xterm.buffer.lines.get(0)[3][2]).eql(1);
     });
     it('café - end of line', function() {
-      xterm.x = xterm.cols - 1 - 3;
+      xterm.buffer.x = xterm.cols - 1 - 3;
       xterm.write('cafe\u0301');
-      expect(xterm.lines.get(0)[xterm.cols-1][1]).eql('e\u0301');
-      expect(xterm.lines.get(0)[xterm.cols-1][1].length).eql(2);
-      expect(xterm.lines.get(0)[xterm.cols-1][2]).eql(1);
-      expect(xterm.lines.get(0)[1][1]).eql(' ');
-      expect(xterm.lines.get(0)[1][1].length).eql(1);
-      expect(xterm.lines.get(0)[1][2]).eql(1);
+      expect(xterm.buffer.lines.get(0)[xterm.cols-1][1]).eql('e\u0301');
+      expect(xterm.buffer.lines.get(0)[xterm.cols-1][1].length).eql(2);
+      expect(xterm.buffer.lines.get(0)[xterm.cols-1][2]).eql(1);
+      expect(xterm.buffer.lines.get(0)[1][1]).eql(' ');
+      expect(xterm.buffer.lines.get(0)[1][1].length).eql(1);
+      expect(xterm.buffer.lines.get(0)[1][2]).eql(1);
     });
     it('multiple combined Ã©', function() {
       xterm.wraparoundMode = true;
       xterm.write(Array(100).join('e\u0301'));
       for (var i=0; i<xterm.cols; ++i) {
-        var tchar = xterm.lines.get(0)[i];
+        var tchar = xterm.buffer.lines.get(0)[i];
         expect(tchar[1]).eql('e\u0301');
         expect(tchar[1].length).eql(2);
         expect(tchar[2]).eql(1);
       }
-      tchar = xterm.lines.get(1)[0];
+      tchar = xterm.buffer.lines.get(1)[0];
       expect(tchar[1]).eql('e\u0301');
       expect(tchar[1].length).eql(2);
       expect(tchar[2]).eql(1);
@@ -670,12 +670,12 @@ describe('xterm.js', function() {
       xterm.wraparoundMode = true;
       xterm.write(Array(100).join('\uD800\uDC00\u0301'));
       for (var i=0; i<xterm.cols; ++i) {
-        var tchar = xterm.lines.get(0)[i];
+        var tchar = xterm.buffer.lines.get(0)[i];
         expect(tchar[1]).eql('\uD800\uDC00\u0301');
         expect(tchar[1].length).eql(3);
         expect(tchar[2]).eql(1);
       }
-      tchar = xterm.lines.get(1)[0];
+      tchar = xterm.buffer.lines.get(1)[0];
       expect(tchar[1]).eql('\uD800\uDC00\u0301');
       expect(tchar[1].length).eql(3);
       expect(tchar[2]).eql(1);
@@ -684,21 +684,21 @@ describe('xterm.js', function() {
 
   describe('unicode - fullwidth characters', function() {
     it('cursor movement even', function() {
-      expect(xterm.x).eql(0);
+      expect(xterm.buffer.x).eql(0);
       xterm.write('ï¿¥');
-      expect(xterm.x).eql(2);
+      expect(xterm.buffer.x).eql(2);
     });
     it('cursor movement odd', function() {
-      xterm.x = 1;
-      expect(xterm.x).eql(1);
+      xterm.buffer.x = 1;
+      expect(xterm.buffer.x).eql(1);
       xterm.write('ï¿¥');
-      expect(xterm.x).eql(3);
+      expect(xterm.buffer.x).eql(3);
     });
     it('line of ï¿¥ even', function() {
       xterm.wraparoundMode = true;
       xterm.write(Array(50).join('ï¿¥'));
       for (var i=0; i<xterm.cols; ++i) {
-        var tchar = xterm.lines.get(0)[i];
+        var tchar = xterm.buffer.lines.get(0)[i];
         if (i % 2) {
           expect(tchar[1]).eql('');
           expect(tchar[1].length).eql(0);
@@ -709,17 +709,17 @@ describe('xterm.js', function() {
           expect(tchar[2]).eql(2);
         }
       }
-      tchar = xterm.lines.get(1)[0];
+      tchar = xterm.buffer.lines.get(1)[0];
       expect(tchar[1]).eql('ï¿¥');
       expect(tchar[1].length).eql(1);
       expect(tchar[2]).eql(2);
     });
     it('line of ï¿¥ odd', function() {
       xterm.wraparoundMode = true;
-      xterm.x = 1;
+      xterm.buffer.x = 1;
       xterm.write(Array(50).join('ï¿¥'));
       for (var i=1; i<xterm.cols-1; ++i) {
-        var tchar = xterm.lines.get(0)[i];
+        var tchar = xterm.buffer.lines.get(0)[i];
         if (!(i % 2)) {
           expect(tchar[1]).eql('');
           expect(tchar[1].length).eql(0);
@@ -730,21 +730,21 @@ describe('xterm.js', function() {
           expect(tchar[2]).eql(2);
         }
       }
-      tchar = xterm.lines.get(0)[xterm.cols-1];
+      tchar = xterm.buffer.lines.get(0)[xterm.cols-1];
       expect(tchar[1]).eql(' ');
       expect(tchar[1].length).eql(1);
       expect(tchar[2]).eql(1);
-      tchar = xterm.lines.get(1)[0];
+      tchar = xterm.buffer.lines.get(1)[0];
       expect(tchar[1]).eql('ï¿¥');
       expect(tchar[1].length).eql(1);
       expect(tchar[2]).eql(2);
     });
     it('line of ï¿¥ with combining odd', function() {
       xterm.wraparoundMode = true;
-      xterm.x = 1;
+      xterm.buffer.x = 1;
       xterm.write(Array(50).join('ï¿¥\u0301'));
       for (var i=1; i<xterm.cols-1; ++i) {
-        var tchar = xterm.lines.get(0)[i];
+        var tchar = xterm.buffer.lines.get(0)[i];
         if (!(i % 2)) {
           expect(tchar[1]).eql('');
           expect(tchar[1].length).eql(0);
@@ -755,11 +755,11 @@ describe('xterm.js', function() {
           expect(tchar[2]).eql(2);
         }
       }
-      tchar = xterm.lines.get(0)[xterm.cols-1];
+      tchar = xterm.buffer.lines.get(0)[xterm.cols-1];
       expect(tchar[1]).eql(' ');
       expect(tchar[1].length).eql(1);
       expect(tchar[2]).eql(1);
-      tchar = xterm.lines.get(1)[0];
+      tchar = xterm.buffer.lines.get(1)[0];
       expect(tchar[1]).eql('ï¿¥\u0301');
       expect(tchar[1].length).eql(2);
       expect(tchar[2]).eql(2);
@@ -768,7 +768,7 @@ describe('xterm.js', function() {
       xterm.wraparoundMode = true;
       xterm.write(Array(50).join('ï¿¥\u0301'));
       for (var i=0; i<xterm.cols; ++i) {
-        var tchar = xterm.lines.get(0)[i];
+        var tchar = xterm.buffer.lines.get(0)[i];
         if (i % 2) {
           expect(tchar[1]).eql('');
           expect(tchar[1].length).eql(0);
@@ -779,17 +779,17 @@ describe('xterm.js', function() {
           expect(tchar[2]).eql(2);
         }
       }
-      tchar = xterm.lines.get(1)[0];
+      tchar = xterm.buffer.lines.get(1)[0];
       expect(tchar[1]).eql('ï¿¥\u0301');
       expect(tchar[1].length).eql(2);
       expect(tchar[2]).eql(2);
     });
     it('line of surrogate fullwidth with combining odd', function() {
       xterm.wraparoundMode = true;
-      xterm.x = 1;
+      xterm.buffer.x = 1;
       xterm.write(Array(50).join('\ud843\ude6d\u0301'));
       for (var i=1; i<xterm.cols-1; ++i) {
-        var tchar = xterm.lines.get(0)[i];
+        var tchar = xterm.buffer.lines.get(0)[i];
         if (!(i % 2)) {
           expect(tchar[1]).eql('');
           expect(tchar[1].length).eql(0);
@@ -800,11 +800,11 @@ describe('xterm.js', function() {
           expect(tchar[2]).eql(2);
         }
       }
-    tchar = xterm.lines.get(0)[xterm.cols-1];
+    tchar = xterm.buffer.lines.get(0)[xterm.cols-1];
       expect(tchar[1]).eql(' ');
       expect(tchar[1].length).eql(1);
       expect(tchar[2]).eql(1);
-      tchar = xterm.lines.get(1)[0];
+      tchar = xterm.buffer.lines.get(1)[0];
       expect(tchar[1]).eql('\ud843\ude6d\u0301');
       expect(tchar[1].length).eql(3);
       expect(tchar[2]).eql(2);
@@ -813,7 +813,7 @@ describe('xterm.js', function() {
       xterm.wraparoundMode = true;
       xterm.write(Array(50).join('\ud843\ude6d\u0301'));
       for (var i=0; i<xterm.cols; ++i) {
-        var tchar = xterm.lines.get(0)[i];
+        var tchar = xterm.buffer.lines.get(0)[i];
         if (i % 2) {
           expect(tchar[1]).eql('');
           expect(tchar[1].length).eql(0);
@@ -824,7 +824,7 @@ describe('xterm.js', function() {
           expect(tchar[2]).eql(2);
         }
       }
-      tchar = xterm.lines.get(1)[0];
+      tchar = xterm.buffer.lines.get(1)[0];
       expect(tchar[1]).eql('\ud843\ude6d\u0301');
       expect(tchar[1].length).eql(3);
       expect(tchar[2]).eql(2);
@@ -834,44 +834,44 @@ describe('xterm.js', function() {
   describe('insert mode', function() {
     it('halfwidth - all', function () {
       xterm.write(Array(9).join('0123456789').slice(-80));
-      xterm.x = 10;
-      xterm.y = 0;
+      xterm.buffer.x = 10;
+      xterm.buffer.y = 0;
       xterm.insertMode = true;
       xterm.write('abcde');
-      expect(xterm.lines.get(0).length).eql(xterm.cols);
-      expect(xterm.lines.get(0)[10][1]).eql('a');
-      expect(xterm.lines.get(0)[14][1]).eql('e');
-      expect(xterm.lines.get(0)[15][1]).eql('0');
-      expect(xterm.lines.get(0)[79][1]).eql('4');
+      expect(xterm.buffer.lines.get(0).length).eql(xterm.cols);
+      expect(xterm.buffer.lines.get(0)[10][1]).eql('a');
+      expect(xterm.buffer.lines.get(0)[14][1]).eql('e');
+      expect(xterm.buffer.lines.get(0)[15][1]).eql('0');
+      expect(xterm.buffer.lines.get(0)[79][1]).eql('4');
     });
     it('fullwidth - insert', function() {
       xterm.write(Array(9).join('0123456789').slice(-80));
-      xterm.x = 10;
-      xterm.y = 0;
+      xterm.buffer.x = 10;
+      xterm.buffer.y = 0;
       xterm.insertMode = true;
       xterm.write('¥¥¥');
-      expect(xterm.lines.get(0).length).eql(xterm.cols);
-      expect(xterm.lines.get(0)[10][1]).eql('ï¿¥');
-      expect(xterm.lines.get(0)[11][1]).eql('');
-      expect(xterm.lines.get(0)[14][1]).eql('ï¿¥');
-      expect(xterm.lines.get(0)[15][1]).eql('');
-      expect(xterm.lines.get(0)[79][1]).eql('3');
+      expect(xterm.buffer.lines.get(0).length).eql(xterm.cols);
+      expect(xterm.buffer.lines.get(0)[10][1]).eql('ï¿¥');
+      expect(xterm.buffer.lines.get(0)[11][1]).eql('');
+      expect(xterm.buffer.lines.get(0)[14][1]).eql('ï¿¥');
+      expect(xterm.buffer.lines.get(0)[15][1]).eql('');
+      expect(xterm.buffer.lines.get(0)[79][1]).eql('3');
     });
     it('fullwidth - right border', function() {
       xterm.write(Array(41).join('ï¿¥'));
-      xterm.x = 10;
-      xterm.y = 0;
+      xterm.buffer.x = 10;
+      xterm.buffer.y = 0;
       xterm.insertMode = true;
       xterm.write('a');
-      expect(xterm.lines.get(0).length).eql(xterm.cols);
-      expect(xterm.lines.get(0)[10][1]).eql('a');
-      expect(xterm.lines.get(0)[11][1]).eql('ï¿¥');
-      expect(xterm.lines.get(0)[79][1]).eql(' ');  // fullwidth char got replaced
+      expect(xterm.buffer.lines.get(0).length).eql(xterm.cols);
+      expect(xterm.buffer.lines.get(0)[10][1]).eql('a');
+      expect(xterm.buffer.lines.get(0)[11][1]).eql('ï¿¥');
+      expect(xterm.buffer.lines.get(0)[79][1]).eql(' ');  // fullwidth char got replaced
       xterm.write('b');
-      expect(xterm.lines.get(0).length).eql(xterm.cols);
-      expect(xterm.lines.get(0)[11][1]).eql('b');
-      expect(xterm.lines.get(0)[12][1]).eql('ï¿¥');
-      expect(xterm.lines.get(0)[79][1]).eql('');  // empty cell after fullwidth
+      expect(xterm.buffer.lines.get(0).length).eql(xterm.cols);
+      expect(xterm.buffer.lines.get(0)[11][1]).eql('b');
+      expect(xterm.buffer.lines.get(0)[12][1]).eql('ï¿¥');
+      expect(xterm.buffer.lines.get(0)[79][1]).eql('');  // empty cell after fullwidth
     });
   });
 });
index a97070961df2e722c77a229a5ce5e20fb5d3a6e8..b1f20694e1ec37a89422403f799906815fed1e12 100644 (file)
@@ -10,6 +10,7 @@
  * @license MIT
  */
 
+import { BufferSet } from './BufferSet';
 import { CompositionHelper } from './CompositionHelper';
 import { EventEmitter } from './EventEmitter';
 import { Viewport } from './Viewport';
@@ -141,33 +142,10 @@ function Terminal(options) {
     this.on('data', options.handler);
   }
 
-  /**
-   * The scroll position of the y cursor, ie. ybase + y = the y position within the entire
-   * buffer
-   */
-  this.ybase = 0;
-
-  /**
-   * The scroll position of the viewport
-   */
-  this.ydisp = 0;
-
-  /**
-   * The cursor's x position after ybase
-   */
-  this.x = 0;
-
-  /**
-   * The cursor's y position after ybase
-   */
-  this.y = 0;
-
   this.cursorState = 0;
   this.cursorHidden = false;
   this.convertEol;
   this.queue = '';
-  this.scrollTop = 0;
-  this.scrollBottom = this.rows - 1;
   this.customKeyEventHandler = null;
   this.cursorBlinkInterval = null;
 
@@ -177,7 +155,6 @@ function Terminal(options) {
   this.originMode = false;
   this.insertMode = false;
   this.wraparoundMode = true; // defaults: xterm - true, vt100 - false
-  this.normal = null;
 
   // charset
   this.charset = null;
@@ -243,21 +220,23 @@ function Terminal(options) {
   // leftover surrogate high from previous write invocation
   this.surrogate_high = '';
 
-  /**
-   * An array of all lines in the entire buffer, including the prompt. The lines are array of
-   * characters which are 2-length arrays where [0] is an attribute and [1] is the character.
-   */
-  this.lines = new CircularList(this.scrollback);
+  // Create the terminal's buffers and set the current buffer
+  this.buffers = new BufferSet(this);
+  this.buffer = this.buffers.active;  // Convenience shortcut;
+  this.buffers.on('activate', function (buffer) {
+    this._terminal.buffer = buffer;
+  });
+
   var i = this.rows;
+
   while (i--) {
-    this.lines.push(this.blankLine());
+    this.buffer.lines.push(this.blankLine());
   }
   // Ensure the selection manager has the correct buffer
   if (this.selectionManager) {
-    this.selectionManager.setBuffer(this.lines);
+    this.selectionManager.setBuffer(this.buffer.lines);
   }
 
-  this.tabs;
   this.setupStops();
 
   // Store if user went browsing history in scrollback
@@ -430,17 +409,17 @@ Terminal.prototype.setOption = function(key, value) {
       }
 
       if (this.options[key] !== value) {
-        if (this.lines.length > 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;
 };
 
 /**