Merge branch 'master' into utf8_input

This commit is contained in:
Daniel Imms
2019-05-11 21:16:05 -07:00
committed by GitHub
6 changed files with 427 additions and 15 deletions
+50 -2
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { Terminal as PublicTerminal, ITerminalOptions as IPublicTerminalOptions, IEventEmitter, IDisposable } from 'xterm';
import { ITerminalOptions as IPublicTerminalOptions, IEventEmitter, IDisposable, IMarker } from 'xterm';
import { IColorSet, IRenderer } from './renderer/Types';
import { ICharset, IAttributeData, ICellData, IBufferLine, CharData } from './core/Types';
import { ICircularList } from './common/Types';
@@ -196,7 +196,7 @@ export interface ILinkifierEvent {
fg: number;
}
export interface ITerminal extends PublicTerminal, IElementAccessor, IBufferAccessor, ILinkifierAccessor {
export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAccessor, ILinkifierAccessor {
screenElement: HTMLElement;
selectionManager: ISelectionManager;
charMeasure: ICharMeasure;
@@ -221,6 +221,54 @@ export interface ITerminal extends PublicTerminal, IElementAccessor, IBufferAcce
showCursor(): void;
}
export interface IPublicTerminal extends IDisposable, IEventEmitter {
textarea: HTMLTextAreaElement;
rows: number;
cols: number;
buffer: IBuffer;
markers: IMarker[];
onCursorMove: IEvent<void>;
onData: IEvent<string>;
onKey: IEvent<{ key: string, domEvent: KeyboardEvent }>;
onLineFeed: IEvent<void>;
onScroll: IEvent<number>;
onSelectionChange: IEvent<void>;
onRender: IEvent<{ start: number, end: number }>;
onResize: IEvent<{ cols: number, rows: number }>;
onTitleChange: IEvent<string>;
blur(): void;
focus(): void;
resize(columns: number, rows: number): void;
writeln(data: string): void;
open(parent: HTMLElement): void;
attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void;
addCsiHandler(flag: string, callback: (params: number[], collect: string) => boolean): IDisposable;
addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable;
registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): number;
deregisterLinkMatcher(matcherId: number): void;
registerCharacterJoiner(handler: (text: string) => [number, number][]): number;
deregisterCharacterJoiner(joinerId: number): void;
addMarker(cursorYOffset: number): IMarker;
hasSelection(): boolean;
getSelection(): string;
clearSelection(): void;
selectAll(): void;
selectLines(start: number, end: number): void;
dispose(): void;
destroy(): void;
scrollLines(amount: number): void;
scrollPages(pageCount: number): void;
scrollToTop(): void;
scrollToBottom(): void;
scrollToLine(line: number): void;
clear(): void;
write(data: string): void;
getOption(key: string): any;
setOption(key: string, value: any): void;
refresh(start: number, end: number): void;
reset(): void;
}
export interface IBufferAccessor {
buffer: IBuffer;
}
+128 -6
View File
@@ -48,7 +48,7 @@ describe('API Integration Tests', () => {
window.term.write('foo');
window.term.write('bar');
`);
assert.equal(await page.evaluate(`window.term._core.buffer.translateBufferLineToString(0, true)`), 'foobar');
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foobar');
});
it('writeln', async function(): Promise<any> {
@@ -58,8 +58,8 @@ describe('API Integration Tests', () => {
window.term.writeln('foo');
window.term.writeln('bar');
`);
assert.equal(await page.evaluate(`window.term._core.buffer.translateBufferLineToString(0, true)`), 'foo');
assert.equal(await page.evaluate(`window.term._core.buffer.translateBufferLineToString(1, true)`), 'bar');
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foo');
assert.equal(await page.evaluate(`window.term.buffer.getLine(1).translateToString(true)`), 'bar');
});
it('clear', async function(): Promise<any> {
@@ -72,10 +72,10 @@ describe('API Integration Tests', () => {
}
`);
await page.evaluate(`window.term.clear()`);
assert.equal(await page.evaluate(`window.term._core.buffer.lines.length`), '5');
assert.equal(await page.evaluate(`window.term._core.buffer.translateBufferLineToString(0, true)`), 'test9');
assert.equal(await page.evaluate(`window.term.buffer.length`), '5');
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'test9');
for (let i = 1; i < 5; i++) {
assert.equal(await page.evaluate(`window.term._core.buffer.translateBufferLineToString(${i}, true)`), '');
assert.equal(await page.evaluate(`window.term.buffer.getLine(${i}).translateToString(true)`), '');
}
});
@@ -231,6 +231,128 @@ describe('API Integration Tests', () => {
assert.deepEqual(await page.evaluate(`window.calls`), ['foo']);
});
});
describe('buffer', () => {
it('cursorX, cursorY', async function(): Promise<any> {
this.timeout(10000);
await openTerminal({ rows: 5, cols: 5 });
assert.equal(await page.evaluate(`window.term.buffer.cursorX`), 0);
assert.equal(await page.evaluate(`window.term.buffer.cursorY`), 0);
await page.evaluate(`window.term.write('foo')`);
assert.equal(await page.evaluate(`window.term.buffer.cursorX`), 3);
assert.equal(await page.evaluate(`window.term.buffer.cursorY`), 0);
await page.evaluate(`window.term.write('\\n')`);
assert.equal(await page.evaluate(`window.term.buffer.cursorX`), 3);
assert.equal(await page.evaluate(`window.term.buffer.cursorY`), 1);
await page.evaluate(`window.term.write('\\r')`);
assert.equal(await page.evaluate(`window.term.buffer.cursorX`), 0);
assert.equal(await page.evaluate(`window.term.buffer.cursorY`), 1);
await page.evaluate(`window.term.write('abcde')`);
assert.equal(await page.evaluate(`window.term.buffer.cursorX`), 5);
assert.equal(await page.evaluate(`window.term.buffer.cursorY`), 1);
await page.evaluate(`window.term.write('\\n\\r\\n\\n\\n\\n\\n')`);
assert.equal(await page.evaluate(`window.term.buffer.cursorX`), 0);
assert.equal(await page.evaluate(`window.term.buffer.cursorY`), 4);
});
it('viewportY', async function(): Promise<any> {
this.timeout(10000);
await openTerminal({ rows: 5 });
assert.equal(await page.evaluate(`window.term.buffer.viewportY`), 0);
await page.evaluate(`window.term.write('\\n\\n\\n\\n')`);
assert.equal(await page.evaluate(`window.term.buffer.viewportY`), 0);
await page.evaluate(`window.term.write('\\n')`);
assert.equal(await page.evaluate(`window.term.buffer.viewportY`), 1);
await page.evaluate(`window.term.write('\\n\\n\\n\\n')`);
assert.equal(await page.evaluate(`window.term.buffer.viewportY`), 5);
await page.evaluate(`window.term.scrollLines(-1)`);
assert.equal(await page.evaluate(`window.term.buffer.viewportY`), 4);
await page.evaluate(`window.term.scrollToTop()`);
assert.equal(await page.evaluate(`window.term.buffer.viewportY`), 0);
});
it('baseY', async function(): Promise<any> {
this.timeout(10000);
await openTerminal({ rows: 5 });
assert.equal(await page.evaluate(`window.term.buffer.baseY`), 0);
await page.evaluate(`window.term.write('\\n\\n\\n\\n')`);
assert.equal(await page.evaluate(`window.term.buffer.baseY`), 0);
await page.evaluate(`window.term.write('\\n')`);
assert.equal(await page.evaluate(`window.term.buffer.baseY`), 1);
await page.evaluate(`window.term.write('\\n\\n\\n\\n')`);
assert.equal(await page.evaluate(`window.term.buffer.baseY`), 5);
await page.evaluate(`window.term.scrollLines(-1)`);
assert.equal(await page.evaluate(`window.term.buffer.baseY`), 5);
await page.evaluate(`window.term.scrollToTop()`);
assert.equal(await page.evaluate(`window.term.buffer.baseY`), 5);
});
it('length', async function(): Promise<any> {
this.timeout(10000);
await openTerminal({ rows: 5 });
assert.equal(await page.evaluate(`window.term.buffer.length`), 5);
await page.evaluate(`window.term.write('\\n\\n\\n\\n')`);
assert.equal(await page.evaluate(`window.term.buffer.length`), 5);
await page.evaluate(`window.term.write('\\n')`);
assert.equal(await page.evaluate(`window.term.buffer.length`), 6);
await page.evaluate(`window.term.write('\\n\\n\\n\\n')`);
assert.equal(await page.evaluate(`window.term.buffer.length`), 10);
});
describe('getLine', () => {
it('invalid index', async function(): Promise<any> {
this.timeout(10000);
await openTerminal({ rows: 5 });
assert.equal(await page.evaluate(`window.term.buffer.getLine(-1)`), undefined);
assert.equal(await page.evaluate(`window.term.buffer.getLine(5)`), undefined);
});
it('isWrapped', async function(): Promise<any> {
this.timeout(10000);
await openTerminal({ cols: 5 });
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).isWrapped`), false);
assert.equal(await page.evaluate(`window.term.buffer.getLine(1).isWrapped`), false);
await page.evaluate(`window.term.write('abcde')`);
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).isWrapped`), false);
assert.equal(await page.evaluate(`window.term.buffer.getLine(1).isWrapped`), false);
await page.evaluate(`window.term.write('f')`);
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).isWrapped`), false);
assert.equal(await page.evaluate(`window.term.buffer.getLine(1).isWrapped`), true);
});
it('translateToString', async function(): Promise<any> {
this.timeout(10000);
await openTerminal({ cols: 5 });
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString()`), ' ');
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), '');
await page.evaluate(`window.term.write('foo')`);
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString()`), 'foo ');
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'foo');
await page.evaluate(`window.term.write('bar')`);
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString()`), 'fooba');
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(true)`), 'fooba');
assert.equal(await page.evaluate(`window.term.buffer.getLine(1).translateToString(true)`), 'r');
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(false, 1)`), 'ooba');
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).translateToString(false, 1, 3)`), 'oo');
});
it('getCell', async function(): Promise<any> {
this.timeout(10000);
await openTerminal({ cols: 5 });
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(-1)`), undefined);
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(5)`), undefined);
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(0).char`), '');
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(0).width`), 1);
await page.evaluate(`window.term.write('a文')`);
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(0).char`), 'a');
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(0).width`), 1);
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(1).char`), '文');
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(1).width`), 2);
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(2).char`), '');
assert.equal(await page.evaluate(`window.term.buffer.getLine(0).getCell(2).width`), 0);
});
});
});
});
async function openTerminal(options: ITerminalOptions = {}): Promise<void> {
+42 -2
View File
@@ -3,8 +3,9 @@
* @license MIT
*/
import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings } from 'xterm';
import { ITerminal } from '../Types';
import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, IBuffer as IBufferApi, IBufferLine as IBufferLineApi, IBufferCell as IBufferCellApi } from 'xterm';
import { ITerminal, IBuffer } from '../Types';
import { IBufferLine } from '../core/Types';
import { Terminal as TerminalCore } from '../Terminal';
import * as Strings from '../Strings';
import { IEvent } from '../common/EventEmitter2';
@@ -30,6 +31,7 @@ export class Terminal implements ITerminalApi {
public get textarea(): HTMLTextAreaElement { return this._core.textarea; }
public get rows(): number { return this._core.rows; }
public get cols(): number { return this._core.cols; }
public get buffer(): IBufferApi { return new BufferApiView(this._core.buffer); }
public get markers(): ReadonlyArray<IMarker> { return this._core.markers; }
public blur(): void {
this._core.blur();
@@ -172,3 +174,41 @@ export class Terminal implements ITerminalApi {
return Strings;
}
}
class BufferApiView implements IBufferApi {
constructor(private _buffer: IBuffer) {}
public get cursorY(): number { return this._buffer.y; }
public get cursorX(): number { return this._buffer.x; }
public get viewportY(): number { return this._buffer.ydisp; }
public get baseY(): number { return this._buffer.ybase; }
public get length(): number { return this._buffer.lines.length; }
public getLine(y: number): IBufferLineApi | undefined {
const line = this._buffer.lines.get(y);
if (!line) {
return undefined;
}
return new BufferLineApiView(line);
}
}
class BufferLineApiView implements IBufferLineApi {
constructor(private _line: IBufferLine) {}
public get isWrapped(): boolean { return this._line.isWrapped; }
public getCell(x: number): IBufferCellApi | undefined {
if (x < 0 || x >= this._line.length) {
return undefined;
}
return new BufferCellApiView(this._line, x);
}
public translateToString(trimRight?: boolean, startColumn?: number, endColumn?: number): string {
return this._line.translateToString(trimRight, startColumn, endColumn);
}
}
class BufferCellApiView implements IBufferCellApi {
constructor(private _line: IBufferLine, private _x: number) {}
public get char(): string { return this._line.getString(this._x); }
public get width(): number { return this._line.getWidth(this._x); }
}
+2 -2
View File
@@ -157,8 +157,8 @@ export class DomRenderer extends Disposable implements IRenderer {
`${this._terminalSelector} .${ROW_CONTAINER_CLASS} {` +
` color: ${this.colorManager.colors.foreground.css};` +
` background-color: ${this.colorManager.colors.background.css};` +
` font-family: ${this._terminal.getOption('fontFamily')};` +
` font-size: ${this._terminal.getOption('fontSize')}px;` +
` font-family: ${this._terminal.options.fontFamily};` +
` font-size: ${this._terminal.options.fontSize}px;` +
`}`;
// Text styles
styles +=