Merge branch 'master' into reverse_wraparound

This commit is contained in:
Jörg Breitbart
2020-04-27 16:11:39 +02:00
53 changed files with 3123 additions and 1535 deletions
+301
View File
@@ -0,0 +1,301 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import * as Strings from 'browser/LocalizableStrings';
import { ITerminal } from 'browser/Types';
import { IBuffer } from 'common/buffer/Types';
import { isMac } from 'common/Platform';
import { RenderDebouncer } from 'browser/RenderDebouncer';
import { addDisposableDomListener } from 'browser/Lifecycle';
import { Disposable } from 'common/Lifecycle';
import { ScreenDprMonitor } from 'browser/ScreenDprMonitor';
import { IRenderService } from 'browser/services/Services';
const MAX_ROWS_TO_READ = 20;
const enum BoundaryPosition {
TOP,
BOTTOM
}
export class AccessibilityManager extends Disposable {
private _accessibilityTreeRoot: HTMLElement;
private _rowContainer: HTMLElement;
private _rowElements: HTMLElement[];
private _liveRegion: HTMLElement;
private _liveRegionLineCount: number = 0;
private _renderRowsDebouncer: RenderDebouncer;
private _screenDprMonitor: ScreenDprMonitor;
private _topBoundaryFocusListener: (e: FocusEvent) => void;
private _bottomBoundaryFocusListener: (e: FocusEvent) => void;
/**
* This queue has a character pushed to it for keys that are pressed, if the
* next character added to the terminal is equal to the key char then it is
* not announced (added to live region) because it has already been announced
* by the textarea event (which cannot be canceled). There are some race
* condition cases if there is typing while data is streaming, but this covers
* the main case of typing into the prompt and inputting the answer to a
* question (Y/N, etc.).
*/
private _charsToConsume: string[] = [];
private _charsToAnnounce: string = '';
constructor(
private readonly _terminal: ITerminal,
private readonly _renderService: IRenderService
) {
super();
this._accessibilityTreeRoot = document.createElement('div');
this._accessibilityTreeRoot.classList.add('xterm-accessibility');
this._rowContainer = document.createElement('div');
this._rowContainer.classList.add('xterm-accessibility-tree');
this._rowContainer.setAttribute('role', 'list');
this._rowElements = [];
for (let i = 0; i < this._terminal.rows; i++) {
this._rowElements[i] = this._createAccessibilityTreeNode();
this._rowContainer.appendChild(this._rowElements[i]);
}
this._topBoundaryFocusListener = e => this._onBoundaryFocus(e, BoundaryPosition.TOP);
this._bottomBoundaryFocusListener = e => this._onBoundaryFocus(e, BoundaryPosition.BOTTOM);
this._rowElements[0].addEventListener('focus', this._topBoundaryFocusListener);
this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener);
this._refreshRowsDimensions();
this._accessibilityTreeRoot.appendChild(this._rowContainer);
this._renderRowsDebouncer = new RenderDebouncer(this._renderRows.bind(this));
this._refreshRows();
this._liveRegion = document.createElement('div');
this._liveRegion.classList.add('live-region');
this._liveRegion.setAttribute('aria-live', 'assertive');
this._accessibilityTreeRoot.appendChild(this._liveRegion);
if (!this._terminal.element) {
throw new Error('Cannot enable accessibility before Terminal.open');
}
this._terminal.element.insertAdjacentElement('afterbegin', this._accessibilityTreeRoot);
this.register(this._renderRowsDebouncer);
this.register(this._terminal.onResize(e => this._onResize(e.rows)));
this.register(this._terminal.onRender(e => this._refreshRows(e.start, e.end)));
this.register(this._terminal.onScroll(() => this._refreshRows()));
// Line feed is an issue as the prompt won't be read out after a command is run
this.register(this._terminal.onA11yChar(char => this._onChar(char)));
this.register(this._terminal.onLineFeed(() => this._onChar('\n')));
this.register(this._terminal.onA11yTab(spaceCount => this._onTab(spaceCount)));
this.register(this._terminal.onKey(e => this._onKey(e.key)));
this.register(this._terminal.onBlur(() => this._clearLiveRegion()));
this.register(this._renderService.onDimensionsChange(() => this._refreshRowsDimensions()));
this._screenDprMonitor = new ScreenDprMonitor();
this.register(this._screenDprMonitor);
this._screenDprMonitor.setListener(() => this._refreshRowsDimensions());
// This shouldn't be needed on modern browsers but is present in case the
// media query that drives the ScreenDprMonitor isn't supported
this.register(addDisposableDomListener(window, 'resize', () => this._refreshRowsDimensions()));
}
public dispose(): void {
super.dispose();
this._terminal.element?.removeChild(this._accessibilityTreeRoot);
this._rowElements.length = 0;
}
private _onBoundaryFocus(e: FocusEvent, position: BoundaryPosition): void {
const boundaryElement = <HTMLElement>e.target;
const beforeBoundaryElement = this._rowElements[position === BoundaryPosition.TOP ? 1 : this._rowElements.length - 2];
// Don't scroll if the buffer top has reached the end in that direction
const posInSet = boundaryElement.getAttribute('aria-posinset');
const lastRowPos = position === BoundaryPosition.TOP ? '1' : `${this._terminal.buffer.lines.length}`;
if (posInSet === lastRowPos) {
return;
}
// Don't scroll when the last focused item was not the second row (focus is going the other
// direction)
if (e.relatedTarget !== beforeBoundaryElement) {
return;
}
// Remove old boundary element from array
let topBoundaryElement: HTMLElement;
let bottomBoundaryElement: HTMLElement;
if (position === BoundaryPosition.TOP) {
topBoundaryElement = boundaryElement;
bottomBoundaryElement = this._rowElements.pop()!;
this._rowContainer.removeChild(bottomBoundaryElement);
} else {
topBoundaryElement = this._rowElements.shift()!;
bottomBoundaryElement = boundaryElement;
this._rowContainer.removeChild(topBoundaryElement);
}
// Remove listeners from old boundary elements
topBoundaryElement.removeEventListener('focus', this._topBoundaryFocusListener);
bottomBoundaryElement.removeEventListener('focus', this._bottomBoundaryFocusListener);
// Add new element to array/DOM
if (position === BoundaryPosition.TOP) {
const newElement = this._createAccessibilityTreeNode();
this._rowElements.unshift(newElement);
this._rowContainer.insertAdjacentElement('afterbegin', newElement);
} else {
const newElement = this._createAccessibilityTreeNode();
this._rowElements.push(newElement);
this._rowContainer.appendChild(newElement);
}
// Add listeners to new boundary elements
this._rowElements[0].addEventListener('focus', this._topBoundaryFocusListener);
this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener);
// Scroll up
this._terminal.scrollLines(position === BoundaryPosition.TOP ? -1 : 1);
// Focus new boundary before element
this._rowElements[position === BoundaryPosition.TOP ? 1 : this._rowElements.length - 2].focus();
// Prevent the standard behavior
e.preventDefault();
e.stopImmediatePropagation();
}
private _onResize(rows: number): void {
// Remove bottom boundary listener
this._rowElements[this._rowElements.length - 1].removeEventListener('focus', this._bottomBoundaryFocusListener);
// Grow rows as required
for (let i = this._rowContainer.children.length; i < this._terminal.rows; i++) {
this._rowElements[i] = this._createAccessibilityTreeNode();
this._rowContainer.appendChild(this._rowElements[i]);
}
// Shrink rows as required
while (this._rowElements.length > rows) {
this._rowContainer.removeChild(this._rowElements.pop()!);
}
// Add bottom boundary listener
this._rowElements[this._rowElements.length - 1].addEventListener('focus', this._bottomBoundaryFocusListener);
this._refreshRowsDimensions();
}
private _createAccessibilityTreeNode(): HTMLElement {
const element = document.createElement('div');
element.setAttribute('role', 'listitem');
element.tabIndex = -1;
this._refreshRowDimensions(element);
return element;
}
private _onTab(spaceCount: number): void {
for (let i = 0; i < spaceCount; i++) {
this._onChar(' ');
}
}
private _onChar(char: string): void {
if (this._liveRegionLineCount < MAX_ROWS_TO_READ + 1) {
if (this._charsToConsume.length > 0) {
// Have the screen reader ignore the char if it was just input
const shiftedChar = this._charsToConsume.shift();
if (shiftedChar !== char) {
this._charsToAnnounce += char;
}
} else {
this._charsToAnnounce += char;
}
if (char === '\n') {
this._liveRegionLineCount++;
if (this._liveRegionLineCount === MAX_ROWS_TO_READ + 1) {
this._liveRegion.textContent += Strings.tooMuchOutput;
}
}
// Only detach/attach on mac as otherwise messages can go unaccounced
if (isMac) {
if (this._liveRegion.textContent && this._liveRegion.textContent.length > 0 && !this._liveRegion.parentNode) {
setTimeout(() => {
this._accessibilityTreeRoot.appendChild(this._liveRegion);
}, 0);
}
}
}
}
private _clearLiveRegion(): void {
this._liveRegion.textContent = '';
this._liveRegionLineCount = 0;
// Only detach/attach on mac as otherwise messages can go unaccounced
if (isMac) {
if (this._liveRegion.parentNode) {
this._accessibilityTreeRoot.removeChild(this._liveRegion);
}
}
}
private _onKey(keyChar: string): void {
this._clearLiveRegion();
this._charsToConsume.push(keyChar);
}
private _refreshRows(start?: number, end?: number): void {
this._renderRowsDebouncer.refresh(start, end, this._terminal.rows);
}
private _renderRows(start: number, end: number): void {
const buffer: IBuffer = this._terminal.buffer;
const setSize = buffer.lines.length.toString();
for (let i = start; i <= end; i++) {
const lineData = buffer.translateBufferLineToString(buffer.ydisp + i, true);
const posInSet = (buffer.ydisp + i + 1).toString();
const element = this._rowElements[i];
if (element) {
if (lineData.length === 0) {
element.innerHTML = '&nbsp;';
} else {
element.textContent = lineData;
}
element.setAttribute('aria-posinset', posInSet);
element.setAttribute('aria-setsize', setSize);
}
}
this._announceCharacters();
}
private _refreshRowsDimensions(): void {
if (!this._renderService.dimensions.actualCellHeight) {
return;
}
if (this._rowElements.length !== this._terminal.rows) {
this._onResize(this._terminal.rows);
}
for (let i = 0; i < this._terminal.rows; i++) {
this._refreshRowDimensions(this._rowElements[i]);
}
}
private _refreshRowDimensions(element: HTMLElement): void {
element.style.height = `${this._renderService.dimensions.actualCellHeight}px`;
}
private _announceCharacters(): void {
if (this._charsToAnnounce.length === 0) {
return;
}
this._liveRegion.textContent += this._charsToAnnounce;
this._charsToAnnounce = '';
}
}
+4 -4
View File
@@ -42,17 +42,17 @@ export function copyHandler(ev: ClipboardEvent, selectionService: ISelectionServ
* @param ev The original paste event to be handled
* @param term The terminal on which to apply the handled paste event
*/
export function handlePasteEvent(ev: ClipboardEvent, textarea: HTMLTextAreaElement, bracketedPasteMode: boolean, coreService: ICoreService): void {
export function handlePasteEvent(ev: ClipboardEvent, textarea: HTMLTextAreaElement, coreService: ICoreService): void {
ev.stopPropagation();
if (ev.clipboardData) {
const text = ev.clipboardData.getData('text/plain');
paste(text, textarea, bracketedPasteMode, coreService);
paste(text, textarea, coreService);
}
}
export function paste(text: string, textarea: HTMLTextAreaElement, bracketedPasteMode: boolean, coreService: ICoreService): void {
export function paste(text: string, textarea: HTMLTextAreaElement, coreService: ICoreService): void {
text = prepareTextForTerminal(text);
text = bracketTextForPaste(text, bracketedPasteMode);
text = bracketTextForPaste(text, coreService.decPrivateModes.bracketedPasteMode);
coreService.triggerDataEvent(text, true);
textarea.value = '';
}
+1 -1
View File
@@ -3,7 +3,7 @@
* @license MIT
*/
import { ILinkifier2, ILinkProvider, IBufferCellPosition, ILink, ILinkifierEvent, ILinkDecorations } from './Types';
import { ILinkifier2, ILinkProvider, IBufferCellPosition, ILink, ILinkifierEvent, ILinkDecorations } from 'browser/Types';
import { IDisposable } from 'common/Types';
import { IMouseService, IRenderService } from './services/Services';
import { IBufferService } from 'common/services/Services';
+1 -1
View File
@@ -26,7 +26,7 @@ export class RenderDebouncer implements IDisposable {
}
}
public refresh(rowStart: number, rowEnd: number, rowCount: number): void {
public refresh(rowStart: number | undefined, rowEnd: number | undefined, rowCount: number): void {
this._rowCount = rowCount;
// Get the min/max row start/end for the arg values
rowStart = rowStart !== undefined ? rowStart : 0;
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+134
View File
@@ -0,0 +1,134 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import * as glob from 'glob';
import * as path from 'path';
import * as os from 'os';
import * as fs from 'fs';
import * as pty from 'node-pty';
import { Terminal } from 'browser/Terminal';
import { IDisposable } from 'xterm';
// all test files expect terminal in 80x25
const COLS = 80;
const ROWS = 25;
const TESTFILES = glob.sync('**/escape_sequence_files/*.in', { cwd: path.join(__dirname, '../..')});
const SKIP_FILES = [
't0084-CBT.in',
't0101-NLM.in',
't0103-reverse_wrap.in', // not comparable, we deviate from xterm reverse wrap on purpose
't0504-vim.in'
];
if (os.platform() === 'darwin') {
// These are failing on macOS only (termios related?)
SKIP_FILES.push(
't0003-line_wrap.in',
't0005-CR.in',
't0009-NEL.in',
't0503-zsh_ls_color.in'
);
}
// filter skipFilenames
const FILES = TESTFILES.filter(value => SKIP_FILES.indexOf(value.split('/').slice(-1)[0]) === -1);
describe('Escape Sequence Files', function(): void {
this.timeout(1000);
let ptyTerm: any;
let slaveEnd: any;
let term: Terminal;
let customHandler: IDisposable | undefined;
before(() => {
if (process.platform === 'win32') {
return;
}
ptyTerm = (pty as any).open({cols: COLS, rows: ROWS});
slaveEnd = ptyTerm._slave;
term = new Terminal({cols: COLS, rows: ROWS});
ptyTerm._master.on('data', (data: string) => term.write(data));
});
after(() => {
if (process.platform === 'win32') {
return;
}
ptyTerm._master.end();
ptyTerm._master.destroy();
});
FILES.forEach(filename => {
(process.platform === 'win32' ? it.skip : it)(filename.split('/').slice(-1)[0], async () => {
// reset terminal and handler
if (customHandler) {
customHandler.dispose();
}
slaveEnd.write('\r\n');
term.reset();
slaveEnd.write('\x1bc\x1b[H');
// register handler to trigger viewport scraping, wait for it to finish
let content = '';
const OSC_CODE = 12345;
await new Promise(resolve => {
customHandler = term.addOscHandler(OSC_CODE, () => {
// grab terminal viewport content
content = terminalToString(term);
resolve();
return true;
});
// write file to slave
slaveEnd.write(fs.readFileSync(filename, 'utf8'));
// trigger custom sequence
slaveEnd.write(`\x1b]${OSC_CODE};\x07`);
});
// compare with expected output (right trimmed)
const expected = fs.readFileSync(filename.split('.')[0] + '.text', 'utf8');
const expectedRightTrimmed = expected.split('\n').map(l => l.replace(/\s+$/, '')).join('\n');
if (content !== expectedRightTrimmed) {
throw new Error(formatError(fs.readFileSync(filename, 'utf8'), content, expected));
}
});
});
});
/**
* Helpers
*/
// generate colorful noisy output to compare xterm and emulator cell states
function formatError(input: string, output: string, expected: string): string {
function addLineNumber(start: number, color: string): (s: string) => string {
let counter = start || 0;
return function(s: string): string {
counter += 1;
return '\x1b[33m' + (' ' + counter).slice(-2) + color + s;
};
}
const line80 = '12345678901234567890123456789012345678901234567890123456789012345678901234567890';
let s = '';
s += `\n\x1b[34m${JSON.stringify(input)}`;
s += `\n\x1b[33m ${line80}\n`;
s += output.split('\n').map(addLineNumber(0, '\x1b[31m')).join('\n');
s += `\n\x1b[33m ${line80}\n`;
s += expected.split('\n').map(addLineNumber(0, '\x1b[32m')).join('\n');
return s;
}
// simple debug output of terminal cells
function terminalToString(term: Terminal): string {
let result = '';
let lineText = '';
for (let line = term.buffer.ybase; line < term.buffer.ybase + term.rows; line++) {
lineText = term.buffer.lines.get(line)!.translateToString(true);
// rtrim empty cells as xterm does
lineText = lineText.replace(/\s+$/, '');
result += lineText;
result += '\n';
}
return result;
}
+324 -6
View File
@@ -3,13 +3,331 @@
* @license MIT
*/
import { IDisposable, IMarker, ISelectionPosition, ILinkProvider } from 'xterm';
import { IEvent, EventEmitter } from 'common/EventEmitter';
import { ICharSizeService, IMouseService, IRenderService } from 'browser/services/Services';
import { IRenderDimensions, IRenderer, CharacterJoinerHandler } from 'browser/renderer/Types';
import { IColorSet } from 'browser/Types';
import { ICharSizeService, IMouseService, IRenderService, ISelectionService } from 'browser/services/Services';
import { IRenderDimensions, IRenderer, CharacterJoinerHandler, IRequestRedrawEvent } from 'browser/renderer/Types';
import { IColorSet, ILinkMatcherOptions, ITerminal, ILinkifier, ILinkifier2, IBrowser, IViewport, IColorManager, ICompositionHelper } from 'browser/Types';
import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types';
import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset, ITerminalOptions } from 'common/Types';
import { Buffer } from 'common/buffer/Buffer';
import * as Browser from 'common/Platform';
import { Terminal } from 'browser/Terminal';
import { IUnicodeService, IOptionsService } from 'common/services/Services';
import { IFunctionIdentifier, IParams } from 'common/parser/Types';
import { AttributeData } from 'common/buffer/AttributeData';
export class TestTerminal extends Terminal {
public get curAttrData(): IAttributeData { return (this as any)._inputHandler._curAttrData; }
public keyDown(ev: any): boolean | undefined { return this._keyDown(ev); }
public keyPress(ev: any): boolean { return this._keyPress(ev); }
}
export class MockTerminal implements ITerminal {
public onBlur!: IEvent<void>;
public onFocus!: IEvent<void>;
public onA11yChar!: IEvent<string>;
public onA11yTab!: IEvent<number>;
public onCursorMove!: IEvent<void>;
public onLineFeed!: IEvent<void>;
public onSelectionChange!: IEvent<void>;
public onData!: IEvent<string>;
public onBinary!: IEvent<string>;
public onTitleChange!: IEvent<string>;
public onScroll!: IEvent<number>;
public onKey!: IEvent<{ key: string, domEvent: KeyboardEvent }>;
public onRender!: IEvent<{ start: number, end: number }>;
public onResize!: IEvent<{ cols: number, rows: number }>;
public markers!: IMarker[];
public optionsService!: IOptionsService;
public unicodeService!: IUnicodeService;
public addMarker(cursorYOffset: number): IMarker {
throw new Error('Method not implemented.');
}
public selectLines(start: number, end: number): void {
throw new Error('Method not implemented.');
}
public scrollToLine(line: number): void {
throw new Error('Method not implemented.');
}
public static string: any;
public setOption(key: any, value: any): void {
throw new Error('Method not implemented.');
}
public blur(): void {
throw new Error('Method not implemented.');
}
public focus(): void {
throw new Error('Method not implemented.');
}
public resize(columns: number, rows: number): void {
throw new Error('Method not implemented.');
}
public writeln(data: string): void {
throw new Error('Method not implemented.');
}
public paste(data: string): void {
throw new Error('Method not implemented.');
}
public open(parent: HTMLElement): void {
throw new Error('Method not implemented.');
}
public attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void {
throw new Error('Method not implemented.');
}
public addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable {
throw new Error('Method not implemented.');
}
public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean): IDisposable {
throw new Error('Method not implemented.');
}
public addEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable {
throw new Error('Method not implemented.');
}
public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
throw new Error('Method not implemented.');
}
public registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => boolean | void, options?: ILinkMatcherOptions): number {
throw new Error('Method not implemented.');
}
public deregisterLinkMatcher(matcherId: number): void {
throw new Error('Method not implemented.');
}
public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
throw new Error('Method not implemented.');
}
public hasSelection(): boolean {
throw new Error('Method not implemented.');
}
public getSelection(): string {
throw new Error('Method not implemented.');
}
public getSelectionPosition(): ISelectionPosition | undefined {
throw new Error('Method not implemented.');
}
public clearSelection(): void {
throw new Error('Method not implemented.');
}
public select(column: number, row: number, length: number): void {
throw new Error('Method not implemented.');
}
public selectAll(): void {
throw new Error('Method not implemented.');
}
public dispose(): void {
throw new Error('Method not implemented.');
}
public scrollPages(pageCount: number): void {
throw new Error('Method not implemented.');
}
public scrollToTop(): void {
throw new Error('Method not implemented.');
}
public scrollToBottom(): void {
throw new Error('Method not implemented.');
}
public clear(): void {
throw new Error('Method not implemented.');
}
public write(data: string): void {
throw new Error('Method not implemented.');
}
public writeUtf8(data: Uint8Array): void {
throw new Error('Method not implemented.');
}
public bracketedPasteMode!: boolean;
public renderer!: IRenderer;
public linkifier!: ILinkifier;
public linkifier2!: ILinkifier2;
public isFocused!: boolean;
public options: ITerminalOptions = {};
public element!: HTMLElement;
public screenElement!: HTMLElement;
public rowContainer!: HTMLElement;
public selectionContainer!: HTMLElement;
public selectionService!: ISelectionService;
public textarea!: HTMLTextAreaElement;
public rows!: number;
public cols!: number;
public browser: IBrowser = <any>Browser;
public writeBuffer!: string[];
public children!: HTMLElement[];
public cursorHidden!: boolean;
public cursorState!: number;
public scrollback!: number;
public buffers!: IBufferSet;
public buffer!: IBuffer;
public viewport!: IViewport;
public applicationCursor!: boolean;
public handler(data: string): void {
throw new Error('Method not implemented.');
}
public on(event: string, callback: (...args: any[]) => void): void {
throw new Error('Method not implemented.');
}
public off(type: string, listener: XtermListener): void {
throw new Error('Method not implemented.');
}
public addDisposableListener(type: string, handler: XtermListener): IDisposable {
throw new Error('Method not implemented.');
}
public scrollLines(disp: number): void {
throw new Error('Method not implemented.');
}
public scrollToRow(absoluteRow: number): number {
throw new Error('Method not implemented.');
}
public cancel(ev: Event, force?: boolean): void {
throw new Error('Method not implemented.');
}
public log(text: string): void {
throw new Error('Method not implemented.');
}
public emit(event: string, data: any): void {
throw new Error('Method not implemented.');
}
public reset(): void {
throw new Error('Method not implemented.');
}
public refresh(start: number, end: number): void {
throw new Error('Method not implemented.');
}
public registerCharacterJoiner(handler: CharacterJoinerHandler): number { return 0; }
public deregisterCharacterJoiner(joinerId: number): void { }
}
export class MockBuffer implements IBuffer {
public markers!: IMarker[];
public addMarker(y: number): IMarker {
throw new Error('Method not implemented.');
}
public isCursorInViewport!: boolean;
public lines!: ICircularList<IBufferLine>;
public ydisp!: number;
public ybase!: number;
public hasScrollback!: boolean;
public y!: number;
public x!: number;
public tabs: any;
public scrollBottom!: number;
public scrollTop!: number;
public savedY!: number;
public savedX!: number;
public savedCharset: ICharset | undefined;
public savedCurAttrData = new AttributeData();
public translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string {
return Buffer.prototype.translateBufferLineToString.apply(this, arguments as any);
}
public getWrappedRangeForLine(y: number): { first: number, last: number } {
return Buffer.prototype.getWrappedRangeForLine.apply(this, arguments as any);
}
public nextStop(x?: number): number {
throw new Error('Method not implemented.');
}
public prevStop(x?: number): number {
throw new Error('Method not implemented.');
}
public setLines(lines: ICircularList<IBufferLine>): void {
this.lines = lines;
}
public getBlankLine(attr: IAttributeData, isWrapped?: boolean): IBufferLine {
return Buffer.prototype.getBlankLine.apply(this, arguments as any);
}
public stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[] {
return Buffer.prototype.stringIndexToBufferIndex.apply(this, arguments as any);
}
public iterator(trimRight: boolean, startIndex?: number, endIndex?: number): IBufferStringIterator {
return Buffer.prototype.iterator.apply(this, arguments as any);
}
public getNullCell(attr?: IAttributeData): ICellData {
throw new Error('Method not implemented.');
}
public getWhitespaceCell(attr?: IAttributeData): ICellData {
throw new Error('Method not implemented.');
}
}
export class MockRenderer implements IRenderer {
public onRequestRedraw!: IEvent<IRequestRedrawEvent>;
public onCanvasResize!: IEvent<{ width: number, height: number }>;
public onRender!: IEvent<{ start: number, end: number }>;
public dispose(): void {
throw new Error('Method not implemented.');
}
public colorManager!: IColorManager;
public on(type: string, listener: XtermListener): void {
throw new Error('Method not implemented.');
}
public off(type: string, listener: XtermListener): void {
throw new Error('Method not implemented.');
}
public emit(type: string, data?: any): void {
throw new Error('Method not implemented.');
}
public addDisposableListener(type: string, handler: XtermListener): IDisposable {
throw new Error('Method not implemented.');
}
public dimensions!: IRenderDimensions;
public setColors(colors: IColorSet): void {
throw new Error('Method not implemented.');
}
public onResize(cols: number, rows: number): void { }
public onCharSizeChanged(): void { }
public onBlur(): void { }
public onFocus(): void { }
public onSelectionChanged(start: [number, number], end: [number, number]): void { }
public onCursorMove(): void { }
public onOptionsChanged(): void { }
public onDevicePixelRatioChange(): void { }
public clear(): void { }
public renderRows(start: number, end: number): void { }
public registerCharacterJoiner(handler: CharacterJoinerHandler): number { return 0; }
public deregisterCharacterJoiner(): boolean { return true; }
}
export class MockViewport implements IViewport {
public dispose(): void {
throw new Error('Method not implemented.');
}
public scrollBarWidth: number = 0;
public onThemeChange(colors: IColorSet): void {
throw new Error('Method not implemented.');
}
public onWheel(ev: WheelEvent): boolean {
throw new Error('Method not implemented.');
}
public onTouchStart(ev: TouchEvent): void {
throw new Error('Method not implemented.');
}
public onTouchMove(ev: TouchEvent): boolean {
throw new Error('Method not implemented.');
}
public syncScrollArea(): void { }
public getLinesScrolled(ev: WheelEvent): number {
throw new Error('Method not implemented.');
}
}
export class MockCompositionHelper implements ICompositionHelper {
public compositionstart(): void {
throw new Error('Method not implemented.');
}
public compositionupdate(ev: CompositionEvent): void {
throw new Error('Method not implemented.');
}
public compositionend(): void {
throw new Error('Method not implemented.');
}
public updateCompositionElements(dontRecurse?: boolean): void {
throw new Error('Method not implemented.');
}
public keydown(ev: KeyboardEvent): boolean {
return true;
}
}
export class MockCharSizeService implements ICharSizeService {
public serviceBrand: any;
public serviceBrand: undefined;
public get hasValidSize(): boolean { return this.width > 0 && this.height > 0; }
public onCharSizeChange: IEvent<void> = new EventEmitter<void>().event;
constructor(public width: number, public height: number) {}
@@ -17,7 +335,7 @@ export class MockCharSizeService implements ICharSizeService {
}
export class MockMouseService implements IMouseService {
public serviceBrand: any;
public serviceBrand: undefined;
public getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined {
throw new Error('Not implemented');
}
@@ -28,7 +346,7 @@ export class MockMouseService implements IMouseService {
}
export class MockRenderService implements IRenderService {
public serviceBrand: any;
public serviceBrand: undefined;
public onDimensionsChange: IEvent<IRenderDimensions> = new EventEmitter<IRenderDimensions>().event;
public onRenderedBufferChange: IEvent<{ start: number, end: number }, void> = new EventEmitter<{ start: number, end: number }>().event;
public onRefreshRequest: IEvent<{ start: number, end: number}, void> = new EventEmitter<{ start: number, end: number }>().event;
+99 -1
View File
@@ -3,9 +3,107 @@
* @license MIT
*/
import { IDisposable, IMarker, ISelectionPosition } from 'xterm';
import { IEvent } from 'common/EventEmitter';
import { IDisposable } from 'common/Types';
import { ICoreTerminal, CharData, ITerminalOptions } from 'common/Types';
import { IMouseService, IRenderService } from './services/Services';
import { IBuffer, IBufferSet } from 'common/buffer/Types';
import { IFunctionIdentifier, IParams } from 'common/parser/Types';
export interface ITerminal extends IPublicTerminal, ICoreTerminal {
element: HTMLElement | undefined;
screenElement: HTMLElement | undefined;
browser: IBrowser;
buffer: IBuffer;
buffers: IBufferSet;
viewport: IViewport | undefined;
// TODO: We should remove options once components adopt optionsService
options: ITerminalOptions;
linkifier: ILinkifier;
linkifier2: ILinkifier2;
onBlur: IEvent<void>;
onFocus: IEvent<void>;
onA11yChar: IEvent<string>;
onA11yTab: IEvent<number>;
cancel(ev: Event, force?: boolean): boolean | void;
}
// Portions of the public API that are required by the internal Terminal
export interface IPublicTerminal extends IDisposable {
textarea: HTMLTextAreaElement | undefined;
rows: number;
cols: number;
buffer: IBuffer;
markers: IMarker[];
onCursorMove: IEvent<void>;
onData: IEvent<string>;
onBinary: 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;
open(parent: HTMLElement): void;
attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void;
addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable;
addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean): IDisposable;
addEscHandler(id: IFunctionIdentifier, callback: () => 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;
registerLinkProvider(linkProvider: ILinkProvider): IDisposable;
registerCharacterJoiner(handler: (text: string) => [number, number][]): number;
deregisterCharacterJoiner(joinerId: number): void;
addMarker(cursorYOffset: number): IMarker | undefined;
hasSelection(): boolean;
getSelection(): string;
getSelectionPosition(): ISelectionPosition | undefined;
clearSelection(): void;
select(column: number, row: number, length: number): void;
selectAll(): void;
selectLines(start: number, end: number): void;
dispose(): void;
scrollLines(amount: number): void;
scrollPages(pageCount: number): void;
scrollToTop(): void;
scrollToBottom(): void;
scrollToLine(line: number): void;
clear(): void;
write(data: string | Uint8Array, callback?: () => void): void;
paste(data: string): void;
refresh(start: number, end: number): void;
reset(): void;
}
export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean;
export type LineData = CharData[];
export interface ICompositionHelper {
compositionstart(): void;
compositionupdate(ev: CompositionEvent): void;
compositionend(): void;
updateCompositionElements(dontRecurse?: boolean): void;
keydown(ev: KeyboardEvent): boolean;
}
export interface IBrowser {
isNode: boolean;
userAgent: string;
platform: string;
isFirefox: boolean;
isMac: boolean;
isIpad: boolean;
isIphone: boolean;
isWindows: boolean;
}
export interface IColorManager {
colors: IColorSet;
+54
View File
@@ -0,0 +1,54 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { assert } from 'chai';
import { AddonManager, ILoadedAddon } from './AddonManager';
import { ITerminalAddon } from 'xterm';
class TestAddonManager extends AddonManager {
public get addons(): ILoadedAddon[] {
return this._addons;
}
}
describe('AddonManager', () => {
let manager: TestAddonManager;
beforeEach(() => {
manager = new TestAddonManager();
});
describe('loadAddon', () => {
it('should call addon constructor', () => {
let called = false;
class Addon implements ITerminalAddon {
public activate(terminal: any): void {
assert.equal(terminal, 'foo', 'The first constructor arg should be Terminal');
called = true;
}
public dispose(): void { }
}
manager.loadAddon('foo' as any, new Addon());
assert.equal(called, true);
});
});
describe('dispose', () => {
it('should dispose all loaded addons', () => {
let called = 0;
class Addon implements ITerminalAddon {
public activate(): void {}
public dispose(): void { called++; }
}
manager.loadAddon(null!, new Addon());
manager.loadAddon(null!, new Addon());
manager.loadAddon(null!, new Addon());
assert.equal(manager.addons.length, 3);
manager.dispose();
assert.equal(called, 3);
assert.equal(manager.addons.length, 0);
});
});
});
+56
View File
@@ -0,0 +1,56 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { ITerminalAddon, IDisposable, Terminal } from 'xterm';
export interface ILoadedAddon {
instance: ITerminalAddon;
dispose: () => void;
isDisposed: boolean;
}
export class AddonManager implements IDisposable {
protected _addons: ILoadedAddon[] = [];
constructor() {
}
public dispose(): void {
for (let i = this._addons.length - 1; i >= 0; i--) {
this._addons[i].instance.dispose();
}
}
public loadAddon(terminal: Terminal, instance: ITerminalAddon): void {
const loadedAddon: ILoadedAddon = {
instance,
dispose: instance.dispose,
isDisposed: false
};
this._addons.push(loadedAddon);
instance.dispose = () => this._wrappedAddonDispose(loadedAddon);
instance.activate(<any>terminal);
}
private _wrappedAddonDispose(loadedAddon: ILoadedAddon): void {
if (loadedAddon.isDisposed) {
// Do nothing if already disposed
return;
}
let index = -1;
for (let i = 0; i < this._addons.length; i++) {
if (this._addons[i] === loadedAddon) {
index = i;
break;
}
}
if (index === -1) {
throw new Error('Could not dispose an addon that has not been loaded');
}
loadedAddon.isDisposed = true;
loadedAddon.dispose.apply(loadedAddon.instance);
this._addons.splice(index, 1);
}
}
+313
View File
@@ -0,0 +1,313 @@
/**
* Copyright (c) 2018 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { Terminal as ITerminalApi, ITerminalOptions, IMarker, IDisposable, ILinkMatcherOptions, ITheme, ILocalizableStrings, ITerminalAddon, ISelectionPosition, IBuffer as IBufferApi, IBufferNamespace as IBufferNamespaceApi, IBufferLine as IBufferLineApi, IBufferCell as IBufferCellApi, IParser, IFunctionIdentifier, ILinkProvider, IUnicodeHandling, IUnicodeVersionProvider } from 'xterm';
import { ITerminal } from 'browser/Types';
import { IBufferLine, ICellData } from 'common/Types';
import { IBuffer, IBufferSet } from 'common/buffer/Types';
import { CellData } from 'common/buffer/CellData';
import { Terminal as TerminalCore } from '../Terminal';
import * as Strings from '../LocalizableStrings';
import { IEvent, EventEmitter } from 'common/EventEmitter';
import { AddonManager } from './AddonManager';
import { IParams } from 'common/parser/Types';
export class Terminal implements ITerminalApi {
private _core: ITerminal;
private _addonManager: AddonManager;
private _parser: IParser | undefined;
constructor(options?: ITerminalOptions) {
this._core = new TerminalCore(options);
this._addonManager = new AddonManager();
}
public get onCursorMove(): IEvent<void> { return this._core.onCursorMove; }
public get onLineFeed(): IEvent<void> { return this._core.onLineFeed; }
public get onSelectionChange(): IEvent<void> { return this._core.onSelectionChange; }
public get onData(): IEvent<string> { return this._core.onData; }
public get onBinary(): IEvent<string> { return this._core.onBinary; }
public get onTitleChange(): IEvent<string> { return this._core.onTitleChange; }
public get onScroll(): IEvent<number> { return this._core.onScroll; }
public get onKey(): IEvent<{ key: string, domEvent: KeyboardEvent }> { return this._core.onKey; }
public get onRender(): IEvent<{ start: number, end: number }> { return this._core.onRender; }
public get onResize(): IEvent<{ cols: number, rows: number }> { return this._core.onResize; }
public get element(): HTMLElement | undefined { return this._core.element; }
public get parser(): IParser {
if (!this._parser) {
this._parser = new ParserApi(this._core);
}
return this._parser;
}
public get unicode(): IUnicodeHandling {
return new UnicodeApi(this._core);
}
public get textarea(): HTMLTextAreaElement | undefined { return this._core.textarea; }
public get rows(): number { return this._core.rows; }
public get cols(): number { return this._core.cols; }
public get buffer(): IBufferNamespaceApi { return new BufferNamespaceApi(this._core.buffers); }
public get markers(): ReadonlyArray<IMarker> { return this._core.markers; }
public blur(): void {
this._core.blur();
}
public focus(): void {
this._core.focus();
}
public resize(columns: number, rows: number): void {
this._verifyIntegers(columns, rows);
this._core.resize(columns, rows);
}
public open(parent: HTMLElement): void {
this._core.open(parent);
}
public attachCustomKeyEventHandler(customKeyEventHandler: (event: KeyboardEvent) => boolean): void {
this._core.attachCustomKeyEventHandler(customKeyEventHandler);
}
public registerLinkMatcher(regex: RegExp, handler: (event: MouseEvent, uri: string) => void, options?: ILinkMatcherOptions): number {
return this._core.registerLinkMatcher(regex, handler, options);
}
public deregisterLinkMatcher(matcherId: number): void {
this._core.deregisterLinkMatcher(matcherId);
}
public registerLinkProvider(linkProvider: ILinkProvider): IDisposable {
return this._core.registerLinkProvider(linkProvider);
}
public registerCharacterJoiner(handler: (text: string) => [number, number][]): number {
return this._core.registerCharacterJoiner(handler);
}
public deregisterCharacterJoiner(joinerId: number): void {
this._core.deregisterCharacterJoiner(joinerId);
}
public registerMarker(cursorYOffset: number): IMarker | undefined {
this._verifyIntegers(cursorYOffset);
return this._core.addMarker(cursorYOffset);
}
public addMarker(cursorYOffset: number): IMarker | undefined {
return this.registerMarker(cursorYOffset);
}
public hasSelection(): boolean {
return this._core.hasSelection();
}
public select(column: number, row: number, length: number): void {
this._verifyIntegers(column, row, length);
this._core.select(column, row, length);
}
public getSelection(): string {
return this._core.getSelection();
}
public getSelectionPosition(): ISelectionPosition | undefined {
return this._core.getSelectionPosition();
}
public clearSelection(): void {
this._core.clearSelection();
}
public selectAll(): void {
this._core.selectAll();
}
public selectLines(start: number, end: number): void {
this._verifyIntegers(start, end);
this._core.selectLines(start, end);
}
public dispose(): void {
this._addonManager.dispose();
this._core.dispose();
}
public scrollLines(amount: number): void {
this._verifyIntegers(amount);
this._core.scrollLines(amount);
}
public scrollPages(pageCount: number): void {
this._verifyIntegers(pageCount);
this._core.scrollPages(pageCount);
}
public scrollToTop(): void {
this._core.scrollToTop();
}
public scrollToBottom(): void {
this._core.scrollToBottom();
}
public scrollToLine(line: number): void {
this._verifyIntegers(line);
this._core.scrollToLine(line);
}
public clear(): void {
this._core.clear();
}
public write(data: string | Uint8Array, callback?: () => void): void {
this._core.write(data, callback);
}
public writeUtf8(data: Uint8Array, callback?: () => void): void {
this._core.write(data, callback);
}
public writeln(data: string | Uint8Array, callback?: () => void): void {
this._core.write(data);
this._core.write('\r\n', callback);
}
public paste(data: string): void {
this._core.paste(data);
}
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' | 'visualBell'): boolean;
public getOption(key: 'cols' | 'fontSize' | 'letterSpacing' | 'lineHeight' | 'rows' | 'tabStopWidth' | 'scrollback'): number;
public getOption(key: string): any;
public getOption(key: any): any {
return this._core.optionsService.getOption(key);
}
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' | 'disableStdin' | 'macOptionIsMeta' | 'rightClickSelectsWord' | 'popOnBell' | 'visualBell', value: boolean): void;
public setOption(key: 'fontSize' | 'letterSpacing' | 'lineHeight' | 'tabStopWidth' | 'scrollback', value: number): void;
public setOption(key: 'theme', value: ITheme): void;
public setOption(key: 'cols' | 'rows', value: number): void;
public setOption(key: string, value: any): void;
public setOption(key: any, value: any): void {
this._core.optionsService.setOption(key, value);
}
public refresh(start: number, end: number): void {
this._verifyIntegers(start, end);
this._core.refresh(start, end);
}
public reset(): void {
this._core.reset();
}
public loadAddon(addon: ITerminalAddon): void {
return this._addonManager.loadAddon(this, addon);
}
public static get strings(): ILocalizableStrings {
return Strings;
}
private _verifyIntegers(...values: number[]): void {
values.forEach(value => {
if (value === Infinity || isNaN(value) || value % 1 !== 0) {
throw new Error('This API only accepts integers');
}
});
}
}
class BufferApiView implements IBufferApi {
constructor(
private _buffer: IBuffer,
public readonly type: 'normal' | 'alternate'
) { }
public init(buffer: IBuffer): BufferApiView {
this._buffer = buffer;
return this;
}
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);
}
public getNullCell(): IBufferCellApi { return new CellData(); }
}
class BufferNamespaceApi implements IBufferNamespaceApi {
private _normal: BufferApiView;
private _alternate: BufferApiView;
private _onBufferChange = new EventEmitter<IBufferApi>();
public get onBufferChange(): IEvent<IBufferApi> { return this._onBufferChange.event; }
constructor(private _buffers: IBufferSet) {
this._normal = new BufferApiView(this._buffers.normal, 'normal');
this._alternate = new BufferApiView(this._buffers.alt, 'alternate');
this._buffers.onBufferActivate(() => this._onBufferChange.fire(this.active));
}
public get active(): IBufferApi {
if (this._buffers.active === this._buffers.normal) { return this.normal; }
if (this._buffers.active === this._buffers.alt) { return this.alternate; }
throw new Error('Active buffer is neither normal nor alternate');
}
public get normal(): IBufferApi {
return this._normal.init(this._buffers.normal);
}
public get alternate(): IBufferApi {
return this._alternate.init(this._buffers.alt);
}
}
class BufferLineApiView implements IBufferLineApi {
constructor(private _line: IBufferLine) { }
public get isWrapped(): boolean { return this._line.isWrapped; }
public get length(): number { return this._line.length; }
public getCell(x: number, cell?: IBufferCellApi): IBufferCellApi | undefined {
if (x < 0 || x >= this._line.length) {
return undefined;
}
if (cell) {
this._line.loadCell(x, <ICellData>cell);
return cell;
}
return this._line.loadCell(x, new CellData());
}
public translateToString(trimRight?: boolean, startColumn?: number, endColumn?: number): string {
return this._line.translateToString(trimRight, startColumn, endColumn);
}
}
class ParserApi implements IParser {
constructor(private _core: ITerminal) { }
public registerCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable {
return this._core.addCsiHandler(id, (params: IParams) => callback(params.toArray()));
}
public addCsiHandler(id: IFunctionIdentifier, callback: (params: (number | number[])[]) => boolean): IDisposable {
return this.registerCsiHandler(id, callback);
}
public registerDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean): IDisposable {
return this._core.addDcsHandler(id, (data: string, params: IParams) => callback(data, params.toArray()));
}
public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: (number | number[])[]) => boolean): IDisposable {
return this.registerDcsHandler(id, callback);
}
public registerEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable {
return this._core.addEscHandler(id, handler);
}
public addEscHandler(id: IFunctionIdentifier, handler: () => boolean): IDisposable {
return this.registerEscHandler(id, handler);
}
public registerOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
return this._core.addOscHandler(ident, callback);
}
public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
return this.registerOscHandler(ident, callback);
}
}
class UnicodeApi implements IUnicodeHandling {
constructor(private _core: ITerminal) { }
public register(provider: IUnicodeVersionProvider): void {
this._core.unicodeService.register(provider);
}
public get versions(): string[] {
return this._core.unicodeService.versions;
}
public get activeVersion(): string {
return this._core.unicodeService.activeVersion;
}
public set activeVersion(version: string) {
this._core.unicodeService.activeVersion = version;
}
}
+1 -1
View File
@@ -8,7 +8,7 @@ import { IEvent, EventEmitter } from 'common/EventEmitter';
import { ICharSizeService } from 'browser/services/Services';
export class CharSizeService implements ICharSizeService {
public serviceBrand: any;
public serviceBrand: undefined;
public width: number = 0;
public height: number = 0;
+1 -1
View File
@@ -6,7 +6,7 @@
import { ICoreBrowserService } from './Services';
export class CoreBrowserService implements ICoreBrowserService {
public serviceBrand: any;
public serviceBrand: undefined;
constructor(
private _textarea: HTMLTextAreaElement
+1 -1
View File
@@ -7,7 +7,7 @@ import { ICharSizeService, IRenderService, IMouseService } from './Services';
import { getCoords, getRawByteCoords } from 'browser/input/Mouse';
export class MouseService implements IMouseService {
public serviceBrand: any;
public serviceBrand: undefined;
constructor(
@IRenderService private readonly _renderService: IRenderService,
+5 -3
View File
@@ -10,7 +10,7 @@ import { Disposable } from 'common/Lifecycle';
import { ScreenDprMonitor } from 'browser/ScreenDprMonitor';
import { addDisposableDomListener } from 'browser/Lifecycle';
import { IColorSet } from 'browser/Types';
import { IOptionsService } from 'common/services/Services';
import { IOptionsService, IBufferService } from 'common/services/Services';
import { ICharSizeService, IRenderService } from 'browser/services/Services';
interface ISelectionState {
@@ -20,7 +20,7 @@ interface ISelectionState {
}
export class RenderService extends Disposable implements IRenderService {
public serviceBrand: any;
public serviceBrand: undefined;
private _renderDebouncer: RenderDebouncer;
private _screenDprMonitor: ScreenDprMonitor;
@@ -51,7 +51,8 @@ export class RenderService extends Disposable implements IRenderService {
private _rowCount: number,
screenElement: HTMLElement,
@IOptionsService optionsService: IOptionsService,
@ICharSizeService charSizeService: ICharSizeService
@ICharSizeService charSizeService: ICharSizeService,
@IBufferService private readonly _bufferService: IBufferService
) {
super();
this._renderDebouncer = new RenderDebouncer((start, end) => this._renderRows(start, end));
@@ -61,6 +62,7 @@ export class RenderService extends Disposable implements IRenderService {
this._screenDprMonitor.setListener(() => this.onDevicePixelRatioChange());
this.register(this._screenDprMonitor);
this.register(this._bufferService.onResize(e => this._fullRefresh()));
this.register(optionsService.onOptionChange(() => this._renderer.onOptionsChanged()));
this.register(charSizeService.onCharSizeChange(() => this.onCharSizeChanged()));
+1 -1
View File
@@ -67,7 +67,7 @@ export const enum SelectionMode {
* when the selection is ready to be redrawn (on an animation frame).
*/
export class SelectionService implements ISelectionService {
public serviceBrand: any;
public serviceBrand: undefined;
protected _model: SelectionModel;
+6 -6
View File
@@ -12,7 +12,7 @@ import { IDisposable } from 'common/Types';
export const ICharSizeService = createDecorator<ICharSizeService>('CharSizeService');
export interface ICharSizeService {
serviceBrand: any;
serviceBrand: undefined;
readonly width: number;
readonly height: number;
@@ -25,14 +25,14 @@ export interface ICharSizeService {
export const ICoreBrowserService = createDecorator<ICoreBrowserService>('CoreBrowserService');
export interface ICoreBrowserService {
serviceBrand: any;
serviceBrand: undefined;
readonly isFocused: boolean;
}
export const IMouseService = createDecorator<IMouseService>('MouseService');
export interface IMouseService {
serviceBrand: any;
serviceBrand: undefined;
getCoords(event: {clientX: number, clientY: number}, element: HTMLElement, colCount: number, rowCount: number, isSelection?: boolean): [number, number] | undefined;
getRawByteCoords(event: MouseEvent, element: HTMLElement, colCount: number, rowCount: number): { x: number, y: number } | undefined;
@@ -40,7 +40,7 @@ export interface IMouseService {
export const IRenderService = createDecorator<IRenderService>('RenderService');
export interface IRenderService extends IDisposable {
serviceBrand: any;
serviceBrand: undefined;
onDimensionsChange: IEvent<IRenderDimensions>;
/**
@@ -72,7 +72,7 @@ export interface IRenderService extends IDisposable {
export const ISelectionService = createDecorator<ISelectionService>('SelectionService');
export interface ISelectionService {
serviceBrand: any;
serviceBrand: undefined;
readonly selectionText: string;
readonly hasSelection: boolean;
@@ -100,7 +100,7 @@ export interface ISelectionService {
export const ISoundService = createDecorator<ISoundService>('SoundService');
export interface ISoundService {
serviceBrand: any;
serviceBrand: undefined;
playBellSound(): void;
}
+1 -1
View File
@@ -7,7 +7,7 @@ import { IOptionsService } from 'common/services/Services';
import { ISoundService } from 'browser/services/Services';
export class SoundService implements ISoundService {
public serviceBrand: any;
public serviceBrand: undefined;
private static _audioContext: AudioContext;
+4 -1
View File
@@ -14,7 +14,10 @@
"common/*": [ "./common/*" ]
}
},
"include": [ "./**/*" ],
"include": [
"./**/*",
"../../typings/xterm.d.ts"
],
"references": [
{ "path": "../common" }
]