mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
Merge branch 'master' into left_right_scroll
This commit is contained in:
@@ -343,9 +343,12 @@ export class SearchAddon implements ITerminalAddon {
|
||||
return false;
|
||||
}
|
||||
terminal.select(result.col, result.row, result.term.length);
|
||||
let scroll = result.row - terminal.buffer.viewportY;
|
||||
scroll = scroll - Math.floor(terminal.rows / 2);
|
||||
terminal.scrollLines(scroll);
|
||||
// If it is not in the viewport then we scroll else it just gets selected
|
||||
if (result.row > (terminal.buffer.viewportY + terminal.rows) || result.row < terminal.buffer.viewportY) {
|
||||
let scroll = result.row - terminal.buffer.viewportY;
|
||||
scroll = scroll - Math.floor(terminal.rows / 2);
|
||||
terminal.scrollLines(scroll);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,6 @@ startServer();
|
||||
* For production builds see `webpack.config.js` in the root directory. If that is built the demo
|
||||
* can use that by switching out which `Terminal` is imported in `client.ts`, this is useful for
|
||||
* validating that the packaged version works correctly.
|
||||
*
|
||||
* The addons are not webpacked right now and are built directly to `lib/` via `tsc` as they are
|
||||
* the legacy format (`applyAddon`) and will be removed soon anyway.
|
||||
*/
|
||||
const clientConfig = {
|
||||
entry: path.resolve(__dirname, 'client.ts'),
|
||||
|
||||
@@ -27,5 +27,5 @@ r
|
||||
s
|
||||
t
|
||||
u[78Gvwxyz
|
||||
[r[24d
|
||||
[r[25d
|
||||
The end.
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
1
|
||||
2
|
||||
6
|
||||
7
|
||||
8
|
||||
9 ABC
|
||||
6
|
||||
7
|
||||
8
|
||||
9 ABC
|
||||
DEF
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
yz qrstu vwx
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
yz vwx
|
||||
The end.
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
[3;7r1D2D3D4D5D6D7D8D9[78GABCDEF[8daDbDcDdDeDfDgDhDiDjDkDlDmDnDoDpDqDrDsDtDu[78Gvwxyz
|
||||
[r[24d
|
||||
[r[25d
|
||||
The end.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
2
|
||||
6
|
||||
7
|
||||
8
|
||||
@@ -21,5 +20,6 @@ DEF
|
||||
o
|
||||
p
|
||||
q
|
||||
The end. rstu vwx
|
||||
yz rstu vwx
|
||||
The end.
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
6 C
|
||||
8 ^^^^
|
||||
9 vvvv DL on line 11, expected: ACD_
|
||||
10 A
|
||||
@@ -13,14 +12,14 @@
|
||||
19 vvvv IL on line 21, expected: A_
|
||||
20 A
|
||||
|
||||
|
||||
22 ^^^^
|
||||
24 A
|
||||
25 B
|
||||
27 vvvv DL on line 28, expected: B_
|
||||
|
||||
23 vvvv IL on line 24, expected: _A
|
||||
25 B
|
||||
26 ^^^^
|
||||
28 A
|
||||
|
||||
29 B
|
||||
30 ^^^^
|
||||
31
|
||||
32
|
||||
32
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
r
|
||||
s
|
||||
t
|
||||
u
|
||||
v
|
||||
w
|
||||
x[10;19r[25E1[24d[25F2[r[24d
|
||||
@@ -0,0 +1,25 @@
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
2
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
r
|
||||
1
|
||||
t
|
||||
u
|
||||
v
|
||||
w
|
||||
x
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
r
|
||||
s
|
||||
t
|
||||
u
|
||||
v
|
||||
w
|
||||
x[10;19r[H[25e1[r[24d
|
||||
@@ -0,0 +1,25 @@
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
r
|
||||
s
|
||||
t
|
||||
u
|
||||
v
|
||||
w
|
||||
x
|
||||
1
|
||||
+36
-16
@@ -21,8 +21,8 @@ import { AttributeData } from 'common/buffer/AttributeData';
|
||||
import { IAttributeData, IDisposable } from 'common/Types';
|
||||
import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService } from 'common/services/Services';
|
||||
import { ISelectionService } from 'browser/services/Services';
|
||||
import { OscHandlerFactory } from 'common/parser/OscParser';
|
||||
import { DcsHandlerFactory } from 'common/parser/DcsParser';
|
||||
import { OscHandler } from 'common/parser/OscParser';
|
||||
import { DcsHandler } from 'common/parser/DcsParser';
|
||||
|
||||
/**
|
||||
* Map collect to glevel. Used in `selectCharset`.
|
||||
@@ -245,10 +245,10 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* OSC handler
|
||||
*/
|
||||
// 0 - icon name + title
|
||||
this._parser.setOscHandler(0, new OscHandlerFactory((data: string) => this.setTitle(data)));
|
||||
this._parser.setOscHandler(0, new OscHandler((data: string) => this.setTitle(data)));
|
||||
// 1 - icon name
|
||||
// 2 - title
|
||||
this._parser.setOscHandler(2, new OscHandlerFactory((data: string) => this.setTitle(data)));
|
||||
this._parser.setOscHandler(2, new OscHandler((data: string) => this.setTitle(data)));
|
||||
// 3 - set property X in the form "prop=value"
|
||||
// 4 - Change Color Number
|
||||
// 5 - Change Special Color Number
|
||||
@@ -433,10 +433,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
if (wraparoundMode) {
|
||||
buffer.x = 0;
|
||||
buffer.y++;
|
||||
if (buffer.y > buffer.scrollBottom) {
|
||||
if (buffer.y === buffer.scrollBottom + 1) {
|
||||
buffer.y--;
|
||||
this._terminal.scroll(true);
|
||||
} else {
|
||||
if (buffer.y >= this._bufferService.rows) {
|
||||
buffer.y = this._bufferService.rows - 1;
|
||||
}
|
||||
// The line already exists (eg. the initial viewport), mark it as a
|
||||
// wrapped line
|
||||
buffer.lines.get(buffer.y).isWrapped = true;
|
||||
@@ -506,7 +509,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Forward addDcsHandler from parser.
|
||||
*/
|
||||
public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean): IDisposable {
|
||||
return this._parser.addDcsHandler(id, new DcsHandlerFactory(callback));
|
||||
return this._parser.addDcsHandler(id, new DcsHandler(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -520,7 +523,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Forward addOscHandler from parser.
|
||||
*/
|
||||
public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
|
||||
return this._parser.addOscHandler(ident, new OscHandlerFactory(callback));
|
||||
return this._parser.addOscHandler(ident, new OscHandler(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -543,9 +546,11 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
buffer.x = 0;
|
||||
}
|
||||
buffer.y++;
|
||||
if (buffer.y > buffer.scrollBottom) {
|
||||
if (buffer.y === buffer.scrollBottom + 1) {
|
||||
buffer.y--;
|
||||
this._terminal.scroll();
|
||||
} else if (buffer.y >= this._bufferService.rows) {
|
||||
buffer.y = this._bufferService.rows - 1;
|
||||
}
|
||||
// If the end of the line is hit, prevent this action from wrapping around to the next line.
|
||||
if (buffer.x >= this._bufferService.cols) {
|
||||
@@ -646,7 +651,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Cursor Up Ps Times (default = 1) (CUU).
|
||||
*/
|
||||
public cursorUp(params: IParams): void {
|
||||
this._moveCursor(0, -(params.params[0] || 1));
|
||||
// stop at scrollTop
|
||||
const diffToTop = this._bufferService.buffer.y - this._bufferService.buffer.scrollTop;
|
||||
if (diffToTop >= 0) {
|
||||
this._moveCursor(0, -Math.min(diffToTop, params.params[0] || 1));
|
||||
} else {
|
||||
this._moveCursor(0, -(params.params[0] || 1));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -654,7 +665,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Cursor Down Ps Times (default = 1) (CUD).
|
||||
*/
|
||||
public cursorDown(params: IParams): void {
|
||||
this._moveCursor(0, params.params[0] || 1);
|
||||
// stop at scrollBottom
|
||||
const diffToBottom = this._bufferService.buffer.scrollBottom - this._bufferService.buffer.y;
|
||||
if (diffToBottom >= 0) {
|
||||
this._moveCursor(0, Math.min(diffToBottom, params.params[0] || 1));
|
||||
} else {
|
||||
this._moveCursor(0, params.params[0] || 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -679,7 +696,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Other than cursorDown (CUD) also set the cursor to first column.
|
||||
*/
|
||||
public cursorNextLine(params: IParams): void {
|
||||
this._moveCursor(0, params.params[0] || 1);
|
||||
this.cursorDown(params);
|
||||
this._bufferService.buffer.x = 0;
|
||||
}
|
||||
|
||||
@@ -689,7 +706,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Other than cursorUp (CUU) also set the cursor to first column.
|
||||
*/
|
||||
public cursorPrecedingLine(params: IParams): void {
|
||||
this._moveCursor(0, -(params.params[0] || 1));
|
||||
this.cursorUp(params);
|
||||
this._bufferService.buffer.x = 0;
|
||||
}
|
||||
|
||||
@@ -1031,7 +1048,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
|
||||
while (param--) {
|
||||
buffer.lines.splice(buffer.ybase + buffer.scrollTop, 1);
|
||||
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(DEFAULT_ATTR_DATA));
|
||||
buffer.lines.splice(buffer.ybase + buffer.scrollBottom, 0, buffer.getBlankLine(this._terminal.eraseAttrData()));
|
||||
}
|
||||
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
|
||||
}
|
||||
@@ -1633,7 +1650,6 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper to extract and apply color params/subparams.
|
||||
* Returns advance for params index.
|
||||
@@ -1906,6 +1922,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public deviceStatusPrivate(params: IParams): void {
|
||||
// modern xterm doesnt seem to
|
||||
// respond to any of these except ?6, 6, and 5
|
||||
@@ -2134,10 +2151,13 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
*/
|
||||
public index(): void {
|
||||
this._restrictCursor();
|
||||
const buffer = this._bufferService.buffer;
|
||||
this._bufferService.buffer.y++;
|
||||
if (this._bufferService.buffer.y > this._bufferService.buffer.scrollBottom) {
|
||||
this._bufferService.buffer.y--;
|
||||
if (buffer.y === buffer.scrollBottom + 1) {
|
||||
buffer.y--;
|
||||
this._terminal.scroll();
|
||||
} else if (buffer.y >= this._bufferService.rows) {
|
||||
buffer.y = this._bufferService.rows - 1;
|
||||
}
|
||||
this._restrictCursor();
|
||||
}
|
||||
|
||||
@@ -1123,6 +1123,241 @@ describe('Terminal', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Buffer.stringIndexToBufferIndex', () => {
|
||||
let terminal: TestTerminal;
|
||||
|
||||
beforeEach(() => {
|
||||
terminal = new TestTerminal({rows: 5, cols: 10, scrollback: 5});
|
||||
});
|
||||
|
||||
it('multiline ascii', () => {
|
||||
const input = 'This is ASCII text spanning multiple lines.';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('combining e\u0301 in a sentence', () => {
|
||||
const input = 'Sitting in the cafe\u0301 drinking coffee.';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 0; i < 19; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
|
||||
}
|
||||
// string index 18 & 19 point to combining char e\u0301 ---> same buffer Index
|
||||
assert.deepEqual(
|
||||
terminal.buffer.stringIndexToBufferIndex(0, 18),
|
||||
terminal.buffer.stringIndexToBufferIndex(0, 19));
|
||||
// after the combining char every string index has an offset of -1
|
||||
for (let i = 19; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([((i - 1) / terminal.cols) | 0, (i - 1) % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('multiline combining e\u0301', () => {
|
||||
const input = 'e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
// every buffer cell index contains 2 string indices
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([((i >> 1) / terminal.cols) | 0, (i >> 1) % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('surrogate char in a sentence', () => {
|
||||
const input = 'The 𝄞 is a clef widely used in modern notation.';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 0; i < 5; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
|
||||
}
|
||||
// string index 4 & 5 point to surrogate char 𝄞 ---> same buffer Index
|
||||
assert.deepEqual(
|
||||
terminal.buffer.stringIndexToBufferIndex(0, 4),
|
||||
terminal.buffer.stringIndexToBufferIndex(0, 5));
|
||||
// after the combining char every string index has an offset of -1
|
||||
for (let i = 5; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([((i - 1) / terminal.cols) | 0, (i - 1) % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('multiline surrogate char', () => {
|
||||
const input = '𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
// every buffer cell index contains 2 string indices
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([((i >> 1) / terminal.cols) | 0, (i >> 1) % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('surrogate char with combining', () => {
|
||||
// eye of Ra with acute accent - string length of 3
|
||||
const input = '𓂀\u0301 - the eye hiroglyph with an acute accent.';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
// index 0..2 should map to 0
|
||||
assert.deepEqual([0, 0], terminal.buffer.stringIndexToBufferIndex(0, 1));
|
||||
assert.deepEqual([0, 0], terminal.buffer.stringIndexToBufferIndex(0, 2));
|
||||
for (let i = 2; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([((i - 2) / terminal.cols) | 0, (i - 2) % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('multiline surrogate with combining', () => {
|
||||
const input = '𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
// every buffer cell index contains 3 string indices
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([(((i / 3) | 0) / terminal.cols) | 0, ((i / 3) | 0) % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('fullwidth chars', () => {
|
||||
const input = 'These 123 are some fat numbers.';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 0; i < 6; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
|
||||
}
|
||||
// string index 6, 7, 8 take 2 cells
|
||||
assert.deepEqual([0, 8], terminal.buffer.stringIndexToBufferIndex(0, 7));
|
||||
assert.deepEqual([1, 0], terminal.buffer.stringIndexToBufferIndex(0, 8));
|
||||
// rest of the string has offset of +3
|
||||
for (let i = 9; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([((i + 3) / terminal.cols) | 0, (i + 3) % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('multiline fullwidth chars', () => {
|
||||
const input = '12345678901234567890';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 9; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
assert.deepEqual([((i << 1) / terminal.cols) | 0, (i << 1) % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('fullwidth combining with emoji - match emoji cell', () => {
|
||||
const input = 'Lots of ¥\u0301 make me 😃.';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
const stringIndex = s.match(/😃/).index;
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, stringIndex);
|
||||
assert(terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars(), '😃');
|
||||
});
|
||||
|
||||
it('multiline fullwidth chars with offset 1 (currently tests for broken behavior)', () => {
|
||||
const input = 'a12345678901234567890';
|
||||
// the 'a' at the beginning moves all fullwidth chars one to the right
|
||||
// now the end of the line contains a dangling empty cell since
|
||||
// the next fullwidth char has to wrap early
|
||||
// the dangling last cell is wrongly added in the string
|
||||
// --> fixable after resolving #1685
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 10; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
|
||||
const j = (i - 0) << 1;
|
||||
assert.deepEqual([(j / terminal.cols) | 0, j % terminal.cols], bufferIndex);
|
||||
}
|
||||
});
|
||||
|
||||
it('test fully wrapped buffer up to last char', () => {
|
||||
const input = Array(6).join('1234567890');
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
|
||||
assert.equal(input[i], terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars());
|
||||
}
|
||||
});
|
||||
|
||||
it('test fully wrapped buffer up to last char with full width odd', () => {
|
||||
const input = 'a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301'
|
||||
+ 'a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(input, s);
|
||||
for (let i = 0; i < input.length; ++i) {
|
||||
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
|
||||
assert.equal(
|
||||
(!(i % 3))
|
||||
? input[i]
|
||||
: (i % 3 === 1)
|
||||
? input.substr(i, 2)
|
||||
: input.substr(i - 1, 2),
|
||||
terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars());
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle \t in lines correctly', () => {
|
||||
const input = '\thttps://google.de';
|
||||
terminal.writeSync(input);
|
||||
const s = terminal.buffer.iterator(true).next().content;
|
||||
assert.equal(s, Array(terminal.optionsService.options.tabStopWidth + 1).join(' ') + 'https://google.de');
|
||||
});
|
||||
});
|
||||
|
||||
describe('BufferStringIterator', function(): void {
|
||||
it('iterator does not overflow buffer limits', function(): void {
|
||||
const terminal = new TestTerminal({rows: 5, cols: 10, scrollback: 5});
|
||||
const data = [
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaa\n',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaa\n',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaa\n',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa'
|
||||
];
|
||||
terminal.writeSync(data.join(''));
|
||||
// brute force test with insane values
|
||||
expect(() => {
|
||||
for (let overscan = 0; overscan < 20; ++overscan) {
|
||||
for (let start = -10; start < 20; ++start) {
|
||||
for (let end = -10; end < 20; ++end) {
|
||||
const it = terminal.buffer.iterator(false, start, end, overscan, overscan);
|
||||
while (it.hasNext()) {
|
||||
it.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}).to.not.throw();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
class TestLinkifier extends Linkifier {
|
||||
|
||||
+5
-2
@@ -394,6 +394,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
||||
case 'rendererType':
|
||||
if (this._renderService) {
|
||||
this._renderService.setRenderer(this._createRenderer());
|
||||
this._renderService.onResize(this.cols, this.rows);
|
||||
}
|
||||
break;
|
||||
case 'scrollback':
|
||||
@@ -643,7 +644,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
||||
this.screenElement);
|
||||
this._instantiationService.setService(ISelectionService, this._selectionService);
|
||||
this.register(this._selectionService.onSelectionChange(() => this._onSelectionChange.fire()));
|
||||
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService.onMouseDown(e)));
|
||||
this.register(this._selectionService.onRedrawRequest(e => this._renderService.onSelectionChanged(e.start, e.end, e.columnSelectMode)));
|
||||
this.register(this._selectionService.onLinuxMouseSelection(text => {
|
||||
// If there's a new selection, put it into the textarea, focus and select it
|
||||
@@ -664,10 +664,13 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
||||
this.register(this.onScroll(() => this._mouseZoneManager.clearAll()));
|
||||
this.linkifier.attachToDom(this.element, this._mouseZoneManager);
|
||||
|
||||
// This event listener must be registered aftre MouseZoneManager is created
|
||||
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService.onMouseDown(e)));
|
||||
|
||||
// apply mouse event classes set by escape codes before terminal was attached
|
||||
this.element.classList.toggle('enable-mouse-events', this.mouseEvents);
|
||||
if (this.mouseEvents) {
|
||||
this._selectionService.disable();
|
||||
this.element.classList.add('enable-mouse-events');
|
||||
} else {
|
||||
this._selectionService.enable();
|
||||
}
|
||||
|
||||
@@ -17,12 +17,6 @@ const ROWS = 25;
|
||||
|
||||
const TESTFILES = glob.sync('**/escape_sequence_files/*.in', { cwd: path.join(__dirname, '..')});
|
||||
const SKIP_FILES = [
|
||||
't0070-DECSTBM_LF.in', // lineFeed not working correctly
|
||||
't0071-DECSTBM_IND.in',
|
||||
't0072-DECSTBM_NEL.in',
|
||||
't0075-DECSTBM_CUU_CUD.in',
|
||||
't0076-DECSTBM_IL_DL.in', // not working due to lineFeed
|
||||
't0077-DECSTBM_quirks.in',
|
||||
't0084-CBT.in',
|
||||
't0101-NLM.in',
|
||||
't0103-reverse_wrap.in',
|
||||
@@ -42,7 +36,7 @@ const FILES = TESTFILES.filter(value => SKIP_FILES.indexOf(value.split('/').slic
|
||||
|
||||
|
||||
describe('Escape Sequence Files', function(): void {
|
||||
this.timeout(20000);
|
||||
this.timeout(1000);
|
||||
|
||||
let ptyTerm: any;
|
||||
let slaveEnd: any;
|
||||
|
||||
Vendored
+1
-1
@@ -116,7 +116,7 @@ export interface IInputHandler {
|
||||
/** CSI a */ hPositionRelative(params: IParams): void;
|
||||
/** CSI b */ repeatPrecedingCharacter(params: IParams): void;
|
||||
/** CSI c */ sendDeviceAttributesPrimary(params: IParams): void;
|
||||
sendDeviceAttributesSecondary(params: IParams): void;
|
||||
/** CSI > c */ sendDeviceAttributesSecondary(params: IParams): void;
|
||||
/** CSI d */ linePosAbsolute(params: IParams): void;
|
||||
/** CSI e */ vPositionRelative(params: IParams): void;
|
||||
/** CSI f */ hVPosition(params: IParams): void;
|
||||
|
||||
@@ -108,7 +108,7 @@ export class RenderService extends Disposable implements IRenderService {
|
||||
}
|
||||
|
||||
public setRenderer(renderer: IRenderer): void {
|
||||
// TODO: RenderCoordinator should be the only one to dispose the renderer
|
||||
// TODO: RenderService should be the only one to dispose the renderer
|
||||
this._renderer.dispose();
|
||||
this._renderer = renderer;
|
||||
this.refreshRows(0, this._rowCount - 1);
|
||||
|
||||
@@ -563,9 +563,10 @@ export class SelectionService implements ISelectionService {
|
||||
// to be sent to the pty.
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
// Something went wrong
|
||||
// Do nothing if there is no selection start, this can happen if the first
|
||||
// click in the terminal is an incremental click
|
||||
if (!this._model.selectionStart) {
|
||||
throw new Error('Selection start position was not set before mousemove event');
|
||||
return;
|
||||
}
|
||||
|
||||
// Record the previous position so we know whether to redraw the selection
|
||||
|
||||
@@ -1163,237 +1163,4 @@ describe('Buffer', () => {
|
||||
assert.equal(str3, '😁a');
|
||||
});
|
||||
});
|
||||
// describe('stringIndexToBufferIndex', () => {
|
||||
// let terminal: TestTerminal;
|
||||
|
||||
// beforeEach(() => {
|
||||
// terminal = new TestTerminal({rows: 5, cols: 10, scrollback: 5});
|
||||
// });
|
||||
|
||||
// it('multiline ascii', () => {
|
||||
// const input = 'This is ASCII text spanning multiple lines.';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// for (let i = 0; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('combining e\u0301 in a sentence', () => {
|
||||
// const input = 'Sitting in the cafe\u0301 drinking coffee.';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// for (let i = 0; i < 19; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// // string index 18 & 19 point to combining char e\u0301 ---> same buffer Index
|
||||
// assert.deepEqual(
|
||||
// terminal.buffer.stringIndexToBufferIndex(0, 18),
|
||||
// terminal.buffer.stringIndexToBufferIndex(0, 19));
|
||||
// // after the combining char every string index has an offset of -1
|
||||
// for (let i = 19; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([((i - 1) / terminal.cols) | 0, (i - 1) % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('multiline combining e\u0301', () => {
|
||||
// const input = 'e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301e\u0301';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// // every buffer cell index contains 2 string indices
|
||||
// for (let i = 0; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([((i >> 1) / terminal.cols) | 0, (i >> 1) % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('surrogate char in a sentence', () => {
|
||||
// const input = 'The 𝄞 is a clef widely used in modern notation.';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// for (let i = 0; i < 5; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// // string index 4 & 5 point to surrogate char 𝄞 ---> same buffer Index
|
||||
// assert.deepEqual(
|
||||
// terminal.buffer.stringIndexToBufferIndex(0, 4),
|
||||
// terminal.buffer.stringIndexToBufferIndex(0, 5));
|
||||
// // after the combining char every string index has an offset of -1
|
||||
// for (let i = 5; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([((i - 1) / terminal.cols) | 0, (i - 1) % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('multiline surrogate char', () => {
|
||||
// const input = '𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞𝄞';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// // every buffer cell index contains 2 string indices
|
||||
// for (let i = 0; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([((i >> 1) / terminal.cols) | 0, (i >> 1) % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('surrogate char with combining', () => {
|
||||
// // eye of Ra with acute accent - string length of 3
|
||||
// const input = '𓂀\u0301 - the eye hiroglyph with an acute accent.';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// // index 0..2 should map to 0
|
||||
// assert.deepEqual([0, 0], terminal.buffer.stringIndexToBufferIndex(0, 1));
|
||||
// assert.deepEqual([0, 0], terminal.buffer.stringIndexToBufferIndex(0, 2));
|
||||
// for (let i = 2; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([((i - 2) / terminal.cols) | 0, (i - 2) % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('multiline surrogate with combining', () => {
|
||||
// const input = '𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301𓂀\u0301';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// // every buffer cell index contains 3 string indices
|
||||
// for (let i = 0; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([(((i / 3) | 0) / terminal.cols) | 0, ((i / 3) | 0) % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('fullwidth chars', () => {
|
||||
// const input = 'These 123 are some fat numbers.';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// for (let i = 0; i < 6; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([(i / terminal.cols) | 0, i % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// // string index 6, 7, 8 take 2 cells
|
||||
// assert.deepEqual([0, 8], terminal.buffer.stringIndexToBufferIndex(0, 7));
|
||||
// assert.deepEqual([1, 0], terminal.buffer.stringIndexToBufferIndex(0, 8));
|
||||
// // rest of the string has offset of +3
|
||||
// for (let i = 9; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([((i + 3) / terminal.cols) | 0, (i + 3) % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('multiline fullwidth chars', () => {
|
||||
// const input = '12345678901234567890';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// for (let i = 9; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i);
|
||||
// assert.deepEqual([((i << 1) / terminal.cols) | 0, (i << 1) % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('fullwidth combining with emoji - match emoji cell', () => {
|
||||
// const input = 'Lots of ¥\u0301 make me 😃.';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// const stringIndex = s.match(/😃/).index;
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, stringIndex);
|
||||
// assert(terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars(), '😃');
|
||||
// });
|
||||
|
||||
// it('multiline fullwidth chars with offset 1 (currently tests for broken behavior)', () => {
|
||||
// const input = 'a12345678901234567890';
|
||||
// // the 'a' at the beginning moves all fullwidth chars one to the right
|
||||
// // now the end of the line contains a dangling empty cell since
|
||||
// // the next fullwidth char has to wrap early
|
||||
// // the dangling last cell is wrongly added in the string
|
||||
// // --> fixable after resolving #1685
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// for (let i = 10; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
|
||||
// const j = (i - 0) << 1;
|
||||
// assert.deepEqual([(j / terminal.cols) | 0, j % terminal.cols], bufferIndex);
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('test fully wrapped buffer up to last char', () => {
|
||||
// const input = Array(6).join('1234567890');
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// for (let i = 0; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
|
||||
// assert.equal(input[i], terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars());
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('test fully wrapped buffer up to last char with full width odd', () => {
|
||||
// const input = 'a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301'
|
||||
// + 'a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301a¥\u0301';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(input, s);
|
||||
// for (let i = 0; i < input.length; ++i) {
|
||||
// const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
|
||||
// assert.equal(
|
||||
// (!(i % 3))
|
||||
// ? input[i]
|
||||
// : (i % 3 === 1)
|
||||
// ? input.substr(i, 2)
|
||||
// : input.substr(i - 1, 2),
|
||||
// terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars());
|
||||
// }
|
||||
// });
|
||||
|
||||
// it('should handle \t in lines correctly', () => {
|
||||
// const input = '\thttps://google.de';
|
||||
// terminal.writeSync(input);
|
||||
// const s = terminal.buffer.iterator(true).next().content;
|
||||
// assert.equal(s, Array(optionsService.options.tabStopWidth + 1).join(' ') + 'https://google.de');
|
||||
// });
|
||||
// });
|
||||
// describe('BufferStringIterator', function(): void {
|
||||
// it('iterator does not overflow buffer limits', function(): void {
|
||||
// const terminal = new TestTerminal({rows: 5, cols: 10, scrollback: 5});
|
||||
// const data = [
|
||||
// 'aaaaaaaaaa',
|
||||
// 'aaaaaaaaa\n',
|
||||
// 'aaaaaaaaaa',
|
||||
// 'aaaaaaaaa\n',
|
||||
// 'aaaaaaaaaa',
|
||||
// 'aaaaaaaaaa',
|
||||
// 'aaaaaaaaaa',
|
||||
// 'aaaaaaaaa\n',
|
||||
// 'aaaaaaaaaa',
|
||||
// 'aaaaaaaaaa'
|
||||
// ];
|
||||
// terminal.writeSync(data.join(''));
|
||||
// // brute force test with insane values
|
||||
// expect(() => {
|
||||
// for (let overscan = 0; overscan < 20; ++overscan) {
|
||||
// for (let start = -10; start < 20; ++start) {
|
||||
// for (let end = -10; end < 20; ++end) {
|
||||
// const it = terminal.buffer.iterator(false, start, end, overscan, overscan);
|
||||
// while (it.hasNext()) {
|
||||
// it.next();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }).to.not.throw();
|
||||
// });
|
||||
// });
|
||||
});
|
||||
|
||||
Vendored
+1
-1
@@ -40,7 +40,7 @@ export interface IBuffer {
|
||||
nextStop(x?: number): number;
|
||||
prevStop(x?: number): number;
|
||||
getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine;
|
||||
stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[];
|
||||
stringIndexToBufferIndex(lineIndex: number, stringIndex: number, trimRight?: boolean): number[];
|
||||
iterator(trimRight: boolean, startIndex?: number, endIndex?: number, startOverscan?: number, endOverscan?: number): IBufferStringIterator;
|
||||
getNullCell(attr?: IAttributeData): ICellData;
|
||||
getWhitespaceCell(attr?: IAttributeData): ICellData;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user