';
Object.keys(stringOptions).forEach(o => {
@@ -282,7 +283,7 @@ function initOptions(term: TerminalType): void {
console.log('change', o, input.value);
if (o === 'cols' || o === 'rows') {
updateTerminalSize();
- } else if (o === 'lineHeight') {
+ } else if (o === 'lineHeight' || o === 'scrollSensitivity') {
term.setOption(o, parseFloat(input.value));
updateTerminalSize();
} else {
diff --git a/demo/server.js b/demo/server.js
index 9a45d06f..f04705aa 100644
--- a/demo/server.js
+++ b/demo/server.js
@@ -14,24 +14,26 @@ function startServer() {
logs = {};
app.use('/xterm.css', express.static(__dirname + '/../css/xterm.css'));
- app.get('/logo.png', (req, res) => res.sendFile(__dirname + '/logo.png'));
-
- app.get('/', function(req, res){
- res.sendFile(__dirname + '/index.html');
+ app.get('/logo.png', (req, res) => {
+ res.sendFile(__dirname + '/logo.png'); // lgtm [js/missing-rate-limiting]
});
- app.get('/test', function(req, res){
- res.sendFile(__dirname + '/test.html');
+ app.get('/', (req, res) => {
+ res.sendFile(__dirname + '/index.html'); // lgtm [js/missing-rate-limiting]
});
- app.get('/style.css', function(req, res){
- res.sendFile(__dirname + '/style.css');
+ app.get('/test', (req, res) => {
+ res.sendFile(__dirname + '/test.html'); // lgtm [js/missing-rate-limiting]
+ });
+
+ app.get('/style.css', (req, res) => {
+ res.sendFile(__dirname + '/style.css'); // lgtm [js/missing-rate-limiting]
});
app.use('/dist', express.static(__dirname + '/dist'));
app.use('/src', express.static(__dirname + '/src'));
- app.post('/terminals', function (req, res) {
+ app.post('/terminals', (req, res) => {
const env = Object.assign({}, process.env);
env['COLORTERM'] = 'truecolor';
var cols = parseInt(req.query.cols),
@@ -55,7 +57,7 @@ function startServer() {
res.end();
});
- app.post('/terminals/:pid/size', function (req, res) {
+ app.post('/terminals/:pid/size', (req, res) => {
var pid = parseInt(req.params.pid),
cols = parseInt(req.query.cols),
rows = parseInt(req.query.rows),
diff --git a/fixtures/escape_sequence_files/t600-DECSTBM_SR.in b/fixtures/escape_sequence_files/t600-DECSTBM_SR.in
new file mode 100644
index 00000000..9fa002fc
--- /dev/null
+++ b/fixtures/escape_sequence_files/t600-DECSTBM_SR.in
@@ -0,0 +1,25 @@
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB[10;15r[5;5H[5 A[12;12H[5 A[20;20H[5 A
\ No newline at end of file
diff --git a/fixtures/escape_sequence_files/t600-DECSTBM_SR.text b/fixtures/escape_sequence_files/t600-DECSTBM_SR.text
new file mode 100644
index 00000000..8592dd1b
--- /dev/null
+++ b/fixtures/escape_sequence_files/t600-DECSTBM_SR.text
@@ -0,0 +1,25 @@
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
+ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
+ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
+ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
+ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
+ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
diff --git a/fixtures/escape_sequence_files/t601-DECSTBM_SL.in b/fixtures/escape_sequence_files/t601-DECSTBM_SL.in
new file mode 100644
index 00000000..e99f5190
--- /dev/null
+++ b/fixtures/escape_sequence_files/t601-DECSTBM_SL.in
@@ -0,0 +1,25 @@
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB[10;15r[5;5H[5 @[12;12H[5 @[20;20H[5 @
\ No newline at end of file
diff --git a/fixtures/escape_sequence_files/t601-DECSTBM_SL.text b/fixtures/escape_sequence_files/t601-DECSTBM_SL.text
new file mode 100644
index 00000000..1912cb56
--- /dev/null
+++ b/fixtures/escape_sequence_files/t601-DECSTBM_SL.text
@@ -0,0 +1,25 @@
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+fghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
diff --git a/fixtures/escape_sequence_files/t602-DECSTBM_DECIC.in b/fixtures/escape_sequence_files/t602-DECSTBM_DECIC.in
new file mode 100644
index 00000000..e31f4e12
--- /dev/null
+++ b/fixtures/escape_sequence_files/t602-DECSTBM_DECIC.in
@@ -0,0 +1,25 @@
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB[10;15r[5;5H[5'}[12;12H[5'}[20;20H[5'}
\ No newline at end of file
diff --git a/fixtures/escape_sequence_files/t602-DECSTBM_DECIC.text b/fixtures/escape_sequence_files/t602-DECSTBM_DECIC.text
new file mode 100644
index 00000000..f3fa17bc
--- /dev/null
+++ b/fixtures/escape_sequence_files/t602-DECSTBM_DECIC.text
@@ -0,0 +1,25 @@
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
+abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
+abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
+abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
+abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
+abcdefghijk lmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
diff --git a/fixtures/escape_sequence_files/t603-DECSTBM_DECDC.in b/fixtures/escape_sequence_files/t603-DECSTBM_DECDC.in
new file mode 100644
index 00000000..ecb966d9
--- /dev/null
+++ b/fixtures/escape_sequence_files/t603-DECSTBM_DECDC.in
@@ -0,0 +1,25 @@
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB[10;15r[5;5H[5'~[12;12H[5'~[20;20H[5'~
\ No newline at end of file
diff --git a/fixtures/escape_sequence_files/t603-DECSTBM_DECDC.text b/fixtures/escape_sequence_files/t603-DECSTBM_DECDC.text
new file mode 100644
index 00000000..b98db23c
--- /dev/null
+++ b/fixtures/escape_sequence_files/t603-DECSTBM_DECDC.text
@@ -0,0 +1,25 @@
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijkqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzAB
diff --git a/package.json b/package.json
index f52f7b79..709a1415 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "xterm",
"description": "Full xterm terminal, in your browser",
- "version": "4.0.0",
+ "version": "4.1.0",
"main": "lib/xterm.js",
"style": "css/xterm.css",
"types": "typings/xterm.d.ts",
@@ -47,7 +47,7 @@
"ts-loader": "^6.0.4",
"tslint": "^5.18.0",
"tslint-consistent-codestyle": "^1.13.0",
- "typescript": "3.5",
+ "typescript": "3.6",
"utf8": "^3.0.0",
"webpack": "^4.35.3",
"webpack-cli": "^3.1.0",
diff --git a/src/InputHandler.test.ts b/src/InputHandler.test.ts
index fbbafc11..ca63bdae 100644
--- a/src/InputHandler.test.ts
+++ b/src/InputHandler.test.ts
@@ -25,6 +25,14 @@ function getCursor(term: TestTerminal): number[] {
];
}
+function getLines(term: TestTerminal, limit: number = term.rows): string[] {
+ const res: string[] = [];
+ for (let i = 0; i < limit; ++i) {
+ res.push(term.buffer.lines.get(i).translateToString(true));
+ }
+ return res;
+}
+
describe('InputHandler', () => {
describe('save and restore cursor', () => {
const terminal = new MockInputHandlingTerminal();
@@ -1125,13 +1133,6 @@ describe('InputHandler', () => {
beforeEach(() => {
term = new TestTerminal({cols: 10, rows: 10});
});
- function getLines(term: TestTerminal, limit: number = term.rows): string[] {
- const res: string[] = [];
- for (let i = 0; i < limit; ++i) {
- res.push(term.buffer.lines.get(i).translateToString(true));
- }
- return res;
- }
it('scrollUp', () => {
term.writeSync('0\r\n1\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7\r\n8\r\n9\x1b[2;4r\x1b[2Sm');
assert.deepEqual(getLines(term), ['m', '3', '', '', '4', '5', '6', '7', '8', '9']);
@@ -1191,4 +1192,60 @@ describe('InputHandler', () => {
assert.deepEqual(getLines(term), ['0', '1', 'n', 'm', '', '', '6', '7', '8', '9']);
});
});
+ describe('SL/SR/DECIC/DECDC', () => {
+ let term: TestTerminal;
+ beforeEach(() => {
+ term = new TestTerminal({cols: 5, rows: 5, scrollback: 1});
+ });
+ it('SL (scrollLeft)', () => {
+ term.writeSync('12345'.repeat(6));
+ term.writeSync('\x1b[ @');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '2345', '2345', '2345', '2345', '2345']);
+ term.writeSync('\x1b[0 @');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '345', '345', '345', '345', '345']);
+ term.writeSync('\x1b[2 @');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '5', '5', '5', '5', '5']);
+ });
+ it('SR (scrollRight)', () => {
+ term.writeSync('12345'.repeat(6));
+ term.writeSync('\x1b[ A');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 1234', ' 1234', ' 1234', ' 1234', ' 1234']);
+ term.writeSync('\x1b[0 A');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 123', ' 123', ' 123', ' 123', ' 123']);
+ term.writeSync('\x1b[2 A');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 1', ' 1', ' 1', ' 1', ' 1']);
+ });
+ it('insertColumns (DECIC)', () => {
+ term.writeSync('12345'.repeat(6));
+ term.writeSync('\x1b[3;3H');
+ term.writeSync('\x1b[\'}');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']);
+ term.reset();
+ term.writeSync('12345'.repeat(6));
+ term.writeSync('\x1b[3;3H');
+ term.writeSync('\x1b[1\'}');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']);
+ term.reset();
+ term.writeSync('12345'.repeat(6));
+ term.writeSync('\x1b[3;3H');
+ term.writeSync('\x1b[2\'}');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 3', '12 3', '12 3', '12 3', '12 3']);
+ });
+ it('deleteColumns (DECDC)', () => {
+ term.writeSync('12345'.repeat(6));
+ term.writeSync('\x1b[3;3H');
+ term.writeSync('\x1b[\'~');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '1245', '1245', '1245', '1245', '1245']);
+ term.reset();
+ term.writeSync('12345'.repeat(6));
+ term.writeSync('\x1b[3;3H');
+ term.writeSync('\x1b[1\'~');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '1245', '1245', '1245', '1245', '1245']);
+ term.reset();
+ term.writeSync('12345'.repeat(6));
+ term.writeSync('\x1b[3;3H');
+ term.writeSync('\x1b[2\'~');
+ assert.deepEqual(getLines(term, term.rows + 1), ['12345', '125', '125', '125', '125', '125']);
+ });
+ });
});
diff --git a/src/InputHandler.ts b/src/InputHandler.ts
index 06fa51d7..1b5dab88 100644
--- a/src/InputHandler.ts
+++ b/src/InputHandler.ts
@@ -175,7 +175,9 @@ export class InputHandler extends Disposable implements IInputHandler {
* CSI handler
*/
this._parser.setCsiHandler({final: '@'}, params => this.insertChars(params));
+ this._parser.setCsiHandler({intermediates: ' ', final: '@'}, params => this.scrollLeft(params));
this._parser.setCsiHandler({final: 'A'}, params => this.cursorUp(params));
+ this._parser.setCsiHandler({intermediates: ' ', final: 'A'}, params => this.scrollRight(params));
this._parser.setCsiHandler({final: 'B'}, params => this.cursorDown(params));
this._parser.setCsiHandler({final: 'C'}, params => this.cursorForward(params));
this._parser.setCsiHandler({final: 'D'}, params => this.cursorBackward(params));
@@ -217,6 +219,8 @@ export class InputHandler extends Disposable implements IInputHandler {
this._parser.setCsiHandler({final: 's'}, params => this.saveCursor(params));
this._parser.setCsiHandler({final: 't'}, params => this.manipulateWindowOptions(params));
this._parser.setCsiHandler({final: 'u'}, params => this.restoreCursor(params));
+ this._parser.setCsiHandler({intermediates: '\'', final: '}'}, params => this.insertColumns(params));
+ this._parser.setCsiHandler({intermediates: '\'', final: '~'}, params => this.deleteColumns(params));
/**
* execute handler
@@ -1034,18 +1038,108 @@ export class InputHandler extends Disposable implements IInputHandler {
* CSI Ps T Scroll down Ps lines (default = 1) (SD).
*/
public scrollDown(params: IParams): void {
- if (params.length < 2) {
- let param = params.params[0] || 1;
+ let param = params.params[0] || 1;
- // make buffer local for faster access
- const buffer = this._bufferService.buffer;
+ // make buffer local for faster access
+ const buffer = this._bufferService.buffer;
- while (param--) {
- buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1);
- buffer.lines.splice(buffer.ybase + buffer.scrollTop, 0, buffer.getBlankLine(this._terminal.eraseAttrData()));
- }
- this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
+ while (param--) {
+ buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 1);
+ buffer.lines.splice(buffer.ybase + buffer.scrollTop, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
}
+ this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
+ }
+
+ /**
+ * CSI Ps SP @ Scroll left Ps columns (default = 1) (SL) ECMA-48
+ *
+ * Notation: (Pn)
+ * Representation: CSI Pn 02/00 04/00
+ * Parameter default value: Pn = 1
+ * SL causes the data in the presentation component to be moved by n character positions
+ * if the line orientation is horizontal, or by n line positions if the line orientation
+ * is vertical, such that the data appear to move to the left; where n equals the value of Pn.
+ * The active presentation position is not affected by this control function.
+ *
+ * Supported:
+ * - always left shift (no line orientation setting respected)
+ */
+ public scrollLeft(params: IParams): void {
+ const buffer = this._bufferService.buffer;
+ if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
+ return;
+ }
+ const param = params.params[0] || 1;
+ for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
+ const line = buffer.lines.get(buffer.ybase + y);
+ line.deleteCells(0, param, buffer.getNullCell(this._terminal.eraseAttrData()));
+ line.isWrapped = false;
+ }
+ this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
+ }
+
+ /**
+ * CSI Ps SP A Scroll right Ps columns (default = 1) (SR) ECMA-48
+ *
+ * Notation: (Pn)
+ * Representation: CSI Pn 02/00 04/01
+ * Parameter default value: Pn = 1
+ * SR causes the data in the presentation component to be moved by n character positions
+ * if the line orientation is horizontal, or by n line positions if the line orientation
+ * is vertical, such that the data appear to move to the right; where n equals the value of Pn.
+ * The active presentation position is not affected by this control function.
+ *
+ * Supported:
+ * - always right shift (no line orientation setting respected)
+ */
+ public scrollRight(params: IParams): void {
+ const buffer = this._bufferService.buffer;
+ if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
+ return;
+ }
+ const param = params.params[0] || 1;
+ for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
+ const line = buffer.lines.get(buffer.ybase + y);
+ line.insertCells(0, param, buffer.getNullCell(this._terminal.eraseAttrData()));
+ line.isWrapped = false;
+ }
+ this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
+ }
+
+ /**
+ * CSI Pm ' }
+ * Insert Ps Column(s) (default = 1) (DECIC), VT420 and up.
+ */
+ public insertColumns(params: IParams): void {
+ const buffer = this._bufferService.buffer;
+ if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
+ return;
+ }
+ const param = params.params[0] || 1;
+ for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
+ const line = this._bufferService.buffer.lines.get(buffer.ybase + y);
+ line.insertCells(buffer.x, param, buffer.getNullCell(this._terminal.eraseAttrData()));
+ line.isWrapped = false;
+ }
+ this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
+ }
+
+ /**
+ * CSI Pm ' ~
+ * Delete Ps Column(s) (default = 1) (DECDC), VT420 and up.
+ */
+ public deleteColumns(params: IParams): void {
+ const buffer = this._bufferService.buffer;
+ if (buffer.y > buffer.scrollBottom || buffer.y < buffer.scrollTop) {
+ return;
+ }
+ const param = params.params[0] || 1;
+ for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
+ const line = buffer.lines.get(buffer.ybase + y);
+ line.deleteCells(buffer.x, param, buffer.getNullCell(this._terminal.eraseAttrData()));
+ line.isWrapped = false;
+ }
+ this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
}
/**
diff --git a/src/Terminal.ts b/src/Terminal.ts
index 63400bcb..3e7e55ca 100644
--- a/src/Terminal.ts
+++ b/src/Terminal.ts
@@ -76,7 +76,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
/**
* The HTMLElement that the terminal is created in, set by Terminal.open.
*/
- private _parent: HTMLElement;
+ private _parent: HTMLElement | null;
private _document: Document;
private _viewportScrollArea: HTMLElement;
private _viewportElement: HTMLElement;
@@ -383,18 +383,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
case 'theme':
this._setTheme(this.optionsService.options.theme);
break;
- case 'scrollback':
- const newBufferLength = this.rows + this.optionsService.options.scrollback;
- if (this.buffer.lines.length > newBufferLength) {
- const amountToTrim = this.buffer.lines.length - newBufferLength;
- const needsRefresh = (this.buffer.ydisp - amountToTrim < 0);
- this.buffer.lines.trimStart(amountToTrim);
- this.buffer.ybase = Math.max(this.buffer.ybase - amountToTrim, 0);
- this.buffer.ydisp = Math.max(this.buffer.ydisp - amountToTrim, 0);
- if (needsRefresh) {
- this.refresh(0, this.rows - 1);
- }
- }
case 'windowsMode':
if (this.optionsService.options.windowsMode) {
if (!this._windowsMode) {
@@ -520,6 +508,10 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
throw new Error('Terminal requires a parent element.');
}
+ if (!document.body.contains(parent)) {
+ this._logService.warn('Terminal.open was called on an element that was not attached to the DOM');
+ }
+
this._document = this._parent.ownerDocument;
// Create main element container
@@ -1469,6 +1461,10 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._charSizeService.measure();
}
+ // Sync the scroll area to make sure scroll events don't fire and scroll the viewport to an
+ // invalid location
+ this.viewport.syncScrollArea(true);
+
this.refresh(0, this.rows - 1);
this._onResize.fire({ cols: x, rows: y });
}
diff --git a/src/Types.d.ts b/src/Types.d.ts
index cb45d3b1..be1019e5 100644
--- a/src/Types.d.ts
+++ b/src/Types.d.ts
@@ -85,7 +85,9 @@ export interface IInputHandler {
/** C0 SI */ shiftIn(): void;
/** CSI @ */ insertChars(params: IParams): void;
+ /** CSI SP @ */ scrollLeft(params: IParams): void;
/** CSI A */ cursorUp(params: IParams): void;
+ /** CSI SP A */ scrollRight(params: IParams): void;
/** CSI B */ cursorDown(params: IParams): void;
/** CSI C */ cursorForward(params: IParams): void;
/** CSI D */ cursorBackward(params: IParams): void;
@@ -121,6 +123,8 @@ export interface IInputHandler {
/** CSI r */ setScrollRegion(params: IParams, collect?: string): void;
/** CSI s */ saveCursor(params: IParams): void;
/** CSI u */ restoreCursor(params: IParams): void;
+ /** CSI ' } */ insertColumns(params: IParams): void;
+ /** CSI ' ~ */ deleteColumns(params: IParams): void;
/** OSC 0
OSC 2 */ setTitle(data: string): void;
/** ESC E */ nextLine(): void;
@@ -173,7 +177,7 @@ export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAcc
// Portions of the public API that are required by the internal Terminal
export interface IPublicTerminal extends IDisposable {
- textarea: HTMLTextAreaElement;
+ textarea: HTMLTextAreaElement | undefined;
rows: number;
cols: number;
buffer: IBuffer;
@@ -226,7 +230,7 @@ export interface IBufferAccessor {
}
export interface IElementAccessor {
- readonly element: HTMLElement;
+ readonly element: HTMLElement | undefined;
}
export interface ILinkifierAccessor {
diff --git a/src/browser/Linkifier.ts b/src/browser/Linkifier.ts
index 53a32aa7..ecaf8af9 100644
--- a/src/browser/Linkifier.ts
+++ b/src/browser/Linkifier.ts
@@ -306,7 +306,9 @@ export class Linkifier implements ILinkifier {
e => {
this._onLinkTooltip.fire(this._createLinkHoverEvent(x1, y1, x2, y2, fg));
if (matcher.hoverTooltipCallback) {
- matcher.hoverTooltipCallback(e, uri);
+ // Note that IViewportRange use 1-based coordinates to align with escape sequences such
+ // as CUP which use 1,1 as the default for row/col
+ matcher.hoverTooltipCallback(e, uri, { start: { row: y1 + 1, col: x1 + 1 }, end: { row: y2 + 1, col: x2 } });
}
},
() => {
diff --git a/src/browser/Types.d.ts b/src/browser/Types.d.ts
index 894beb36..274fc16f 100644
--- a/src/browser/Types.d.ts
+++ b/src/browser/Types.d.ts
@@ -35,7 +35,7 @@ export interface IPartialColorSet {
export interface IViewport extends IDisposable {
scrollBarWidth: number;
- syncScrollArea(): void;
+ syncScrollArea(immediate?: boolean): void;
getLinesScrolled(ev: WheelEvent): number;
onWheel(ev: WheelEvent): boolean;
onTouchStart(ev: TouchEvent): void;
@@ -43,14 +43,25 @@ export interface IViewport extends IDisposable {
onThemeChange(colors: IColorSet): void;
}
+export interface IViewportRange {
+ start: IViewportCellPosition;
+ end: IViewportCellPosition;
+}
+
+export interface IViewportCellPosition {
+ col: number;
+ row: number;
+}
+
export type LinkMatcherHandler = (event: MouseEvent, uri: string) => void;
+export type LinkMatcherHoverTooltipCallback = (event: MouseEvent, uri: string, position: IViewportRange) => void;
export type LinkMatcherValidationCallback = (uri: string, callback: (isValid: boolean) => void) => void;
export interface ILinkMatcher {
id: number;
regex: RegExp;
handler: LinkMatcherHandler;
- hoverTooltipCallback?: LinkMatcherHandler;
+ hoverTooltipCallback?: LinkMatcherHoverTooltipCallback;
hoverLeaveCallback?: () => void;
matchIndex?: number;
validationCallback?: LinkMatcherValidationCallback;
@@ -96,7 +107,7 @@ export interface ILinkMatcherOptions {
/**
* A callback that fires when the mouse hovers over a link.
*/
- tooltipCallback?: LinkMatcherHandler;
+ tooltipCallback?: LinkMatcherHoverTooltipCallback;
/**
* A callback that fires when the mouse leaves a link that was hovered.
*/
diff --git a/src/browser/Viewport.ts b/src/browser/Viewport.ts
index 4f9363d9..5edd7b80 100644
--- a/src/browser/Viewport.ts
+++ b/src/browser/Viewport.ts
@@ -7,7 +7,7 @@ import { Disposable } from 'common/Lifecycle';
import { addDisposableDomListener } from 'browser/Lifecycle';
import { IColorSet, IViewport } from 'browser/Types';
import { ICharSizeService, IRenderService } from 'browser/services/Services';
-import { IBufferService } from 'common/services/Services';
+import { IBufferService, IOptionsService } from 'common/services/Services';
const FALLBACK_SCROLL_BAR_WIDTH = 15;
@@ -37,6 +37,7 @@ export class Viewport extends Disposable implements IViewport {
private readonly _viewportElement: HTMLElement,
private readonly _scrollArea: HTMLElement,
@IBufferService private readonly _bufferService: IBufferService,
+ @IOptionsService private readonly _optionsService: IOptionsService,
@ICharSizeService private readonly _charSizeService: ICharSizeService,
@IRenderService private readonly _renderService: IRenderService
) {
@@ -60,7 +61,14 @@ export class Viewport extends Disposable implements IViewport {
* Refreshes row height, setting line-height, viewport height and scroll area height if
* necessary.
*/
- private _refresh(): void {
+ private _refresh(immediate: boolean): void {
+ if (immediate) {
+ this._innerRefresh();
+ if (this._refreshAnimationFrame !== null) {
+ cancelAnimationFrame(this._refreshAnimationFrame);
+ }
+ return;
+ }
if (this._refreshAnimationFrame === null) {
this._refreshAnimationFrame = requestAnimationFrame(() => this._innerRefresh());
}
@@ -88,40 +96,39 @@ export class Viewport extends Disposable implements IViewport {
this._refreshAnimationFrame = null;
}
-
/**
* Updates dimensions and synchronizes the scroll area if necessary.
*/
- public syncScrollArea(): void {
+ public syncScrollArea(immediate: boolean = false): void {
// If buffer height changed
if (this._lastRecordedBufferLength !== this._bufferService.buffer.lines.length) {
this._lastRecordedBufferLength = this._bufferService.buffer.lines.length;
- this._refresh();
+ this._refresh(immediate);
return;
}
// If viewport height changed
if (this._lastRecordedViewportHeight !== this._renderService.dimensions.canvasHeight) {
- this._refresh();
+ this._refresh(immediate);
return;
}
// If the buffer position doesn't match last scroll top
const newScrollTop = this._bufferService.buffer.ydisp * this._currentRowHeight;
if (this._lastScrollTop !== newScrollTop) {
- this._refresh();
+ this._refresh(immediate);
return;
}
// If element's scroll top changed, this can happen when hiding the element
if (this._lastScrollTop !== this._viewportElement.scrollTop) {
- this._refresh();
+ this._refresh(immediate);
return;
}
// If row height changed
if (this._renderService.dimensions.scaledCellHeight / window.devicePixelRatio !== this._currentRowHeight) {
- this._refresh();
+ this._refresh(immediate);
return;
}
}
@@ -191,7 +198,7 @@ export class Viewport extends Disposable implements IViewport {
}
// Fallback to WheelEvent.DOM_DELTA_PIXEL
- let amount = ev.deltaY;
+ let amount = this._applyScrollModifier(ev.deltaY, ev);
if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) {
amount *= this._currentRowHeight;
} else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
@@ -212,7 +219,7 @@ export class Viewport extends Disposable implements IViewport {
}
// Fallback to WheelEvent.DOM_DELTA_LINE
- let amount = ev.deltaY;
+ let amount = this._applyScrollModifier(ev.deltaY, ev);
if (ev.deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
amount /= this._currentRowHeight + 0.0; // Prevent integer division
this._wheelPartialScroll += amount;
@@ -224,6 +231,18 @@ export class Viewport extends Disposable implements IViewport {
return amount;
}
+ private _applyScrollModifier(amount: number, ev: WheelEvent): number {
+ const modifier = this._optionsService.options.fastScrollModifier;
+ // Multiply the scroll speed when the modifier is down
+ if ((modifier === 'alt' && ev.altKey) ||
+ (modifier === 'ctrl' && ev.ctrlKey) ||
+ (modifier === 'shift' && ev.shiftKey)) {
+ return amount * this._optionsService.options.fastScrollSensitivity;
+ }
+
+ return amount * this._optionsService.options.scrollSensitivity;
+ }
+
/**
* Handles the touchstart event, recording the touch occurred.
* @param ev The touch event.
diff --git a/src/browser/renderer/CharacterJoinerRegistry.ts b/src/browser/renderer/CharacterJoinerRegistry.ts
index a5abe80f..5385b76b 100644
--- a/src/browser/renderer/CharacterJoinerRegistry.ts
+++ b/src/browser/renderer/CharacterJoinerRegistry.ts
@@ -303,7 +303,6 @@ export class CharacterJoinerRegistry implements ICharacterJoinerRegistry {
// current range
ranges[i - 1][1] = Math.max(newRange[1], range[1]);
ranges.splice(i, 1);
- inRange = false;
return ranges;
}
diff --git a/src/browser/renderer/dom/DomRendererRowFactory.ts b/src/browser/renderer/dom/DomRendererRowFactory.ts
index 316645f8..3e3b5df0 100644
--- a/src/browser/renderer/dom/DomRendererRowFactory.ts
+++ b/src/browser/renderer/dom/DomRendererRowFactory.ts
@@ -8,7 +8,7 @@ import { INVERTED_DEFAULT_COLOR } from 'browser/renderer/atlas/Constants';
import { AttributeData } from 'common/buffer/AttributeData';
import { NULL_CELL_CODE, WHITESPACE_CELL_CHAR } from 'common/buffer/Constants';
import { CellData } from 'common/buffer/CellData';
-import { ITerminalOptions, IOptionsService } from 'common/services/Services';
+import { IOptionsService } from 'common/services/Services';
export const BOLD_CLASS = 'xterm-bold';
export const DIM_CLASS = 'xterm-dim';
diff --git a/src/browser/services/SelectionService.ts b/src/browser/services/SelectionService.ts
index 2efb095d..3631bbb7 100644
--- a/src/browser/services/SelectionService.ts
+++ b/src/browser/services/SelectionService.ts
@@ -210,7 +210,7 @@ export class SelectionService implements ISelectionService {
for (let i = start[1] + 1; i <= end[1] - 1; i++) {
const bufferLine = buffer.lines.get(i);
const lineText = buffer.translateBufferLineToString(i, true);
- if (bufferLine!.isWrapped) {
+ if (bufferLine && bufferLine.isWrapped) {
result[result.length - 1] += lineText;
} else {
result.push(lineText);
@@ -221,7 +221,7 @@ export class SelectionService implements ISelectionService {
if (start[1] !== end[1]) {
const bufferLine = buffer.lines.get(end[1]);
const lineText = buffer.translateBufferLineToString(end[1], true, 0, end[0]);
- if (bufferLine!.isWrapped) {
+ if (bufferLine && bufferLine!.isWrapped) {
result[result.length - 1] += lineText;
} else {
result.push(lineText);
diff --git a/src/common/buffer/Buffer.ts b/src/common/buffer/Buffer.ts
index 1b3e5d86..152bab7a 100644
--- a/src/common/buffer/Buffer.ts
+++ b/src/common/buffer/Buffer.ts
@@ -167,19 +167,25 @@ export class Buffer implements IBuffer {
if (this._rows < newRows) {
for (let y = this._rows; y < newRows; y++) {
if (this.lines.length < newRows + this.ybase) {
- if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) {
- // There is room above the buffer and there are no empty elements below the line,
- // scroll up
- this.ybase--;
- addToY++;
- if (this.ydisp > 0) {
- // Viewport is at the top of the buffer, must increase downwards
- this.ydisp--;
- }
- } else {
- // Add a blank line if there is no buffer left at the top to scroll to, or if there
- // are blank lines after the cursor
+ if (this._optionsService.options.windowsMode) {
+ // Just add the new missing rows on Windows as conpty reprints the screen with it's
+ // view of the world. Once a line enters scrollback for conpty it remains there
this.lines.push(new BufferLine(newCols, nullCell));
+ } else {
+ if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) {
+ // There is room above the buffer and there are no empty elements below the line,
+ // scroll up
+ this.ybase--;
+ addToY++;
+ if (this.ydisp > 0) {
+ // Viewport is at the top of the buffer, must increase downwards
+ this.ydisp--;
+ }
+ } else {
+ // Add a blank line if there is no buffer left at the top to scroll to, or if there
+ // are blank lines after the cursor
+ this.lines.push(new BufferLine(newCols, nullCell));
+ }
}
}
}
diff --git a/src/common/buffer/BufferLine.ts b/src/common/buffer/BufferLine.ts
index 8e742be3..1e95e004 100644
--- a/src/common/buffer/BufferLine.ts
+++ b/src/common/buffer/BufferLine.ts
@@ -5,7 +5,7 @@
import { CharData, IBufferLine, ICellData } from 'common/Types';
import { stringFromCodePoint } from 'common/input/TextDecoder';
-import { DEFAULT_COLOR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Content } from 'common/buffer/Constants';
+import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_ATTR_INDEX, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE, WHITESPACE_CELL_CHAR, Content } from 'common/buffer/Constants';
import { CellData } from 'common/buffer/CellData';
import { AttributeData } from 'common/buffer/AttributeData';
diff --git a/src/common/input/Keyboard.test.ts b/src/common/input/Keyboard.test.ts
index 409a3192..a304923e 100644
--- a/src/common/input/Keyboard.test.ts
+++ b/src/common/input/Keyboard.test.ts
@@ -108,6 +108,12 @@ describe('Keyboard', () => {
it('should return \\x1b[5C for alt+right', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: false }).key, '\x1b[1;5C'); // CSI 5 C
});
+ it('should return \\x1b[5D for alt+up', () => {
+ assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }, { isMac: false }).key, '\x1b[1;5A'); // CSI 5 D
+ });
+ it('should return \\x1b[5C for alt+down', () => {
+ assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 40 }, { isMac: false }).key, '\x1b[1;5B'); // CSI 5 C
+ });
it('should return \\x1ba for alt+a', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: false }).key, '\x1ba');
});
@@ -120,6 +126,12 @@ describe('Keyboard', () => {
it('should return \\x1bf for alt+right', () => {
assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 39 }, { isMac: true }).key, '\x1bf'); // CSI 5 C
});
+ it('should return \\x1bb for alt+up', () => {
+ assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 38 }, { isMac: true }).key, '\x1b[1;3A'); // CSI 5 D
+ });
+ it('should return \\x1bf for alt+down', () => {
+ assert.equal(testEvaluateKeyboardEvent({ altKey: true, keyCode: 40 }, { isMac: true }).key, '\x1b[1;3B'); // CSI 5 C
+ });
it('should return undefined for alt+a', () => {
assert.strictEqual(testEvaluateKeyboardEvent({ altKey: true, keyCode: 65 }, { isMac: true }).key, undefined), { isMac: true };
});
diff --git a/src/common/input/Keyboard.ts b/src/common/input/Keyboard.ts
index 2f54add6..1bf378c1 100644
--- a/src/common/input/Keyboard.ts
+++ b/src/common/input/Keyboard.ts
@@ -113,13 +113,16 @@ export function evaluateKeyboardEvent(
break;
case 37:
// left-arrow
+ if (ev.metaKey) {
+ break;
+ }
if (modifiers) {
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'D';
// HACK: Make Alt + left-arrow behave like Ctrl + left-arrow: move one word backwards
// http://unix.stackexchange.com/a/108106
// macOS uses different escape sequences than linux
if (result.key === C0.ESC + '[1;3D') {
- result.key = isMac ? C0.ESC + 'b' : C0.ESC + '[1;5D';
+ result.key = C0.ESC + (isMac ? 'b' : '[1;5D');
}
} else if (applicationCursorMode) {
result.key = C0.ESC + 'OD';
@@ -129,13 +132,16 @@ export function evaluateKeyboardEvent(
break;
case 39:
// right-arrow
+ if (ev.metaKey) {
+ break;
+ }
if (modifiers) {
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'C';
// HACK: Make Alt + right-arrow behave like Ctrl + right-arrow: move one word forward
// http://unix.stackexchange.com/a/108106
// macOS uses different escape sequences than linux
if (result.key === C0.ESC + '[1;3C') {
- result.key = isMac ? C0.ESC + 'f' : C0.ESC + '[1;5C';
+ result.key = C0.ESC + (isMac ? 'f' : '[1;5C');
}
} else if (applicationCursorMode) {
result.key = C0.ESC + 'OC';
@@ -145,11 +151,15 @@ export function evaluateKeyboardEvent(
break;
case 38:
// up-arrow
+ if (ev.metaKey) {
+ break;
+ }
if (modifiers) {
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'A';
// HACK: Make Alt + up-arrow behave like Ctrl + up-arrow
// http://unix.stackexchange.com/a/108106
- if (result.key === C0.ESC + '[1;3A') {
+ // macOS uses different escape sequences than linux
+ if (!isMac && result.key === C0.ESC + '[1;3A') {
result.key = C0.ESC + '[1;5A';
}
} else if (applicationCursorMode) {
@@ -160,11 +170,15 @@ export function evaluateKeyboardEvent(
break;
case 40:
// down-arrow
+ if (ev.metaKey) {
+ break;
+ }
if (modifiers) {
result.key = C0.ESC + '[1;' + (modifiers + 1) + 'B';
// HACK: Make Alt + down-arrow behave like Ctrl + down-arrow
// http://unix.stackexchange.com/a/108106
- if (result.key === C0.ESC + '[1;3B') {
+ // macOS uses different escape sequences than linux
+ if (!isMac && result.key === C0.ESC + '[1;3B') {
result.key = C0.ESC + '[1;5B';
}
} else if (applicationCursorMode) {
diff --git a/src/common/input/TextDecoder.test.ts b/src/common/input/TextDecoder.test.ts
index cda74a18..92b0e03a 100644
--- a/src/common/input/TextDecoder.test.ts
+++ b/src/common/input/TextDecoder.test.ts
@@ -7,6 +7,7 @@ import { assert } from 'chai';
import { StringToUtf32, stringFromCodePoint, Utf8ToUtf32, utf32ToString } from 'common/input/TextDecoder';
import { encode } from 'utf8';
+
// convert UTF32 codepoints to string
function toString(data: Uint32Array, length: number): string {
if ((String as any).fromCodePoint) {
@@ -214,6 +215,16 @@ describe('text encodings', () => {
}
assert(decoded, 'Ä€𝄞Ö𝄞€Ü𝄞€');
});
+ it('test break after 3 bytes - issue #2495', () => {
+ const decoder = new Utf8ToUtf32();
+ const target = new Uint32Array(5);
+ const utf8Data = fromByteString('\xf0\xa0\x9c\x8e');
+ let written = decoder.decode(utf8Data.slice(0, 3), target);
+ assert.equal(written, 0);
+ written = decoder.decode(utf8Data.slice(3), target);
+ assert.equal(written, 1);
+ assert(toString(target, written), '𠜎');
+ });
});
});
});
diff --git a/src/common/input/TextDecoder.ts b/src/common/input/TextDecoder.ts
index 7e141e02..397d25a7 100644
--- a/src/common/input/TextDecoder.ts
+++ b/src/common/input/TextDecoder.ts
@@ -194,7 +194,7 @@ export class Utf8ToUtf32 {
target[size++] = cp;
}
} else {
- if (codepoint < 0x010000 || codepoint > 0x10FFFF) {
+ if (cp < 0x010000 || cp > 0x10FFFF) {
// illegal codepoint
} else {
target[size++] = cp;
diff --git a/src/common/services/OptionsService.ts b/src/common/services/OptionsService.ts
index 9a5d2151..d9ea60d5 100644
--- a/src/common/services/OptionsService.ts
+++ b/src/common/services/OptionsService.ts
@@ -12,7 +12,7 @@ import { clone } from 'common/Clone';
// This sound is released under the Creative Commons Attribution 3.0 Unported
// (CC BY 3.0) license. It was created by 'altemark'. No modifications have been
// made, apart from the conversion to base64.
-export const DEFAULT_BELL_SOUND = 'data:audio/wav;base64,UklGRigBAABXQVZFZm10IBAAAAABAAEARKwAAIhYAQACABAAZGF0YQQBAADpAFgCwAMlBZoG/wdmCcoKRAypDQ8PbRDBEQQTOxRtFYcWlBePGIUZXhoiG88bcBz7HHIdzh0WHlMeZx51HmkeUx4WHs8dah0AHXwc3hs9G4saxRnyGBIYGBcQFv8U4RPAEoYRQBACD70NWwwHC6gJOwjWBloF7gOBAhABkf8b/qv8R/ve+Xf4Ife79W/0JfPZ8Z/wde9N7ijtE+wU6xvqM+lb6H7nw+YX5mrlxuQz5Mzje+Ma49fioeKD4nXiYeJy4pHitOL04j/jn+MN5IPkFOWs5U3mDefM55/ogOl36m7rdOyE7abuyu8D8Unyj/Pg9D/2qfcb+Yn6/vuK/Qj/lAAlAg==';
+export const DEFAULT_BELL_SOUND = 'data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjMyLjEwNAAAAAAAAAAAAAAA//tQxAADB8AhSmxhIIEVCSiJrDCQBTcu3UrAIwUdkRgQbFAZC1CQEwTJ9mjRvBA4UOLD8nKVOWfh+UlK3z/177OXrfOdKl7pyn3Xf//WreyTRUoAWgBgkOAGbZHBgG1OF6zM82DWbZaUmMBptgQhGjsyYqc9ae9XFz280948NMBWInljyzsNRFLPWdnZGWrddDsjK1unuSrVN9jJsK8KuQtQCtMBjCEtImISdNKJOopIpBFpNSMbIHCSRpRR5iakjTiyzLhchUUBwCgyKiweBv/7UsQbg8isVNoMPMjAAAA0gAAABEVFGmgqK////9bP/6XCykxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq';
// TODO: Freeze?
export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
@@ -23,6 +23,8 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
bellSound: DEFAULT_BELL_SOUND,
bellStyle: 'none',
drawBoldTextInBrightColors: true,
+ fastScrollModifier: 'alt',
+ fastScrollSensitivity: 5,
fontFamily: 'courier-new, courier, monospace',
fontSize: 15,
fontWeight: 'normal',
@@ -31,6 +33,7 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
letterSpacing: 0,
logLevel: 'info',
scrollback: 1000,
+ scrollSensitivity: 1,
screenReaderMode: false,
macOptionIsMeta: false,
macOptionClickForcesSelection: false,
@@ -47,7 +50,7 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
screenKeys: false,
cancelEvents: false,
useFlowControl: false,
- wordSeparator: ' ()[]{}\'"'
+ wordSeparator: ' ()[]{}\',:;"'
});
/**
@@ -119,6 +122,12 @@ export class OptionsService implements IOptionsService {
throw new Error(`${key} cannot be less than 0, value: ${value}`);
}
break;
+ case 'fastScrollSensitivity':
+ case 'scrollSensitivity':
+ if (value <= 0) {
+ throw new Error(`${key} cannot be less than or equal to 0, value: ${value}`);
+ }
+ break;
}
return value;
}
diff --git a/src/common/services/Services.ts b/src/common/services/Services.ts
index 568eeaff..0872d3db 100644
--- a/src/common/services/Services.ts
+++ b/src/common/services/Services.ts
@@ -180,6 +180,8 @@ export interface IPartialTerminalOptions {
cursorStyle?: 'block' | 'underline' | 'bar';
disableStdin?: boolean;
drawBoldTextInBrightColors?: boolean;
+ fastScrollModifier?: 'alt' | 'ctrl' | 'shift';
+ fastScrollSensitivity?: number;
fontSize?: number;
fontFamily?: string;
fontWeight?: FontWeight;
@@ -194,6 +196,7 @@ export interface IPartialTerminalOptions {
rows?: number;
screenReaderMode?: boolean;
scrollback?: number;
+ scrollSensitivity?: number;
tabStopWidth?: number;
theme?: ITheme;
windowsMode?: boolean;
@@ -209,6 +212,8 @@ export interface ITerminalOptions {
cursorStyle: 'block' | 'underline' | 'bar';
disableStdin: boolean;
drawBoldTextInBrightColors: boolean;
+ fastScrollModifier: 'alt' | 'ctrl' | 'shift' | undefined;
+ fastScrollSensitivity: number;
fontSize: number;
fontFamily: string;
fontWeight: FontWeight;
@@ -223,6 +228,7 @@ export interface ITerminalOptions {
rows: number;
screenReaderMode: boolean;
scrollback: number;
+ scrollSensitivity: number;
tabStopWidth: number;
theme: ITheme;
windowsMode: boolean;
diff --git a/src/public/Terminal.ts b/src/public/Terminal.ts
index b8a70ff7..c167bed8 100644
--- a/src/public/Terminal.ts
+++ b/src/public/Terminal.ts
@@ -33,14 +33,14 @@ export class Terminal implements ITerminalApi {
public get onRender(): IEvent<{ start: number, end: number }> { return this._core.onRender; }
public get onResize(): IEvent<{ cols: number, rows: number }> { return this._core.onResize; }
- public get element(): HTMLElement { return this._core.element; }
+ public get element(): HTMLElement | undefined { return this._core.element; }
public get parser(): IParser {
if (!this._parser) {
this._parser = new ParserApi(this._core);
}
return this._parser;
}
- public get textarea(): HTMLTextAreaElement { return this._core.textarea; }
+ public get textarea(): HTMLTextAreaElement | undefined { return this._core.textarea; }
public get rows(): number { return this._core.rows; }
public get cols(): number { return this._core.cols; }
public get buffer(): IBufferApi { return new BufferApiView(this._core.buffer); }
@@ -178,8 +178,8 @@ export class Terminal implements ITerminalApi {
private _verifyIntegers(...values: number[]): void {
values.forEach(value => {
- if (value % 1 !== 0) {
- throw new Error('This API does not accept floating point numbers');
+ if (value === Infinity || isNaN(value) || value % 1 !== 0) {
+ throw new Error('This API only accepts integers');
}
});
}
diff --git a/src/renderer/CursorRenderLayer.ts b/src/renderer/CursorRenderLayer.ts
index f847c5f6..1f2791f1 100644
--- a/src/renderer/CursorRenderLayer.ts
+++ b/src/renderer/CursorRenderLayer.ts
@@ -103,10 +103,10 @@ export class CursorRenderLayer extends BaseRenderLayer {
this._cursorBlinkStateManager.dispose();
this._cursorBlinkStateManager = null;
}
- // Request a refresh from the terminal as management of rendering is being
- // moved back to the terminal
- this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y);
}
+ // Request a refresh from the terminal as management of rendering is being
+ // moved back to the terminal
+ this._terminal.refresh(this._bufferService.buffer.y, this._bufferService.buffer.y);
}
public onCursorMove(): void {
@@ -148,12 +148,17 @@ export class CursorRenderLayer extends BaseRenderLayer {
this._clearCursor();
this._ctx.save();
this._ctx.fillStyle = this._colors.cursor.css;
- this._renderBlurCursor(this._bufferService.buffer.x, viewportRelativeCursorY, this._cell);
+ const cursorStyle = this._optionsService.options.cursorStyle;
+ if (cursorStyle && cursorStyle !== 'block') {
+ this._cursorRenderers[cursorStyle](this._bufferService.buffer.x, viewportRelativeCursorY, this._cell);
+ } else {
+ this._renderBlurCursor(this._bufferService.buffer.x, viewportRelativeCursorY, this._cell);
+ }
this._ctx.restore();
this._state.x = this._bufferService.buffer.x;
this._state.y = viewportRelativeCursorY;
this._state.isFocused = false;
- this._state.style = this._optionsService.options.cursorStyle;
+ this._state.style = cursorStyle;
this._state.width = this._cell.getWidth();
return;
}
diff --git a/src/renderer/dom/DomRenderer.ts b/src/renderer/dom/DomRenderer.ts
index d2c80235..ef927f20 100644
--- a/src/renderer/dom/DomRenderer.ts
+++ b/src/renderer/dom/DomRenderer.ts
@@ -183,7 +183,7 @@ export class DomRenderer extends Disposable implements IRenderer {
`}`;
// Cursor
styles +=
- `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS} {` +
+ `${this._terminalSelector} .${ROW_CONTAINER_CLASS}:not(.${FOCUS_CLASS}) .${CURSOR_CLASS}.${CURSOR_STYLE_BLOCK_CLASS} {` +
` outline: 1px solid ${this._colors.cursor.css};` +
` outline-offset: -1px;` +
`}` +
@@ -197,10 +197,10 @@ export class DomRenderer extends Disposable implements IRenderer {
` background-color: ${this._colors.cursor.css};` +
` color: ${this._colors.cursorAccent.css};` +
`}` +
- `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` +
+ `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_BAR_CLASS} {` +
` box-shadow: 1px 0 0 ${this._colors.cursor.css} inset;` +
`}` +
- `${this._terminalSelector} .${ROW_CONTAINER_CLASS}.${FOCUS_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` +
+ `${this._terminalSelector} .${ROW_CONTAINER_CLASS} .${CURSOR_CLASS}.${CURSOR_STYLE_UNDERLINE_CLASS} {` +
` box-shadow: 0 -1px 0 ${this._colors.cursor.css} inset;` +
`}`;
// Selection
diff --git a/test/api/CharWidth.api.ts b/test/api/CharWidth.api.ts
index 035208cc..0561bf07 100644
--- a/test/api/CharWidth.api.ts
+++ b/test/api/CharWidth.api.ts
@@ -21,7 +21,7 @@ describe('CharWidth Integration Tests', function(): void {
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
- args: [`--window-size=${width},${height}`]
+ args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
diff --git a/test/api/InputHandler.api.ts b/test/api/InputHandler.api.ts
index 05b16010..fe068610 100644
--- a/test/api/InputHandler.api.ts
+++ b/test/api/InputHandler.api.ts
@@ -21,7 +21,7 @@ describe('InputHandler Integration Tests', function(): void {
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
- args: [`--window-size=${width},${height}`]
+ args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
diff --git a/test/api/MouseTracking.api.ts b/test/api/MouseTracking.api.ts
index 90c18f75..1372b567 100644
--- a/test/api/MouseTracking.api.ts
+++ b/test/api/MouseTracking.api.ts
@@ -11,8 +11,10 @@ const APP = 'http://127.0.0.1:3000/test';
let browser: puppeteer.Browser;
let page: puppeteer.Page;
-const width = 1024;
-const height = 768;
+// adjusted to work inside devcontainer
+// see https://github.com/xtermjs/xterm.js/issues/2379
+const width = 1280;
+const height = 960;
// adjust terminal row/col size so we can test
// >80 up to 223 and >255
@@ -216,7 +218,7 @@ describe('Mouse Tracking Tests', function(): void {
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
- args: [`--window-size=${width},${height}`]
+ args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
diff --git a/test/api/Parser.api.ts b/test/api/Parser.api.ts
index 28f7d8b3..e46242ab 100644
--- a/test/api/Parser.api.ts
+++ b/test/api/Parser.api.ts
@@ -21,7 +21,7 @@ describe('Parser Integration Tests', function(): void {
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
- args: [`--window-size=${width},${height}`]
+ args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
diff --git a/test/api/Terminal.api.ts b/test/api/Terminal.api.ts
index 034b04c4..08963d9c 100644
--- a/test/api/Terminal.api.ts
+++ b/test/api/Terminal.api.ts
@@ -21,7 +21,7 @@ describe('API Integration Tests', function(): void {
browser = await puppeteer.launch({
headless: process.argv.indexOf('--headless') !== -1,
slowMo: 80,
- args: [`--window-size=${width},${height}`]
+ args: [`--window-size=${width},${height}`, `--no-sandbox`]
});
page = (await browser.pages())[0];
await page.setViewport({ width, height });
@@ -510,6 +510,20 @@ describe('API Integration Tests', function(): void {
});
});
});
+
+ it('dispose', async function(): Promise
{
+ await page.evaluate(`
+ window.term = new Terminal();
+ window.term.dispose();
+ `);
+ assert.equal(await page.evaluate(`window.term._core._isDisposed`), true);
+ });
+
+ it('dispose (opened)', async function(): Promise {
+ await openTerminal();
+ await page.evaluate(`window.term.dispose()`);
+ assert.equal(await page.evaluate(`window.term._core._isDisposed`), true);
+ });
});
async function openTerminal(options: ITerminalOptions = {}): Promise {
diff --git a/typings/xterm.d.ts b/typings/xterm.d.ts
index 44ef66bd..62670f3d 100644
--- a/typings/xterm.d.ts
+++ b/typings/xterm.d.ts
@@ -82,6 +82,16 @@ declare module 'xterm' {
*/
drawBoldTextInBrightColors?: boolean;
+ /**
+ * The modifier key hold to multiply scroll speed.
+ */
+ fastScrollModifier?: 'alt' | 'ctrl' | 'shift' | undefined;
+
+ /**
+ * The scroll speed multiplier used for fast scrolling.
+ */
+ fastScrollSensitivity?: number;
+
/**
* The font size used to render text.
*/
@@ -173,6 +183,11 @@ declare module 'xterm' {
*/
scrollback?: number;
+ /**
+ * The scrolling speed multiplier used for adjusting normal scrolling speed.
+ */
+ scrollSensitivity?: number;
+
/**
* The size of tab stops in the terminal.
*/
@@ -269,7 +284,7 @@ declare module 'xterm' {
/**
* A callback that fires when the mouse hovers over a link for a moment.
*/
- tooltipCallback?: (event: MouseEvent, uri: string) => boolean | void;
+ tooltipCallback?: (event: MouseEvent, uri: string, location: IViewportRange) => boolean | void;
/**
* A callback that fires when the mouse leaves a link. Note that this can
@@ -352,12 +367,12 @@ declare module 'xterm' {
/**
* The element containing the terminal.
*/
- readonly element: HTMLElement;
+ readonly element: HTMLElement | undefined;
/**
* The textarea that accepts input for the terminal.
*/
- readonly textarea: HTMLTextAreaElement;
+ readonly textarea: HTMLTextAreaElement | undefined;
/**
* The number of rows in the terminal's viewport. Use
@@ -434,7 +449,7 @@ declare module 'xterm' {
onLineFeed: IEvent;
/**
- * Adds an event listener for when a scroll occurs. The event value is the
+ * Adds an event listener for when a scroll occurs. The event value is the
* new position of the viewport.
* @returns an `IDisposable` to stop listening.
*/
@@ -842,6 +857,36 @@ declare module 'xterm' {
endRow: number;
}
+ /**
+ * An object representing a range within the viewport of the terminal.
+ */
+ export interface IViewportRange {
+ /**
+ * The start cell of the range.
+ */
+ start: IViewportCellPosition;
+
+ /**
+ * The end cell of the range.
+ */
+ end: IViewportCellPosition;
+ }
+
+ /**
+ * An object representing a cell position within the viewport of the terminal.
+ */
+ interface IViewportCellPosition {
+ /**
+ * The column of the cell. Note that this is 1-based; the first column is column 1.
+ */
+ col: number;
+
+ /**
+ * The row of the cell. Note that this is 1-based; the first row is row 1.
+ */
+ row: number;
+ }
+
/**
* Represents a terminal buffer.
*/
diff --git a/yarn.lock b/yarn.lock
index 7b987a00..0019809a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4719,7 +4719,12 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
-typescript@3.5, typescript@^3.5.1:
+typescript@3.6:
+ version "3.6.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz#fea942fabb20f7e1ca7164ff626f1a9f3f70b4da"
+ integrity sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==
+
+typescript@^3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.1.tgz#ba72a6a600b2158139c5dd8850f700e231464202"
integrity sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw==