Merge pull request #2304 from Tyriar/inputhandler_bufferservice

Use IBufferService for buffer access in InputHandler
This commit is contained in:
Daniel Imms
2019-07-13 11:09:04 -07:00
committed by GitHub
15 changed files with 455 additions and 354 deletions
+2 -2
View File
@@ -24,7 +24,7 @@ export function createProgram(gl: WebGLRenderingContext, vertexSource: string, f
return program;
}
console.log(gl.getProgramInfoLog(program));
console.error(gl.getProgramInfoLog(program));
gl.deleteProgram(program);
}
@@ -37,7 +37,7 @@ export function createShader(gl: WebGLRenderingContext, type: number, source: st
return shader;
}
console.log(gl.getShaderInfoLog(shader));
console.error(gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
}
+1 -1
View File
@@ -220,7 +220,6 @@ function initOptions(term: TerminalType): void {
// Internal only options
'cancelEvents',
'convertEol',
'debug',
'handler',
'screenKeys',
'termName',
@@ -235,6 +234,7 @@ function initOptions(term: TerminalType): void {
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'],
logLevel: ['debug', 'info', 'warn', 'error', 'off'],
rendererType: ['dom', 'canvas'],
wordSeparator: null
};
+161 -155
View File
@@ -13,7 +13,8 @@ import { CellData } from 'common/buffer/CellData';
import { Attributes } from 'common/buffer/Constants';
import { AttributeData } from 'common/buffer/AttributeData';
import { Params } from 'common/parser/Params';
import { MockCoreService } from 'common/TestUtils.test';
import { MockCoreService, MockBufferService, MockOptionsService, MockLogService } from 'common/TestUtils.test';
import { IBufferService } from 'common/services/Services';
function getCursor(term: TestTerminal): number[] {
return [
@@ -25,32 +26,31 @@ function getCursor(term: TestTerminal): number[] {
describe('InputHandler', () => {
describe('save and restore cursor', () => {
const terminal = new MockInputHandlingTerminal();
terminal.cols = 80;
terminal.rows = 30;
terminal.buffer.x = 1;
terminal.buffer.y = 2;
terminal.buffer.ybase = 0;
terminal.curAttrData.fg = 3;
const inputHandler = new InputHandler(terminal, new MockCoreService());
const bufferService = new MockBufferService(80, 30);
bufferService.buffer.x = 1;
bufferService.buffer.y = 2;
bufferService.buffer.ybase = 0;
const inputHandler = new InputHandler(terminal, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService());
// Save cursor position
inputHandler.saveCursor();
assert.equal(terminal.buffer.x, 1);
assert.equal(terminal.buffer.y, 2);
assert.equal(bufferService.buffer.x, 1);
assert.equal(bufferService.buffer.y, 2);
assert.equal(terminal.curAttrData.fg, 3);
// Change cursor position
terminal.buffer.x = 10;
terminal.buffer.y = 20;
bufferService.buffer.x = 10;
bufferService.buffer.y = 20;
terminal.curAttrData.fg = 30;
// Restore cursor position
inputHandler.restoreCursor();
assert.equal(terminal.buffer.x, 1);
assert.equal(terminal.buffer.y, 2);
assert.equal(bufferService.buffer.x, 1);
assert.equal(bufferService.buffer.y, 2);
assert.equal(terminal.curAttrData.fg, 3);
});
describe('setCursorStyle', () => {
it('should call Terminal.setOption with correct params', () => {
const terminal = new MockInputHandlingTerminal();
const inputHandler = new InputHandler(terminal, new MockCoreService());
const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockLogService(), new MockOptionsService());
const collect = ' ';
inputHandler.setCursorStyle(Params.fromArray([0]), collect);
@@ -93,7 +93,7 @@ describe('InputHandler', () => {
const terminal = new MockInputHandlingTerminal();
const collect = '?';
terminal.bracketedPasteMode = false;
const inputHandler = new InputHandler(terminal, new MockCoreService());
const inputHandler = new InputHandler(terminal, new MockBufferService(80, 30), new MockCoreService(), new MockLogService(), new MockOptionsService());
// Set bracketed paste mode
inputHandler.setMode(Params.fromArray([2004]), collect);
assert.equal(terminal.bracketedPasteMode, true);
@@ -103,194 +103,198 @@ describe('InputHandler', () => {
});
});
describe('regression tests', function(): void {
function termContent(term: Terminal, trim: boolean): string[] {
function termContent(bufferService: IBufferService, trim: boolean): string[] {
const result = [];
for (let i = 0; i < term.rows; ++i) result.push(term.buffer.lines.get(i).translateToString(trim));
for (let i = 0; i < bufferService.rows; ++i) result.push(bufferService.buffer.lines.get(i).translateToString(trim));
return result;
}
it('insertChars', function(): void {
const term = new Terminal();
const inputHandler = new InputHandler(term, new MockCoreService());
const bufferService = new MockBufferService(80, 30);
const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService());
// insert some data in first and second line
inputHandler.parse(Array(term.cols - 9).join('a'));
inputHandler.parse(Array(bufferService.cols - 9).join('a'));
inputHandler.parse('1234567890');
inputHandler.parse(Array(term.cols - 9).join('a'));
inputHandler.parse(Array(bufferService.cols - 9).join('a'));
inputHandler.parse('1234567890');
const line1: IBufferLine = term.buffer.lines.get(0);
expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + '1234567890');
const line1: IBufferLine = bufferService.buffer.lines.get(0);
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '1234567890');
// insert one char from params = [0]
term.buffer.y = 0;
term.buffer.x = 70;
bufferService.buffer.y = 0;
bufferService.buffer.x = 70;
inputHandler.insertChars(Params.fromArray([0]));
expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + ' 123456789');
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 123456789');
// insert one char from params = [1]
term.buffer.y = 0;
term.buffer.x = 70;
bufferService.buffer.y = 0;
bufferService.buffer.x = 70;
inputHandler.insertChars(Params.fromArray([1]));
expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + ' 12345678');
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 12345678');
// insert two chars from params = [2]
term.buffer.y = 0;
term.buffer.x = 70;
bufferService.buffer.y = 0;
bufferService.buffer.x = 70;
inputHandler.insertChars(Params.fromArray([2]));
expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + ' 123456');
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' 123456');
// insert 10 chars from params = [10]
term.buffer.y = 0;
term.buffer.x = 70;
bufferService.buffer.y = 0;
bufferService.buffer.x = 70;
inputHandler.insertChars(Params.fromArray([10]));
expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + ' ');
expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a'));
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' ');
expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a'));
});
it('deleteChars', function(): void {
const term = new Terminal();
const inputHandler = new InputHandler(term, new MockCoreService());
const bufferService = new MockBufferService(80, 30);
const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService());
// insert some data in first and second line
inputHandler.parse(Array(term.cols - 9).join('a'));
inputHandler.parse(Array(bufferService.cols - 9).join('a'));
inputHandler.parse('1234567890');
inputHandler.parse(Array(term.cols - 9).join('a'));
inputHandler.parse(Array(bufferService.cols - 9).join('a'));
inputHandler.parse('1234567890');
const line1: IBufferLine = term.buffer.lines.get(0);
expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + '1234567890');
const line1: IBufferLine = bufferService.buffer.lines.get(0);
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '1234567890');
// delete one char from params = [0]
term.buffer.y = 0;
term.buffer.x = 70;
bufferService.buffer.y = 0;
bufferService.buffer.x = 70;
inputHandler.deleteChars(Params.fromArray([0]));
expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + '234567890 ');
expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a') + '234567890');
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '234567890 ');
expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '234567890');
// insert one char from params = [1]
term.buffer.y = 0;
term.buffer.x = 70;
bufferService.buffer.y = 0;
bufferService.buffer.x = 70;
inputHandler.deleteChars(Params.fromArray([1]));
expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + '34567890 ');
expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a') + '34567890');
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '34567890 ');
expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '34567890');
// insert two chars from params = [2]
term.buffer.y = 0;
term.buffer.x = 70;
bufferService.buffer.y = 0;
bufferService.buffer.x = 70;
inputHandler.deleteChars(Params.fromArray([2]));
expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + '567890 ');
expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a') + '567890');
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + '567890 ');
expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a') + '567890');
// insert 10 chars from params = [10]
term.buffer.y = 0;
term.buffer.x = 70;
bufferService.buffer.y = 0;
bufferService.buffer.x = 70;
inputHandler.deleteChars(Params.fromArray([10]));
expect(line1.translateToString(false)).equals(Array(term.cols - 9).join('a') + ' ');
expect(line1.translateToString(true)).equals(Array(term.cols - 9).join('a'));
expect(line1.translateToString(false)).equals(Array(bufferService.cols - 9).join('a') + ' ');
expect(line1.translateToString(true)).equals(Array(bufferService.cols - 9).join('a'));
});
it('eraseInLine', function(): void {
const term = new Terminal();
const inputHandler = new InputHandler(term, new MockCoreService());
const bufferService = new MockBufferService(80, 30);
const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService());
// fill 6 lines to test 3 different states
inputHandler.parse(Array(term.cols + 1).join('a'));
inputHandler.parse(Array(term.cols + 1).join('a'));
inputHandler.parse(Array(term.cols + 1).join('a'));
inputHandler.parse(Array(bufferService.cols + 1).join('a'));
inputHandler.parse(Array(bufferService.cols + 1).join('a'));
inputHandler.parse(Array(bufferService.cols + 1).join('a'));
// params[0] - right erase
term.buffer.y = 0;
term.buffer.x = 70;
bufferService.buffer.y = 0;
bufferService.buffer.x = 70;
inputHandler.eraseInLine(Params.fromArray([0]));
expect(term.buffer.lines.get(0).translateToString(false)).equals(Array(71).join('a') + ' ');
expect(bufferService.buffer.lines.get(0).translateToString(false)).equals(Array(71).join('a') + ' ');
// params[1] - left erase
term.buffer.y = 1;
term.buffer.x = 70;
bufferService.buffer.y = 1;
bufferService.buffer.x = 70;
inputHandler.eraseInLine(Params.fromArray([1]));
expect(term.buffer.lines.get(1).translateToString(false)).equals(Array(71).join(' ') + ' aaaaaaaaa');
expect(bufferService.buffer.lines.get(1).translateToString(false)).equals(Array(71).join(' ') + ' aaaaaaaaa');
// params[1] - left erase
term.buffer.y = 2;
term.buffer.x = 70;
bufferService.buffer.y = 2;
bufferService.buffer.x = 70;
inputHandler.eraseInLine(Params.fromArray([2]));
expect(term.buffer.lines.get(2).translateToString(false)).equals(Array(term.cols + 1).join(' '));
expect(bufferService.buffer.lines.get(2).translateToString(false)).equals(Array(bufferService.cols + 1).join(' '));
});
it('eraseInDisplay', function(): void {
const term = new Terminal({cols: 80, rows: 7});
const inputHandler = new InputHandler(term, new MockCoreService());
const bufferService = new MockBufferService(80, 7);
const inputHandler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService());
// fill display with a's
for (let i = 0; i < term.rows; ++i) inputHandler.parse(Array(term.cols + 1).join('a'));
for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
// params [0] - right and below erase
term.buffer.y = 5;
term.buffer.x = 40;
bufferService.buffer.y = 5;
bufferService.buffer.x = 40;
inputHandler.eraseInDisplay(Params.fromArray([0]));
expect(termContent(term, false)).eql([
Array(term.cols + 1).join('a'),
Array(term.cols + 1).join('a'),
Array(term.cols + 1).join('a'),
Array(term.cols + 1).join('a'),
Array(term.cols + 1).join('a'),
Array(40 + 1).join('a') + Array(term.cols - 40 + 1).join(' '),
Array(term.cols + 1).join(' ')
expect(termContent(bufferService, false)).eql([
Array(bufferService.cols + 1).join('a'),
Array(bufferService.cols + 1).join('a'),
Array(bufferService.cols + 1).join('a'),
Array(bufferService.cols + 1).join('a'),
Array(bufferService.cols + 1).join('a'),
Array(40 + 1).join('a') + Array(bufferService.cols - 40 + 1).join(' '),
Array(bufferService.cols + 1).join(' ')
]);
expect(termContent(term, true)).eql([
Array(term.cols + 1).join('a'),
Array(term.cols + 1).join('a'),
Array(term.cols + 1).join('a'),
Array(term.cols + 1).join('a'),
Array(term.cols + 1).join('a'),
expect(termContent(bufferService, true)).eql([
Array(bufferService.cols + 1).join('a'),
Array(bufferService.cols + 1).join('a'),
Array(bufferService.cols + 1).join('a'),
Array(bufferService.cols + 1).join('a'),
Array(bufferService.cols + 1).join('a'),
Array(40 + 1).join('a'),
''
]);
// reset
term.buffer.y = 0;
term.buffer.x = 0;
for (let i = 0; i < term.rows; ++i) inputHandler.parse(Array(term.cols + 1).join('a'));
bufferService.buffer.y = 0;
bufferService.buffer.x = 0;
for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
// params [1] - left and above
term.buffer.y = 5;
term.buffer.x = 40;
bufferService.buffer.y = 5;
bufferService.buffer.x = 40;
inputHandler.eraseInDisplay(Params.fromArray([1]));
expect(termContent(term, false)).eql([
Array(term.cols + 1).join(' '),
Array(term.cols + 1).join(' '),
Array(term.cols + 1).join(' '),
Array(term.cols + 1).join(' '),
Array(term.cols + 1).join(' '),
Array(41 + 1).join(' ') + Array(term.cols - 41 + 1).join('a'),
Array(term.cols + 1).join('a')
expect(termContent(bufferService, false)).eql([
Array(bufferService.cols + 1).join(' '),
Array(bufferService.cols + 1).join(' '),
Array(bufferService.cols + 1).join(' '),
Array(bufferService.cols + 1).join(' '),
Array(bufferService.cols + 1).join(' '),
Array(41 + 1).join(' ') + Array(bufferService.cols - 41 + 1).join('a'),
Array(bufferService.cols + 1).join('a')
]);
expect(termContent(term, true)).eql([
expect(termContent(bufferService, true)).eql([
'',
'',
'',
'',
'',
Array(41 + 1).join(' ') + Array(term.cols - 41 + 1).join('a'),
Array(term.cols + 1).join('a')
Array(41 + 1).join(' ') + Array(bufferService.cols - 41 + 1).join('a'),
Array(bufferService.cols + 1).join('a')
]);
// reset
term.buffer.y = 0;
term.buffer.x = 0;
for (let i = 0; i < term.rows; ++i) inputHandler.parse(Array(term.cols + 1).join('a'));
bufferService.buffer.y = 0;
bufferService.buffer.x = 0;
for (let i = 0; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
// params [2] - whole screen
term.buffer.y = 5;
term.buffer.x = 40;
bufferService.buffer.y = 5;
bufferService.buffer.x = 40;
inputHandler.eraseInDisplay(Params.fromArray([2]));
expect(termContent(term, false)).eql([
Array(term.cols + 1).join(' '),
Array(term.cols + 1).join(' '),
Array(term.cols + 1).join(' '),
Array(term.cols + 1).join(' '),
Array(term.cols + 1).join(' '),
Array(term.cols + 1).join(' '),
Array(term.cols + 1).join(' ')
expect(termContent(bufferService, false)).eql([
Array(bufferService.cols + 1).join(' '),
Array(bufferService.cols + 1).join(' '),
Array(bufferService.cols + 1).join(' '),
Array(bufferService.cols + 1).join(' '),
Array(bufferService.cols + 1).join(' '),
Array(bufferService.cols + 1).join(' '),
Array(bufferService.cols + 1).join(' ')
]);
expect(termContent(term, true)).eql([
expect(termContent(bufferService, true)).eql([
'',
'',
'',
@@ -301,34 +305,34 @@ describe('InputHandler', () => {
]);
// reset and add a wrapped line
term.buffer.y = 0;
term.buffer.x = 0;
inputHandler.parse(Array(term.cols + 1).join('a')); // line 0
inputHandler.parse(Array(term.cols + 10).join('a')); // line 1 and 2
for (let i = 3; i < term.rows; ++i) inputHandler.parse(Array(term.cols + 1).join('a'));
bufferService.buffer.y = 0;
bufferService.buffer.x = 0;
inputHandler.parse(Array(bufferService.cols + 1).join('a')); // line 0
inputHandler.parse(Array(bufferService.cols + 10).join('a')); // line 1 and 2
for (let i = 3; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
// params[1] left and above with wrap
// confirm precondition that line 2 is wrapped
expect(term.buffer.lines.get(2).isWrapped).true;
term.buffer.y = 2;
term.buffer.x = 40;
expect(bufferService.buffer.lines.get(2).isWrapped).true;
bufferService.buffer.y = 2;
bufferService.buffer.x = 40;
inputHandler.eraseInDisplay(Params.fromArray([1]));
expect(term.buffer.lines.get(2).isWrapped).false;
expect(bufferService.buffer.lines.get(2).isWrapped).false;
// reset and add a wrapped line
term.buffer.y = 0;
term.buffer.x = 0;
inputHandler.parse(Array(term.cols + 1).join('a')); // line 0
inputHandler.parse(Array(term.cols + 10).join('a')); // line 1 and 2
for (let i = 3; i < term.rows; ++i) inputHandler.parse(Array(term.cols + 1).join('a'));
bufferService.buffer.y = 0;
bufferService.buffer.x = 0;
inputHandler.parse(Array(bufferService.cols + 1).join('a')); // line 0
inputHandler.parse(Array(bufferService.cols + 10).join('a')); // line 1 and 2
for (let i = 3; i < bufferService.rows; ++i) inputHandler.parse(Array(bufferService.cols + 1).join('a'));
// params[1] left and above with wrap
// confirm precondition that line 2 is wrapped
expect(term.buffer.lines.get(2).isWrapped).true;
term.buffer.y = 1;
term.buffer.x = 90; // Cursor is beyond last column
expect(bufferService.buffer.lines.get(2).isWrapped).true;
bufferService.buffer.y = 1;
bufferService.buffer.x = 90; // Cursor is beyond last column
inputHandler.eraseInDisplay(Params.fromArray([1]));
expect(term.buffer.lines.get(2).isWrapped).false;
expect(bufferService.buffer.lines.get(2).isWrapped).false;
});
});
it('convertEol setting', function(): void {
@@ -351,7 +355,7 @@ describe('InputHandler', () => {
describe('print', () => {
it('should not cause an infinite loop (regression test)', () => {
const term = new Terminal();
const inputHandler = new InputHandler(term, new MockCoreService());
const inputHandler = new InputHandler(term, new MockBufferService(80, 30), new MockCoreService(), new MockLogService(), new MockOptionsService());
const container = new Uint32Array(10);
container[0] = 0x200B;
inputHandler.print(container, 0, 1);
@@ -360,56 +364,58 @@ describe('InputHandler', () => {
describe('alt screen', () => {
let term: Terminal;
let bufferService: IBufferService;
let handler: InputHandler;
beforeEach(() => {
term = new Terminal();
handler = new InputHandler(term, new MockCoreService());
bufferService = new MockBufferService(80, 30);
handler = new InputHandler(term, bufferService, new MockCoreService(), new MockLogService(), new MockOptionsService());
});
it('should handle DECSET/DECRST 47 (alt screen buffer)', () => {
handler.parse('\x1b[?47h\r\n\x1b[31mJUNK\x1b[?47lTEST');
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('');
expect(term.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('');
expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
// Text color of 'TEST' should be red
expect((term.buffer.lines.get(1).loadCell(4, new CellData()).getFgColor())).to.equal(1);
expect((bufferService.buffer.lines.get(1).loadCell(4, new CellData()).getFgColor())).to.equal(1);
});
it('should handle DECSET/DECRST 1047 (alt screen buffer)', () => {
handler.parse('\x1b[?1047h\r\n\x1b[31mJUNK\x1b[?1047lTEST');
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('');
expect(term.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('');
expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal(' TEST');
// Text color of 'TEST' should be red
expect((term.buffer.lines.get(1).loadCell(4, new CellData()).getFgColor())).to.equal(1);
expect((bufferService.buffer.lines.get(1).loadCell(4, new CellData()).getFgColor())).to.equal(1);
});
it('should handle DECSET/DECRST 1048 (alt screen cursor)', () => {
handler.parse('\x1b[?1048h\r\n\x1b[31mJUNK\x1b[?1048lTEST');
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
expect(term.buffer.translateBufferLineToString(1, true)).to.equal('JUNK');
expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('JUNK');
// Text color of 'TEST' should be default
expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
expect(bufferService.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
// Text color of 'JUNK' should be red
expect((term.buffer.lines.get(1).loadCell(0, new CellData()).getFgColor())).to.equal(1);
expect((bufferService.buffer.lines.get(1).loadCell(0, new CellData()).getFgColor())).to.equal(1);
});
it('should handle DECSET/DECRST 1049 (alt screen buffer+cursor)', () => {
handler.parse('\x1b[?1049h\r\n\x1b[31mJUNK\x1b[?1049lTEST');
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
expect(term.buffer.translateBufferLineToString(1, true)).to.equal('');
expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('');
// Text color of 'TEST' should be default
expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
expect(bufferService.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
});
it('should handle DECSET/DECRST 1049 - maintains saved cursor for alt buffer', () => {
handler.parse('\x1b[?1049h\r\n\x1b[31m\x1b[s\x1b[?1049lTEST');
expect(term.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
expect(bufferService.buffer.translateBufferLineToString(0, true)).to.equal('TEST');
// Text color of 'TEST' should be default
expect(term.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
expect(bufferService.buffer.lines.get(0).loadCell(0, new CellData()).fg).to.equal(DEFAULT_ATTR_DATA.fg);
handler.parse('\x1b[?1049h\x1b[uTEST');
expect(term.buffer.translateBufferLineToString(1, true)).to.equal('TEST');
expect(bufferService.buffer.translateBufferLineToString(1, true)).to.equal('TEST');
// Text color of 'TEST' should be red
expect((term.buffer.lines.get(1).loadCell(0, new CellData()).getFgColor())).to.equal(1);
expect((bufferService.buffer.lines.get(1).loadCell(0, new CellData()).getFgColor())).to.equal(1);
});
it('should handle DECSET/DECRST 1049 - clears alt buffer with erase attributes', () => {
handler.parse('\x1b[42m\x1b[?1049h');
// Buffer should be filled with green background
expect(term.buffer.lines.get(20).loadCell(10, new CellData()).getBgColor()).to.equal(2);
expect(bufferService.buffer.lines.get(20).loadCell(10, new CellData()).getBgColor()).to.equal(2);
});
});
+150 -148
View File
File diff suppressed because it is too large Load Diff
+2 -1
View File
@@ -11,10 +11,11 @@ import { MockBuffer, MockTerminal, TestTerminal } from './TestUtils.test';
import { CircularList } from 'common/CircularList';
import { BufferLine } from 'common/buffer/BufferLine';
import { CellData } from 'common/buffer/CellData';
import { MockLogService } from 'common/TestUtils.test';
class TestLinkifier extends Linkifier {
constructor(terminal: ITerminal) {
super(terminal);
super(terminal, new MockLogService());
Linkifier._timeBeforeLatency = 0;
}
+4 -6
View File
@@ -8,6 +8,7 @@ import { IBufferStringIteratorResult } from 'common/buffer/Types';
import { MouseZone } from './MouseZoneManager';
import { getStringCellWidth } from 'common/CharWidth';
import { EventEmitter, IEvent } from 'common/EventEmitter';
import { ILogService } from 'common/services/Services';
/**
* Limit of the unwrapping line expansion (overscan) at the top and bottom
@@ -42,7 +43,8 @@ export class Linkifier implements ILinkifier {
public get onLinkTooltip(): IEvent<ILinkifierEvent> { return this._onLinkTooltip.event; }
constructor(
protected _terminal: ITerminal
protected _terminal: ITerminal,
private _logService: ILogService
) {
this._rowsToLinkify = {
start: null,
@@ -210,11 +212,7 @@ export class Linkifier implements ILinkifier {
if (!uri) {
// something matched but does not comply with the given matchIndex
// since this is most likely a bug the regex itself we simply do nothing here
// DEBUG: print match and throw
if ((<any>this._terminal).debug) {
console.log({match, matcher});
throw new Error('match found without corresponding matchIndex');
}
this._logService.debug('match found without corresponding matchIndex', match, matcher);
break;
}
+6 -24
View File
@@ -47,7 +47,7 @@ import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { applyWindowsMode } from './WindowsMode';
import { ColorManager } from 'browser/ColorManager';
import { RenderService } from 'browser/services/RenderService';
import { IOptionsService, IBufferService, ICoreService } from 'common/services/Services';
import { IOptionsService, IBufferService, ICoreService, ILogService } from 'common/services/Services';
import { OptionsService } from 'common/services/OptionsService';
import { ICharSizeService, IRenderService, IMouseService, ISelectionService, ISoundService } from 'browser/services/Services';
import { CharSizeService } from 'browser/services/CharSizeService';
@@ -58,6 +58,7 @@ import { Attributes } from 'common/buffer/Constants';
import { MouseService } from 'browser/services/MouseService';
import { IParams } from 'common/parser/Types';
import { CoreService } from 'common/services/CoreService';
import { LogService } from 'common/services/LogService';
// Let it work inside Node.js for automated testing purposes.
const document = (typeof window !== 'undefined') ? window.document : null;
@@ -87,7 +88,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
* The HTMLElement that the terminal is created in, set by Terminal.open.
*/
private _parent: HTMLElement;
private _context: Window;
private _document: Document;
private _viewportScrollArea: HTMLElement;
private _viewportElement: HTMLElement;
@@ -110,6 +110,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
// common services
private _bufferService: IBufferService;
private _coreService: ICoreService;
private _logService: ILogService;
public optionsService: IOptionsService;
// browser services
@@ -241,6 +242,7 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._bufferService = new BufferService(this.optionsService);
this._coreService = new CoreService(() => this.scrollToBottom(), this._bufferService, this.optionsService);
this._coreService.onData(e => this._onData.fire(e));
this._logService = new LogService(this.optionsService);
this._setupOptionsListeners();
this._setup();
@@ -297,13 +299,13 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
this._userScrolling = false;
// Register input handler and refire/handle events
this._inputHandler = new InputHandler(this, this._coreService);
this._inputHandler = new InputHandler(this, this._bufferService, this._coreService, this._logService, this.optionsService);
this._inputHandler.onCursorMove(() => this._onCursorMove.fire());
this._inputHandler.onLineFeed(() => this._onLineFeed.fire());
this.register(this._inputHandler);
this._selectionService = this._selectionService || null;
this.linkifier = this.linkifier || new Linkifier(this);
this.linkifier = this.linkifier || new Linkifier(this, this._logService);
this._mouseZoneManager = this._mouseZoneManager || null;
if (this.options.windowsMode) {
@@ -534,8 +536,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
throw new Error('Terminal requires a parent element.');
}
// Grab global elements
this._context = this._parent.ownerDocument.defaultView;
this._document = this._parent.ownerDocument;
// Create main element container
@@ -1676,24 +1676,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
}
}
/**
* Log the current state to the console.
*/
public log(text: string, data?: any): void {
if (!this.options.debug) return;
if (!this._context.console || !this._context.console.log) return;
this._context.console.log(text, data);
}
/**
* Log the current state as error to the console.
*/
public error(text: string, data?: any): void {
if (!this.options.debug) return;
if (!this._context.console || !this._context.console.error) return;
this._context.console.error(text, data);
}
/**
* Resizes the terminal.
*
-4
View File
@@ -65,11 +65,9 @@ export interface IInputHandlingTerminal {
is(term: string): boolean;
setgCharset(g: number, charset: ICharset): void;
resize(x: number, y: number): void;
log(text: string, data?: any): void;
reset(): void;
showCursor(): void;
refresh(start: number, end: number): void;
error(text: string, data?: any): void;
handleTitle(title: string): void;
}
@@ -212,7 +210,6 @@ export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAcc
scrollLines(disp: number, suppressScrollEvent?: boolean): void;
cancel(ev: Event, force?: boolean): boolean | void;
log(text: string): void;
showCursor(): void;
}
@@ -282,7 +279,6 @@ export interface ITerminalOptions extends IPublicTerminalOptions {
[key: string]: any;
cancelEvents?: boolean;
convertEol?: boolean;
debug?: boolean;
handler?: (data: string) => void;
screenKeys?: boolean;
termName?: string;
@@ -13,7 +13,6 @@ import { IBufferService, IOptionsService } from 'common/services/Services';
import { MockCharSizeService, MockMouseService } from 'browser/TestUtils.test';
import { CellData } from 'common/buffer/CellData';
import { IBuffer } from 'common/buffer/Types';
import { isWindows } from 'common/Platform';
class TestSelectionService extends SelectionService {
constructor(
@@ -360,8 +359,6 @@ describe('SelectionService', () => {
buffer.lines.set(3, stringToRow('4'));
buffer.lines.set(4, stringToRow('5'));
selectionService.selectAll();
console.log(selectionService.selectionText.length);
console.log(isWindows);
assert.equal(selectionService.selectionText, '1\n2\n3\n4\n5');
});
});
+8 -1
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { IBufferService, ICoreService, IOptionsService, ITerminalOptions, IPartialTerminalOptions } from 'common/services/Services';
import { IBufferService, ICoreService, ILogService, IOptionsService, ITerminalOptions, IPartialTerminalOptions } from 'common/services/Services';
import { IEvent, EventEmitter } from 'common/EventEmitter';
import { clone } from 'common/Clone';
import { DEFAULT_OPTIONS } from 'common/services/OptionsService';
@@ -36,6 +36,13 @@ export class MockCoreService implements ICoreService {
triggerDataEvent(data: string, wasUserInput?: boolean): void {}
}
export class MockLogService implements ILogService {
debug(message: any, ...optionalParams: any[]): void {}
info(message: any, ...optionalParams: any[]): void {}
warn(message: any, ...optionalParams: any[]): void {}
error(message: any, ...optionalParams: any[]): void {}
}
export class MockOptionsService implements IOptionsService {
options: ITerminalOptions = clone(DEFAULT_OPTIONS);
onOptionChange: IEvent<string> = new EventEmitter<string>().event;
+80
View File
@@ -0,0 +1,80 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { ILogService, IOptionsService } from 'common/services/Services';
interface IConsole {
log(message?: any, ...optionalParams: any[]): void;
error(message?: any, ...optionalParams: any[]): void;
info(message?: any, ...optionalParams: any[]): void;
trace(message?: any, ...optionalParams: any[]): void;
warn(message?: any, ...optionalParams: any[]): void;
}
// console is available on both node.js and browser contexts but the common
// module doesn't depend on them so we need to explicitly declare it.
declare const console: IConsole;
export enum LogLevel {
DEBUG = 0,
INFO = 1,
WARN = 2,
ERROR = 3,
OFF = 4
}
const optionsKeyToLogLevel: { [key: string]: LogLevel } = {
debug: LogLevel.DEBUG,
info: LogLevel.INFO,
warn: LogLevel.WARN,
error: LogLevel.ERROR,
off: LogLevel.OFF
};
const LOG_PREFIX = 'xterm.js: ';
export class LogService implements ILogService {
private _logLevel!: LogLevel;
constructor(
private readonly _optionsService: IOptionsService
) {
this._updateLogLevel();
this._optionsService.onOptionChange(key => {
if (key === 'logLevel') {
this._updateLogLevel();
}
});
}
private _updateLogLevel(): void {
this._logLevel = optionsKeyToLogLevel[this._optionsService.options.logLevel];
}
debug(message: string, ...optionalParams: any[]): void {
if (this._logLevel <= LogLevel.DEBUG) {
console.log.call(console, LOG_PREFIX + message, ...optionalParams);
}
}
info(message: string, ...optionalParams: any[]): void {
if (this._logLevel <= LogLevel.INFO) {
console.info.call(console, LOG_PREFIX + message, ...optionalParams);
}
}
warn(message: string, ...optionalParams: any[]): void {
if (this._logLevel <= LogLevel.WARN) {
console.warn.call(console, LOG_PREFIX + message, ...optionalParams);
}
}
error(message: string, ...optionalParams: any[]): void {
if (this._logLevel <= LogLevel.ERROR) {
console.error.call(console, LOG_PREFIX + message, ...optionalParams);
}
}
}
+1 -1
View File
@@ -29,6 +29,7 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
fontWeightBold: 'bold',
lineHeight: 1.0,
letterSpacing: 0,
logLevel: 'info',
scrollback: 1000,
screenReaderMode: false,
macOptionIsMeta: false,
@@ -44,7 +45,6 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
convertEol: false,
termName: 'xterm',
screenKeys: false,
debug: false,
cancelEvents: false,
useFlowControl: false,
wordSeparator: ' ()[]{}\'"'
+10 -2
View File
@@ -38,6 +38,13 @@ export interface ICoreService {
triggerDataEvent(data: string, wasUserInput?: boolean): void;
}
export interface ILogService {
debug(message: any, ...optionalParams: any[]): void;
info(message: any, ...optionalParams: any[]): void;
warn(message: any, ...optionalParams: any[]): void;
error(message: any, ...optionalParams: any[]): void;
}
export interface IOptionsService {
readonly options: ITerminalOptions;
@@ -48,7 +55,7 @@ export interface IOptionsService {
}
export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'off';
export type RendererType = 'dom' | 'canvas';
export interface IPartialTerminalOptions {
@@ -66,6 +73,7 @@ export interface IPartialTerminalOptions {
fontWeightBold?: FontWeight;
letterSpacing?: number;
lineHeight?: number;
logLevel?: LogLevel;
macOptionIsMeta?: boolean;
macOptionClickForcesSelection?: boolean;
rendererType?: RendererType;
@@ -94,6 +102,7 @@ export interface ITerminalOptions {
fontWeightBold: FontWeight;
letterSpacing: number;
lineHeight: number;
logLevel: LogLevel;
macOptionIsMeta: boolean;
macOptionClickForcesSelection: boolean;
rendererType: RendererType;
@@ -109,7 +118,6 @@ export interface ITerminalOptions {
[key: string]: any;
cancelEvents: boolean;
convertEol: boolean;
debug: boolean;
screenKeys: boolean;
termName: string;
useFlowControl: boolean;
+4 -3
View File
@@ -133,8 +133,8 @@ export class Terminal implements ITerminalApi {
public writeUtf8(data: Uint8Array): void {
this._core.writeUtf8(data);
}
public getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'fontFamily' | 'fontWeight' | 'fontWeightBold' | 'rendererType' | 'termName' | 'wordSeparator'): string;
public getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell'): boolean;
public getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'fontFamily' | 'fontWeight' | 'fontWeightBold' | 'logLevel' | 'rendererType' | 'termName' | 'wordSeparator'): string;
public getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell'): boolean;
public getOption(key: 'colors'): string[];
public getOption(key: 'cols' | 'fontSize' | 'letterSpacing' | 'lineHeight' | 'rows' | 'tabStopWidth' | 'scrollback'): number;
public getOption(key: 'handler'): (data: string) => void;
@@ -144,9 +144,10 @@ export class Terminal implements ITerminalApi {
}
public setOption(key: 'bellSound' | 'fontFamily' | 'termName' | 'wordSeparator', value: string): void;
public setOption(key: 'fontWeight' | 'fontWeightBold', value: 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'): void;
public setOption(key: 'logLevel', value: 'debug' | 'info' | 'warn' | 'error' | 'off'): void;
public setOption(key: 'bellStyle', value: 'none' | 'visual' | 'sound' | 'both'): void;
public setOption(key: 'cursorStyle', value: 'block' | 'underline' | 'bar'): void;
public setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell', value: boolean): void;
public setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell', value: boolean): void;
public setOption(key: 'colors', value: string[]): void;
public setOption(key: 'fontSize' | 'letterSpacing' | 'lineHeight' | 'tabStopWidth' | 'scrollback', value: number): void;
public setOption(key: 'handler', value: (data: string) => void): void;
+26 -3
View File
@@ -15,6 +15,11 @@ declare module 'xterm' {
*/
export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
/**
* A string representing log level.
*/
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'off';
/**
* A string representing a renderer type.
*/
@@ -107,6 +112,18 @@ declare module 'xterm' {
*/
lineHeight?: number;
/**
* What log level to use, this will log for all levels below and including
* what is set:
*
* 1. debug
* 2. info (default)
* 3. warn
* 4. error
* 5. off
*/
logLevel?: LogLevel;
/**
* Whether to treat option as the meta key.
*/
@@ -676,12 +693,12 @@ declare module 'xterm' {
* Retrieves an option's value from the terminal.
* @param key The option key.
*/
getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'fontFamily' | 'fontWeight' | 'fontWeightBold'| 'rendererType' | 'termName' | 'wordSeparator'): string;
getOption(key: 'bellSound' | 'bellStyle' | 'cursorStyle' | 'fontFamily' | 'fontWeight' | 'fontWeightBold' | 'logLevel' | 'rendererType' | 'termName' | 'wordSeparator'): string;
/**
* Retrieves an option's value from the terminal.
* @param key The option key.
*/
getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode'): boolean;
getOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode'): boolean;
/**
* Retrieves an option's value from the terminal.
* @param key The option key.
@@ -715,6 +732,12 @@ declare module 'xterm' {
* @param value The option value.
*/
setOption(key: 'fontWeight' | 'fontWeightBold', value: null | 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900'): void;
/**
* Sets an option on the terminal.
* @param key The option key.
* @param value The option value.
*/
setOption(key: 'logLevel', value: LogLevel): void;
/**
* Sets an option on the terminal.
* @param key The option key.
@@ -732,7 +755,7 @@ declare module 'xterm' {
* @param key The option key.
* @param value The option value.
*/
setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'debug' | 'disableStdin' | 'macOptionIsMeta' | 'popOnBell' | 'rightClickSelectsWord' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode', value: boolean): void;
setOption(key: 'allowTransparency' | 'cancelEvents' | 'convertEol' | 'cursorBlink' | 'disableStdin' | 'macOptionIsMeta' | 'popOnBell' | 'rightClickSelectsWord' | 'screenKeys' | 'useFlowControl' | 'visualBell' | 'windowsMode', value: boolean): void;
/**
* Sets an option on the terminal.
* @param key The option key.