Comparing version 3.10.1 to 3.11.0
@@ -16,5 +16,6 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var BufferLine_1 = require("./BufferLine"); | ||
var BufferReflow_1 = require("./BufferReflow"); | ||
var CircularList_1 = require("./common/CircularList"); | ||
var EventEmitter_1 = require("./common/EventEmitter"); | ||
var BufferLine_1 = require("./BufferLine"); | ||
var Types_1 = require("./renderer/atlas/Types"); | ||
@@ -33,2 +34,3 @@ exports.DEFAULT_ATTR = (0 << 18) | (Types_1.DEFAULT_COLOR << 9) | (256 << 0); | ||
exports.WHITESPACE_CELL_CODE = 32; | ||
exports.FILL_CHAR_DATA = [exports.DEFAULT_ATTR, exports.NULL_CELL_CHAR, exports.NULL_CELL_WIDTH, exports.NULL_CELL_CODE]; | ||
var Buffer = (function () { | ||
@@ -39,37 +41,13 @@ function Buffer(_terminal, _hasScrollback) { | ||
this.markers = []; | ||
this._cols = this._terminal.cols; | ||
this._rows = this._terminal.rows; | ||
this.clear(); | ||
} | ||
Buffer.prototype.setBufferLineFactory = function (type) { | ||
if (type === 'JsArray') { | ||
if (this._bufferLineConstructor !== BufferLine_1.BufferLineJSArray) { | ||
this._bufferLineConstructor = BufferLine_1.BufferLineJSArray; | ||
this._recreateLines(); | ||
} | ||
} | ||
else { | ||
if (this._bufferLineConstructor !== BufferLine_1.BufferLine) { | ||
this._bufferLineConstructor = BufferLine_1.BufferLine; | ||
this._recreateLines(); | ||
} | ||
} | ||
}; | ||
Buffer.prototype._recreateLines = function () { | ||
if (!this.lines) | ||
return; | ||
for (var i = 0; i < this.lines.length; ++i) { | ||
var oldLine = this.lines.get(i); | ||
var newLine = new this._bufferLineConstructor(oldLine.length); | ||
for (var j = 0; j < oldLine.length; ++j) { | ||
newLine.set(j, oldLine.get(j)); | ||
} | ||
this.lines.set(i, newLine); | ||
} | ||
}; | ||
Buffer.prototype.getBlankLine = function (attr, isWrapped) { | ||
var fillCharData = [attr, exports.NULL_CELL_CHAR, exports.NULL_CELL_WIDTH, exports.NULL_CELL_CODE]; | ||
return new this._bufferLineConstructor(this._terminal.cols, fillCharData, isWrapped); | ||
return new BufferLine_1.BufferLine(this._cols, fillCharData, isWrapped); | ||
}; | ||
Object.defineProperty(Buffer.prototype, "hasScrollback", { | ||
get: function () { | ||
return this._hasScrollback && this.lines.maxLength > this._terminal.rows; | ||
return this._hasScrollback && this.lines.maxLength > this._rows; | ||
}, | ||
@@ -83,3 +61,3 @@ enumerable: true, | ||
var relativeY = absoluteY - this.ydisp; | ||
return (relativeY >= 0 && relativeY < this._terminal.rows); | ||
return (relativeY >= 0 && relativeY < this._rows); | ||
}, | ||
@@ -101,3 +79,3 @@ enumerable: true, | ||
} | ||
var i = this._terminal.rows; | ||
var i = this._rows; | ||
while (i--) { | ||
@@ -109,3 +87,2 @@ this.lines.push(this.getBlankLine(fillAttr)); | ||
Buffer.prototype.clear = function () { | ||
this.setBufferLineFactory(this._terminal.options.experimentalBufferLineImpl); | ||
this.ydisp = 0; | ||
@@ -115,5 +92,5 @@ this.ybase = 0; | ||
this.x = 0; | ||
this.lines = new CircularList_1.CircularList(this._getCorrectBufferLength(this._terminal.rows)); | ||
this.lines = new CircularList_1.CircularList(this._getCorrectBufferLength(this._rows)); | ||
this.scrollTop = 0; | ||
this.scrollBottom = this._terminal.rows - 1; | ||
this.scrollBottom = this._rows - 1; | ||
this.setupTabStops(); | ||
@@ -127,11 +104,10 @@ }; | ||
if (this.lines.length > 0) { | ||
if (this._terminal.cols < newCols) { | ||
var ch = [exports.DEFAULT_ATTR, exports.NULL_CELL_CHAR, exports.NULL_CELL_WIDTH, exports.NULL_CELL_CODE]; | ||
if (this._cols < newCols) { | ||
for (var i = 0; i < this.lines.length; i++) { | ||
this.lines.get(i).resize(newCols, ch); | ||
this.lines.get(i).resize(newCols, exports.FILL_CHAR_DATA); | ||
} | ||
} | ||
var addToY = 0; | ||
if (this._terminal.rows < newRows) { | ||
for (var y = this._terminal.rows; y < newRows; y++) { | ||
if (this._rows < newRows) { | ||
for (var y = this._rows; y < newRows; y++) { | ||
if (this.lines.length < newRows + this.ybase) { | ||
@@ -146,4 +122,3 @@ if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) { | ||
else { | ||
var fillCharData = [exports.DEFAULT_ATTR, exports.NULL_CELL_CHAR, exports.NULL_CELL_WIDTH, exports.NULL_CELL_CODE]; | ||
this.lines.push(new this._bufferLineConstructor(newCols, fillCharData)); | ||
this.lines.push(new BufferLine_1.BufferLine(newCols, exports.FILL_CHAR_DATA)); | ||
} | ||
@@ -154,3 +129,3 @@ } | ||
else { | ||
for (var y = this._terminal.rows; y > newRows; y--) { | ||
for (var y = this._rows; y > newRows; y--) { | ||
if (this.lines.length > newRows + this.ybase) { | ||
@@ -186,4 +161,183 @@ if (this.lines.length > this.ybase + this.y + 1) { | ||
this.scrollBottom = newRows - 1; | ||
if (this._hasScrollback) { | ||
this._reflow(newCols, newRows); | ||
if (this._cols > newCols) { | ||
for (var i = 0; i < this.lines.length; i++) { | ||
this.lines.get(i).resize(newCols, exports.FILL_CHAR_DATA); | ||
} | ||
} | ||
} | ||
this._cols = newCols; | ||
this._rows = newRows; | ||
}; | ||
Buffer.prototype.stringIndexToBufferIndex = function (lineIndex, stringIndex) { | ||
Buffer.prototype._reflow = function (newCols, newRows) { | ||
if (this._cols === newCols) { | ||
return; | ||
} | ||
if (newCols > this._cols) { | ||
this._reflowLarger(newCols); | ||
} | ||
else { | ||
this._reflowSmaller(newCols, newRows); | ||
} | ||
}; | ||
Buffer.prototype._reflowLarger = function (newCols) { | ||
var toRemove = BufferReflow_1.reflowLargerGetLinesToRemove(this.lines, newCols, this.ybase + this.y); | ||
if (toRemove.length > 0) { | ||
var newLayoutResult = BufferReflow_1.reflowLargerCreateNewLayout(this.lines, toRemove); | ||
BufferReflow_1.reflowLargerApplyNewLayout(this.lines, newLayoutResult.layout); | ||
this._reflowLargerAdjustViewport(newCols, newLayoutResult.countRemoved); | ||
} | ||
}; | ||
Buffer.prototype._reflowLargerAdjustViewport = function (newCols, countRemoved) { | ||
var viewportAdjustments = countRemoved; | ||
while (viewportAdjustments-- > 0) { | ||
if (this.ybase === 0) { | ||
if (this.y > 0) { | ||
this.y--; | ||
} | ||
if (this.lines.length < this._rows) { | ||
this.lines.push(new BufferLine_1.BufferLine(newCols, exports.FILL_CHAR_DATA)); | ||
} | ||
} | ||
else { | ||
if (this.ydisp === this.ybase) { | ||
this.ydisp--; | ||
} | ||
this.ybase--; | ||
} | ||
} | ||
}; | ||
Buffer.prototype._reflowSmaller = function (newCols, newRows) { | ||
var toInsert = []; | ||
var countToInsert = 0; | ||
for (var y = this.lines.length - 1; y >= 0; y--) { | ||
var nextLine = this.lines.get(y); | ||
if (!nextLine.isWrapped && nextLine.getTrimmedLength() <= newCols) { | ||
continue; | ||
} | ||
var wrappedLines = [nextLine]; | ||
while (nextLine.isWrapped && y > 0) { | ||
nextLine = this.lines.get(--y); | ||
wrappedLines.unshift(nextLine); | ||
} | ||
var absoluteY = this.ybase + this.y; | ||
if (absoluteY >= y && absoluteY < y + wrappedLines.length) { | ||
continue; | ||
} | ||
var lastLineLength = wrappedLines[wrappedLines.length - 1].getTrimmedLength(); | ||
var destLineLengths = BufferReflow_1.reflowSmallerGetNewLineLengths(wrappedLines, this._cols, newCols); | ||
var linesToAdd = destLineLengths.length - wrappedLines.length; | ||
var trimmedLines = void 0; | ||
if (this.ybase === 0 && this.y !== this.lines.length - 1) { | ||
trimmedLines = Math.max(0, this.y - this.lines.maxLength + linesToAdd); | ||
} | ||
else { | ||
trimmedLines = Math.max(0, this.lines.length - this.lines.maxLength + linesToAdd); | ||
} | ||
var newLines = []; | ||
for (var i = 0; i < linesToAdd; i++) { | ||
var newLine = this.getBlankLine(exports.DEFAULT_ATTR, true); | ||
newLines.push(newLine); | ||
} | ||
if (newLines.length > 0) { | ||
toInsert.push({ | ||
start: y + wrappedLines.length + countToInsert, | ||
newLines: newLines | ||
}); | ||
countToInsert += newLines.length; | ||
} | ||
wrappedLines.push.apply(wrappedLines, newLines); | ||
var destLineIndex = destLineLengths.length - 1; | ||
var destCol = destLineLengths[destLineIndex]; | ||
if (destCol === 0) { | ||
destLineIndex--; | ||
destCol = destLineLengths[destLineIndex]; | ||
} | ||
var srcLineIndex = wrappedLines.length - linesToAdd - 1; | ||
var srcCol = lastLineLength; | ||
while (srcLineIndex >= 0) { | ||
var cellsToCopy = Math.min(srcCol, destCol); | ||
wrappedLines[destLineIndex].copyCellsFrom(wrappedLines[srcLineIndex], srcCol - cellsToCopy, destCol - cellsToCopy, cellsToCopy, true); | ||
destCol -= cellsToCopy; | ||
if (destCol === 0) { | ||
destLineIndex--; | ||
destCol = destLineLengths[destLineIndex]; | ||
} | ||
srcCol -= cellsToCopy; | ||
if (srcCol === 0) { | ||
srcLineIndex--; | ||
srcCol = wrappedLines[Math.max(srcLineIndex, 0)].getTrimmedLength(); | ||
} | ||
} | ||
for (var i = 0; i < wrappedLines.length; i++) { | ||
if (destLineLengths[i] < newCols) { | ||
wrappedLines[i].set(destLineLengths[i], exports.FILL_CHAR_DATA); | ||
} | ||
} | ||
var viewportAdjustments = linesToAdd - trimmedLines; | ||
while (viewportAdjustments-- > 0) { | ||
if (this.ybase === 0) { | ||
if (this.y < this._rows - 1) { | ||
this.y++; | ||
this.lines.pop(); | ||
} | ||
else { | ||
this.ybase++; | ||
this.ydisp++; | ||
} | ||
} | ||
else { | ||
if (this.ybase < Math.min(this.lines.maxLength, this.lines.length + countToInsert) - newRows) { | ||
if (this.ybase === this.ydisp) { | ||
this.ydisp++; | ||
} | ||
this.ybase++; | ||
} | ||
} | ||
} | ||
} | ||
if (toInsert.length > 0) { | ||
var insertEvents = []; | ||
var originalLines = []; | ||
for (var i = 0; i < this.lines.length; i++) { | ||
originalLines.push(this.lines.get(i)); | ||
} | ||
var originalLinesLength = this.lines.length; | ||
var originalLineIndex = originalLinesLength - 1; | ||
var nextToInsertIndex = 0; | ||
var nextToInsert = toInsert[nextToInsertIndex]; | ||
this.lines.length = Math.min(this.lines.maxLength, this.lines.length + countToInsert); | ||
var countInsertedSoFar = 0; | ||
for (var i = Math.min(this.lines.maxLength - 1, originalLinesLength + countToInsert - 1); i >= 0; i--) { | ||
if (nextToInsert && nextToInsert.start > originalLineIndex + countInsertedSoFar) { | ||
for (var nextI = nextToInsert.newLines.length - 1; nextI >= 0; nextI--) { | ||
this.lines.set(i--, nextToInsert.newLines[nextI]); | ||
} | ||
i++; | ||
insertEvents.push({ | ||
index: originalLineIndex + 1, | ||
amount: nextToInsert.newLines.length | ||
}); | ||
countInsertedSoFar += nextToInsert.newLines.length; | ||
nextToInsert = toInsert[++nextToInsertIndex]; | ||
} | ||
else { | ||
this.lines.set(i, originalLines[originalLineIndex--]); | ||
} | ||
} | ||
var insertCountEmitted = 0; | ||
for (var i = insertEvents.length - 1; i >= 0; i--) { | ||
insertEvents[i].index += insertCountEmitted; | ||
this.lines.emit('insert', insertEvents[i]); | ||
insertCountEmitted += insertEvents[i].amount; | ||
} | ||
var amountToTrim = Math.max(0, originalLinesLength + countToInsert - this.lines.maxLength); | ||
if (amountToTrim > 0) { | ||
this.lines.emitMayRemoveListeners('trim', amountToTrim); | ||
} | ||
} | ||
}; | ||
Buffer.prototype.stringIndexToBufferIndex = function (lineIndex, stringIndex, trimRight) { | ||
if (trimRight === void 0) { trimRight = false; } | ||
while (stringIndex) { | ||
@@ -194,4 +348,7 @@ var line = this.lines.get(lineIndex); | ||
} | ||
for (var i = 0; i < line.length; ++i) { | ||
stringIndex -= line.get(i)[exports.CHAR_DATA_CHAR_INDEX].length; | ||
var length_1 = (trimRight) ? line.getTrimmedLength() : line.length; | ||
for (var i = 0; i < length_1; ++i) { | ||
if (line.get(i)[exports.CHAR_DATA_WIDTH_INDEX]) { | ||
stringIndex -= line.get(i)[exports.CHAR_DATA_CHAR_INDEX].length || 1; | ||
} | ||
if (stringIndex < 0) { | ||
@@ -234,3 +391,3 @@ return [lineIndex, i]; | ||
} | ||
for (; i < this._terminal.cols; i += this._terminal.options.tabStopWidth) { | ||
for (; i < this._cols; i += this._terminal.options.tabStopWidth) { | ||
this.tabs[i] = true; | ||
@@ -245,3 +402,3 @@ } | ||
; | ||
return x >= this._terminal.cols ? this._terminal.cols - 1 : x < 0 ? 0 : x; | ||
return x >= this._cols ? this._cols - 1 : x < 0 ? 0 : x; | ||
}; | ||
@@ -252,5 +409,5 @@ Buffer.prototype.nextStop = function (x) { | ||
} | ||
while (!this.tabs[++x] && x < this._terminal.cols) | ||
while (!this.tabs[++x] && x < this._cols) | ||
; | ||
return x >= this._terminal.cols ? this._terminal.cols - 1 : x < 0 ? 0 : x; | ||
return x >= this._cols ? this._cols - 1 : x < 0 ? 0 : x; | ||
}; | ||
@@ -267,2 +424,15 @@ Buffer.prototype.addMarker = function (y) { | ||
})); | ||
marker.register(this.lines.addDisposableListener('insert', function (event) { | ||
if (marker.line >= event.index) { | ||
marker.line += event.amount; | ||
} | ||
})); | ||
marker.register(this.lines.addDisposableListener('delete', function (event) { | ||
if (marker.line >= event.index && marker.line < event.index + event.amount) { | ||
marker.dispose(); | ||
} | ||
if (marker.line > event.index) { | ||
marker.line -= event.amount; | ||
} | ||
})); | ||
marker.register(marker.addDisposableListener('dispose', function () { return _this._removeMarker(marker); })); | ||
@@ -269,0 +439,0 @@ return marker; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var Buffer_1 = require("./Buffer"); | ||
var BufferLineJSArray = (function () { | ||
function BufferLineJSArray(cols, fillCharData, isWrapped) { | ||
this.isWrapped = false; | ||
this._data = []; | ||
if (!fillCharData) { | ||
fillCharData = [0, Buffer_1.NULL_CELL_CHAR, Buffer_1.NULL_CELL_WIDTH, Buffer_1.NULL_CELL_CODE]; | ||
} | ||
for (var i = 0; i < cols; i++) { | ||
this._push(fillCharData); | ||
} | ||
if (isWrapped) { | ||
this.isWrapped = true; | ||
} | ||
this.length = this._data.length; | ||
} | ||
BufferLineJSArray.prototype._pop = function () { | ||
var data = this._data.pop(); | ||
this.length = this._data.length; | ||
return data; | ||
}; | ||
BufferLineJSArray.prototype._push = function (data) { | ||
this._data.push(data); | ||
this.length = this._data.length; | ||
}; | ||
BufferLineJSArray.prototype._splice = function (start, deleteCount) { | ||
var items = []; | ||
for (var _i = 2; _i < arguments.length; _i++) { | ||
items[_i - 2] = arguments[_i]; | ||
} | ||
var _a; | ||
var removed = (_a = this._data).splice.apply(_a, [start, deleteCount].concat(items)); | ||
this.length = this._data.length; | ||
return removed; | ||
}; | ||
BufferLineJSArray.prototype.get = function (index) { | ||
return this._data[index]; | ||
}; | ||
BufferLineJSArray.prototype.set = function (index, data) { | ||
this._data[index] = data; | ||
}; | ||
BufferLineJSArray.prototype.insertCells = function (pos, n, ch) { | ||
while (n--) { | ||
this._splice(pos, 0, ch); | ||
this._pop(); | ||
} | ||
}; | ||
BufferLineJSArray.prototype.deleteCells = function (pos, n, fillCharData) { | ||
while (n--) { | ||
this._splice(pos, 1); | ||
this._push(fillCharData); | ||
} | ||
}; | ||
BufferLineJSArray.prototype.replaceCells = function (start, end, fillCharData) { | ||
while (start < end && start < this.length) { | ||
this.set(start++, fillCharData); | ||
} | ||
}; | ||
BufferLineJSArray.prototype.resize = function (cols, fillCharData, shrink) { | ||
if (shrink === void 0) { shrink = false; } | ||
while (this._data.length < cols) { | ||
this._data.push(fillCharData); | ||
} | ||
if (shrink) { | ||
while (this._data.length > cols) { | ||
this._data.pop(); | ||
} | ||
} | ||
this.length = this._data.length; | ||
}; | ||
BufferLineJSArray.prototype.fill = function (fillCharData) { | ||
for (var i = 0; i < this.length; ++i) { | ||
this.set(i, fillCharData); | ||
} | ||
}; | ||
BufferLineJSArray.prototype.copyFrom = function (line) { | ||
this._data = line._data.slice(0); | ||
this.length = line.length; | ||
this.isWrapped = line.isWrapped; | ||
}; | ||
BufferLineJSArray.prototype.clone = function () { | ||
var newLine = new BufferLineJSArray(0); | ||
newLine.copyFrom(this); | ||
return newLine; | ||
}; | ||
BufferLineJSArray.prototype.getTrimmedLength = function () { | ||
for (var i = this.length - 1; i >= 0; --i) { | ||
var ch = this.get(i); | ||
if (ch[Buffer_1.CHAR_DATA_CHAR_INDEX] !== '') { | ||
return i + ch[Buffer_1.CHAR_DATA_WIDTH_INDEX]; | ||
} | ||
} | ||
return 0; | ||
}; | ||
BufferLineJSArray.prototype.translateToString = function (trimRight, startCol, endCol) { | ||
if (trimRight === void 0) { trimRight = false; } | ||
if (startCol === void 0) { startCol = 0; } | ||
if (endCol === void 0) { endCol = this.length; } | ||
if (trimRight) { | ||
endCol = Math.min(endCol, this.getTrimmedLength()); | ||
} | ||
var result = ''; | ||
while (startCol < endCol) { | ||
result += this.get(startCol)[Buffer_1.CHAR_DATA_CHAR_INDEX] || Buffer_1.WHITESPACE_CELL_CHAR; | ||
startCol += this.get(startCol)[Buffer_1.CHAR_DATA_WIDTH_INDEX] || 1; | ||
} | ||
return result; | ||
}; | ||
return BufferLineJSArray; | ||
}()); | ||
exports.BufferLineJSArray = BufferLineJSArray; | ||
var CELL_SIZE = 3; | ||
@@ -146,2 +36,5 @@ var IS_COMBINED_BIT_MASK = 0x80000000; | ||
}; | ||
BufferLine.prototype.getWidth = function (index) { | ||
return this._data[index * CELL_SIZE + 2]; | ||
}; | ||
BufferLine.prototype.set = function (index, value) { | ||
@@ -195,5 +88,4 @@ this._data[index * CELL_SIZE + 0] = value[0]; | ||
}; | ||
BufferLine.prototype.resize = function (cols, fillCharData, shrink) { | ||
if (shrink === void 0) { shrink = false; } | ||
if (cols === this.length || (!shrink && cols < this.length)) { | ||
BufferLine.prototype.resize = function (cols, fillCharData) { | ||
if (cols === this.length) { | ||
return; | ||
@@ -216,3 +108,3 @@ } | ||
} | ||
else if (shrink) { | ||
else { | ||
if (cols) { | ||
@@ -222,5 +114,13 @@ var data = new Uint32Array(cols * CELL_SIZE); | ||
this._data = data; | ||
var keys = Object.keys(this._combined); | ||
for (var i = 0; i < keys.length; i++) { | ||
var key = parseInt(keys[i], 10); | ||
if (key >= cols) { | ||
delete this._combined[key]; | ||
} | ||
} | ||
} | ||
else { | ||
this._data = null; | ||
this._combined = {}; | ||
} | ||
@@ -268,2 +168,26 @@ } | ||
}; | ||
BufferLine.prototype.copyCellsFrom = function (src, srcCol, destCol, length, applyInReverse) { | ||
var srcData = src._data; | ||
if (applyInReverse) { | ||
for (var cell = length - 1; cell >= 0; cell--) { | ||
for (var i = 0; i < CELL_SIZE; i++) { | ||
this._data[(destCol + cell) * CELL_SIZE + i] = srcData[(srcCol + cell) * CELL_SIZE + i]; | ||
} | ||
} | ||
} | ||
else { | ||
for (var cell = 0; cell < length; cell++) { | ||
for (var i = 0; i < CELL_SIZE; i++) { | ||
this._data[(destCol + cell) * CELL_SIZE + i] = srcData[(srcCol + cell) * CELL_SIZE + i]; | ||
} | ||
} | ||
} | ||
var srcCombinedKeys = Object.keys(src._combined); | ||
for (var i = 0; i < srcCombinedKeys.length; i++) { | ||
var key = parseInt(srcCombinedKeys[i], 10); | ||
if (key >= srcCol) { | ||
this._combined[key - srcCol + destCol] = src._combined[key]; | ||
} | ||
} | ||
}; | ||
BufferLine.prototype.translateToString = function (trimRight, startCol, endCol) { | ||
@@ -270,0 +194,0 @@ if (trimRight === void 0) { trimRight = false; } |
@@ -71,3 +71,3 @@ "use strict"; | ||
this._startIndex = ++this._startIndex % this._maxLength; | ||
this.emit('trim', 1); | ||
this.emitMayRemoveListeners('trim', 1); | ||
} | ||
@@ -83,3 +83,3 @@ else { | ||
this._startIndex = ++this._startIndex % this._maxLength; | ||
this.emit('trim', 1); | ||
this.emitMayRemoveListeners('trim', 1); | ||
return this._array[this._getCyclicIndex(this._length - 1)]; | ||
@@ -108,19 +108,17 @@ }; | ||
} | ||
if (items && items.length) { | ||
for (var i = this._length - 1; i >= start; i--) { | ||
this._array[this._getCyclicIndex(i + items.length)] = this._array[this._getCyclicIndex(i)]; | ||
} | ||
for (var i = 0; i < items.length; i++) { | ||
this._array[this._getCyclicIndex(start + i)] = items[i]; | ||
} | ||
if (this._length + items.length > this._maxLength) { | ||
var countToTrim = (this._length + items.length) - this._maxLength; | ||
this._startIndex += countToTrim; | ||
this._length = this._maxLength; | ||
this.emit('trim', countToTrim); | ||
} | ||
else { | ||
this._length += items.length; | ||
} | ||
for (var i = this._length - 1; i >= start; i--) { | ||
this._array[this._getCyclicIndex(i + items.length)] = this._array[this._getCyclicIndex(i)]; | ||
} | ||
for (var i = 0; i < items.length; i++) { | ||
this._array[this._getCyclicIndex(start + i)] = items[i]; | ||
} | ||
if (this._length + items.length > this._maxLength) { | ||
var countToTrim = (this._length + items.length) - this._maxLength; | ||
this._startIndex += countToTrim; | ||
this._length = this._maxLength; | ||
this.emitMayRemoveListeners('trim', countToTrim); | ||
} | ||
else { | ||
this._length += items.length; | ||
} | ||
}; | ||
@@ -133,3 +131,3 @@ CircularList.prototype.trimStart = function (count) { | ||
this._length -= count; | ||
this.emit('trim', count); | ||
this.emitMayRemoveListeners('trim', count); | ||
}; | ||
@@ -156,3 +154,3 @@ CircularList.prototype.shiftElements = function (start, count, offset) { | ||
this._startIndex++; | ||
this.emit('trim', 1); | ||
this.emitMayRemoveListeners('trim', 1); | ||
} | ||
@@ -159,0 +157,0 @@ } |
@@ -73,2 +73,18 @@ "use strict"; | ||
}; | ||
EventEmitter.prototype.emitMayRemoveListeners = function (type) { | ||
var args = []; | ||
for (var _i = 1; _i < arguments.length; _i++) { | ||
args[_i - 1] = arguments[_i]; | ||
} | ||
if (!this._events[type]) { | ||
return; | ||
} | ||
var obj = this._events[type]; | ||
var length = obj.length; | ||
for (var i = 0; i < obj.length; i++) { | ||
obj[i].apply(this, args); | ||
i -= length - obj.length; | ||
length = obj.length; | ||
} | ||
}; | ||
EventEmitter.prototype.listeners = function (type) { | ||
@@ -75,0 +91,0 @@ return this._events[type] || []; |
@@ -29,2 +29,9 @@ "use strict"; | ||
exports.fillFallback = fillFallback; | ||
function concat(a, b) { | ||
var result = new a.constructor(a.length + b.length); | ||
result.set(a); | ||
result.set(b, a.length); | ||
return result; | ||
} | ||
exports.concat = concat; | ||
//# sourceMappingURL=TypedArrayUtils.js.map |
@@ -45,3 +45,3 @@ "use strict"; | ||
}; | ||
CompositionHelper.prototype._finalizeComposition = function (waitForPropogation) { | ||
CompositionHelper.prototype._finalizeComposition = function (waitForPropagation) { | ||
var _this = this; | ||
@@ -51,3 +51,3 @@ this._compositionView.classList.remove('active'); | ||
this._clearTextareaPosition(); | ||
if (!waitForPropogation) { | ||
if (!waitForPropagation) { | ||
this._isSendingComposition = false; | ||
@@ -54,0 +54,0 @@ var input = this._textarea.value.substring(this._compositionPosition.start, this._compositionPosition.end); |
@@ -17,2 +17,3 @@ "use strict"; | ||
var Lifecycle_1 = require("./common/Lifecycle"); | ||
var TextDecoder_1 = require("./core/input/TextDecoder"); | ||
function r(low, high) { | ||
@@ -91,2 +92,3 @@ var c = high - low; | ||
table.add(0x9c, 7, 0, 0); | ||
table.add(0x7f, 7, 0, 7); | ||
table.add(0x5b, 1, 11, 3); | ||
@@ -305,3 +307,3 @@ table.addMany(r(0x40, 0x7f), 3, 7, 0); | ||
}; | ||
EscapeSequenceParser.prototype.parse = function (data) { | ||
EscapeSequenceParser.prototype.parse = function (data, length) { | ||
var code = 0; | ||
@@ -319,5 +321,4 @@ var transition = 0; | ||
var callback = null; | ||
var l = data.length; | ||
for (var i = 0; i < l; ++i) { | ||
code = data.charCodeAt(i); | ||
for (var i = 0; i < length; ++i) { | ||
code = data[i]; | ||
if (currentState === 0 && code > 0x1f && code < 0x80) { | ||
@@ -327,3 +328,3 @@ print = (~print) ? print : i; | ||
i++; | ||
while (i < l && data.charCodeAt(i) > 0x1f && data.charCodeAt(i) < 0x80); | ||
while (i < length && data[i] > 0x1f && data[i] < 0x80); | ||
i--; | ||
@@ -472,6 +473,6 @@ continue; | ||
for (var j_1 = i + 1;; j_1++) { | ||
if (j_1 >= l | ||
|| (code = data.charCodeAt(j_1)) < 0x20 | ||
if (j_1 >= length | ||
|| (code = data[j_1]) < 0x20 | ||
|| (code > 0x7f && code <= 0x9f)) { | ||
osc += data.substring(i, j_1); | ||
osc += TextDecoder_1.utf32ToString(data, i, j_1); | ||
i = j_1 - 1; | ||
@@ -514,6 +515,6 @@ break; | ||
if (currentState === 0 && ~print) { | ||
this._printHandler(data, print, data.length); | ||
this._printHandler(data, print, length); | ||
} | ||
else if (currentState === 13 && ~dcs && dcsHandler) { | ||
dcsHandler.put(data, dcs, data.length); | ||
dcsHandler.put(data, dcs, length); | ||
} | ||
@@ -520,0 +521,0 @@ this._osc = osc; |
@@ -22,2 +22,4 @@ "use strict"; | ||
var Lifecycle_1 = require("./common/Lifecycle"); | ||
var TypedArrayUtils_1 = require("./common/TypedArrayUtils"); | ||
var TextDecoder_1 = require("./core/input/TextDecoder"); | ||
var GLEVEL = { '(': 0, ')': 1, '*': 2, '+': 3, '-': 1, '.': 2 }; | ||
@@ -27,11 +29,14 @@ var DECRQSS = (function () { | ||
this._terminal = _terminal; | ||
this._data = new Uint32Array(0); | ||
} | ||
DECRQSS.prototype.hook = function (collect, params, flag) { | ||
this._data = ''; | ||
this._data = new Uint32Array(0); | ||
}; | ||
DECRQSS.prototype.put = function (data, start, end) { | ||
this._data += data.substring(start, end); | ||
this._data = TypedArrayUtils_1.concat(this._data, data.subarray(start, end)); | ||
}; | ||
DECRQSS.prototype.unhook = function () { | ||
switch (this._data) { | ||
var data = TextDecoder_1.utf32ToString(this._data); | ||
this._data = new Uint32Array(0); | ||
switch (data) { | ||
case '"q': | ||
@@ -53,3 +58,3 @@ return this._terminal.handler(EscapeSequences_1.C0.ESC + "P1$r0\"q" + EscapeSequences_1.C0.ESC + "\\"); | ||
default: | ||
this._terminal.error('Unknown DCS $q %s', this._data); | ||
this._terminal.error('Unknown DCS $q %s', data); | ||
this._terminal.handler(EscapeSequences_1.C0.ESC + "P0$r" + EscapeSequences_1.C0.ESC + "\\"); | ||
@@ -67,4 +72,5 @@ } | ||
_this._parser = _parser; | ||
_this._parseBuffer = new Uint32Array(4096); | ||
_this._stringDecoder = new TextDecoder_1.StringToUtf32(); | ||
_this.register(_this._parser); | ||
_this._surrogateFirst = ''; | ||
_this._parser.setCsiHandlerFallback(function (collect, params, flag) { | ||
@@ -183,7 +189,9 @@ _this._terminal.error('Unknown CSI code: ', { collect: collect, params: params, flag: String.fromCharCode(flag) }); | ||
} | ||
if (this._surrogateFirst) { | ||
data = this._surrogateFirst + data; | ||
this._surrogateFirst = ''; | ||
if (this._parseBuffer.length < data.length) { | ||
this._parseBuffer = new Uint32Array(data.length); | ||
} | ||
this._parser.parse(data); | ||
for (var i = 0; i < data.length; ++i) { | ||
this._parseBuffer[i] = data.charCodeAt(i); | ||
} | ||
this._parser.parse(this._parseBuffer, this._stringDecoder.decode(data, this._parseBuffer)); | ||
buffer = this._terminal.buffer; | ||
@@ -195,4 +203,4 @@ if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) { | ||
InputHandler.prototype.print = function (data, start, end) { | ||
var code; | ||
var char; | ||
var code; | ||
var chWidth; | ||
@@ -208,24 +216,13 @@ var buffer = this._terminal.buffer; | ||
this._terminal.updateRange(buffer.y); | ||
for (var stringPosition = start; stringPosition < end; ++stringPosition) { | ||
char = data.charAt(stringPosition); | ||
code = data.charCodeAt(stringPosition); | ||
if (0xD800 <= code && code <= 0xDBFF) { | ||
if (++stringPosition >= end) { | ||
this._surrogateFirst = char; | ||
continue; | ||
for (var pos = start; pos < end; ++pos) { | ||
code = data[pos]; | ||
char = TextDecoder_1.stringFromCodePoint(code); | ||
chWidth = CharWidth_1.wcwidth(code); | ||
if (code < 127 && charset) { | ||
var ch = charset[char]; | ||
if (ch) { | ||
code = ch.charCodeAt(0); | ||
char = ch; | ||
} | ||
var second = data.charCodeAt(stringPosition); | ||
if (0xDC00 <= second && second <= 0xDFFF) { | ||
code = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; | ||
char += data.charAt(stringPosition); | ||
} | ||
else { | ||
stringPosition--; | ||
} | ||
} | ||
chWidth = CharWidth_1.wcwidth(code); | ||
if (charset) { | ||
char = charset[char] || char; | ||
code = char.charCodeAt(0); | ||
} | ||
if (screenReaderMode) { | ||
@@ -731,3 +728,5 @@ this._terminal.emit('a11y.char', char); | ||
this._terminal.mouseEvents = true; | ||
this._terminal.element.classList.add('enable-mouse-events'); | ||
if (this._terminal.element) { | ||
this._terminal.element.classList.add('enable-mouse-events'); | ||
} | ||
this._terminal.selectionManager.disable(); | ||
@@ -821,3 +820,5 @@ this._terminal.log('Binding to mouse events.'); | ||
this._terminal.mouseEvents = false; | ||
this._terminal.element.classList.remove('enable-mouse-events'); | ||
if (this._terminal.element) { | ||
this._terminal.element.classList.remove('enable-mouse-events'); | ||
} | ||
this._terminal.selectionManager.enable(); | ||
@@ -824,0 +825,0 @@ break; |
@@ -44,2 +44,4 @@ "use strict"; | ||
var WRITE_BATCH_SIZE = 300; | ||
var MINIMUM_COLS = 2; | ||
var MINIMUM_ROWS = 1; | ||
var CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows']; | ||
@@ -77,4 +79,3 @@ var DEFAULT_OPTIONS = { | ||
rightClickSelectsWord: Browser.isMac, | ||
rendererType: 'canvas', | ||
experimentalBufferLineImpl: 'TypedArray' | ||
rendererType: 'canvas' | ||
}; | ||
@@ -113,4 +114,4 @@ var Terminal = (function (_super) { | ||
this._parent = document ? document.body : null; | ||
this.cols = this.options.cols; | ||
this.rows = this.options.rows; | ||
this.cols = Math.max(this.options.cols, MINIMUM_COLS); | ||
this.rows = Math.max(this.options.rows, MINIMUM_ROWS); | ||
if (this.options.handler) { | ||
@@ -311,7 +312,2 @@ this.on('data', this.options.handler); | ||
break; | ||
case 'experimentalBufferLineImpl': | ||
this.buffers.normal.setBufferLineFactory(value); | ||
this.buffers.alt.setBufferLineFactory(value); | ||
this._blankLine = null; | ||
break; | ||
} | ||
@@ -358,3 +354,3 @@ if (this.renderer) { | ||
if (event.button === 2) { | ||
Clipboard_1.rightClickHandler(event, _this.textarea, _this.selectionManager, _this.options.rightClickSelectsWord); | ||
Clipboard_1.rightClickHandler(event, _this, _this.selectionManager, _this.options.rightClickSelectsWord); | ||
} | ||
@@ -365,3 +361,3 @@ })); | ||
this.register(Lifecycle_1.addDisposableDomListener(this.element, 'contextmenu', function (event) { | ||
Clipboard_1.rightClickHandler(event, _this.textarea, _this.selectionManager, _this.options.rightClickSelectsWord); | ||
Clipboard_1.rightClickHandler(event, _this, _this.selectionManager, _this.options.rightClickSelectsWord); | ||
})); | ||
@@ -372,3 +368,3 @@ } | ||
if (event.button === 1) { | ||
Clipboard_1.moveTextAreaUnderMouseCursor(event, _this.textarea); | ||
Clipboard_1.moveTextAreaUnderMouseCursor(event, _this); | ||
} | ||
@@ -486,2 +482,3 @@ })); | ||
this.mouseHelper = new MouseHelper_1.MouseHelper(this.renderer); | ||
this.element.classList.toggle('enable-mouse-events', this.mouseEvents); | ||
if (this.options.screenReaderMode) { | ||
@@ -790,14 +787,8 @@ this._accessibilityManager = new AccessibilityManager_1.AccessibilityManager(this); | ||
var newLine; | ||
var useRecycling = this.options.experimentalBufferLineImpl !== 'JsArray'; | ||
if (useRecycling) { | ||
newLine = this._blankLine; | ||
if (!newLine || newLine.length !== this.cols || newLine.get(0)[Buffer_1.CHAR_DATA_ATTR_INDEX] !== this.eraseAttr()) { | ||
newLine = this.buffer.getBlankLine(this.eraseAttr(), isWrapped); | ||
this._blankLine = newLine; | ||
} | ||
newLine.isWrapped = isWrapped; | ||
} | ||
else { | ||
newLine = this._blankLine; | ||
if (!newLine || newLine.length !== this.cols || newLine.get(0)[Buffer_1.CHAR_DATA_ATTR_INDEX] !== this.eraseAttr()) { | ||
newLine = this.buffer.getBlankLine(this.eraseAttr(), isWrapped); | ||
this._blankLine = newLine; | ||
} | ||
newLine.isWrapped = isWrapped; | ||
var topRow = this.buffer.ybase + this.buffer.scrollTop; | ||
@@ -808,16 +799,11 @@ var bottomRow = this.buffer.ybase + this.buffer.scrollBottom; | ||
if (bottomRow === this.buffer.lines.length - 1) { | ||
if (useRecycling) { | ||
if (willBufferBeTrimmed) { | ||
this.buffer.lines.recycle().copyFrom(newLine); | ||
} | ||
else { | ||
this.buffer.lines.push(newLine.clone()); | ||
} | ||
if (willBufferBeTrimmed) { | ||
this.buffer.lines.recycle().copyFrom(newLine); | ||
} | ||
else { | ||
this.buffer.lines.push(newLine); | ||
this.buffer.lines.push(newLine.clone()); | ||
} | ||
} | ||
else { | ||
this.buffer.lines.splice(bottomRow + 1, 0, (useRecycling) ? newLine.clone() : newLine); | ||
this.buffer.lines.splice(bottomRow + 1, 0, newLine.clone()); | ||
} | ||
@@ -839,3 +825,3 @@ if (!willBufferBeTrimmed) { | ||
this.buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1); | ||
this.buffer.lines.set(bottomRow, (useRecycling) ? newLine.clone() : newLine); | ||
this.buffer.lines.set(bottomRow, newLine.clone()); | ||
} | ||
@@ -1117,6 +1103,6 @@ if (!this._userScrolling) { | ||
} | ||
if (x < 1) | ||
x = 1; | ||
if (y < 1) | ||
y = 1; | ||
if (x < MINIMUM_COLS) | ||
x = MINIMUM_COLS; | ||
if (y < MINIMUM_ROWS) | ||
y = MINIMUM_ROWS; | ||
this.buffers.resize(x, y); | ||
@@ -1123,0 +1109,0 @@ this.cols = x; |
@@ -51,5 +51,6 @@ "use strict"; | ||
} | ||
if (this._width !== geometry.width || this._height !== geometry.height) { | ||
var adjustedHeight = Math.ceil(geometry.height); | ||
if (this._width !== geometry.width || this._height !== adjustedHeight) { | ||
this._width = geometry.width; | ||
this._height = Math.ceil(geometry.height); | ||
this._height = adjustedHeight; | ||
this.emit('charsizechanged'); | ||
@@ -56,0 +57,0 @@ } |
@@ -49,29 +49,32 @@ "use strict"; | ||
exports.pasteHandler = pasteHandler; | ||
function moveTextAreaUnderMouseCursor(ev, textarea) { | ||
textarea.style.position = 'fixed'; | ||
textarea.style.width = '20px'; | ||
textarea.style.height = '20px'; | ||
textarea.style.left = (ev.clientX - 10) + 'px'; | ||
textarea.style.top = (ev.clientY - 10) + 'px'; | ||
textarea.style.zIndex = '1000'; | ||
textarea.focus(); | ||
function moveTextAreaUnderMouseCursor(ev, term) { | ||
var pos = term.screenElement.getBoundingClientRect(); | ||
var left = ev.clientX - pos.left - 10; | ||
var top = ev.clientY - pos.top - 10; | ||
term.textarea.style.position = 'absolute'; | ||
term.textarea.style.width = '20px'; | ||
term.textarea.style.height = '20px'; | ||
term.textarea.style.left = left + "px"; | ||
term.textarea.style.top = top + "px"; | ||
term.textarea.style.zIndex = '1000'; | ||
term.textarea.focus(); | ||
setTimeout(function () { | ||
textarea.style.position = null; | ||
textarea.style.width = null; | ||
textarea.style.height = null; | ||
textarea.style.left = null; | ||
textarea.style.top = null; | ||
textarea.style.zIndex = null; | ||
term.textarea.style.position = null; | ||
term.textarea.style.width = null; | ||
term.textarea.style.height = null; | ||
term.textarea.style.left = null; | ||
term.textarea.style.top = null; | ||
term.textarea.style.zIndex = null; | ||
}, 200); | ||
} | ||
exports.moveTextAreaUnderMouseCursor = moveTextAreaUnderMouseCursor; | ||
function rightClickHandler(ev, textarea, selectionManager, shouldSelectWord) { | ||
moveTextAreaUnderMouseCursor(ev, textarea); | ||
function rightClickHandler(ev, term, selectionManager, shouldSelectWord) { | ||
moveTextAreaUnderMouseCursor(ev, term); | ||
if (shouldSelectWord && !selectionManager.isClickInSelection(ev)) { | ||
selectionManager.selectWordAtCursor(ev); | ||
} | ||
textarea.value = selectionManager.selectionText; | ||
textarea.select(); | ||
term.textarea.value = selectionManager.selectionText; | ||
term.textarea.select(); | ||
} | ||
exports.rightClickHandler = rightClickHandler; | ||
//# sourceMappingURL=Clipboard.js.map |
{ | ||
"name": "xterm", | ||
"description": "Full xterm terminal, in your browser", | ||
"version": "3.10.1", | ||
"version": "3.11.0", | ||
"main": "lib/public/Terminal.js", | ||
@@ -6,0 +6,0 @@ "types": "typings/xterm.d.ts", |
@@ -6,8 +6,9 @@ /** | ||
import { CircularList } from './common/CircularList'; | ||
import { CharData, ITerminal, IBuffer, IBufferLine, BufferIndex, IBufferStringIterator, IBufferStringIteratorResult, IBufferLineConstructor } from './Types'; | ||
import { IMarker } from 'xterm'; | ||
import { BufferLine } from './BufferLine'; | ||
import { reflowLargerApplyNewLayout, reflowLargerCreateNewLayout, reflowLargerGetLinesToRemove, reflowSmallerGetNewLineLengths } from './BufferReflow'; | ||
import { CircularList, IDeleteEvent, IInsertEvent } from './common/CircularList'; | ||
import { EventEmitter } from './common/EventEmitter'; | ||
import { IMarker } from 'xterm'; | ||
import { BufferLine, BufferLineJSArray } from './BufferLine'; | ||
import { DEFAULT_COLOR } from './renderer/atlas/Types'; | ||
import { BufferIndex, CharData, IBuffer, IBufferLine, IBufferStringIterator, IBufferStringIteratorResult, ITerminal } from './Types'; | ||
@@ -29,2 +30,4 @@ export const DEFAULT_ATTR = (0 << 18) | (DEFAULT_COLOR << 9) | (256 << 0); | ||
export const FILL_CHAR_DATA: CharData = [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; | ||
/** | ||
@@ -50,3 +53,4 @@ * This class represents a terminal buffer (an internal state of the terminal), where the | ||
public markers: Marker[] = []; | ||
private _bufferLineConstructor: IBufferLineConstructor; | ||
private _cols: number; | ||
private _rows: number; | ||
@@ -63,38 +67,14 @@ /** | ||
) { | ||
this._cols = this._terminal.cols; | ||
this._rows = this._terminal.rows; | ||
this.clear(); | ||
} | ||
public setBufferLineFactory(type: string): void { | ||
if (type === 'JsArray') { | ||
if (this._bufferLineConstructor !== BufferLineJSArray) { | ||
this._bufferLineConstructor = BufferLineJSArray; | ||
this._recreateLines(); | ||
} | ||
} else { | ||
if (this._bufferLineConstructor !== BufferLine) { | ||
this._bufferLineConstructor = BufferLine; | ||
this._recreateLines(); | ||
} | ||
} | ||
} | ||
private _recreateLines(): void { | ||
if (!this.lines) return; | ||
for (let i = 0; i < this.lines.length; ++i) { | ||
const oldLine = this.lines.get(i); | ||
const newLine = new this._bufferLineConstructor(oldLine.length); | ||
for (let j = 0; j < oldLine.length; ++j) { | ||
newLine.set(j, oldLine.get(j)); | ||
} | ||
this.lines.set(i, newLine); | ||
} | ||
} | ||
public getBlankLine(attr: number, isWrapped?: boolean): IBufferLine { | ||
const fillCharData: CharData = [attr, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; | ||
return new this._bufferLineConstructor(this._terminal.cols, fillCharData, isWrapped); | ||
return new BufferLine(this._cols, fillCharData, isWrapped); | ||
} | ||
public get hasScrollback(): boolean { | ||
return this._hasScrollback && this.lines.maxLength > this._terminal.rows; | ||
return this._hasScrollback && this.lines.maxLength > this._rows; | ||
} | ||
@@ -105,3 +85,3 @@ | ||
const relativeY = absoluteY - this.ydisp; | ||
return (relativeY >= 0 && relativeY < this._terminal.rows); | ||
return (relativeY >= 0 && relativeY < this._rows); | ||
} | ||
@@ -132,3 +112,3 @@ | ||
} | ||
let i = this._terminal.rows; | ||
let i = this._rows; | ||
while (i--) { | ||
@@ -144,3 +124,2 @@ this.lines.push(this.getBlankLine(fillAttr)); | ||
public clear(): void { | ||
this.setBufferLineFactory(this._terminal.options.experimentalBufferLineImpl); | ||
this.ydisp = 0; | ||
@@ -150,5 +129,5 @@ this.ybase = 0; | ||
this.x = 0; | ||
this.lines = new CircularList<IBufferLine>(this._getCorrectBufferLength(this._terminal.rows)); | ||
this.lines = new CircularList<IBufferLine>(this._getCorrectBufferLength(this._rows)); | ||
this.scrollTop = 0; | ||
this.scrollBottom = this._terminal.rows - 1; | ||
this.scrollBottom = this._rows - 1; | ||
this.setupTabStops(); | ||
@@ -173,7 +152,6 @@ } | ||
if (this.lines.length > 0) { | ||
// Deal with columns increasing (we don't do anything when columns reduce) | ||
if (this._terminal.cols < newCols) { | ||
const ch: CharData = [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; // does xterm use the default attr? | ||
// Deal with columns increasing (reducing needs to happen after reflow) | ||
if (this._cols < newCols) { | ||
for (let i = 0; i < this.lines.length; i++) { | ||
this.lines.get(i).resize(newCols, ch); | ||
this.lines.get(i).resize(newCols, FILL_CHAR_DATA); | ||
} | ||
@@ -184,4 +162,4 @@ } | ||
let addToY = 0; | ||
if (this._terminal.rows < newRows) { | ||
for (let y = this._terminal.rows; y < newRows; y++) { | ||
if (this._rows < newRows) { | ||
for (let y = this._rows; y < newRows; y++) { | ||
if (this.lines.length < newRows + this.ybase) { | ||
@@ -200,9 +178,8 @@ if (this.ybase > 0 && this.lines.length <= this.ybase + this.y + addToY + 1) { | ||
// are blank lines after the cursor | ||
const fillCharData: CharData = [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; | ||
this.lines.push(new this._bufferLineConstructor(newCols, fillCharData)); | ||
this.lines.push(new BufferLine(newCols, FILL_CHAR_DATA)); | ||
} | ||
} | ||
} | ||
} else { // (this._terminal.rows >= newRows) | ||
for (let y = this._terminal.rows; y > newRows; y--) { | ||
} else { // (this._rows >= newRows) | ||
for (let y = this._rows; y > newRows; y--) { | ||
if (this.lines.length > newRows + this.ybase) { | ||
@@ -247,4 +224,228 @@ if (this.lines.length > this.ybase + this.y + 1) { | ||
this.scrollBottom = newRows - 1; | ||
if (this._hasScrollback) { | ||
this._reflow(newCols, newRows); | ||
// Trim the end of the line off if cols shrunk | ||
if (this._cols > newCols) { | ||
for (let i = 0; i < this.lines.length; i++) { | ||
this.lines.get(i).resize(newCols, FILL_CHAR_DATA); | ||
} | ||
} | ||
} | ||
this._cols = newCols; | ||
this._rows = newRows; | ||
} | ||
private _reflow(newCols: number, newRows: number): void { | ||
if (this._cols === newCols) { | ||
return; | ||
} | ||
// Iterate through rows, ignore the last one as it cannot be wrapped | ||
if (newCols > this._cols) { | ||
this._reflowLarger(newCols); | ||
} else { | ||
this._reflowSmaller(newCols, newRows); | ||
} | ||
} | ||
private _reflowLarger(newCols: number): void { | ||
const toRemove: number[] = reflowLargerGetLinesToRemove(this.lines, newCols, this.ybase + this.y); | ||
if (toRemove.length > 0) { | ||
const newLayoutResult = reflowLargerCreateNewLayout(this.lines, toRemove); | ||
reflowLargerApplyNewLayout(this.lines, newLayoutResult.layout); | ||
this._reflowLargerAdjustViewport(newCols, newLayoutResult.countRemoved); | ||
} | ||
} | ||
private _reflowLargerAdjustViewport(newCols: number, countRemoved: number): void { | ||
// Adjust viewport based on number of items removed | ||
let viewportAdjustments = countRemoved; | ||
while (viewportAdjustments-- > 0) { | ||
if (this.ybase === 0) { | ||
if (this.y > 0) { | ||
this.y--; | ||
} | ||
if (this.lines.length < this._rows) { | ||
// Add an extra row at the bottom of the viewport | ||
this.lines.push(new BufferLine(newCols, FILL_CHAR_DATA)); | ||
} | ||
} else { | ||
if (this.ydisp === this.ybase) { | ||
this.ydisp--; | ||
} | ||
this.ybase--; | ||
} | ||
} | ||
} | ||
private _reflowSmaller(newCols: number, newRows: number): void { | ||
// Gather all BufferLines that need to be inserted into the Buffer here so that they can be | ||
// batched up and only committed once | ||
const toInsert = []; | ||
let countToInsert = 0; | ||
// Go backwards as many lines may be trimmed and this will avoid considering them | ||
for (let y = this.lines.length - 1; y >= 0; y--) { | ||
// Check whether this line is a problem | ||
let nextLine = this.lines.get(y) as BufferLine; | ||
if (!nextLine.isWrapped && nextLine.getTrimmedLength() <= newCols) { | ||
continue; | ||
} | ||
// Gather wrapped lines and adjust y to be the starting line | ||
const wrappedLines: BufferLine[] = [nextLine]; | ||
while (nextLine.isWrapped && y > 0) { | ||
nextLine = this.lines.get(--y) as BufferLine; | ||
wrappedLines.unshift(nextLine); | ||
} | ||
// If these lines contain the cursor don't touch them, the program will handle fixing up | ||
// wrapped lines with the cursor | ||
const absoluteY = this.ybase + this.y; | ||
if (absoluteY >= y && absoluteY < y + wrappedLines.length) { | ||
continue; | ||
} | ||
const lastLineLength = wrappedLines[wrappedLines.length - 1].getTrimmedLength(); | ||
const destLineLengths = reflowSmallerGetNewLineLengths(wrappedLines, this._cols, newCols); | ||
const linesToAdd = destLineLengths.length - wrappedLines.length; | ||
let trimmedLines: number; | ||
if (this.ybase === 0 && this.y !== this.lines.length - 1) { | ||
// If the top section of the buffer is not yet filled | ||
trimmedLines = Math.max(0, this.y - this.lines.maxLength + linesToAdd); | ||
} else { | ||
trimmedLines = Math.max(0, this.lines.length - this.lines.maxLength + linesToAdd); | ||
} | ||
// Add the new lines | ||
const newLines: BufferLine[] = []; | ||
for (let i = 0; i < linesToAdd; i++) { | ||
const newLine = this.getBlankLine(DEFAULT_ATTR, true) as BufferLine; | ||
newLines.push(newLine); | ||
} | ||
if (newLines.length > 0) { | ||
toInsert.push({ | ||
// countToInsert here gets the actual index, taking into account other inserted items. | ||
// using this we can iterate through the list forwards | ||
start: y + wrappedLines.length + countToInsert, | ||
newLines | ||
}); | ||
countToInsert += newLines.length; | ||
} | ||
wrappedLines.push(...newLines); | ||
// Copy buffer data to new locations, this needs to happen backwards to do in-place | ||
let destLineIndex = destLineLengths.length - 1; // Math.floor(cellsNeeded / newCols); | ||
let destCol = destLineLengths[destLineIndex]; // cellsNeeded % newCols; | ||
if (destCol === 0) { | ||
destLineIndex--; | ||
destCol = destLineLengths[destLineIndex]; | ||
} | ||
let srcLineIndex = wrappedLines.length - linesToAdd - 1; | ||
let srcCol = lastLineLength; | ||
while (srcLineIndex >= 0) { | ||
const cellsToCopy = Math.min(srcCol, destCol); | ||
wrappedLines[destLineIndex].copyCellsFrom(wrappedLines[srcLineIndex], srcCol - cellsToCopy, destCol - cellsToCopy, cellsToCopy, true); | ||
destCol -= cellsToCopy; | ||
if (destCol === 0) { | ||
destLineIndex--; | ||
destCol = destLineLengths[destLineIndex]; | ||
} | ||
srcCol -= cellsToCopy; | ||
if (srcCol === 0) { | ||
srcLineIndex--; | ||
// TODO: srcCol shoudl take trimmed length into account | ||
srcCol = wrappedLines[Math.max(srcLineIndex, 0)].getTrimmedLength(); // this._cols; | ||
} | ||
} | ||
// Null out the end of the line ends if a wide character wrapped to the following line | ||
for (let i = 0; i < wrappedLines.length; i++) { | ||
if (destLineLengths[i] < newCols) { | ||
wrappedLines[i].set(destLineLengths[i], FILL_CHAR_DATA); | ||
} | ||
} | ||
// Adjust viewport as needed | ||
let viewportAdjustments = linesToAdd - trimmedLines; | ||
while (viewportAdjustments-- > 0) { | ||
if (this.ybase === 0) { | ||
if (this.y < this._rows - 1) { | ||
this.y++; | ||
this.lines.pop(); | ||
} else { | ||
this.ybase++; | ||
this.ydisp++; | ||
} | ||
} else { | ||
// Ensure ybase does not exceed its maximum value | ||
if (this.ybase < Math.min(this.lines.maxLength, this.lines.length + countToInsert) - newRows) { | ||
if (this.ybase === this.ydisp) { | ||
this.ydisp++; | ||
} | ||
this.ybase++; | ||
} | ||
} | ||
} | ||
} | ||
// Rearrange lines in the buffer if there are any insertions, this is done at the end rather | ||
// than earlier so that it's a single O(n) pass through the buffer, instead of O(n^2) from many | ||
// costly calls to CircularList.splice. | ||
if (toInsert.length > 0) { | ||
// Record buffer insert events and then play them back backwards so that the indexes are | ||
// correct | ||
const insertEvents: IInsertEvent[] = []; | ||
// Record original lines so they don't get overridden when we rearrange the list | ||
const originalLines: BufferLine[] = []; | ||
for (let i = 0; i < this.lines.length; i++) { | ||
originalLines.push(this.lines.get(i) as BufferLine); | ||
} | ||
const originalLinesLength = this.lines.length; | ||
let originalLineIndex = originalLinesLength - 1; | ||
let nextToInsertIndex = 0; | ||
let nextToInsert = toInsert[nextToInsertIndex]; | ||
this.lines.length = Math.min(this.lines.maxLength, this.lines.length + countToInsert); | ||
let countInsertedSoFar = 0; | ||
for (let i = Math.min(this.lines.maxLength - 1, originalLinesLength + countToInsert - 1); i >= 0; i--) { | ||
if (nextToInsert && nextToInsert.start > originalLineIndex + countInsertedSoFar) { | ||
// Insert extra lines here, adjusting i as needed | ||
for (let nextI = nextToInsert.newLines.length - 1; nextI >= 0; nextI--) { | ||
this.lines.set(i--, nextToInsert.newLines[nextI]); | ||
} | ||
i++; | ||
// Create insert events for later | ||
insertEvents.push({ | ||
index: originalLineIndex + 1, | ||
amount: nextToInsert.newLines.length | ||
} as IInsertEvent); | ||
countInsertedSoFar += nextToInsert.newLines.length; | ||
nextToInsert = toInsert[++nextToInsertIndex]; | ||
} else { | ||
this.lines.set(i, originalLines[originalLineIndex--]); | ||
} | ||
} | ||
// Update markers | ||
let insertCountEmitted = 0; | ||
for (let i = insertEvents.length - 1; i >= 0; i--) { | ||
insertEvents[i].index += insertCountEmitted; | ||
this.lines.emit('insert', insertEvents[i]); | ||
insertCountEmitted += insertEvents[i].amount; | ||
} | ||
const amountToTrim = Math.max(0, originalLinesLength + countToInsert - this.lines.maxLength); | ||
if (amountToTrim > 0) { | ||
this.lines.emitMayRemoveListeners('trim', amountToTrim); | ||
} | ||
} | ||
} | ||
// private _reflowSmallerGetLinesNeeded() | ||
/** | ||
@@ -263,3 +464,3 @@ * Translates a string index back to a BufferIndex. | ||
*/ | ||
public stringIndexToBufferIndex(lineIndex: number, stringIndex: number): BufferIndex { | ||
public stringIndexToBufferIndex(lineIndex: number, stringIndex: number, trimRight: boolean = false): BufferIndex { | ||
while (stringIndex) { | ||
@@ -270,4 +471,9 @@ const line = this.lines.get(lineIndex); | ||
} | ||
for (let i = 0; i < line.length; ++i) { | ||
stringIndex -= line.get(i)[CHAR_DATA_CHAR_INDEX].length; | ||
const length = (trimRight) ? line.getTrimmedLength() : line.length; | ||
for (let i = 0; i < length; ++i) { | ||
if (line.get(i)[CHAR_DATA_WIDTH_INDEX]) { | ||
// empty cells report a string length of 0, but get replaced | ||
// with a whitespace in translateToString, thus replace with 1 | ||
stringIndex -= line.get(i)[CHAR_DATA_CHAR_INDEX].length || 1; | ||
} | ||
if (stringIndex < 0) { | ||
@@ -328,3 +534,3 @@ return [lineIndex, i]; | ||
for (; i < this._terminal.cols; i += this._terminal.options.tabStopWidth) { | ||
for (; i < this._cols; i += this._terminal.options.tabStopWidth) { | ||
this.tabs[i] = true; | ||
@@ -343,3 +549,3 @@ } | ||
while (!this.tabs[--x] && x > 0); | ||
return x >= this._terminal.cols ? this._terminal.cols - 1 : x < 0 ? 0 : x; | ||
return x >= this._cols ? this._cols - 1 : x < 0 ? 0 : x; | ||
} | ||
@@ -355,4 +561,4 @@ | ||
} | ||
while (!this.tabs[++x] && x < this._terminal.cols); | ||
return x >= this._terminal.cols ? this._terminal.cols - 1 : x < 0 ? 0 : x; | ||
while (!this.tabs[++x] && x < this._cols); | ||
return x >= this._cols ? this._cols - 1 : x < 0 ? 0 : x; | ||
} | ||
@@ -370,2 +576,18 @@ | ||
})); | ||
marker.register(this.lines.addDisposableListener('insert', (event: IInsertEvent) => { | ||
if (marker.line >= event.index) { | ||
marker.line += event.amount; | ||
} | ||
})); | ||
marker.register(this.lines.addDisposableListener('delete', (event: IDeleteEvent) => { | ||
// Delete the marker if it's within the range | ||
if (marker.line >= event.index && marker.line < event.index + event.amount) { | ||
marker.dispose(); | ||
} | ||
// Shift the marker if it's after the deleted range | ||
if (marker.line > event.index) { | ||
marker.line -= event.amount; | ||
} | ||
})); | ||
marker.register(marker.addDisposableListener('dispose', () => this._removeMarker(marker))); | ||
@@ -376,3 +598,2 @@ return marker; | ||
private _removeMarker(marker: Marker): void { | ||
// TODO: This could probably be optimized by relying on sort order and trimming the array using .length | ||
this.markers.splice(this.markers.indexOf(marker), 1); | ||
@@ -379,0 +600,0 @@ } |
@@ -6,130 +6,5 @@ /** | ||
import { CharData, IBufferLine } from './Types'; | ||
import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, WHITESPACE_CELL_CHAR } from './Buffer'; | ||
import { NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR, WHITESPACE_CELL_CHAR } from './Buffer'; | ||
/** | ||
* Class representing a terminal line. | ||
* | ||
* @deprecated to be removed with one of the next releases | ||
*/ | ||
export class BufferLineJSArray implements IBufferLine { | ||
protected _data: CharData[]; | ||
public isWrapped = false; | ||
public length: number; | ||
constructor(cols: number, fillCharData?: CharData, isWrapped?: boolean) { | ||
this._data = []; | ||
if (!fillCharData) { | ||
fillCharData = [0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; | ||
} | ||
for (let i = 0; i < cols; i++) { | ||
this._push(fillCharData); // Note: the ctor ch is not cloned (resembles old behavior) | ||
} | ||
if (isWrapped) { | ||
this.isWrapped = true; | ||
} | ||
this.length = this._data.length; | ||
} | ||
private _pop(): CharData | undefined { | ||
const data = this._data.pop(); | ||
this.length = this._data.length; | ||
return data; | ||
} | ||
private _push(data: CharData): void { | ||
this._data.push(data); | ||
this.length = this._data.length; | ||
} | ||
private _splice(start: number, deleteCount: number, ...items: CharData[]): CharData[] { | ||
const removed = this._data.splice(start, deleteCount, ...items); | ||
this.length = this._data.length; | ||
return removed; | ||
} | ||
public get(index: number): CharData { | ||
return this._data[index]; | ||
} | ||
public set(index: number, data: CharData): void { | ||
this._data[index] = data; | ||
} | ||
/** insert n cells ch at pos, right cells are lost (stable length) */ | ||
public insertCells(pos: number, n: number, ch: CharData): void { | ||
while (n--) { | ||
this._splice(pos, 0, ch); | ||
this._pop(); | ||
} | ||
} | ||
/** delete n cells at pos, right side is filled with fill (stable length) */ | ||
public deleteCells(pos: number, n: number, fillCharData: CharData): void { | ||
while (n--) { | ||
this._splice(pos, 1); | ||
this._push(fillCharData); | ||
} | ||
} | ||
/** replace cells from pos to pos + n - 1 with fill */ | ||
public replaceCells(start: number, end: number, fillCharData: CharData): void { | ||
while (start < end && start < this.length) { | ||
this.set(start++, fillCharData); // Note: fill is not cloned (resembles old behavior) | ||
} | ||
} | ||
/** resize line to cols filling new cells with fill */ | ||
public resize(cols: number, fillCharData: CharData, shrink: boolean = false): void { | ||
while (this._data.length < cols) { | ||
this._data.push(fillCharData); | ||
} | ||
if (shrink) { | ||
while (this._data.length > cols) { | ||
this._data.pop(); | ||
} | ||
} | ||
this.length = this._data.length; | ||
} | ||
public fill(fillCharData: CharData): void { | ||
for (let i = 0; i < this.length; ++i) { | ||
this.set(i, fillCharData); | ||
} | ||
} | ||
public copyFrom(line: BufferLineJSArray): void { | ||
this._data = line._data.slice(0); | ||
this.length = line.length; | ||
this.isWrapped = line.isWrapped; | ||
} | ||
public clone(): IBufferLine { | ||
const newLine = new BufferLineJSArray(0); | ||
newLine.copyFrom(this); | ||
return newLine; | ||
} | ||
public getTrimmedLength(): number { | ||
for (let i = this.length - 1; i >= 0; --i) { | ||
const ch = this.get(i); | ||
if (ch[CHAR_DATA_CHAR_INDEX] !== '') { | ||
return i + ch[CHAR_DATA_WIDTH_INDEX]; | ||
} | ||
} | ||
return 0; | ||
} | ||
public translateToString(trimRight: boolean = false, startCol: number = 0, endCol: number = this.length): string { | ||
if (trimRight) { | ||
endCol = Math.min(endCol, this.getTrimmedLength()); | ||
} | ||
let result = ''; | ||
while (startCol < endCol) { | ||
result += this.get(startCol)[CHAR_DATA_CHAR_INDEX] || WHITESPACE_CELL_CHAR; | ||
startCol += this.get(startCol)[CHAR_DATA_WIDTH_INDEX] || 1; | ||
} | ||
return result; | ||
} | ||
} | ||
/** typed array slots taken by one cell */ | ||
@@ -183,2 +58,6 @@ const CELL_SIZE = 3; | ||
public getWidth(index: number): number { | ||
return this._data[index * CELL_SIZE + Cell.WIDTH]; | ||
} | ||
public set(index: number, value: CharData): void { | ||
@@ -233,4 +112,4 @@ this._data[index * CELL_SIZE + Cell.FLAGS] = value[0]; | ||
public resize(cols: number, fillCharData: CharData, shrink: boolean = false): void { | ||
if (cols === this.length || (!shrink && cols < this.length)) { | ||
public resize(cols: number, fillCharData: CharData): void { | ||
if (cols === this.length) { | ||
return; | ||
@@ -251,3 +130,3 @@ } | ||
} | ||
} else if (shrink) { | ||
} else { | ||
if (cols) { | ||
@@ -257,4 +136,13 @@ const data = new Uint32Array(cols * CELL_SIZE); | ||
this._data = data; | ||
// Remove any cut off combined data | ||
const keys = Object.keys(this._combined); | ||
for (let i = 0; i < keys.length; i++) { | ||
const key = parseInt(keys[i], 10); | ||
if (key >= cols) { | ||
delete this._combined[key]; | ||
} | ||
} | ||
} else { | ||
this._data = null; | ||
this._combined = {}; | ||
} | ||
@@ -312,2 +200,28 @@ } | ||
public copyCellsFrom(src: BufferLine, srcCol: number, destCol: number, length: number, applyInReverse: boolean): void { | ||
const srcData = src._data; | ||
if (applyInReverse) { | ||
for (let cell = length - 1; cell >= 0; cell--) { | ||
for (let i = 0; i < CELL_SIZE; i++) { | ||
this._data[(destCol + cell) * CELL_SIZE + i] = srcData[(srcCol + cell) * CELL_SIZE + i]; | ||
} | ||
} | ||
} else { | ||
for (let cell = 0; cell < length; cell++) { | ||
for (let i = 0; i < CELL_SIZE; i++) { | ||
this._data[(destCol + cell) * CELL_SIZE + i] = srcData[(srcCol + cell) * CELL_SIZE + i]; | ||
} | ||
} | ||
} | ||
// Move any combined data over as needed | ||
const srcCombinedKeys = Object.keys(src._combined); | ||
for (let i = 0; i < srcCombinedKeys.length; i++) { | ||
const key = parseInt(srcCombinedKeys[i], 10); | ||
if (key >= srcCol) { | ||
this._combined[key - srcCol + destCol] = src._combined[key]; | ||
} | ||
} | ||
} | ||
public translateToString(trimRight: boolean = false, startCol: number = 0, endCol: number = this.length): string { | ||
@@ -314,0 +228,0 @@ if (trimRight) { |
@@ -9,2 +9,12 @@ /** | ||
export interface IInsertEvent { | ||
index: number; | ||
amount: number; | ||
} | ||
export interface IDeleteEvent { | ||
index: number; | ||
amount: number; | ||
} | ||
/** | ||
@@ -95,3 +105,3 @@ * Represents a circular list; a list with a maximum size that wraps around when push is called, | ||
this._startIndex = ++this._startIndex % this._maxLength; | ||
this.emit('trim', 1); | ||
this.emitMayRemoveListeners('trim', 1); | ||
} else { | ||
@@ -112,3 +122,3 @@ this._length++; | ||
this._startIndex = ++this._startIndex % this._maxLength; | ||
this.emit('trim', 1); | ||
this.emitMayRemoveListeners('trim', 1); | ||
return this._array[this._getCyclicIndex(this._length - 1)]!; | ||
@@ -150,20 +160,18 @@ } | ||
if (items && items.length) { | ||
// Add items | ||
for (let i = this._length - 1; i >= start; i--) { | ||
this._array[this._getCyclicIndex(i + items.length)] = this._array[this._getCyclicIndex(i)]; | ||
} | ||
for (let i = 0; i < items.length; i++) { | ||
this._array[this._getCyclicIndex(start + i)] = items[i]; | ||
} | ||
// Add items | ||
for (let i = this._length - 1; i >= start; i--) { | ||
this._array[this._getCyclicIndex(i + items.length)] = this._array[this._getCyclicIndex(i)]; | ||
} | ||
for (let i = 0; i < items.length; i++) { | ||
this._array[this._getCyclicIndex(start + i)] = items[i]; | ||
} | ||
// Adjust length as needed | ||
if (this._length + items.length > this._maxLength) { | ||
const countToTrim = (this._length + items.length) - this._maxLength; | ||
this._startIndex += countToTrim; | ||
this._length = this._maxLength; | ||
this.emit('trim', countToTrim); | ||
} else { | ||
this._length += items.length; | ||
} | ||
// Adjust length as needed | ||
if (this._length + items.length > this._maxLength) { | ||
const countToTrim = (this._length + items.length) - this._maxLength; | ||
this._startIndex += countToTrim; | ||
this._length = this._maxLength; | ||
this.emitMayRemoveListeners('trim', countToTrim); | ||
} else { | ||
this._length += items.length; | ||
} | ||
@@ -182,3 +190,3 @@ } | ||
this._length -= count; | ||
this.emit('trim', count); | ||
this.emitMayRemoveListeners('trim', count); | ||
} | ||
@@ -207,3 +215,3 @@ | ||
this._startIndex++; | ||
this.emit('trim', 1); | ||
this.emitMayRemoveListeners('trim', 1); | ||
} | ||
@@ -210,0 +218,0 @@ } |
@@ -26,3 +26,3 @@ /** | ||
/** | ||
* Adds a disposabe listener to the EventEmitter, returning the disposable. | ||
* Adds a disposable listener to the EventEmitter, returning the disposable. | ||
* @param type The event type. | ||
@@ -79,2 +79,15 @@ * @param handler The handler for the listener. | ||
public emitMayRemoveListeners(type: string, ...args: any[]): void { | ||
if (!this._events[type]) { | ||
return; | ||
} | ||
const obj = this._events[type]; | ||
let length = obj.length; | ||
for (let i = 0; i < obj.length; i++) { | ||
obj[i].apply(this, args); | ||
i -= length - obj.length; | ||
length = obj.length; | ||
} | ||
} | ||
public listeners(type: string): XtermListener[] { | ||
@@ -81,0 +94,0 @@ return this._events[type] || []; |
@@ -6,2 +6,7 @@ /** | ||
export type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray | ||
| Int8Array | Int16Array | Int32Array | ||
| Float32Array | Float64Array; | ||
/** | ||
@@ -11,7 +16,2 @@ * polyfill for TypedArray.fill | ||
*/ | ||
type TypedArray = Uint8Array | Uint16Array | Uint32Array | Uint8ClampedArray | ||
| Int8Array | Int16Array | Int32Array | ||
| Float32Array | Float64Array; | ||
export function fill<T extends TypedArray>(array: T, value: number, start?: number, end?: number): T { | ||
@@ -44,1 +44,12 @@ // all modern engines that support .fill | ||
} | ||
/** | ||
* Concat two typed arrays `a` and `b`. | ||
* Returns a new typed array. | ||
*/ | ||
export function concat<T extends TypedArray>(a: T, b: T): T { | ||
const result = new (a.constructor as any)(a.length + b.length); | ||
result.set(a); | ||
result.set(b, a.length); | ||
return result; | ||
} |
@@ -114,8 +114,8 @@ /** | ||
* is ending. | ||
* @param waitForPropogation Whether to wait for events to propogate before sending | ||
* @param waitForPropagation Whether to wait for events to propagate before sending | ||
* the input. This should be false if a non-composition keystroke is entered before the | ||
* compositionend event is triggered, such as enter, so that the composition is send before | ||
* compositionend event is triggered, such as enter, so that the composition is sent before | ||
* the command is executed. | ||
*/ | ||
private _finalizeComposition(waitForPropogation: boolean): void { | ||
private _finalizeComposition(waitForPropagation: boolean): void { | ||
this._compositionView.classList.remove('active'); | ||
@@ -125,3 +125,3 @@ this._isComposing = false; | ||
if (!waitForPropogation) { | ||
if (!waitForPropagation) { | ||
// Cancel any delayed composition send requests and send the input immediately. | ||
@@ -141,4 +141,4 @@ this._isSendingComposition = false; | ||
// browsers, use a setTimeout with 0ms time to allow the native compositionend event to | ||
// complete. This ensures the correct character is retrieved, this solution was used | ||
// because: | ||
// complete. This ensures the correct character is retrieved. | ||
// This solution was used because: | ||
// - The compositionend event's data property is unreliable, at least on Chromium | ||
@@ -145,0 +145,0 @@ // - The last compositionupdate event's data property does not always accurately describe |
@@ -47,3 +47,3 @@ /** | ||
type: KeyboardResultType.SEND_KEY, | ||
// Whether to cancel event propogation (NOTE: this may not be needed since the event is | ||
// Whether to cancel event propagation (NOTE: this may not be needed since the event is | ||
// canceled at the end of keyDown | ||
@@ -50,0 +50,0 @@ cancel: false, |
@@ -9,2 +9,3 @@ /** | ||
import { Disposable } from './common/Lifecycle'; | ||
import { utf32ToString } from './core/input/TextDecoder'; | ||
@@ -138,2 +139,3 @@ interface IHandlerCollection<T> { | ||
table.add(0x9c, ParserState.SOS_PM_APC_STRING, ParserAction.IGNORE, ParserState.GROUND); | ||
table.add(0x7f, ParserState.SOS_PM_APC_STRING, ParserAction.IGNORE, ParserState.SOS_PM_APC_STRING); | ||
// csi entries | ||
@@ -207,3 +209,3 @@ table.add(0x5b, ParserState.ESCAPE, ParserAction.CLEAR, ParserState.CSI_ENTRY); | ||
hook(collect: string, params: number[], flag: number): void { } | ||
put(data: string, start: number, end: number): void { } | ||
put(data: Uint32Array, start: number, end: number): void { } | ||
unhook(): void { } | ||
@@ -234,3 +236,3 @@ } | ||
// handler lookup containers | ||
protected _printHandler: (data: string, start: number, end: number) => void; | ||
protected _printHandler: (data: Uint32Array, start: number, end: number) => void; | ||
protected _executeHandlers: any; | ||
@@ -245,3 +247,3 @@ protected _csiHandlers: IHandlerCollection<CsiHandler>; | ||
// fallback handlers | ||
protected _printHandlerFb: (data: string, start: number, end: number) => void; | ||
protected _printHandlerFb: (data: Uint32Array, start: number, end: number) => void; | ||
protected _executeHandlerFb: (code: number) => void; | ||
@@ -302,3 +304,3 @@ protected _csiHandlerFb: (collect: string, params: number[], flag: number) => void; | ||
setPrintHandler(callback: (data: string, start: number, end: number) => void): void { | ||
setPrintHandler(callback: (data: Uint32Array, start: number, end: number) => void): void { | ||
this._printHandler = callback; | ||
@@ -406,3 +408,3 @@ } | ||
parse(data: string): void { | ||
parse(data: Uint32Array, length: number): void { | ||
let code = 0; | ||
@@ -422,5 +424,4 @@ let transition = 0; | ||
// process input string | ||
const l = data.length; | ||
for (let i = 0; i < l; ++i) { | ||
code = data.charCodeAt(i); | ||
for (let i = 0; i < length; ++i) { | ||
code = data[i]; | ||
@@ -431,3 +432,3 @@ // shortcut for most chars (print action) | ||
do i++; | ||
while (i < l && data.charCodeAt(i) > 0x1f && data.charCodeAt(i) < 0x80); | ||
while (i < length && data[i] > 0x1f && data[i] < 0x80); | ||
i--; | ||
@@ -575,6 +576,6 @@ continue; | ||
for (let j = i + 1; ; j++) { | ||
if (j >= l | ||
|| (code = data.charCodeAt(j)) < 0x20 | ||
if (j >= length | ||
|| (code = data[j]) < 0x20 | ||
|| (code > 0x7f && code <= 0x9f)) { | ||
osc += data.substring(i, j); | ||
osc += utf32ToString(data, i, j); | ||
i = j - 1; | ||
@@ -623,5 +624,5 @@ break; | ||
if (currentState === ParserState.GROUND && ~print) { | ||
this._printHandler(data, print, data.length); | ||
this._printHandler(data, print, length); | ||
} else if (currentState === ParserState.DCS_PASSTHROUGH && ~dcs && dcsHandler) { | ||
dcsHandler.put(data, dcs, data.length); | ||
dcsHandler.put(data, dcs, length); | ||
} | ||
@@ -628,0 +629,0 @@ |
@@ -555,3 +555,3 @@ /** | ||
// If the mousemove listener is active it means that a selection is | ||
// currently being made, we should stop propogation to prevent mouse events | ||
// currently being made, we should stop propagation to prevent mouse events | ||
// to be sent to the pty. | ||
@@ -558,0 +558,0 @@ event.stopImmediatePropagation(); |
@@ -72,2 +72,5 @@ /** | ||
const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars | ||
const MINIMUM_ROWS = 1; | ||
/** | ||
@@ -109,4 +112,3 @@ * The set of options that only have an effect when set in the Terminal constructor. | ||
rightClickSelectsWord: Browser.isMac, | ||
rendererType: 'canvas', | ||
experimentalBufferLineImpl: 'TypedArray' | ||
rendererType: 'canvas' | ||
}; | ||
@@ -268,4 +270,4 @@ | ||
this.cols = this.options.cols; | ||
this.rows = this.options.rows; | ||
this.cols = Math.max(this.options.cols, MINIMUM_COLS); | ||
this.rows = Math.max(this.options.rows, MINIMUM_ROWS); | ||
@@ -503,7 +505,2 @@ if (this.options.handler) { | ||
case 'tabStopWidth': this.buffers.setupTabStops(); break; | ||
case 'experimentalBufferLineImpl': | ||
this.buffers.normal.setBufferLineFactory(value); | ||
this.buffers.alt.setBufferLineFactory(value); | ||
this._blankLine = null; | ||
break; | ||
} | ||
@@ -576,3 +573,3 @@ // Inform renderer of changes | ||
if (event.button === 2) { | ||
rightClickHandler(event, this.textarea, this.selectionManager, this.options.rightClickSelectsWord); | ||
rightClickHandler(event, this, this.selectionManager, this.options.rightClickSelectsWord); | ||
} | ||
@@ -582,3 +579,3 @@ })); | ||
this.register(addDisposableDomListener(this.element, 'contextmenu', (event: MouseEvent) => { | ||
rightClickHandler(event, this.textarea, this.selectionManager, this.options.rightClickSelectsWord); | ||
rightClickHandler(event, this, this.selectionManager, this.options.rightClickSelectsWord); | ||
})); | ||
@@ -595,3 +592,3 @@ } | ||
if (event.button === 1) { | ||
moveTextAreaUnderMouseCursor(event, this.textarea); | ||
moveTextAreaUnderMouseCursor(event, this); | ||
} | ||
@@ -749,2 +746,4 @@ })); | ||
this.mouseHelper = new MouseHelper(this.renderer); | ||
// apply mouse event classes set by escape codes before terminal was attached | ||
this.element.classList.toggle('enable-mouse-events', this.mouseEvents); | ||
@@ -1191,13 +1190,8 @@ if (this.options.screenReaderMode) { | ||
let newLine: IBufferLine; | ||
const useRecycling = this.options.experimentalBufferLineImpl !== 'JsArray'; | ||
if (useRecycling) { | ||
newLine = this._blankLine; | ||
if (!newLine || newLine.length !== this.cols || newLine.get(0)[CHAR_DATA_ATTR_INDEX] !== this.eraseAttr()) { | ||
newLine = this.buffer.getBlankLine(this.eraseAttr(), isWrapped); | ||
this._blankLine = newLine; | ||
} | ||
newLine.isWrapped = isWrapped; | ||
} else { | ||
newLine = this._blankLine; | ||
if (!newLine || newLine.length !== this.cols || newLine.get(0)[CHAR_DATA_ATTR_INDEX] !== this.eraseAttr()) { | ||
newLine = this.buffer.getBlankLine(this.eraseAttr(), isWrapped); | ||
this._blankLine = newLine; | ||
} | ||
newLine.isWrapped = isWrapped; | ||
@@ -1213,13 +1207,9 @@ const topRow = this.buffer.ybase + this.buffer.scrollTop; | ||
if (bottomRow === this.buffer.lines.length - 1) { | ||
if (useRecycling) { | ||
if (willBufferBeTrimmed) { | ||
this.buffer.lines.recycle().copyFrom(newLine); | ||
} else { | ||
this.buffer.lines.push(newLine.clone()); | ||
} | ||
if (willBufferBeTrimmed) { | ||
this.buffer.lines.recycle().copyFrom(newLine); | ||
} else { | ||
this.buffer.lines.push(newLine); | ||
this.buffer.lines.push(newLine.clone()); | ||
} | ||
} else { | ||
this.buffer.lines.splice(bottomRow + 1, 0, (useRecycling) ? newLine.clone() : newLine); | ||
this.buffer.lines.splice(bottomRow + 1, 0, newLine.clone()); | ||
} | ||
@@ -1246,3 +1236,3 @@ | ||
this.buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1); | ||
this.buffer.lines.set(bottomRow, (useRecycling) ? newLine.clone() : newLine); | ||
this.buffer.lines.set(bottomRow, newLine.clone()); | ||
} | ||
@@ -1420,3 +1410,3 @@ | ||
* This is a function that takes a KeyboardEvent, allowing consumers to stop | ||
* propogation and/or prevent the default action. The function returns whether | ||
* propagation and/or prevent the default action. The function returns whether | ||
* the event should be processed by xterm.js. | ||
@@ -1721,4 +1711,4 @@ */ | ||
if (x < 1) x = 1; | ||
if (y < 1) y = 1; | ||
if (x < MINIMUM_COLS) x = MINIMUM_COLS; | ||
if (y < MINIMUM_ROWS) y = MINIMUM_ROWS; | ||
@@ -1725,0 +1715,0 @@ this.buffers.resize(x, y); |
@@ -114,3 +114,3 @@ /** | ||
parse(data: string): void; | ||
print(data: string, start: number, end: number): void; | ||
print(data: Uint32Array, start: number, end: number): void; | ||
@@ -241,3 +241,3 @@ /** C0 BEL */ bell(): void; | ||
export interface IElementAccessor { | ||
element: HTMLElement; | ||
readonly element: HTMLElement; | ||
} | ||
@@ -457,14 +457,22 @@ | ||
* subparsers that get hook/unhooked and can handle | ||
* arbitrary amount of print data. | ||
* arbitrary amount of data. | ||
* | ||
* On entering a DSC sequence `hook` is called by | ||
* `EscapeSequenceParser`. Use it to initialize or reset | ||
* states needed to handle the current DCS sequence. | ||
* Note: A DCS parser is only instantiated once, therefore | ||
* you cannot rely on the ctor to reinitialize state. | ||
* | ||
* EscapeSequenceParser will call `put` several times if the | ||
* parsed string got splitted, therefore you might have to collect | ||
* `data` until `unhook` is called. `unhook` marks the end | ||
* of the current DCS sequence. | ||
* parsed data got split, therefore you might have to collect | ||
* `data` until `unhook` is called. | ||
* Note: `data` is borrowed, if you cannot process the data | ||
* in chunks you have to copy it, doing otherwise will lead to | ||
* data losses or corruption. | ||
* | ||
* `unhook` marks the end of the current DCS sequence. | ||
*/ | ||
export interface IDcsHandler { | ||
hook(collect: string, params: number[], flag: number): void; | ||
put(data: string, start: number, end: number): void; | ||
put(data: Uint32Array, start: number, end: number): void; | ||
unhook(): void; | ||
@@ -486,5 +494,5 @@ } | ||
*/ | ||
parse(data: string): void; | ||
parse(data: Uint32Array, length: number): void; | ||
setPrintHandler(callback: (data: string, start: number, end: number) => void): void; | ||
setPrintHandler(callback: (data: Uint32Array, start: number, end: number) => void): void; | ||
clearPrintHandler(): void; | ||
@@ -529,3 +537,3 @@ | ||
replaceCells(start: number, end: number, fill: CharData): void; | ||
resize(cols: number, fill: CharData, shrink?: boolean): void; | ||
resize(cols: number, fill: CharData): void; | ||
fill(fillCharData: CharData): void; | ||
@@ -537,5 +545,1 @@ copyFrom(line: IBufferLine): void; | ||
} | ||
export interface IBufferLineConstructor { | ||
new(cols: number, fillCharData?: CharData, isWrapped?: boolean): IBufferLine; | ||
} |
@@ -49,5 +49,6 @@ /** | ||
} | ||
if (this._width !== geometry.width || this._height !== geometry.height) { | ||
const adjustedHeight = Math.ceil(geometry.height); | ||
if (this._width !== geometry.width || this._height !== adjustedHeight) { | ||
this._width = geometry.width; | ||
this._height = Math.ceil(geometry.height); | ||
this._height = adjustedHeight; | ||
this.emit('charsizechanged'); | ||
@@ -54,0 +55,0 @@ } |
@@ -88,12 +88,18 @@ /** | ||
*/ | ||
export function moveTextAreaUnderMouseCursor(ev: MouseEvent, textarea: HTMLTextAreaElement): void { | ||
export function moveTextAreaUnderMouseCursor(ev: MouseEvent, term: ITerminal): void { | ||
// Calculate textarea position relative to the screen element | ||
const pos = term.screenElement.getBoundingClientRect(); | ||
const left = ev.clientX - pos.left - 10; | ||
const top = ev.clientY - pos.top - 10; | ||
// Bring textarea at the cursor position | ||
textarea.style.position = 'fixed'; | ||
textarea.style.width = '20px'; | ||
textarea.style.height = '20px'; | ||
textarea.style.left = (ev.clientX - 10) + 'px'; | ||
textarea.style.top = (ev.clientY - 10) + 'px'; | ||
textarea.style.zIndex = '1000'; | ||
term.textarea.style.position = 'absolute'; | ||
term.textarea.style.width = '20px'; | ||
term.textarea.style.height = '20px'; | ||
term.textarea.style.left = `${left}px`; | ||
term.textarea.style.top = `${top}px`; | ||
term.textarea.style.zIndex = '1000'; | ||
textarea.focus(); | ||
term.textarea.focus(); | ||
@@ -103,8 +109,8 @@ // Reset the terminal textarea's styling | ||
setTimeout(() => { | ||
textarea.style.position = null; | ||
textarea.style.width = null; | ||
textarea.style.height = null; | ||
textarea.style.left = null; | ||
textarea.style.top = null; | ||
textarea.style.zIndex = null; | ||
term.textarea.style.position = null; | ||
term.textarea.style.width = null; | ||
term.textarea.style.height = null; | ||
term.textarea.style.left = null; | ||
term.textarea.style.top = null; | ||
term.textarea.style.zIndex = null; | ||
}, 200); | ||
@@ -120,4 +126,4 @@ } | ||
*/ | ||
export function rightClickHandler(ev: MouseEvent, textarea: HTMLTextAreaElement, selectionManager: ISelectionManager, shouldSelectWord: boolean): void { | ||
moveTextAreaUnderMouseCursor(ev, textarea); | ||
export function rightClickHandler(ev: MouseEvent, term: ITerminal, selectionManager: ISelectionManager, shouldSelectWord: boolean): void { | ||
moveTextAreaUnderMouseCursor(ev, term); | ||
@@ -129,4 +135,4 @@ if (shouldSelectWord && !selectionManager.isClickInSelection(ev)) { | ||
// Get textarea ready to copy from the context menu | ||
textarea.value = selectionManager.selectionText; | ||
textarea.select(); | ||
term.textarea.value = selectionManager.selectionText; | ||
term.textarea.select(); | ||
} |
@@ -9,3 +9,3 @@ /** | ||
/** | ||
* Adds a disposabe listener to a node in the DOM, returning the disposable. | ||
* Adds a disposable listener to a node in the DOM, returning the disposable. | ||
* @param type The event type. | ||
@@ -12,0 +12,0 @@ * @param handler The handler for the listener. |
@@ -105,13 +105,2 @@ /** | ||
/** | ||
* (EXPERIMENTAL) Defines which implementation to use for buffer lines. | ||
* | ||
* - 'JsArray': The default/stable implementation. | ||
* - 'TypedArray': The new experimental implementation based on TypedArrays that is expected to | ||
* significantly boost performance and memory consumption. Use at your own risk. | ||
* | ||
* @deprecated This option will be removed in the future. | ||
*/ | ||
experimentalBufferLineImpl?: 'JsArray' | 'TypedArray'; | ||
/** | ||
* The font size used to render text. | ||
@@ -329,3 +318,3 @@ */ | ||
*/ | ||
element: HTMLElement; | ||
readonly element: HTMLElement; | ||
@@ -335,13 +324,17 @@ /** | ||
*/ | ||
textarea: HTMLTextAreaElement; | ||
readonly textarea: HTMLTextAreaElement; | ||
/** | ||
* The number of rows in the terminal's viewport. | ||
* The number of rows in the terminal's viewport. Use | ||
* `ITerminalOptions.rows` to set this in the constructor and | ||
* `Terminal.resize` for when the terminal exists. | ||
*/ | ||
rows: number; | ||
readonly rows: number; | ||
/** | ||
* The number of columns in the terminal's viewport. | ||
* The number of columns in the terminal's viewport. Use | ||
* `ITerminalOptions.cols` to set this in the constructor and | ||
* `Terminal.resize` for when the terminal exists. | ||
*/ | ||
cols: number; | ||
readonly cols: number; | ||
@@ -352,3 +345,3 @@ /** | ||
*/ | ||
markers: IMarker[]; | ||
readonly markers: IMarker[]; | ||
@@ -457,3 +450,5 @@ /** | ||
/** | ||
* Resizes the terminal. | ||
* Resizes the terminal. It's best practice to debounce calls to resize, | ||
* this will help ensure that the pty can respond to the resize event | ||
* before another one occurs. | ||
* @param x The number of columns to resize to. | ||
@@ -484,3 +479,3 @@ * @param y The number of rows to resize to. | ||
* This is a function that takes a KeyboardEvent, allowing consumers to stop | ||
* propogation and/or prevent the default action. The function returns | ||
* propagation and/or prevent the default action. The function returns | ||
* whether the event should be processed by xterm.js. | ||
@@ -487,0 +482,0 @@ */ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
2415674
288
36434