mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge branch 'master' into line-height
This commit is contained in:
+1
-1
@@ -16,4 +16,4 @@ RUN npm install
|
||||
COPY . /usr/src/app
|
||||
|
||||
# Run the tests and build, to make sure everything is working nicely
|
||||
RUN npm run build && npm run webpack && npm run test
|
||||
RUN npm run build && npm run test
|
||||
|
||||
@@ -159,7 +159,7 @@ computational environment for Jupyter, supporting interactive data science and s
|
||||
- [**rtty**](https://github.com/zhaojh329/rtty): A reverse proxy WebTTY. It is composed of the client and the server.
|
||||
- [**Pisth**](https://github.com/ColdGrub1384/Pisth): An SFTP and SSH client for iOS
|
||||
- [**abstruse**](https://github.com/bleenco/abstruse): Abstruse CI is a continuous integration platform based on Node.JS and Docker.
|
||||
- [**Microsoft SQL Operations Studio**](https://github.com/Microsoft/sqlopsstudio): A data management tool that enables working with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux
|
||||
- [**Azure Data Studio**](https://github.com/Microsoft/azuredatastudio): A data management tool that enables working with SQL Server, Azure SQL DB and SQL DW from Windows, macOS and Linux.
|
||||
- [**FreeMAN**](https://github.com/matthew-matvei/freeman): A free, cross-platform file manager for power users
|
||||
- [**Fluent Terminal**](https://github.com/felixse/FluentTerminal): A terminal emulator based on UWP and web technologies.
|
||||
- [**Hyper**](https://hyper.is): A terminal built on web technologies
|
||||
@@ -171,6 +171,7 @@ computational environment for Jupyter, supporting interactive data science and s
|
||||
- [**SSH Web Client**](https://github.com/roke22/PHP-SSH2-Web-Client): SSH Web Client with PHP.
|
||||
- [**Shellvault**](https://www.shellvault.io): The cloud-based SSH terminal you can access from anywhere.
|
||||
- [**Juno**](http://junolab.org/): A flexible Julia IDE, based on Atom.
|
||||
- [**webssh**](https://github.com/huashengdun/webssh): Web based ssh client.
|
||||
|
||||
[And much more...](https://github.com/xtermjs/xterm.js/network/dependents)
|
||||
|
||||
|
||||
+17
-1
@@ -58,9 +58,25 @@ app.ws('/terminals/:pid', function (ws, req) {
|
||||
console.log('Connected to terminal ' + term.pid);
|
||||
ws.send(logs[term.pid]);
|
||||
|
||||
function buffer(socket, timeout) {
|
||||
let s = '';
|
||||
let sender = null;
|
||||
return (data) => {
|
||||
s += data;
|
||||
if (!sender) {
|
||||
sender = setTimeout(() => {
|
||||
socket.send(s);
|
||||
s = '';
|
||||
sender = null;
|
||||
}, timeout);
|
||||
}
|
||||
};
|
||||
}
|
||||
const send = buffer(ws, 5);
|
||||
|
||||
term.on('data', function(data) {
|
||||
try {
|
||||
ws.send(data);
|
||||
send(data);
|
||||
} catch (ex) {
|
||||
// The WebSocket is not open, ignore
|
||||
}
|
||||
|
||||
@@ -63,7 +63,6 @@
|
||||
"build": "gulp build",
|
||||
"prepublish": "npm run build",
|
||||
"coveralls": "nyc report --reporter=text-lcov | coveralls",
|
||||
"webpack": "gulp webpack",
|
||||
"watch": "concurrently --kill-others-on-fail --names \"lib,css\" \"tsc -w\" \"gulp watch-css\"",
|
||||
"watch-addons": "concurrently --kill-others-on-fail --names \"attach,fit,fullscreen,search,terminado,webLinks,winptyCompat,zmodem\" \"tsc -w -p ./src/addons/attach\" \"tsc -w -p ./src/addons/fit\" \"tsc -w -p ./src/addons/fullscreen\" \"tsc -w -p ./src/addons/search\" \"tsc -w -p ./src/addons/terminado\" \"tsc -w -p ./src/addons/webLinks\" \"tsc -w -p ./src/addons/winptyCompat\" \"tsc -w -p ./src/addons/zmodem\"",
|
||||
"layering": "concurrently --kill-others-on-fail --names \"common,core\" \"tsc -p ./src/common\" \"tsc -p ./src/core\""
|
||||
|
||||
+66
-26
@@ -133,31 +133,71 @@ describe('BufferLine', function(): void {
|
||||
const line3 = line.clone();
|
||||
chai.expect(TestBufferLine.prototype.toArray.apply(line3)).eql(line.toArray());
|
||||
});
|
||||
it('resize enlarge', function(): void {
|
||||
const line = new TestBufferLine(5, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(10, [1, 'a', 0, 'a'.charCodeAt(0)]);
|
||||
chai.expect(line.toArray()).eql(Array(10).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('resize shrink(true)', function(): void {
|
||||
const line = new TestBufferLine(10, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(5, [1, 'a', 0, 'a'.charCodeAt(0)], true);
|
||||
chai.expect(line.toArray()).eql(Array(5).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('resize shrink(false)', function(): void {
|
||||
const line = new TestBufferLine(10, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(5, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
chai.expect(line.toArray()).eql(Array(5).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('resize shrink(false) + shrink(false)', function(): void {
|
||||
const line = new TestBufferLine(20, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(10, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(5, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
chai.expect(line.toArray()).eql(Array(5).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('resize shrink(false) + enlarge', function(): void {
|
||||
const line = new TestBufferLine(20, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(10, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(15, [1, 'a', 0, 'a'.charCodeAt(0)]);
|
||||
chai.expect(line.toArray()).eql(Array(15).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
describe('resize', function(): void {
|
||||
it('enlarge(false)', function(): void {
|
||||
const line = new TestBufferLine(5, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(10, [1, 'a', 0, 'a'.charCodeAt(0)]);
|
||||
chai.expect(line.toArray()).eql(Array(10).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('enlarge(true)', function(): void {
|
||||
const line = new TestBufferLine(5, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(10, [1, 'a', 0, 'a'.charCodeAt(0)], true);
|
||||
chai.expect(line.toArray()).eql(Array(10).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('shrink(true) - should apply new size', function(): void {
|
||||
const line = new TestBufferLine(10, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(5, [1, 'a', 0, 'a'.charCodeAt(0)], true);
|
||||
chai.expect(line.toArray()).eql(Array(5).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('shrink(false) - should not apply new size', function(): void {
|
||||
const line = new TestBufferLine(10, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(5, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
chai.expect(line.toArray()).eql(Array(10).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('shrink(false) + shrink(false) - should not apply new size', function(): void {
|
||||
const line = new TestBufferLine(20, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(10, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(5, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
chai.expect(line.toArray()).eql(Array(20).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('shrink(false) + enlarge(false) to smaller than before', function(): void {
|
||||
const line = new TestBufferLine(20, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(10, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(15, [1, 'a', 0, 'a'.charCodeAt(0)]);
|
||||
chai.expect(line.toArray()).eql(Array(20).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('shrink(false) + enlarge(false) to bigger than before', function(): void {
|
||||
const line = new TestBufferLine(20, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(10, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(25, [1, 'a', 0, 'a'.charCodeAt(0)]);
|
||||
chai.expect(line.toArray()).eql(Array(25).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('shrink(false) + resize shrink=true should enforce shrinking', function(): void {
|
||||
const line = new TestBufferLine(20, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(10, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(10, [1, 'a', 0, 'a'.charCodeAt(0)], true);
|
||||
chai.expect(line.toArray()).eql(Array(10).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('enlarge from 0 length', function(): void {
|
||||
const line = new TestBufferLine(0, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(10, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
chai.expect(line.toArray()).eql(Array(10).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('shrink to 0 length', function(): void {
|
||||
const line = new TestBufferLine(10, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(0, [1, 'a', 0, 'a'.charCodeAt(0)], true);
|
||||
chai.expect(line.toArray()).eql(Array(0).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
it('shrink(false) to 0 and enlarge to different sizes', function(): void {
|
||||
const line = new TestBufferLine(10, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
line.resize(0, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
chai.expect(line.toArray()).eql(Array(10).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
line.resize(5, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
chai.expect(line.toArray()).eql(Array(10).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
line.resize(7, [1, 'a', 0, 'a'.charCodeAt(0)], false);
|
||||
chai.expect(line.toArray()).eql(Array(10).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
line.resize(7, [1, 'a', 0, 'a'.charCodeAt(0)], true);
|
||||
chai.expect(line.toArray()).eql(Array(7).fill([1, 'a', 0, 'a'.charCodeAt(0)]));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+12
-10
@@ -77,15 +77,15 @@ export class BufferLine implements IBufferLine {
|
||||
|
||||
/** 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();
|
||||
}
|
||||
}
|
||||
while (this._data.length < cols) {
|
||||
this._data.push(fillCharData);
|
||||
}
|
||||
this.length = cols;
|
||||
this.length = this._data.length;
|
||||
}
|
||||
|
||||
public fill(fillCharData: CharData): void {
|
||||
@@ -141,11 +141,13 @@ export class BufferLineTypedArray implements IBufferLine {
|
||||
if (!fillCharData) {
|
||||
fillCharData = [0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE];
|
||||
}
|
||||
this._data = new Uint32Array(cols * CELL_SIZE);
|
||||
for (let i = 0; i < cols; ++i) {
|
||||
this.set(i, fillCharData);
|
||||
if (cols) {
|
||||
this._data = new Uint32Array(cols * CELL_SIZE);
|
||||
for (let i = 0; i < cols; ++i) {
|
||||
this.set(i, fillCharData);
|
||||
}
|
||||
}
|
||||
this.length = cols || 0;
|
||||
this.length = cols;
|
||||
}
|
||||
|
||||
public get(index: number): CharData {
|
||||
@@ -212,12 +214,12 @@ export class BufferLineTypedArray implements IBufferLine {
|
||||
}
|
||||
|
||||
public resize(cols: number, fillCharData: CharData, shrink: boolean = false): void {
|
||||
if (cols === this.length) {
|
||||
if (cols === this.length || (!shrink && cols < this.length)) {
|
||||
return;
|
||||
}
|
||||
if (cols > this.length) {
|
||||
const data = new Uint32Array(cols * CELL_SIZE);
|
||||
if (this._data) {
|
||||
if (this.length) {
|
||||
if (cols * CELL_SIZE < this._data.length) {
|
||||
data.set(this._data.subarray(0, cols * CELL_SIZE));
|
||||
} else {
|
||||
|
||||
@@ -438,6 +438,36 @@ describe('InputHandler', () => {
|
||||
termNew.buffer.x = 40;
|
||||
inputHandlerNew.eraseInDisplay([2]);
|
||||
expect(termContent(termNew)).eql(termContent(termOld));
|
||||
|
||||
// reset and add a wrapped line
|
||||
termNew.buffer.y = 0;
|
||||
termNew.buffer.x = 0;
|
||||
inputHandlerNew.parse(Array(termNew.cols + 1).join('a')); // line 0
|
||||
inputHandlerNew.parse(Array(termNew.cols + 10).join('a')); // line 1 and 2
|
||||
for (let i = 3; i < termOld.rows; ++i) inputHandlerNew.parse(Array(termNew.cols + 1).join('a'));
|
||||
|
||||
// params[1] left and above with wrap
|
||||
// confirm precondition that line 2 is wrapped
|
||||
expect(termNew.buffer.lines.get(2).isWrapped).true;
|
||||
termNew.buffer.y = 2;
|
||||
termNew.buffer.x = 40;
|
||||
inputHandlerNew.eraseInDisplay([1]);
|
||||
expect(termNew.buffer.lines.get(2).isWrapped).false;
|
||||
|
||||
// reset and add a wrapped line
|
||||
termNew.buffer.y = 0;
|
||||
termNew.buffer.x = 0;
|
||||
inputHandlerNew.parse(Array(termNew.cols + 1).join('a')); // line 0
|
||||
inputHandlerNew.parse(Array(termNew.cols + 10).join('a')); // line 1 and 2
|
||||
for (let i = 3; i < termOld.rows; ++i) inputHandlerNew.parse(Array(termNew.cols + 1).join('a'));
|
||||
|
||||
// params[1] left and above with wrap
|
||||
// confirm precondition that line 2 is wrapped
|
||||
expect(termNew.buffer.lines.get(2).isWrapped).true;
|
||||
termNew.buffer.y = 1;
|
||||
termNew.buffer.x = 90; // Cursor is beyond last column
|
||||
inputHandlerNew.eraseInDisplay([1]);
|
||||
expect(termNew.buffer.lines.get(2).isWrapped).false;
|
||||
});
|
||||
});
|
||||
it('convertEol setting', function(): void {
|
||||
|
||||
+26
-29
@@ -24,26 +24,6 @@ const GLEVEL: {[key: string]: number} = {'(': 0, ')': 1, '*': 2, '+': 3, '-': 1,
|
||||
* DCS subparser implementations
|
||||
*/
|
||||
|
||||
/**
|
||||
* DCS + q Pt ST (xterm)
|
||||
* Request Terminfo String
|
||||
* not supported
|
||||
*/
|
||||
class RequestTerminfo implements IDcsHandler {
|
||||
private _data: string;
|
||||
constructor(private _terminal: any) { }
|
||||
hook(collect: string, params: number[], flag: number): void {
|
||||
this._data = '';
|
||||
}
|
||||
put(data: string, start: number, end: number): void {
|
||||
this._data += data.substring(start, end);
|
||||
}
|
||||
unhook(): void {
|
||||
// invalid: DCS 0 + r Pt ST
|
||||
this._terminal.handler(`${C0.ESC}P0+r${this._data}${C0.ESC}\\`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* DCS $ q Pt ST
|
||||
* DECRQSS (https://vt100.net/docs/vt510-rm/DECRQSS.html)
|
||||
@@ -86,7 +66,7 @@ class DECRQSS implements IDcsHandler {
|
||||
default:
|
||||
// invalid: DCS 0 $ r Pt ST (xterm)
|
||||
this._terminal.error('Unknown DCS $q %s', this._data);
|
||||
this._terminal.handler(`${C0.ESC}P0$r${this._data}${C0.ESC}\\`);
|
||||
this._terminal.handler(`${C0.ESC}P0$r${C0.ESC}\\`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -287,7 +267,6 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* DCS handler
|
||||
*/
|
||||
this._parser.setDcsHandler('$q', new DECRQSS(this._terminal));
|
||||
this._parser.setDcsHandler('+q', new RequestTerminfo(this._terminal));
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
@@ -722,12 +701,25 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* @param start first cell index to be erased
|
||||
* @param end end - 1 is last erased cell
|
||||
*/
|
||||
private _eraseInBufferLine(y: number, start: number, end: number): void {
|
||||
this._terminal.buffer.lines.get(this._terminal.buffer.ybase + y).replaceCells(
|
||||
private _eraseInBufferLine(y: number, start: number, end: number, clearWrap: boolean = false): void {
|
||||
const line = this._terminal.buffer.lines.get(this._terminal.buffer.ybase + y);
|
||||
line.replaceCells(
|
||||
start,
|
||||
end,
|
||||
[this._terminal.eraseAttr(), NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]
|
||||
);
|
||||
if (clearWrap) {
|
||||
line.isWrapped = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to reset cells in a terminal row.
|
||||
* The cell gets replaced with the eraseChar of the terminal and the isWrapped property is set to false.
|
||||
* @param y row index
|
||||
*/
|
||||
private _resetBufferLine(y: number): void {
|
||||
this._eraseInBufferLine(y, 0, this._terminal.cols, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -748,18 +740,23 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
case 0:
|
||||
j = this._terminal.buffer.y;
|
||||
this._terminal.updateRange(j);
|
||||
this._eraseInBufferLine(j++, this._terminal.buffer.x, this._terminal.cols);
|
||||
this._eraseInBufferLine(j++, this._terminal.buffer.x, this._terminal.cols, this._terminal.buffer.x === 0);
|
||||
for (; j < this._terminal.rows; j++) {
|
||||
this._eraseInBufferLine(j, 0, this._terminal.cols);
|
||||
this._resetBufferLine(j);
|
||||
}
|
||||
this._terminal.updateRange(j);
|
||||
break;
|
||||
case 1:
|
||||
j = this._terminal.buffer.y;
|
||||
this._terminal.updateRange(j);
|
||||
this._eraseInBufferLine(j, 0, this._terminal.buffer.x + 1);
|
||||
// Deleted front part of line and everything before. This line will no longer be wrapped.
|
||||
this._eraseInBufferLine(j, 0, this._terminal.buffer.x + 1, true);
|
||||
if (this._terminal.buffer.x + 1 >= this._terminal.cols) {
|
||||
// Deleted entire previous line. This next line can no longer be wrapped.
|
||||
this._terminal.buffer.lines.get(j + 1).isWrapped = false;
|
||||
}
|
||||
while (j--) {
|
||||
this._eraseInBufferLine(j, 0, this._terminal.cols);
|
||||
this._resetBufferLine(j);
|
||||
}
|
||||
this._terminal.updateRange(0);
|
||||
break;
|
||||
@@ -767,7 +764,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
j = this._terminal.rows;
|
||||
this._terminal.updateRange(j - 1);
|
||||
while (j--) {
|
||||
this._eraseInBufferLine(j, 0, this._terminal.cols);
|
||||
this._resetBufferLine(j);
|
||||
}
|
||||
this._terminal.updateRange(0);
|
||||
break;
|
||||
|
||||
+2
-2
@@ -342,7 +342,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
}
|
||||
|
||||
public get isFocused(): boolean {
|
||||
return document.activeElement === this.textarea;
|
||||
return document.activeElement === this.textarea && document.hasFocus();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1174,7 +1174,7 @@ export class Terminal extends EventEmitter implements ITerminal, IDisposable, II
|
||||
* @param isWrapped Whether the new line is wrapped from the previous line.
|
||||
*/
|
||||
public scroll(isWrapped?: boolean): void {
|
||||
const newLine = this.buffer.getBlankLine(DEFAULT_ATTR, isWrapped);
|
||||
const newLine = this.buffer.getBlankLine(this.eraseAttr(), isWrapped);
|
||||
const topRow = this.buffer.ybase + this.buffer.scrollTop;
|
||||
const bottomRow = this.buffer.ybase + this.buffer.scrollBottom;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ export class SearchHelper implements ISearchHelper {
|
||||
/**
|
||||
* Find the next instance of the term, then scroll to and select it. If it
|
||||
* doesn't exist, do nothing.
|
||||
* @param term Tne search term.
|
||||
* @param term The search term.
|
||||
* @param searchOptions Search options.
|
||||
* @return Whether a result was found.
|
||||
*/
|
||||
@@ -33,7 +33,9 @@ export class SearchHelper implements ISearchHelper {
|
||||
let startRow = this._terminal._core.buffer.ydisp;
|
||||
if (this._terminal._core.selectionManager.selectionEnd) {
|
||||
// Start from the selection end if there is a selection
|
||||
startRow = this._terminal._core.selectionManager.selectionEnd[1];
|
||||
if (this._terminal.getSelection().length !== 0) {
|
||||
startRow = this._terminal._core.selectionManager.selectionEnd[1];
|
||||
}
|
||||
}
|
||||
|
||||
// Search from ydisp + 1 to end
|
||||
@@ -61,7 +63,7 @@ export class SearchHelper implements ISearchHelper {
|
||||
/**
|
||||
* Find the previous instance of the term, then scroll to and select it. If it
|
||||
* doesn't exist, do nothing.
|
||||
* @param term Tne search term.
|
||||
* @param term The search term.
|
||||
* @param searchOptions Search options.
|
||||
* @return Whether a result was found.
|
||||
*/
|
||||
@@ -75,7 +77,9 @@ export class SearchHelper implements ISearchHelper {
|
||||
let startRow = this._terminal._core.buffer.ydisp;
|
||||
if (this._terminal._core.selectionManager.selectionStart) {
|
||||
// Start from the selection end if there is a selection
|
||||
startRow = this._terminal._core.selectionManager.selectionStart[1];
|
||||
if (this._terminal.getSelection().length !== 0) {
|
||||
startRow = this._terminal._core.selectionManager.selectionStart[1];
|
||||
}
|
||||
}
|
||||
|
||||
// Search from ydisp + 1 to end
|
||||
@@ -108,7 +112,7 @@ export class SearchHelper implements ISearchHelper {
|
||||
*/
|
||||
private _isWholeWord(searchIndex: number, line: string, term: string): boolean {
|
||||
return (((searchIndex === 0) || (nonWordCharacters.indexOf(line[searchIndex - 1]) !== -1)) &&
|
||||
(((searchIndex + term.length) === line.length) || (nonWordCharacters.indexOf(line[searchIndex + term.length]) !== -1)));
|
||||
(((searchIndex + term.length) === line.length) || (nonWordCharacters.indexOf(line[searchIndex + term.length]) !== -1)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +120,7 @@ export class SearchHelper implements ISearchHelper {
|
||||
* subsequent terminal lines if the text is wrapped. If the provided line number is part of a wrapped text line that
|
||||
* started on an earlier line then it is skipped since it will be properly searched when the terminal line that the
|
||||
* text starts on is searched.
|
||||
* @param term Tne search term.
|
||||
* @param term The search term.
|
||||
* @param y The line to search.
|
||||
* @param searchOptions Search options.
|
||||
* @return The search result if it was found.
|
||||
@@ -191,7 +195,7 @@ export class SearchHelper implements ISearchHelper {
|
||||
do {
|
||||
const nextLine = this._terminal._core.buffer.lines.get(lineIndex + 1);
|
||||
lineWrapsToNext = nextLine ? nextLine.isWrapped : false;
|
||||
lineString += this._terminal._core.buffer.translateBufferLineToString(lineIndex, !lineWrapsToNext && trimRight);
|
||||
lineString += this._terminal._core.buffer.translateBufferLineToString(lineIndex, !lineWrapsToNext && trimRight).substring(0, this._terminal.cols);
|
||||
lineIndex++;
|
||||
} while (lineWrapsToNext);
|
||||
|
||||
@@ -205,6 +209,7 @@ export class SearchHelper implements ISearchHelper {
|
||||
*/
|
||||
private _selectResult(result: ISearchResult): boolean {
|
||||
if (!result) {
|
||||
this._terminal.clearSelection();
|
||||
return false;
|
||||
}
|
||||
this._terminal._core.selectionManager.setSelection(result.col, result.row, result.term.length);
|
||||
|
||||
@@ -81,6 +81,9 @@ describe('search addon', () => {
|
||||
expect(hello3).eql(undefined);
|
||||
expect(llo).eql(undefined);
|
||||
expect(goodbye).eql({col: 0, row: 5, term: 'goodbye'});
|
||||
term.core.resize(9, 5);
|
||||
const hello0Resize = term.searchHelper.findInLine('Hello', 0);
|
||||
expect(hello0Resize).eql({col: 8, row: 0, term: 'Hello'});
|
||||
});
|
||||
it('should respect search regex', () => {
|
||||
search.apply(<any>MockTerminal);
|
||||
|
||||
@@ -10,7 +10,7 @@ import { ISearchAddonTerminal, ISearchOptions } from './Interfaces';
|
||||
/**
|
||||
* Find the next instance of the term, then scroll to and select it. If it
|
||||
* doesn't exist, do nothing.
|
||||
* @param term Tne search term.
|
||||
* @param term The search term.
|
||||
* @param searchOptions Search options
|
||||
* @return Whether a result was found.
|
||||
*/
|
||||
@@ -25,7 +25,7 @@ export function findNext(terminal: Terminal, term: string, searchOptions: ISearc
|
||||
/**
|
||||
* Find the previous instance of the term, then scroll to and select it. If it
|
||||
* doesn't exist, do nothing.
|
||||
* @param term Tne search term.
|
||||
* @param term The search term.
|
||||
* @param searchOptions Search options
|
||||
* @return Whether a result was found.
|
||||
*/
|
||||
|
||||
@@ -129,12 +129,17 @@ export default class DynamicCharAtlas extends BaseCharAtlas {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Exit early for uncachable glyphs
|
||||
if (!this._canCache(glyph)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const glyphKey = getGlyphCacheKey(glyph);
|
||||
const cacheValue = this._cacheMap.get(glyphKey);
|
||||
if (cacheValue !== null && cacheValue !== undefined) {
|
||||
this._drawFromCache(ctx, cacheValue, x, y);
|
||||
return true;
|
||||
} else if (this._canCache(glyph) && this._drawToCacheCount < FRAME_CACHE_DRAW_LIMIT) {
|
||||
} else if (this._drawToCacheCount < FRAME_CACHE_DRAW_LIMIT) {
|
||||
let index;
|
||||
if (this._cacheMap.size < this._cacheMap.capacity) {
|
||||
index = this._cacheMap.size;
|
||||
|
||||
@@ -353,7 +353,11 @@ export class DomRenderer extends EventEmitter implements IRenderer {
|
||||
|
||||
private _setCellUnderline(x: number, x2: number, y: number, y2: number, cols: number, enabled: boolean): void {
|
||||
while (x !== x2 || y !== y2) {
|
||||
const span = <HTMLElement>this._rowElements[y].children[x];
|
||||
const row = this._rowElements[y];
|
||||
if (!row) {
|
||||
return;
|
||||
}
|
||||
const span = <HTMLElement>row.children[x];
|
||||
span.style.textDecoration = enabled ? 'underline' : 'none';
|
||||
x = (x + 1) % cols;
|
||||
if (x === 0) {
|
||||
|
||||
@@ -121,7 +121,7 @@ export class MockTerminal implements ITerminal {
|
||||
handler(data: string): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
on(event: string, callback: () => void): void {
|
||||
on(event: string, callback: (...args: any[]) => void): void {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
off(type: string, listener: XtermListener): void {
|
||||
|
||||
Vendored
+6
-6
@@ -387,37 +387,37 @@ declare module 'xterm' {
|
||||
* @param type The type of the event.
|
||||
* @param listener The listener.
|
||||
*/
|
||||
on(type: 'key', listener: (key?: string, event?: KeyboardEvent) => void): void;
|
||||
on(type: 'key', listener: (key: string, event: KeyboardEvent) => void): void;
|
||||
/**
|
||||
* Registers an event listener.
|
||||
* @param type The type of the event.
|
||||
* @param listener The listener.
|
||||
*/
|
||||
on(type: 'keypress' | 'keydown', listener: (event?: KeyboardEvent) => void): void;
|
||||
on(type: 'keypress' | 'keydown', listener: (event: KeyboardEvent) => void): void;
|
||||
/**
|
||||
* Registers an event listener.
|
||||
* @param type The type of the event.
|
||||
* @param listener The listener.
|
||||
*/
|
||||
on(type: 'refresh', listener: (data?: {start: number, end: number}) => void): void;
|
||||
on(type: 'refresh', listener: (data: {start: number, end: number}) => void): void;
|
||||
/**
|
||||
* Registers an event listener.
|
||||
* @param type The type of the event.
|
||||
* @param listener The listener.
|
||||
*/
|
||||
on(type: 'resize', listener: (data?: {cols: number, rows: number}) => void): void;
|
||||
on(type: 'resize', listener: (data: {cols: number, rows: number}) => void): void;
|
||||
/**
|
||||
* Registers an event listener.
|
||||
* @param type The type of the event.
|
||||
* @param listener The listener.
|
||||
*/
|
||||
on(type: 'scroll', listener: (ydisp?: number) => void): void;
|
||||
on(type: 'scroll', listener: (ydisp: number) => void): void;
|
||||
/**
|
||||
* Registers an event listener.
|
||||
* @param type The type of the event.
|
||||
* @param listener The listener.
|
||||
*/
|
||||
on(type: 'title', listener: (title?: string) => void): void;
|
||||
on(type: 'title', listener: (title: string) => void): void;
|
||||
/**
|
||||
* Registers an event listener.
|
||||
* @param type The type of the event.
|
||||
|
||||
Reference in New Issue
Block a user