Merge branch 'master' into window_manipulation
This commit is contained in:
@@ -1248,4 +1248,22 @@ describe('InputHandler', () => {
|
||||
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '125', '125', '125', '125', '125']);
|
||||
});
|
||||
});
|
||||
it('should parse big chunks in smaller subchunks', () => {
|
||||
// max single chunk size is hardcoded as 131072
|
||||
const calls: any[] = [];
|
||||
const term = new TestTerminal({cols: 10, rows: 10});
|
||||
(term as any)._inputHandler._parser.parse = (data: Uint32Array, length: number) => {
|
||||
calls.push([data.length, length]);
|
||||
};
|
||||
term.writeSync('12345');
|
||||
term.writeSync('a'.repeat(10000));
|
||||
term.writeSync('a'.repeat(200000));
|
||||
term.writeSync('a'.repeat(300000));
|
||||
assert.deepEqual(calls, [
|
||||
[4096, 5],
|
||||
[10000, 10000],
|
||||
[131072, 131072], [131072, 200000 - 131072],
|
||||
[131072, 131072], [131072, 131072], [131072, 300000 - 131072 - 131072]
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
+32
-15
@@ -28,6 +28,11 @@ import { DcsHandler } from 'common/parser/DcsParser';
|
||||
*/
|
||||
const GLEVEL: {[key: string]: number} = {'(': 0, ')': 1, '*': 2, '+': 3, '-': 1, '.': 2};
|
||||
|
||||
/**
|
||||
* Max length of the UTF32 input buffer. Real memory consumption is 4 times higher.
|
||||
*/
|
||||
const MAX_PARSEBUFFER_LENGTH = 131072;
|
||||
|
||||
|
||||
/**
|
||||
* DCS subparser implementations
|
||||
@@ -336,14 +341,28 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
|
||||
this._logService.debug('parsing data', data);
|
||||
|
||||
// resize input buffer if needed
|
||||
if (this._parseBuffer.length < data.length) {
|
||||
this._parseBuffer = new Uint32Array(data.length);
|
||||
if (this._parseBuffer.length < MAX_PARSEBUFFER_LENGTH) {
|
||||
this._parseBuffer = new Uint32Array(Math.min(data.length, MAX_PARSEBUFFER_LENGTH));
|
||||
}
|
||||
}
|
||||
this._parser.parse(this._parseBuffer,
|
||||
(typeof data === 'string')
|
||||
|
||||
// process big data in smaller chunks
|
||||
if (data.length > MAX_PARSEBUFFER_LENGTH) {
|
||||
for (let i = 0; i < data.length; i += MAX_PARSEBUFFER_LENGTH) {
|
||||
const end = i + MAX_PARSEBUFFER_LENGTH < data.length ? i + MAX_PARSEBUFFER_LENGTH : data.length;
|
||||
const len = (typeof data === 'string')
|
||||
? this._stringDecoder.decode(data.substring(i, end), this._parseBuffer)
|
||||
: this._utf8Decoder.decode(data.subarray(i, end), this._parseBuffer);
|
||||
this._parser.parse(this._parseBuffer, len);
|
||||
}
|
||||
} else {
|
||||
const len = (typeof data === 'string')
|
||||
? this._stringDecoder.decode(data, this._parseBuffer)
|
||||
: this._utf8Decoder.decode(data, this._parseBuffer)
|
||||
);
|
||||
: this._utf8Decoder.decode(data, this._parseBuffer);
|
||||
this._parser.parse(this._parseBuffer, len);
|
||||
}
|
||||
|
||||
buffer = this._bufferService.buffer;
|
||||
if (buffer.x !== cursorStartX || buffer.y !== cursorStartY) {
|
||||
@@ -1416,16 +1435,14 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// focusout: ^[[O
|
||||
this._terminal.sendFocus = true;
|
||||
break;
|
||||
case 1005: // utf8 ext mode mouse
|
||||
// for wide terminals
|
||||
// simply encodes large values as utf8 characters
|
||||
this._coreMouseService.activeEncoding = 'UTF8';
|
||||
case 1005: // utf8 ext mode mouse - removed in #2507
|
||||
this._logService.debug('DECSET 1005 not supported (see #2507)');
|
||||
break;
|
||||
case 1006: // sgr ext mode mouse
|
||||
this._coreMouseService.activeEncoding = 'SGR';
|
||||
break;
|
||||
case 1015: // urxvt ext mode mouse
|
||||
this._coreMouseService.activeEncoding = 'URXVT';
|
||||
case 1015: // urxvt ext mode mouse - removed in #2507
|
||||
this._logService.debug('DECSET 1015 not supported (see #2507)');
|
||||
break;
|
||||
case 25: // show cursor
|
||||
this._terminal.cursorHidden = false;
|
||||
@@ -1589,14 +1606,14 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
case 1004: // send focusin/focusout events
|
||||
this._terminal.sendFocus = false;
|
||||
break;
|
||||
case 1005: // utf8 ext mode mouse
|
||||
this._coreMouseService.activeEncoding = 'DEFAULT';
|
||||
case 1005: // utf8 ext mode mouse - removed in #2507
|
||||
this._logService.debug('DECRST 1005 not supported (see #2507)');
|
||||
break;
|
||||
case 1006: // sgr ext mode mouse
|
||||
this._coreMouseService.activeEncoding = 'DEFAULT';
|
||||
break;
|
||||
case 1015: // urxvt ext mode mouse
|
||||
this._coreMouseService.activeEncoding = 'DEFAULT';
|
||||
case 1015: // urxvt ext mode mouse - removed in #2507
|
||||
this._logService.debug('DECRST 1015 not supported (see #2507)');
|
||||
break;
|
||||
case 25: // hide cursor
|
||||
this._terminal.cursorHidden = true;
|
||||
|
||||
@@ -308,7 +308,7 @@ export class Linkifier implements ILinkifier {
|
||||
if (matcher.hoverTooltipCallback) {
|
||||
// Note that IViewportRange use 1-based coordinates to align with escape sequences such
|
||||
// as CUP which use 1,1 as the default for row/col
|
||||
matcher.hoverTooltipCallback(e, uri, { start: { row: y1 + 1, col: x1 + 1 }, end: { row: y2 + 1, col: x2 } });
|
||||
matcher.hoverTooltipCallback(e, uri, { start: { x: x1, y: y1 }, end: { x: x2, y: y2 } });
|
||||
}
|
||||
},
|
||||
() => {
|
||||
|
||||
Vendored
+5
-5
@@ -44,13 +44,13 @@ export interface IViewport extends IDisposable {
|
||||
}
|
||||
|
||||
export interface IViewportRange {
|
||||
start: IViewportCellPosition;
|
||||
end: IViewportCellPosition;
|
||||
start: IViewportRangePosition;
|
||||
end: IViewportRangePosition;
|
||||
}
|
||||
|
||||
export interface IViewportCellPosition {
|
||||
col: number;
|
||||
row: number;
|
||||
export interface IViewportRangePosition {
|
||||
x: number;
|
||||
y: number;
|
||||
}
|
||||
|
||||
export type LinkMatcherHandler = (event: MouseEvent, uri: string) => void;
|
||||
|
||||
@@ -237,7 +237,7 @@ export class Viewport extends Disposable implements IViewport {
|
||||
if ((modifier === 'alt' && ev.altKey) ||
|
||||
(modifier === 'ctrl' && ev.ctrlKey) ||
|
||||
(modifier === 'shift' && ev.shiftKey)) {
|
||||
return amount * this._optionsService.options.fastScrollSensitivity;
|
||||
return amount * this._optionsService.options.fastScrollSensitivity * this._optionsService.options.scrollSensitivity;
|
||||
}
|
||||
|
||||
return amount * this._optionsService.options.scrollSensitivity;
|
||||
|
||||
@@ -16,11 +16,13 @@ export interface IEvent<T> {
|
||||
export interface IEventEmitter<T> {
|
||||
event: IEvent<T>;
|
||||
fire(data: T): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export class EventEmitter<T> implements IEventEmitter<T> {
|
||||
private _listeners: IListener<T>[] = [];
|
||||
private _event?: IEvent<T>;
|
||||
private _disposed: boolean = false;
|
||||
|
||||
public get event(): IEvent<T> {
|
||||
if (!this._event) {
|
||||
@@ -28,10 +30,12 @@ export class EventEmitter<T> implements IEventEmitter<T> {
|
||||
this._listeners.push(listener);
|
||||
const disposable = {
|
||||
dispose: () => {
|
||||
for (let i = 0; i < this._listeners.length; i++) {
|
||||
if (this._listeners[i] === listener) {
|
||||
this._listeners.splice(i, 1);
|
||||
return;
|
||||
if (!this._disposed) {
|
||||
for (let i = 0; i < this._listeners.length; i++) {
|
||||
if (this._listeners[i] === listener) {
|
||||
this._listeners.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,4 +55,11 @@ export class EventEmitter<T> implements IEventEmitter<T> {
|
||||
queue[i].call(undefined, data);
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this._listeners) {
|
||||
this._listeners.length = 0;
|
||||
}
|
||||
this._disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ export class Marker extends Disposable implements IMarker {
|
||||
return;
|
||||
}
|
||||
this.isDisposed = true;
|
||||
this.line = -1;
|
||||
// Emit before super.dispose such that dispose listeners get a change to react
|
||||
this._onDispose.fire();
|
||||
}
|
||||
|
||||
@@ -32,9 +32,9 @@ describe('CoreMouseService', () => {
|
||||
const cms = new CoreMouseService(bufferService, coreService);
|
||||
assert.deepEqual(Object.keys((cms as any)._protocols), ['NONE', 'X10', 'VT200', 'DRAG', 'ANY']);
|
||||
});
|
||||
it('default encodings - DEFAULT, UTF8, SGR, URXVT', () => {
|
||||
it('default encodings - DEFAULT, SGR', () => {
|
||||
const cms = new CoreMouseService(bufferService, coreService);
|
||||
assert.deepEqual(Object.keys((cms as any)._encodings), ['DEFAULT', 'UTF8', 'SGR', 'URXVT']);
|
||||
assert.deepEqual(Object.keys((cms as any)._encodings), ['DEFAULT', 'SGR']);
|
||||
});
|
||||
it('protocol/encoding setter, reset', () => {
|
||||
const cms = new CoreMouseService(bufferService, coreService);
|
||||
@@ -151,14 +151,6 @@ describe('CoreMouseService', () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
it('UTF8 encoding', () => {
|
||||
cms.activeProtocol = 'ANY';
|
||||
cms.activeEncoding = 'UTF8';
|
||||
for (let i = 0; i < bufferService.cols; ++i) {
|
||||
assert.equal(cms.triggerMouseEvent({ col: i, row: 0, button: CoreMouseButton.LEFT, action: CoreMouseAction.DOWN }), true);
|
||||
assert.deepEqual(toBytes(reports.pop()), [0x1b, 0x5b, 0x4d, 0x20, i + 33, 0x21]);
|
||||
}
|
||||
});
|
||||
it('SGR encoding', () => {
|
||||
cms.activeProtocol = 'ANY';
|
||||
cms.activeEncoding = 'SGR';
|
||||
@@ -167,14 +159,6 @@ describe('CoreMouseService', () => {
|
||||
assert.deepEqual(reports.pop(), `\x1b[<0;${i + 1};1M`);
|
||||
}
|
||||
});
|
||||
it('URXVT', () => {
|
||||
cms.activeProtocol = 'ANY';
|
||||
cms.activeEncoding = 'URXVT';
|
||||
for (let i = 0; i < bufferService.cols; ++i) {
|
||||
assert.equal(cms.triggerMouseEvent({ col: i, row: 0, button: CoreMouseButton.LEFT, action: CoreMouseAction.DOWN }), true);
|
||||
assert.deepEqual(reports.pop(), `\x1b[32;${i + 1};1M`);
|
||||
}
|
||||
});
|
||||
});
|
||||
it('eventCodes with modifiers (DEFAULT encoding)', () => {
|
||||
// TODO: implement AUX button tests
|
||||
|
||||
@@ -132,17 +132,6 @@ const DEFAULT_ENCODINGS: {[key: string]: CoreMouseEncoding} = {
|
||||
// FIXED: params = params.map(v => (v > 255) ? 0 : value);
|
||||
return `\x1b[M${S(params[0])}${S(params[1])}${S(params[2])}`;
|
||||
},
|
||||
/**
|
||||
* UTF8 - CSI M Pb Px Py
|
||||
* Same as DEFAULT, but with optional 2-byte UTF8
|
||||
* encoding for values > 223 (can encode up to 2015).
|
||||
*/
|
||||
UTF8: (e: ICoreMouseEvent) => {
|
||||
let params = [eventCode(e, false) + 32, e.col + 32, e.row + 32];
|
||||
// limit to 2-byte UTF8
|
||||
params = params.map(v => (v > 2047) ? 0 : v);
|
||||
return `\x1b[M${S(params[0])}${S(params[1])}${S(params[2])}`;
|
||||
},
|
||||
/**
|
||||
* SGR - CSI < Pb ; Px ; Py M|m
|
||||
* No encoding limitation.
|
||||
@@ -151,14 +140,6 @@ const DEFAULT_ENCODINGS: {[key: string]: CoreMouseEncoding} = {
|
||||
SGR: (e: ICoreMouseEvent) => {
|
||||
const final = (e.action === CoreMouseAction.UP && e.button !== CoreMouseButton.WHEEL) ? 'm' : 'M';
|
||||
return `\x1b[<${eventCode(e, true)};${e.col};${e.row}${final}`;
|
||||
},
|
||||
/**
|
||||
* URXVT - CSI Pb ; Px ; Py M
|
||||
* Same button encoding as default, decimal encoding for coords.
|
||||
* Ambiguity with other sequences, should not be used.
|
||||
*/
|
||||
URXVT: (e: ICoreMouseEvent) => {
|
||||
return `\x1b[${eventCode(e, false) + 32};${e.col};${e.row}M`;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -167,7 +148,7 @@ const DEFAULT_ENCODINGS: {[key: string]: CoreMouseEncoding} = {
|
||||
*
|
||||
* Provides mouse tracking reports with different protocols and encodings.
|
||||
* - protocols: NONE (default), X10, VT200, DRAG, ANY
|
||||
* - encodings: DEFAULT, SGR, UTF8, URXVT
|
||||
* - encodings: DEFAULT, SGR (UTF8, URXVT removed in #2507)
|
||||
*
|
||||
* Custom protocols/encodings can be added by `addProtocol` / `addEncoding`.
|
||||
* To activate a protocol/encoding, set `activeProtocol` / `activeEncoding`.
|
||||
|
||||
Reference in New Issue
Block a user