mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge branch 'master' into 1567_npe
This commit is contained in:
@@ -1,6 +1,9 @@
|
||||
# [](https://xtermjs.org)
|
||||
|
||||
[](https://travis-ci.org/xtermjs/xterm.js) [](https://xtermjs.visualstudio.com/xterm.js/_build/index?definitionId=1) [](https://coveralls.io/github/xtermjs/xterm.js?branch=master) [](https://gitter.im/sourcelair/xterm.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [](https://www.jsdelivr.com/package/npm/xterm)
|
||||
[](https://xtermjs.visualstudio.com/xterm.js/_build/index?definitionId=1)
|
||||
[](https://coveralls.io/github/xtermjs/xterm.js?branch=master)
|
||||
[](https://gitter.im/sourcelair/xterm.js?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
[](https://www.jsdelivr.com/package/npm/xterm)
|
||||
|
||||
Xterm.js is a terminal front-end component written in JavaScript that works in the browser.
|
||||
|
||||
@@ -57,6 +60,12 @@ The proposed way to load xterm.js is via the ES6 module syntax.
|
||||
import { Terminal } from 'xterm';
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
The full API for xterm.js is contained within the [TypeScript declaration file](https://github.com/xtermjs/xterm.js/blob/master/typings/xterm.d.ts), use the branch/tag picker in GitHub (`w`) to navigate to the correct version of the API.
|
||||
|
||||
Note that some APIs are marked *experimental*, these are added so we can experiment with new ideas without committing to support it like a normal semver API. Note that these APIs can change radically between versions so be sure to read release notes if you plan on using experimental APIs.
|
||||
|
||||
### Addons
|
||||
|
||||
Addons are JavaScript modules that extend the `Terminal` prototype with new methods and attributes to provide additional functionality. There are a handful available in the main repository in the `src/addons` directory and you can even write your own, by using xterm.js' public API.
|
||||
@@ -74,6 +83,8 @@ var xterm = new Terminal(); // Instantiate the terminal
|
||||
xterm.fit(); // Use the `fit` method, provided by the `fit` addon
|
||||
```
|
||||
|
||||
You will also need to include the addon's CSS file if it has one in the folder.
|
||||
|
||||
#### Importing Addons in TypeScript
|
||||
|
||||
There are currently no typings for addons if they are accessed via extending Terminal prototype, so you will need to upcast if using TypeScript, eg. `(<any>xterm).fit()`.
|
||||
@@ -107,6 +118,10 @@ Since xterm.js is typically implemented as a developer tool, only modern browser
|
||||
|
||||
Xterm.js works seamlessly in Electron apps and may even work on earlier versions of the browsers but these are the browsers we strive to keep working.
|
||||
|
||||
## API
|
||||
|
||||
The current full API documentation is available in the [TypeScript declaration file on the repository](https://github.com/xtermjs/xterm.js/blob/master/typings/xterm.d.ts), switch the tag (press `w` when viewing the file) to point at the specific version tag you're using.
|
||||
|
||||
## Real-world uses
|
||||
Xterm.js is used in several world-class applications to provide great terminal experiences.
|
||||
|
||||
@@ -155,6 +170,8 @@ computational environment for Jupyter, supporting interactive data science and s
|
||||
- [**Nutanix**](https://github.com/nutanix): Nutanix Enterprise Cloud uses xterm in the webssh functionality within Nutanix Calm, and is also looking to move our old noserial (termjs) functionality to xterm.js
|
||||
- [**SSH Web Client**](https://github.com/roke22/PHP-SSH2-Web-Client): SSH Web Client with PHP.
|
||||
|
||||
[And much more...](https://github.com/xtermjs/xterm.js/network/dependents)
|
||||
|
||||
Do you use xterm.js in your application as well? Please [open a Pull Request](https://github.com/sourcelair/xterm.js/pulls) to include it here. We would love to have it in our list. Note: Please add any new contributions to the end of the list only.
|
||||
|
||||
## Releases
|
||||
|
||||
+3
-4
@@ -174,9 +174,7 @@ function initOptions(term) {
|
||||
'termName',
|
||||
'useFlowControl',
|
||||
// Complex option
|
||||
'theme',
|
||||
// Only in constructor
|
||||
'rendererType'
|
||||
'theme'
|
||||
];
|
||||
var stringOptions = {
|
||||
bellSound: null,
|
||||
@@ -185,7 +183,8 @@ function initOptions(term) {
|
||||
experimentalCharAtlas: ['none', 'static', 'dynamic'],
|
||||
fontFamily: null,
|
||||
fontWeight: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
|
||||
fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900']
|
||||
fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
|
||||
rendererType: ['dom', 'canvas']
|
||||
};
|
||||
var options = Object.keys(term._core.options);
|
||||
var booleanOptions = [];
|
||||
|
||||
@@ -72,7 +72,7 @@ export class AccessibilityManager extends Disposable {
|
||||
this._terminal.element.insertAdjacentElement('afterbegin', this._accessibilityTreeRoot);
|
||||
|
||||
this.register(this._renderRowsDebouncer);
|
||||
this.register(this._terminal.addDisposableListener('resize', data => this._onResize(data.cols, data.rows)));
|
||||
this.register(this._terminal.addDisposableListener('resize', data => this._onResize(data.rows)));
|
||||
this.register(this._terminal.addDisposableListener('refresh', data => this._refreshRows(data.start, data.end)));
|
||||
this.register(this._terminal.addDisposableListener('scroll', data => this._refreshRows()));
|
||||
// Line feed is an issue as the prompt won't be read out after a command is run
|
||||
@@ -157,7 +157,7 @@ export class AccessibilityManager extends Disposable {
|
||||
e.stopImmediatePropagation();
|
||||
}
|
||||
|
||||
private _onResize(cols: number, rows: number): void {
|
||||
private _onResize(rows: number): void {
|
||||
// Remove bottom boundary listener
|
||||
this._rowElements[this._rowElements.length - 1].removeEventListener('focus', this._bottomBoundaryFocusListener);
|
||||
|
||||
@@ -259,6 +259,9 @@ export class AccessibilityManager extends Disposable {
|
||||
if (!this._terminal.renderer.dimensions.actualCellHeight) {
|
||||
return;
|
||||
}
|
||||
if (this._rowElements.length !== this._terminal.rows) {
|
||||
this._onResize(this._terminal.rows);
|
||||
}
|
||||
for (let i = 0; i < this._terminal.rows; i++) {
|
||||
this._refreshRowDimensions(this._rowElements[i]);
|
||||
}
|
||||
|
||||
+5
-1
@@ -15,6 +15,10 @@ export const CHAR_DATA_WIDTH_INDEX = 2;
|
||||
export const CHAR_DATA_CODE_INDEX = 3;
|
||||
export const MAX_BUFFER_SIZE = 4294967295; // 2^32 - 1
|
||||
|
||||
export const NULL_CELL_CHAR = ' ';
|
||||
export const NULL_CELL_WIDTH = 1;
|
||||
export const NULL_CELL_CODE = 32;
|
||||
|
||||
/**
|
||||
* This class represents a terminal buffer (an internal state of the terminal), where the
|
||||
* following information is stored (in high-level):
|
||||
@@ -117,7 +121,7 @@ export class Buffer implements IBuffer {
|
||||
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, ' ', 1, 32]; // does xterm use the default attr?
|
||||
const ch: CharData = [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; // does xterm use the default attr?
|
||||
for (let i = 0; i < this.lines.length; i++) {
|
||||
while (this.lines.get(i).length < newCols) {
|
||||
this.lines.get(i).push(ch);
|
||||
|
||||
+1
-4
@@ -18,7 +18,7 @@ export class BufferSet extends EventEmitter implements IBufferSet {
|
||||
|
||||
/**
|
||||
* Create a new BufferSet for the given terminal.
|
||||
* @param {Terminal} terminal - The terminal the BufferSet will belong to
|
||||
* @param _terminal - The terminal the BufferSet will belong to
|
||||
*/
|
||||
constructor(private _terminal: ITerminal) {
|
||||
super();
|
||||
@@ -35,7 +35,6 @@ export class BufferSet extends EventEmitter implements IBufferSet {
|
||||
|
||||
/**
|
||||
* Returns the alt Buffer of the BufferSet
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
public get alt(): Buffer {
|
||||
return this._alt;
|
||||
@@ -43,7 +42,6 @@ export class BufferSet extends EventEmitter implements IBufferSet {
|
||||
|
||||
/**
|
||||
* Returns the normal Buffer of the BufferSet
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
public get active(): Buffer {
|
||||
return this._activeBuffer;
|
||||
@@ -51,7 +49,6 @@ export class BufferSet extends EventEmitter implements IBufferSet {
|
||||
|
||||
/**
|
||||
* Returns the currently active Buffer of the BufferSet
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
public get normal(): Buffer {
|
||||
return this._normal;
|
||||
|
||||
@@ -61,7 +61,7 @@ export class CompositionHelper {
|
||||
|
||||
/**
|
||||
* Handles the compositionupdate event, updating the composition view.
|
||||
* @param {CompositionEvent} ev The event.
|
||||
* @param ev The event.
|
||||
*/
|
||||
public compositionupdate(ev: CompositionEvent): void {
|
||||
this._compositionView.textContent = ev.data;
|
||||
|
||||
+7
-7
@@ -7,7 +7,7 @@
|
||||
import { CharData, IInputHandler, IDcsHandler, IEscapeSequenceParser, IBuffer } from './Types';
|
||||
import { C0, C1 } from './common/data/EscapeSequences';
|
||||
import { CHARSETS, DEFAULT_CHARSET } from './core/data/Charsets';
|
||||
import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CODE_INDEX, DEFAULT_ATTR } from './Buffer';
|
||||
import { CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CODE_INDEX, DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE } from './Buffer';
|
||||
import { FLAGS } from './renderer/Types';
|
||||
import { wcwidth } from './CharWidth';
|
||||
import { EscapeSequenceParser } from './EscapeSequenceParser';
|
||||
@@ -436,11 +436,11 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
if (removed[CHAR_DATA_WIDTH_INDEX] === 0
|
||||
&& bufferRow[this._terminal.cols - 2]
|
||||
&& bufferRow[this._terminal.cols - 2][CHAR_DATA_WIDTH_INDEX] === 2) {
|
||||
bufferRow[this._terminal.cols - 2] = [curAttr, ' ', 1, 32 /* ' '.charCodeAt(0) */ ];
|
||||
bufferRow[this._terminal.cols - 2] = [curAttr, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE];
|
||||
}
|
||||
|
||||
// insert empty cell at cursor
|
||||
bufferRow.splice(buffer.x, 0, [curAttr, ' ', 1, 32 /* ' '.charCodeAt(0) */ ]);
|
||||
bufferRow.splice(buffer.x, 0, [curAttr, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -552,7 +552,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
|
||||
const row = buffer.y + buffer.ybase;
|
||||
let j = buffer.x;
|
||||
const ch: CharData = [this._terminal.eraseAttr(), ' ', 1, 32]; // xterm
|
||||
const ch: CharData = [this._terminal.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; // xterm
|
||||
|
||||
while (param-- && j < this._terminal.cols) {
|
||||
buffer.lines.get(row).splice(j++, 0, ch);
|
||||
@@ -862,7 +862,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
const buffer = this._terminal.buffer;
|
||||
|
||||
const row = buffer.y + buffer.ybase;
|
||||
const ch: CharData = [this._terminal.eraseAttr(), ' ', 1, 32]; // xterm
|
||||
const ch: CharData = [this._terminal.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; // xterm
|
||||
|
||||
while (param--) {
|
||||
buffer.lines.get(row).splice(buffer.x, 1);
|
||||
@@ -924,7 +924,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
|
||||
const row = buffer.y + buffer.ybase;
|
||||
let j = buffer.x;
|
||||
const ch: CharData = [this._terminal.eraseAttr(), ' ', 1, 32]; // xterm
|
||||
const ch: CharData = [this._terminal.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; // xterm
|
||||
|
||||
while (param-- && j < this._terminal.cols) {
|
||||
buffer.lines.get(row)[j++] = ch;
|
||||
@@ -986,7 +986,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
const buffer = this._terminal.buffer;
|
||||
|
||||
const line = buffer.lines.get(buffer.ybase + buffer.y);
|
||||
const ch = line[buffer.x - 1] || [DEFAULT_ATTR, ' ', 1, 32];
|
||||
const ch = line[buffer.x - 1] || [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE];
|
||||
|
||||
while (param--) {
|
||||
line[buffer.x++] = ch;
|
||||
|
||||
@@ -183,6 +183,42 @@ describe('SelectionManager', () => {
|
||||
selectionManager.selectWordAt([15, 0]);
|
||||
assert.equal(selectionManager.selectionText, 'ij"');
|
||||
});
|
||||
it('should expand upwards or downards for wrapped lines', () => {
|
||||
buffer.lines.set(0, stringToRow(' foo'));
|
||||
buffer.lines.set(1, stringToRow('bar '));
|
||||
(<any>buffer.lines.get(1)).isWrapped = true;
|
||||
selectionManager.selectWordAt([1, 1]);
|
||||
assert.equal(selectionManager.selectionText, 'foobar');
|
||||
selectionManager.model.clearSelection();
|
||||
selectionManager.selectWordAt([78, 0]);
|
||||
assert.equal(selectionManager.selectionText, 'foobar');
|
||||
});
|
||||
it('should expand both upwards and downwards for word wrapped over many lines', () => {
|
||||
const expectedText = 'fooaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccbar';
|
||||
buffer.lines.set(0, stringToRow(' foo'));
|
||||
buffer.lines.set(1, stringToRow('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'));
|
||||
buffer.lines.set(2, stringToRow('bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'));
|
||||
buffer.lines.set(3, stringToRow('cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc'));
|
||||
buffer.lines.set(4, stringToRow('bar '));
|
||||
(<any>buffer.lines.get(1)).isWrapped = true;
|
||||
(<any>buffer.lines.get(2)).isWrapped = true;
|
||||
(<any>buffer.lines.get(3)).isWrapped = true;
|
||||
(<any>buffer.lines.get(4)).isWrapped = true;
|
||||
selectionManager.selectWordAt([78, 0]);
|
||||
assert.equal(selectionManager.selectionText, expectedText);
|
||||
selectionManager.model.clearSelection();
|
||||
selectionManager.selectWordAt([40, 1]);
|
||||
assert.equal(selectionManager.selectionText, expectedText);
|
||||
selectionManager.model.clearSelection();
|
||||
selectionManager.selectWordAt([40, 2]);
|
||||
assert.equal(selectionManager.selectionText, expectedText);
|
||||
selectionManager.model.clearSelection();
|
||||
selectionManager.selectWordAt([40, 3]);
|
||||
assert.equal(selectionManager.selectionText, expectedText);
|
||||
selectionManager.model.clearSelection();
|
||||
selectionManager.selectWordAt([1, 4]);
|
||||
assert.equal(selectionManager.selectionText, expectedText);
|
||||
});
|
||||
describe('emoji', () => {
|
||||
it('should treat a single emoji as a word when wrapped in spaces', () => {
|
||||
buffer.lines.set(0, stringToRow(' ⚽ a')); // The a is here to prevent the space being trimmed in selectionText
|
||||
|
||||
+59
-7
@@ -9,7 +9,7 @@ import * as Browser from './shared/utils/Browser';
|
||||
import { CharMeasure } from './ui/CharMeasure';
|
||||
import { EventEmitter } from './EventEmitter';
|
||||
import { SelectionModel } from './SelectionModel';
|
||||
import { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX } from './Buffer';
|
||||
import { CHAR_DATA_WIDTH_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_CODE_INDEX } from './Buffer';
|
||||
import { AltClickHandler } from './handlers/AltClickHandler';
|
||||
|
||||
/**
|
||||
@@ -451,8 +451,10 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
* Removes the listeners that are registered when mousedown is triggered.
|
||||
*/
|
||||
private _removeMouseDownListeners(): void {
|
||||
this._terminal.element.ownerDocument.removeEventListener('mousemove', this._mouseMoveListener);
|
||||
this._terminal.element.ownerDocument.removeEventListener('mouseup', this._mouseUpListener);
|
||||
if (this._terminal.element.ownerDocument) {
|
||||
this._terminal.element.ownerDocument.removeEventListener('mousemove', this._mouseMoveListener);
|
||||
this._terminal.element.ownerDocument.removeEventListener('mouseup', this._mouseUpListener);
|
||||
}
|
||||
clearInterval(this._dragScrollIntervalTimer);
|
||||
this._dragScrollIntervalTimer = null;
|
||||
}
|
||||
@@ -677,7 +679,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
* Gets positional information for the word at the coordinated specified.
|
||||
* @param coords The coordinates to get the word at.
|
||||
*/
|
||||
private _getWordAt(coords: [number, number], allowWhitespaceOnlySelection: boolean): IWordPosition {
|
||||
private _getWordAt(coords: [number, number], allowWhitespaceOnlySelection: boolean, followWrappedLinesAbove: boolean = true, followWrappedLinesBelow: boolean = true): IWordPosition {
|
||||
// Ensure coords are within viewport (eg. not within scroll bar)
|
||||
if (coords[0] >= this._terminal.cols) {
|
||||
return null;
|
||||
@@ -772,7 +774,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
|
||||
// Calculate the start _column_, converting the the string indexes back to
|
||||
// column coordinates.
|
||||
const start =
|
||||
let start =
|
||||
startIndex // The index of the selection's start char in the line string
|
||||
+ charOffset // The difference between the initial char's column and index
|
||||
- leftWideCharCount // The number of wide chars left of the initial char
|
||||
@@ -780,7 +782,7 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
|
||||
// Calculate the length in _columns_, converting the the string indexes back
|
||||
// to column coordinates.
|
||||
const length = Math.min(this._terminal.cols, // Disallow lengths larger than the terminal cols
|
||||
let length = Math.min(this._terminal.cols, // Disallow lengths larger than the terminal cols
|
||||
endIndex // The index of the selection's end char in the line string
|
||||
- startIndex // The index of the selection's start char in the line string
|
||||
+ leftWideCharCount // The number of wide chars left of the initial char
|
||||
@@ -792,6 +794,34 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
return null;
|
||||
}
|
||||
|
||||
// Recurse upwards if the line is wrapped and the word wraps to the above line
|
||||
if (followWrappedLinesAbove) {
|
||||
if (start === 0 && bufferLine[0][CHAR_DATA_CODE_INDEX] !== 32 /*' '*/) {
|
||||
const previousBufferLine = this._buffer.lines.get(coords[1] - 1);
|
||||
if (previousBufferLine && (<any>bufferLine).isWrapped && previousBufferLine[this._terminal.cols - 1][CHAR_DATA_CODE_INDEX] !== 32 /*' '*/) {
|
||||
const previousLineWordPosition = this._getWordAt([this._terminal.cols - 1, coords[1] - 1], false, true, false);
|
||||
if (previousLineWordPosition) {
|
||||
const offset = this._terminal.cols - previousLineWordPosition.start;
|
||||
start -= offset;
|
||||
length += offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recurse downwards if the line is wrapped and the word wraps to the next line
|
||||
if (followWrappedLinesBelow) {
|
||||
if (start + length === this._terminal.cols && bufferLine[this._terminal.cols - 1][CHAR_DATA_CODE_INDEX] !== 32 /*' '*/) {
|
||||
const nextBufferLine = this._buffer.lines.get(coords[1] + 1);
|
||||
if (nextBufferLine && (<any>nextBufferLine).isWrapped && nextBufferLine[0][CHAR_DATA_CODE_INDEX] !== 32 /*' '*/) {
|
||||
const nextLineWordPosition = this._getWordAt([0, coords[1] + 1], false, false, true);
|
||||
if (nextLineWordPosition) {
|
||||
length += nextLineWordPosition.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { start, length };
|
||||
}
|
||||
|
||||
@@ -803,6 +833,11 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
protected _selectWordAt(coords: [number, number], allowWhitespaceOnlySelection: boolean): void {
|
||||
const wordPosition = this._getWordAt(coords, allowWhitespaceOnlySelection);
|
||||
if (wordPosition) {
|
||||
// Adjust negative start value
|
||||
while (wordPosition.start < 0) {
|
||||
wordPosition.start += this._terminal.cols;
|
||||
coords[1]--;
|
||||
}
|
||||
this._model.selectionStart = [wordPosition.start, coords[1]];
|
||||
this._model.selectionStartLength = wordPosition.length;
|
||||
}
|
||||
@@ -815,7 +850,24 @@ export class SelectionManager extends EventEmitter implements ISelectionManager
|
||||
private _selectToWordAt(coords: [number, number]): void {
|
||||
const wordPosition = this._getWordAt(coords, true);
|
||||
if (wordPosition) {
|
||||
this._model.selectionEnd = [this._model.areSelectionValuesReversed() ? wordPosition.start : (wordPosition.start + wordPosition.length), coords[1]];
|
||||
let endRow = coords[1];
|
||||
|
||||
// Adjust negative start value
|
||||
while (wordPosition.start < 0) {
|
||||
wordPosition.start += this._terminal.cols;
|
||||
endRow--;
|
||||
}
|
||||
|
||||
// Adjust wrapped length value, this only needs to happen when values are reversed as in that
|
||||
// case we're interested in the start of the word, not the end
|
||||
if (!this._model.areSelectionValuesReversed()) {
|
||||
while (wordPosition.start + wordPosition.length > this._terminal.cols) {
|
||||
wordPosition.length -= this._terminal.cols;
|
||||
endRow++;
|
||||
}
|
||||
}
|
||||
|
||||
this._model.selectionEnd = [this._model.areSelectionValuesReversed() ? wordPosition.start : wordPosition.start + wordPosition.length, endRow];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,6 +128,11 @@ describe('SelectionManager', () => {
|
||||
model.selectionEnd = [3, 2];
|
||||
assert.deepEqual(model.finalSelectionEnd, [4, 2]);
|
||||
});
|
||||
it('should return the end on a different row when start + length overflows onto a following row', () => {
|
||||
model.selectionStart = [78, 2];
|
||||
model.selectionStartLength = 4;
|
||||
assert.deepEqual(model.finalSelectionEnd, [2, 3]);
|
||||
});
|
||||
it('should return selection end if selection end is after selection start + length', () => {
|
||||
model.selectionStart = [2, 2];
|
||||
model.selectionStartLength = 2;
|
||||
|
||||
@@ -76,9 +76,13 @@ export class SelectionModel {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Use the selection start if the end doesn't exist or they're reversed
|
||||
// Use the selection start + length if the end doesn't exist or they're reversed
|
||||
if (!this.selectionEnd || this.areSelectionValuesReversed()) {
|
||||
return [this.selectionStart[0] + this.selectionStartLength, this.selectionStart[1]];
|
||||
const startPlusLength = this.selectionStart[0] + this.selectionStartLength;
|
||||
if (startPlusLength > this._terminal.cols) {
|
||||
return [startPlusLength % this._terminal.cols, this.selectionStart[1] + Math.floor(startPlusLength / this._terminal.cols)];
|
||||
}
|
||||
return [startPlusLength, this.selectionStart[1]];
|
||||
}
|
||||
|
||||
// Ensure the the word/line is selected after a double/triple click
|
||||
|
||||
+70
-44
@@ -25,7 +25,7 @@ import { IInputHandlingTerminal, IViewport, ICompositionHelper, ITerminalOptions
|
||||
import { IMouseZoneManager } from './ui/Types';
|
||||
import { IRenderer } from './renderer/Types';
|
||||
import { BufferSet } from './BufferSet';
|
||||
import { Buffer, MAX_BUFFER_SIZE, DEFAULT_ATTR } from './Buffer';
|
||||
import { Buffer, MAX_BUFFER_SIZE, DEFAULT_ATTR, NULL_CELL_CODE, NULL_CELL_WIDTH, NULL_CELL_CHAR } from './Buffer';
|
||||
import { CompositionHelper } from './CompositionHelper';
|
||||
import { EventEmitter } from './EventEmitter';
|
||||
import { Viewport } from './Viewport';
|
||||
@@ -72,7 +72,7 @@ const WRITE_BATCH_SIZE = 300;
|
||||
/**
|
||||
* The set of options that only have an effect when set in the Terminal constructor.
|
||||
*/
|
||||
const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows', 'rendererType'];
|
||||
const CONSTRUCTOR_ONLY_OPTIONS = ['cols', 'rows'];
|
||||
|
||||
const DEFAULT_OPTIONS: ITerminalOptions = {
|
||||
cols: 80,
|
||||
@@ -208,6 +208,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
public mouseHelper: MouseHelper;
|
||||
private _accessibilityManager: AccessibilityManager;
|
||||
private _screenDprMonitor: ScreenDprMonitor;
|
||||
private _theme: ITheme;
|
||||
|
||||
public cols: number;
|
||||
public rows: number;
|
||||
@@ -215,7 +216,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
/**
|
||||
* Creates a new `Terminal` object.
|
||||
*
|
||||
* @param {object} options An object containing a set of options, the available options are:
|
||||
* @param options An object containing a set of options, the available options are:
|
||||
* - `cursorBlink` (boolean): Whether the terminal cursor blinks
|
||||
* - `cols` (number): The number of columns of the terminal (horizontal size)
|
||||
* - `rows` (number): The number of rows of the terminal (vertical size)
|
||||
@@ -350,7 +351,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
/**
|
||||
* Retrieves an option's value from the terminal.
|
||||
* @param {string} key The option key.
|
||||
* @param key The option key.
|
||||
*/
|
||||
public getOption(key: string): any {
|
||||
if (!(key in DEFAULT_OPTIONS)) {
|
||||
@@ -362,8 +363,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
/**
|
||||
* Sets an option on the terminal.
|
||||
* @param {string} key The option key.
|
||||
* @param {any} value The option value.
|
||||
* @param key The option key.
|
||||
* @param value The option value.
|
||||
*/
|
||||
public setOption(key: string, value: any): void {
|
||||
if (!(key in DEFAULT_OPTIONS)) {
|
||||
@@ -372,6 +373,9 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
if (CONSTRUCTOR_ONLY_OPTIONS.indexOf(key) !== -1) {
|
||||
console.error(`Option "${key}" can only be set in the constructor`);
|
||||
}
|
||||
if (this.options[key] === value) {
|
||||
return;
|
||||
}
|
||||
switch (key) {
|
||||
case 'bellStyle':
|
||||
if (!value) {
|
||||
@@ -398,6 +402,11 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
console.warn(`${key} cannot be less than 1, value: ${value}`);
|
||||
return;
|
||||
}
|
||||
case 'rendererType':
|
||||
if (!value) {
|
||||
value = 'canvas';
|
||||
}
|
||||
break;
|
||||
case 'tabStopWidth':
|
||||
if (value < 1) {
|
||||
console.warn(`${key} cannot be less than 1, value: ${value}`);
|
||||
@@ -457,6 +466,18 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
this.renderer.onResize(this.cols, this.rows);
|
||||
this.refresh(0, this.rows - 1);
|
||||
}
|
||||
case 'rendererType':
|
||||
if (this.renderer) {
|
||||
this.unregister(this.renderer);
|
||||
this.renderer.dispose();
|
||||
this.renderer = null;
|
||||
}
|
||||
this._setupRenderer();
|
||||
this.renderer.onCharSizeChanged();
|
||||
if (this._theme) {
|
||||
this.renderer.setTheme(this._theme);
|
||||
}
|
||||
break;
|
||||
case 'scrollback':
|
||||
this.buffers.resize(this.cols, this.rows);
|
||||
if (this.viewport) {
|
||||
@@ -604,7 +625,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
/**
|
||||
* Opens the terminal within an element.
|
||||
*
|
||||
* @param {HTMLElement} parent The element to create the terminal within.
|
||||
* @param parent The element to create the terminal within.
|
||||
*/
|
||||
public open(parent: HTMLElement): void {
|
||||
this._parent = parent || this._parent;
|
||||
@@ -676,12 +697,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
// Performance: Add viewport and helper elements from the fragment
|
||||
this.element.appendChild(fragment);
|
||||
|
||||
switch (this.options.rendererType) {
|
||||
case 'canvas': this.renderer = new Renderer(this, this.options.theme); break;
|
||||
case 'dom': this.renderer = new DomRenderer(this, this.options.theme); break;
|
||||
default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`);
|
||||
}
|
||||
this.register(this.renderer);
|
||||
this._setupRenderer();
|
||||
this._theme = this.options.theme;
|
||||
this.options.theme = null;
|
||||
this.viewport = new Viewport(this, this._viewportElement, this._viewportScrollArea, this.charMeasure);
|
||||
this.viewport.onThemeChanged(this.renderer.colorManager.colors);
|
||||
@@ -738,11 +755,21 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
}
|
||||
|
||||
private _setupRenderer(): void {
|
||||
switch (this.options.rendererType) {
|
||||
case 'canvas': this.renderer = new Renderer(this, this.options.theme); break;
|
||||
case 'dom': this.renderer = new DomRenderer(this, this.options.theme); break;
|
||||
default: throw new Error(`Unrecognized rendererType "${this.options.rendererType}"`);
|
||||
}
|
||||
this.register(this.renderer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the theme on the renderer. The renderer must have been initialized.
|
||||
* @param theme The theme to ste.
|
||||
* @param theme The theme to set.
|
||||
*/
|
||||
private _setTheme(theme: ITheme): void {
|
||||
this._theme = theme;
|
||||
const colors = this.renderer.setTheme(theme);
|
||||
if (this.viewport) {
|
||||
this.viewport.onThemeChanged(colors);
|
||||
@@ -1100,8 +1127,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
/**
|
||||
* Tells the renderer to refresh terminal content between two rows (inclusive) at the next
|
||||
* opportunity.
|
||||
* @param {number} start The row to start from (between 0 and this.rows - 1).
|
||||
* @param {number} end The row to end at (between start and this.rows - 1).
|
||||
* @param start The row to start from (between 0 and this.rows - 1).
|
||||
* @param end The row to end at (between start and this.rows - 1).
|
||||
*/
|
||||
public refresh(start: number, end: number): void {
|
||||
if (this.renderer) {
|
||||
@@ -1111,8 +1138,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
/**
|
||||
* Queues linkification for the specified rows.
|
||||
* @param {number} start The row to start from (between 0 and this.rows - 1).
|
||||
* @param {number} end The row to end at (between start and this.rows - 1).
|
||||
* @param start The row to start from (between 0 and this.rows - 1).
|
||||
* @param end The row to end at (between start and this.rows - 1).
|
||||
*/
|
||||
private _queueLinkification(start: number, end: number): void {
|
||||
if (this.linkifier) {
|
||||
@@ -1204,8 +1231,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
/**
|
||||
* Scroll the display of the terminal
|
||||
* @param {number} disp The number of lines to scroll down (negative scroll up).
|
||||
* @param {boolean} suppressScrollEvent Don't emit the scroll event as scrollLines. This is used
|
||||
* @param disp The number of lines to scroll down (negative scroll up).
|
||||
* @param suppressScrollEvent Don't emit the scroll event as scrollLines. This is used
|
||||
* to avoid unwanted events being handled by the viewport when the event was triggered from the
|
||||
* viewport originally.
|
||||
*/
|
||||
@@ -1236,7 +1263,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
/**
|
||||
* Scroll the display of the terminal by a number of pages.
|
||||
* @param {number} pageCount The number of pages to scroll (negative scrolls up).
|
||||
* @param pageCount The number of pages to scroll (negative scrolls up).
|
||||
*/
|
||||
public scrollPages(pageCount: number): void {
|
||||
this.scrollLines(pageCount * (this.rows - 1));
|
||||
@@ -1265,7 +1292,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
/**
|
||||
* Writes text to the terminal.
|
||||
* @param {string} data The text to write to the terminal.
|
||||
* @param data The text to write to the terminal.
|
||||
*/
|
||||
public write(data: string): void {
|
||||
// Ensure the terminal isn't disposed
|
||||
@@ -1341,7 +1368,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
/**
|
||||
* Writes text to the terminal, followed by a break line character (\n).
|
||||
* @param {string} data The text to write to the terminal.
|
||||
* @param data The text to write to the terminal.
|
||||
*/
|
||||
public writeln(data: string): void {
|
||||
this.write(data + '\r\n');
|
||||
@@ -1454,7 +1481,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
* Handle a keydown event
|
||||
* Key Resources:
|
||||
* - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent
|
||||
* @param {KeyboardEvent} ev The keydown event to be handled.
|
||||
* @param ev The keydown event to be handled.
|
||||
*/
|
||||
protected _keyDown(event: KeyboardEvent): boolean {
|
||||
if (this._customKeyEventHandler && this._customKeyEventHandler(event) === false) {
|
||||
@@ -1551,7 +1578,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
* Handle a keypress event.
|
||||
* Key Resources:
|
||||
* - https://developer.mozilla.org/en-US/docs/DOM/KeyboardEvent
|
||||
* @param {KeyboardEvent} ev The keypress event to be handled.
|
||||
* @param ev The keypress event to be handled.
|
||||
*/
|
||||
protected _keyPress(ev: KeyboardEvent): boolean {
|
||||
let key;
|
||||
@@ -1590,7 +1617,6 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
/**
|
||||
* Send data for handling to the terminal
|
||||
* @param {string} data
|
||||
*/
|
||||
public send(data: string): void {
|
||||
if (!this._sendDataQueue) {
|
||||
@@ -1643,8 +1669,8 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
/**
|
||||
* Resizes the terminal.
|
||||
*
|
||||
* @param {number} x The number of columns to resize to.
|
||||
* @param {number} y The number of rows to resize to.
|
||||
* @param x The number of columns to resize to.
|
||||
* @param y The number of rows to resize to.
|
||||
*/
|
||||
public resize(x: number, y: number): void {
|
||||
if (isNaN(x) || isNaN(y)) {
|
||||
@@ -1678,7 +1704,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
/**
|
||||
* Updates the range of rows to refresh
|
||||
* @param {number} y The number of rows to refresh next.
|
||||
* @param y The number of rows to refresh next.
|
||||
*/
|
||||
public updateRange(y: number): void {
|
||||
if (y < this._refreshStart) this._refreshStart = y;
|
||||
@@ -1701,15 +1727,15 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
/**
|
||||
* Erase in the identified line everything from "x" to the end of the line (right).
|
||||
* @param {number} x The column from which to start erasing to the end of the line.
|
||||
* @param {number} y The line in which to operate.
|
||||
* @param x The column from which to start erasing to the end of the line.
|
||||
* @param y The line in which to operate.
|
||||
*/
|
||||
public eraseRight(x: number, y: number): void {
|
||||
const line = this.buffer.lines.get(this.buffer.ybase + y);
|
||||
if (!line) {
|
||||
return;
|
||||
}
|
||||
const ch: CharData = [this.eraseAttr(), ' ', 1, 32 /* ' '.charCodeAt(0) */]; // xterm
|
||||
const ch: CharData = [this.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; // xterm
|
||||
for (; x < this.cols; x++) {
|
||||
line[x] = ch;
|
||||
}
|
||||
@@ -1718,15 +1744,15 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
/**
|
||||
* Erase in the identified line everything from "x" to the start of the line (left).
|
||||
* @param {number} x The column from which to start erasing to the start of the line.
|
||||
* @param {number} y The line in which to operate.
|
||||
* @param x The column from which to start erasing to the start of the line.
|
||||
* @param y The line in which to operate.
|
||||
*/
|
||||
public eraseLeft(x: number, y: number): void {
|
||||
const line = this.buffer.lines.get(this.buffer.ybase + y);
|
||||
if (!line) {
|
||||
return;
|
||||
}
|
||||
const ch: CharData = [this.eraseAttr(), ' ', 1, 32 /* ' '.charCodeAt(0) */]; // xterm
|
||||
const ch: CharData = [this.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; // xterm
|
||||
x++;
|
||||
while (x--) {
|
||||
line[x] = ch;
|
||||
@@ -1756,7 +1782,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
/**
|
||||
* Erase all content in the given line
|
||||
* @param {number} y The line to erase all of its contents.
|
||||
* @param y The line to erase all of its contents.
|
||||
*/
|
||||
public eraseLine(y: number): void {
|
||||
this.eraseRight(0, y);
|
||||
@@ -1764,15 +1790,15 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
/**
|
||||
* Return the data array of a blank line
|
||||
* @param {boolean} cur First bunch of data for each "blank" character.
|
||||
* @param {boolean} isWrapped Whether the new line is wrapped from the previous line.
|
||||
* @param {boolean} cols The number of columns in the terminal, if this is not
|
||||
* @param cur First bunch of data for each "blank" character.
|
||||
* @param isWrapped Whether the new line is wrapped from the previous line.
|
||||
* @param cols The number of columns in the terminal, if this is not
|
||||
* set, the terminal's current column count would be used.
|
||||
*/
|
||||
public blankLine(cur?: boolean, isWrapped?: boolean, cols?: number): LineData {
|
||||
const attr = cur ? this.eraseAttr() : DEFAULT_ATTR;
|
||||
|
||||
const ch: CharData = [attr, ' ', 1, 32 /* ' '.charCodeAt(0) */]; // width defaults to 1 halfwidth character
|
||||
const ch: CharData = [attr, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]; // width defaults to 1 halfwidth character
|
||||
const line: LineData = [];
|
||||
|
||||
// TODO: It is not ideal that this is a property on an array, a buffer line
|
||||
@@ -1795,9 +1821,9 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
*/
|
||||
public ch(cur?: boolean): CharData {
|
||||
if (cur) {
|
||||
return [this.eraseAttr(), ' ', 1, 32 /* ' '.charCodeAt(0) */];
|
||||
return [this.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE];
|
||||
}
|
||||
return [DEFAULT_ATTR, ' ', 1, 32 /* ' '.charCodeAt(0) */];
|
||||
return [DEFAULT_ATTR, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1810,7 +1836,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
/**
|
||||
* Emit the 'data' event and populate the given data.
|
||||
* @param {string} data The data to populate in the event.
|
||||
* @param data The data to populate in the event.
|
||||
*/
|
||||
public handler(data: string): void {
|
||||
// Prevents all events to pty process if stdin is disabled
|
||||
@@ -1832,7 +1858,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
|
||||
/**
|
||||
* Emit the 'title' event and populate the given title.
|
||||
* @param {string} title The title to populate in the event.
|
||||
* @param title The title to populate in the event.
|
||||
*/
|
||||
public handleTitle(title: string): void {
|
||||
/**
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
import { Terminal } from 'xterm';
|
||||
import { IWinptyCompatAddonTerminal } from './Interfaces';
|
||||
|
||||
const CHAR_DATA_CODE_INDEX = 3;
|
||||
const NULL_CELL_CODE = 32;
|
||||
|
||||
export function winptyCompatInit(terminal: Terminal): void {
|
||||
const addonTerminal = <IWinptyCompatAddonTerminal>terminal;
|
||||
|
||||
@@ -29,7 +32,7 @@ export function winptyCompatInit(terminal: Terminal): void {
|
||||
const line = addonTerminal._core.buffer.lines.get(addonTerminal._core.buffer.ybase + addonTerminal._core.buffer.y - 1);
|
||||
const lastChar = line[addonTerminal.cols - 1];
|
||||
|
||||
if (lastChar[3] !== 32 /* ' ' */) {
|
||||
if (lastChar[CHAR_DATA_CODE_INDEX] !== NULL_CELL_CODE) {
|
||||
const nextLine = addonTerminal._core.buffer.lines.get(addonTerminal._core.buffer.ybase + addonTerminal._core.buffer.y);
|
||||
(<any>nextLine).isWrapped = true;
|
||||
}
|
||||
|
||||
@@ -30,4 +30,16 @@ export abstract class Disposable implements IDisposable {
|
||||
public register<T extends IDisposable>(d: T): void {
|
||||
this._disposables.push(d);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a disposable object if it has been registered, if not do
|
||||
* nothing.
|
||||
* @param d The disposable to unregister.
|
||||
*/
|
||||
public unregister<T extends IDisposable>(d: T): void {
|
||||
const index = this._disposables.indexOf(d);
|
||||
if (index !== -1) {
|
||||
this._disposables.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ export function bracketTextForPaste(text: string, bracketedPasteMode: boolean):
|
||||
|
||||
/**
|
||||
* Binds copy functionality to the given terminal.
|
||||
* @param {ClipboardEvent} ev The original copy event to be handled
|
||||
* @param ev The original copy event to be handled
|
||||
*/
|
||||
export function copyHandler(ev: ClipboardEvent, term: ITerminal, selectionManager: ISelectionManager): void {
|
||||
if (term.browser.isMSIE) {
|
||||
@@ -50,8 +50,8 @@ export function copyHandler(ev: ClipboardEvent, term: ITerminal, selectionManage
|
||||
|
||||
/**
|
||||
* Redirect the clipboard's data to the terminal's input handler.
|
||||
* @param {ClipboardEvent} ev The original paste event to be handled
|
||||
* @param {Terminal} term The terminal on which to apply the handled paste event
|
||||
* @param ev The original paste event to be handled
|
||||
* @param term The terminal on which to apply the handled paste event
|
||||
*/
|
||||
export function pasteHandler(ev: ClipboardEvent, term: ITerminal): void {
|
||||
ev.stopPropagation();
|
||||
|
||||
@@ -36,6 +36,10 @@ export abstract class BaseRenderLayer implements IRenderLayer {
|
||||
this._container.appendChild(this._canvas);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._container.removeChild(this._canvas);
|
||||
}
|
||||
|
||||
private _initCanvas(): void {
|
||||
this._ctx = this._canvas.getContext('2d', {alpha: this._alpha});
|
||||
// Draw the background if this is an opaque layer
|
||||
|
||||
@@ -76,6 +76,11 @@ export class Renderer extends EventEmitter implements IRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
this._renderLayers.forEach(l => l.dispose());
|
||||
}
|
||||
|
||||
public onIntersectionChange(entry: IntersectionObserverEntry): void {
|
||||
this._isPaused = entry.intersectionRatio === 0;
|
||||
if (!this._isPaused && this._needsFullRefresh) {
|
||||
@@ -174,8 +179,8 @@ export class Renderer extends EventEmitter implements IRenderer {
|
||||
/**
|
||||
* Queues a refresh between two rows (inclusive), to be done on next animation
|
||||
* frame.
|
||||
* @param {number} start The start row.
|
||||
* @param {number} end The end row.
|
||||
* @param start The start row.
|
||||
* @param end The end row.
|
||||
*/
|
||||
public refreshRows(start: number, end: number): void {
|
||||
if (this._isPaused) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { CHAR_DATA_ATTR_INDEX, CHAR_DATA_CODE_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX } from '../Buffer';
|
||||
import { CHAR_DATA_ATTR_INDEX, CHAR_DATA_CODE_INDEX, CHAR_DATA_CHAR_INDEX, CHAR_DATA_WIDTH_INDEX, NULL_CELL_CODE } from '../Buffer';
|
||||
import { FLAGS, IColorSet, IRenderDimensions, ICharacterJoinerRegistry } from './Types';
|
||||
import { CharData, ITerminal } from '../Types';
|
||||
import { INVERTED_DEFAULT_COLOR } from './atlas/Types';
|
||||
@@ -124,7 +124,7 @@ export class TextRenderLayer extends BaseRenderLayer {
|
||||
// get removed, and `a` would not re-render because it thinks it's
|
||||
// already in the correct state.
|
||||
// this._state.cache[x][y] = OVERLAP_OWNED_CHAR_DATA;
|
||||
if (lastCharX < line.length - 1 && line[lastCharX + 1][CHAR_DATA_CODE_INDEX] === 32 /*' '*/) {
|
||||
if (lastCharX < line.length - 1 && line[lastCharX + 1][CHAR_DATA_CODE_INDEX] === NULL_CELL_CODE) {
|
||||
width = 2;
|
||||
// this._clearChar(x + 1, y);
|
||||
// The overlapping char's char data will force a clear and render when the
|
||||
|
||||
@@ -66,7 +66,7 @@ export interface IRenderDimensions {
|
||||
actualCellHeight: number;
|
||||
}
|
||||
|
||||
export interface IRenderLayer {
|
||||
export interface IRenderLayer extends IDisposable {
|
||||
/**
|
||||
* Called when the terminal loses focus.
|
||||
*/
|
||||
|
||||
@@ -81,6 +81,15 @@ export class DomRenderer extends EventEmitter implements IRenderer {
|
||||
this._terminal.screenElement.appendChild(this._selectionContainer);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._terminal.element.classList.remove(TERMINAL_CLASS_PREFIX + this._terminalClass);
|
||||
this._terminal.screenElement.removeChild(this._rowContainer);
|
||||
this._terminal.screenElement.removeChild(this._selectionContainer);
|
||||
this._terminal.screenElement.removeChild(this._themeStyleElement);
|
||||
this._terminal.screenElement.removeChild(this._dimensionsStyleElement);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private _updateDimensions(): void {
|
||||
this.dimensions.scaledCharWidth = this._terminal.charMeasure.width * window.devicePixelRatio;
|
||||
this.dimensions.scaledCharHeight = this._terminal.charMeasure.height * window.devicePixelRatio;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user