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
-360
View File
@@ -1,360 +0,0 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { IRenderer, IRenderDimensions, CharacterJoinerHandler, IRequestRedrawEvent } from 'browser/renderer/Types';
import { IInputHandlingTerminal, ICompositionHelper, ITerminal, IBrowser, ITerminalOptions } from './Types';
import { IBuffer, IBufferStringIterator, IBufferSet } from 'common/buffer/Types';
import { IBufferLine, ICellData, IAttributeData, ICircularList, XtermListener, ICharset } from 'common/Types';
import { Buffer } from 'common/buffer/Buffer';
import * as Browser from 'common/Platform';
import { IDisposable, IMarker, IEvent, ISelectionPosition, ILinkProvider } from 'xterm';
import { Terminal } from './Terminal';
import { AttributeData } from 'common/buffer/AttributeData';
import { IColorManager, IColorSet, ILinkMatcherOptions, ILinkifier, IViewport, ILinkifier2 } from 'browser/Types';
import { IOptionsService, IUnicodeService } from 'common/services/Services';
import { EventEmitter } from 'common/EventEmitter';
import { IParams, IFunctionIdentifier } from 'common/parser/Types';
import { ISelectionService } from 'browser/services/Services';
export class TestTerminal extends Terminal {
public get curAttrData(): IAttributeData { return (this as any)._inputHandler._curAttrData; }
public keyDown(ev: any): boolean { 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, suppressScrollEvent: boolean): 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 showCursor(): 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 MockInputHandlingTerminal implements IInputHandlingTerminal {
public onA11yCharEmitter: EventEmitter<string>;
public onA11yTabEmitter: EventEmitter<number>;
public insertMode: boolean;
public bracketedPasteMode: boolean;
public sendFocus: boolean;
public buffers: IBufferSet;
public buffer: IBuffer = new MockBuffer();
public viewport: IViewport;
public scroll(eraseAttr: IAttributeData, isWrapped?: boolean): void {
throw new Error('Method not implemented.');
}
public is(term: string): boolean {
throw new Error('Method not implemented.');
}
public resize(x: number, y: number): void {
throw new Error('Method not implemented.');
}
public showCursor(): void {
throw new Error('Method not implemented.');
}
public handler(data: string): void {
throw new Error('Method not implemented.');
}
public handleTitle(title: string): void {
throw new Error('Method not implemented.');
}
}
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 | null;
public savedCurAttrData = new AttributeData();
public translateBufferLineToString(lineIndex: number, trimRight: boolean, startCol?: number, endCol?: number): string {
return Buffer.prototype.translateBufferLineToString.apply(this, arguments);
}
public getWrappedRangeForLine(y: number): { first: number, last: number } {
return Buffer.prototype.getWrappedRangeForLine.apply(this, arguments);
}
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);
}
public stringIndexToBufferIndex(lineIndex: number, stringIndex: number): number[] {
return Buffer.prototype.stringIndexToBufferIndex.apply(this, arguments);
}
public iterator(trimRight: boolean, startIndex?: number, endIndex?: number): IBufferStringIterator {
return Buffer.prototype.iterator.apply(this, arguments);
}
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;
}
}
-236
View File
@@ -1,236 +0,0 @@
/**
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
* @license MIT
*/
import { ITerminalOptions as IPublicTerminalOptions, IDisposable, IMarker, ISelectionPosition, ILinkProvider } from 'xterm';
import { ICharset, IAttributeData, CharData, CoreMouseEventType } from 'common/Types';
import { IEvent, IEventEmitter } from 'common/EventEmitter';
import { IColorSet, ILinkifier, ILinkMatcherOptions, IViewport, ILinkifier2 } from 'browser/Types';
import { IOptionsService, IUnicodeService } from 'common/services/Services';
import { IBuffer, IBufferSet } from 'common/buffer/Types';
import { IParams, IFunctionIdentifier } from 'common/parser/Types';
export type CustomKeyEventHandler = (event: KeyboardEvent) => boolean;
export type LineData = CharData[];
/**
* This interface encapsulates everything needed from the Terminal by the
* InputHandler. This cleanly separates the large amount of methods needed by
* InputHandler cleanly from the ITerminal interface.
*/
export interface IInputHandlingTerminal {
insertMode: boolean;
bracketedPasteMode: boolean;
sendFocus: boolean;
buffers: IBufferSet;
buffer: IBuffer;
viewport: IViewport;
onA11yCharEmitter: IEventEmitter<string>;
onA11yTabEmitter: IEventEmitter<number>;
scroll(eraseAttr: IAttributeData, isWrapped?: boolean): void;
is(term: string): boolean;
resize(x: number, y: number): void;
showCursor(): void;
handleTitle(title: string): void;
}
export interface ICompositionHelper {
compositionstart(): void;
compositionupdate(ev: CompositionEvent): void;
compositionend(): void;
updateCompositionElements(dontRecurse?: boolean): void;
keydown(ev: KeyboardEvent): boolean;
}
/**
* Calls the parser and handles actions generated by the parser.
*/
export interface IInputHandler {
parse(data: string | Uint8Array): void;
print(data: Uint32Array, start: number, end: number): void;
/** C0 BEL */ bell(): void;
/** C0 LF */ lineFeed(): void;
/** C0 CR */ carriageReturn(): void;
/** C0 BS */ backspace(): void;
/** C0 HT */ tab(): void;
/** C0 SO */ shiftOut(): void;
/** C0 SI */ shiftIn(): void;
/** CSI @ */ insertChars(params: IParams): void;
/** CSI SP @ */ scrollLeft(params: IParams): void;
/** CSI A */ cursorUp(params: IParams): void;
/** CSI SP A */ scrollRight(params: IParams): void;
/** CSI B */ cursorDown(params: IParams): void;
/** CSI C */ cursorForward(params: IParams): void;
/** CSI D */ cursorBackward(params: IParams): void;
/** CSI E */ cursorNextLine(params: IParams): void;
/** CSI F */ cursorPrecedingLine(params: IParams): void;
/** CSI G */ cursorCharAbsolute(params: IParams): void;
/** CSI H */ cursorPosition(params: IParams): void;
/** CSI I */ cursorForwardTab(params: IParams): void;
/** CSI J */ eraseInDisplay(params: IParams): void;
/** CSI K */ eraseInLine(params: IParams): void;
/** CSI L */ insertLines(params: IParams): void;
/** CSI M */ deleteLines(params: IParams): void;
/** CSI P */ deleteChars(params: IParams): void;
/** CSI S */ scrollUp(params: IParams): void;
/** CSI T */ scrollDown(params: IParams, collect?: string): void;
/** CSI X */ eraseChars(params: IParams): void;
/** CSI Z */ cursorBackwardTab(params: IParams): void;
/** CSI ` */ charPosAbsolute(params: IParams): void;
/** CSI a */ hPositionRelative(params: IParams): void;
/** CSI b */ repeatPrecedingCharacter(params: IParams): void;
/** CSI c */ sendDeviceAttributesPrimary(params: IParams): void;
/** CSI > c */ sendDeviceAttributesSecondary(params: IParams): void;
/** CSI d */ linePosAbsolute(params: IParams): void;
/** CSI e */ vPositionRelative(params: IParams): void;
/** CSI f */ hVPosition(params: IParams): void;
/** CSI g */ tabClear(params: IParams): void;
/** CSI h */ setMode(params: IParams, collect?: string): void;
/** CSI l */ resetMode(params: IParams, collect?: string): void;
/** CSI m */ charAttributes(params: IParams): void;
/** CSI n */ deviceStatus(params: IParams, collect?: string): void;
/** CSI p */ softReset(params: IParams, collect?: string): void;
/** CSI q */ setCursorStyle(params: IParams, collect?: string): void;
/** CSI r */ setScrollRegion(params: IParams, collect?: string): void;
/** CSI s */ saveCursor(params: IParams): void;
/** CSI u */ restoreCursor(params: IParams): void;
/** CSI ' } */ insertColumns(params: IParams): void;
/** CSI ' ~ */ deleteColumns(params: IParams): void;
/** OSC 0
OSC 2 */ setTitle(data: string): void;
/** ESC E */ nextLine(): void;
/** ESC = */ keypadApplicationMode(): void;
/** ESC > */ keypadNumericMode(): void;
/** ESC % G
ESC % @ */ selectDefaultCharset(): void;
/** ESC ( C
ESC ) C
ESC * C
ESC + C
ESC - C
ESC . C
ESC / C */ selectCharset(collectAndFlag: string): void;
/** ESC D */ index(): void;
/** ESC H */ tabSet(): void;
/** ESC M */ reverseIndex(): void;
/** ESC c */ fullReset(): void;
/** ESC n
ESC o
ESC |
ESC }
ESC ~ */ setgLevel(level: number): void;
/** ESC # 8 */ screenAlignmentPattern(): void;
}
export interface ITerminal extends IPublicTerminal, IElementAccessor, IBufferAccessor, ILinkifierAccessor {
screenElement: HTMLElement;
browser: IBrowser;
buffer: IBuffer;
buffers: IBufferSet;
viewport: IViewport;
bracketedPasteMode: boolean;
optionsService: IOptionsService;
// TODO: We should remove options once components adopt optionsService
options: ITerminalOptions;
unicodeService: IUnicodeService;
onBlur: IEvent<void>;
onFocus: IEvent<void>;
onA11yChar: IEvent<string>;
onA11yTab: IEvent<number>;
scrollLines(disp: number, suppressScrollEvent?: boolean): void;
cancel(ev: Event, force?: boolean): boolean | void;
showCursor(): 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;
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 interface IBufferAccessor {
buffer: IBuffer;
}
export interface IElementAccessor {
readonly element: HTMLElement | undefined;
}
export interface ILinkifierAccessor {
linkifier: ILinkifier;
linkifier2: ILinkifier2;
}
// TODO: The options that are not in the public API should be reviewed
export interface ITerminalOptions extends IPublicTerminalOptions {
[key: string]: any;
cancelEvents?: boolean;
convertEol?: boolean;
termName?: string;
}
export interface IBrowser {
isNode: boolean;
userAgent: string;
platform: string;
isFirefox: boolean;
isMac: boolean;
isIpad: boolean;
isIphone: boolean;
isWindows: boolean;
}
@@ -3,8 +3,8 @@
* @license MIT
*/
import * as Strings from './browser/LocalizableStrings';
import { ITerminal } from './Types';
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';
@@ -79,6 +79,9 @@ export class AccessibilityManager extends Disposable {
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);
@@ -103,7 +106,7 @@ export class AccessibilityManager extends Disposable {
public dispose(): void {
super.dispose();
this._terminal.element.removeChild(this._accessibilityTreeRoot);
this._terminal.element?.removeChild(this._accessibilityTreeRoot);
this._rowElements.length = 0;
}
+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;
@@ -4,7 +4,7 @@
*/
import { assert, expect } from 'chai';
import { MockViewport, MockCompositionHelper, MockRenderer, TestTerminal } from './TestUtils.test';
import { MockViewport, MockCompositionHelper, MockRenderer, TestTerminal } from 'browser/TestUtils.test';
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { CellData } from 'common/buffer/CellData';
import { IBufferService, IUnicodeService } from 'common/services/Services';
@@ -121,7 +121,7 @@ describe('Terminal', () => {
assert.equal(e, 'title');
done();
});
term.handleTitle('title');
term.write('\x1b]2;title\x07');
});
});
@@ -140,7 +140,6 @@ describe('Terminal', () => {
};
beforeEach(() => {
term.showCursor = () => { };
term.clearSelection = () => { };
});
@@ -396,62 +395,62 @@ describe('Terminal', () => {
describe('scroll() function', () => {
describe('when scrollback > 0', () => {
it('should create a new line and scroll', () => {
term.buffer.lines.get(0).setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
term.buffer.lines.get(INIT_ROWS - 1).setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
term.buffer.lines.get(0)!.setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
term.buffer.lines.get(INIT_ROWS - 1)!.setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
term.buffer.y = INIT_ROWS - 1; // Move cursor to last line
term.scroll(DEFAULT_ATTR_DATA.clone());
assert.equal(term.buffer.lines.length, INIT_ROWS + 1);
assert.equal(term.buffer.lines.get(0).loadCell(0, new CellData()).getChars(), 'a');
assert.equal(term.buffer.lines.get(INIT_ROWS - 1).loadCell(0, new CellData()).getChars(), 'b');
assert.equal(term.buffer.lines.get(INIT_ROWS).loadCell(0, new CellData()).getChars(), '');
assert.equal(term.buffer.lines.get(0)!.loadCell(0, new CellData()).getChars(), 'a');
assert.equal(term.buffer.lines.get(INIT_ROWS - 1)!.loadCell(0, new CellData()).getChars(), 'b');
assert.equal(term.buffer.lines.get(INIT_ROWS)!.loadCell(0, new CellData()).getChars(), '');
});
it('should properly scroll inside a scroll region (scrollTop set)', () => {
term.buffer.lines.get(0).setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
term.buffer.lines.get(1).setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
term.buffer.lines.get(2).setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
term.buffer.lines.get(0)!.setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
term.buffer.lines.get(1)!.setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
term.buffer.lines.get(2)!.setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
term.buffer.y = INIT_ROWS - 1; // Move cursor to last line
term.buffer.scrollTop = 1;
term.scroll(DEFAULT_ATTR_DATA.clone());
assert.equal(term.buffer.lines.length, INIT_ROWS);
assert.equal(term.buffer.lines.get(0).loadCell(0, new CellData()).getChars(), 'a');
assert.equal(term.buffer.lines.get(1).loadCell(0, new CellData()).getChars(), 'c');
assert.equal(term.buffer.lines.get(0)!.loadCell(0, new CellData()).getChars(), 'a');
assert.equal(term.buffer.lines.get(1)!.loadCell(0, new CellData()).getChars(), 'c');
});
it('should properly scroll inside a scroll region (scrollBottom set)', () => {
term.buffer.lines.get(0).setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
term.buffer.lines.get(1).setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
term.buffer.lines.get(2).setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
term.buffer.lines.get(3).setCell(0, CellData.fromCharData([0, 'd', 0, 'd'.charCodeAt(0)]));
term.buffer.lines.get(4).setCell(0, CellData.fromCharData([0, 'e', 0, 'e'.charCodeAt(0)]));
term.buffer.lines.get(0)!.setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
term.buffer.lines.get(1)!.setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
term.buffer.lines.get(2)!.setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
term.buffer.lines.get(3)!.setCell(0, CellData.fromCharData([0, 'd', 0, 'd'.charCodeAt(0)]));
term.buffer.lines.get(4)!.setCell(0, CellData.fromCharData([0, 'e', 0, 'e'.charCodeAt(0)]));
term.buffer.y = 3;
term.buffer.scrollBottom = 3;
term.scroll(DEFAULT_ATTR_DATA.clone());
assert.equal(term.buffer.lines.length, INIT_ROWS + 1);
assert.equal(term.buffer.lines.get(0).loadCell(0, new CellData()).getChars(), 'a', '\'a\' should be pushed to the scrollback');
assert.equal(term.buffer.lines.get(1).loadCell(0, new CellData()).getChars(), 'b');
assert.equal(term.buffer.lines.get(2).loadCell(0, new CellData()).getChars(), 'c');
assert.equal(term.buffer.lines.get(3).loadCell(0, new CellData()).getChars(), 'd');
assert.equal(term.buffer.lines.get(4).loadCell(0, new CellData()).getChars(), '', 'a blank line should be added at scrollBottom\'s index');
assert.equal(term.buffer.lines.get(5).loadCell(0, new CellData()).getChars(), 'e');
assert.equal(term.buffer.lines.get(0)!.loadCell(0, new CellData()).getChars(), 'a', '\'a\' should be pushed to the scrollback');
assert.equal(term.buffer.lines.get(1)!.loadCell(0, new CellData()).getChars(), 'b');
assert.equal(term.buffer.lines.get(2)!.loadCell(0, new CellData()).getChars(), 'c');
assert.equal(term.buffer.lines.get(3)!.loadCell(0, new CellData()).getChars(), 'd');
assert.equal(term.buffer.lines.get(4)!.loadCell(0, new CellData()).getChars(), '', 'a blank line should be added at scrollBottom\'s index');
assert.equal(term.buffer.lines.get(5)!.loadCell(0, new CellData()).getChars(), 'e');
});
it('should properly scroll inside a scroll region (scrollTop and scrollBottom set)', () => {
term.buffer.lines.get(0).setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
term.buffer.lines.get(1).setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
term.buffer.lines.get(2).setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
term.buffer.lines.get(3).setCell(0, CellData.fromCharData([0, 'd', 0, 'd'.charCodeAt(0)]));
term.buffer.lines.get(4).setCell(0, CellData.fromCharData([0, 'e', 0, 'e'.charCodeAt(0)]));
term.buffer.lines.get(0)!.setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
term.buffer.lines.get(1)!.setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
term.buffer.lines.get(2)!.setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
term.buffer.lines.get(3)!.setCell(0, CellData.fromCharData([0, 'd', 0, 'd'.charCodeAt(0)]));
term.buffer.lines.get(4)!.setCell(0, CellData.fromCharData([0, 'e', 0, 'e'.charCodeAt(0)]));
term.buffer.y = INIT_ROWS - 1; // Move cursor to last line
term.buffer.scrollTop = 1;
term.buffer.scrollBottom = 3;
term.scroll(DEFAULT_ATTR_DATA.clone());
assert.equal(term.buffer.lines.length, INIT_ROWS);
assert.equal(term.buffer.lines.get(0).loadCell(0, new CellData()).getChars(), 'a');
assert.equal(term.buffer.lines.get(1).loadCell(0, new CellData()).getChars(), 'c', '\'b\' should be removed from the buffer');
assert.equal(term.buffer.lines.get(2).loadCell(0, new CellData()).getChars(), 'd');
assert.equal(term.buffer.lines.get(3).loadCell(0, new CellData()).getChars(), '', 'a blank line should be added at scrollBottom\'s index');
assert.equal(term.buffer.lines.get(4).loadCell(0, new CellData()).getChars(), 'e');
assert.equal(term.buffer.lines.get(0)!.loadCell(0, new CellData()).getChars(), 'a');
assert.equal(term.buffer.lines.get(1)!.loadCell(0, new CellData()).getChars(), 'c', '\'b\' should be removed from the buffer');
assert.equal(term.buffer.lines.get(2)!.loadCell(0, new CellData()).getChars(), 'd');
assert.equal(term.buffer.lines.get(3)!.loadCell(0, new CellData()).getChars(), '', 'a blank line should be added at scrollBottom\'s index');
assert.equal(term.buffer.lines.get(4)!.loadCell(0, new CellData()).getChars(), 'e');
});
});
@@ -462,65 +461,65 @@ describe('Terminal', () => {
});
it('should create a new line and shift everything up', () => {
term.buffer.lines.get(0).setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
term.buffer.lines.get(1).setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
term.buffer.lines.get(INIT_ROWS - 1).setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
term.buffer.lines.get(0)!.setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
term.buffer.lines.get(1)!.setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
term.buffer.lines.get(INIT_ROWS - 1)!.setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
term.buffer.y = INIT_ROWS - 1; // Move cursor to last line
assert.equal(term.buffer.lines.length, INIT_ROWS);
term.scroll(DEFAULT_ATTR_DATA.clone());
assert.equal(term.buffer.lines.length, INIT_ROWS);
// 'a' gets pushed out of buffer
assert.equal(term.buffer.lines.get(0).loadCell(0, new CellData()).getChars(), 'b');
assert.equal(term.buffer.lines.get(1).loadCell(0, new CellData()).getChars(), '');
assert.equal(term.buffer.lines.get(INIT_ROWS - 2).loadCell(0, new CellData()).getChars(), 'c');
assert.equal(term.buffer.lines.get(INIT_ROWS - 1).loadCell(0, new CellData()).getChars(), '');
assert.equal(term.buffer.lines.get(0)!.loadCell(0, new CellData()).getChars(), 'b');
assert.equal(term.buffer.lines.get(1)!.loadCell(0, new CellData()).getChars(), '');
assert.equal(term.buffer.lines.get(INIT_ROWS - 2)!.loadCell(0, new CellData()).getChars(), 'c');
assert.equal(term.buffer.lines.get(INIT_ROWS - 1)!.loadCell(0, new CellData()).getChars(), '');
});
it('should properly scroll inside a scroll region (scrollTop set)', () => {
term.buffer.lines.get(0).setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
term.buffer.lines.get(1).setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
term.buffer.lines.get(2).setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
term.buffer.lines.get(0)!.setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
term.buffer.lines.get(1)!.setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
term.buffer.lines.get(2)!.setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
term.buffer.y = INIT_ROWS - 1; // Move cursor to last line
term.buffer.scrollTop = 1;
term.scroll(DEFAULT_ATTR_DATA.clone());
assert.equal(term.buffer.lines.length, INIT_ROWS);
assert.equal(term.buffer.lines.get(0).loadCell(0, new CellData()).getChars(), 'a');
assert.equal(term.buffer.lines.get(1).loadCell(0, new CellData()).getChars(), 'c');
assert.equal(term.buffer.lines.get(0)!.loadCell(0, new CellData()).getChars(), 'a');
assert.equal(term.buffer.lines.get(1)!.loadCell(0, new CellData()).getChars(), 'c');
});
it('should properly scroll inside a scroll region (scrollBottom set)', () => {
term.buffer.lines.get(0).setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
term.buffer.lines.get(1).setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
term.buffer.lines.get(2).setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
term.buffer.lines.get(3).setCell(0, CellData.fromCharData([0, 'd', 0, 'd'.charCodeAt(0)]));
term.buffer.lines.get(4).setCell(0, CellData.fromCharData([0, 'e', 0, 'e'.charCodeAt(0)]));
term.buffer.lines.get(0)!.setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
term.buffer.lines.get(1)!.setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
term.buffer.lines.get(2)!.setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
term.buffer.lines.get(3)!.setCell(0, CellData.fromCharData([0, 'd', 0, 'd'.charCodeAt(0)]));
term.buffer.lines.get(4)!.setCell(0, CellData.fromCharData([0, 'e', 0, 'e'.charCodeAt(0)]));
term.buffer.y = 3;
term.buffer.scrollBottom = 3;
term.scroll(DEFAULT_ATTR_DATA.clone());
assert.equal(term.buffer.lines.length, INIT_ROWS);
assert.equal(term.buffer.lines.get(0).loadCell(0, new CellData()).getChars(), 'b');
assert.equal(term.buffer.lines.get(1).loadCell(0, new CellData()).getChars(), 'c');
assert.equal(term.buffer.lines.get(2).loadCell(0, new CellData()).getChars(), 'd');
assert.equal(term.buffer.lines.get(3).loadCell(0, new CellData()).getChars(), '', 'a blank line should be added at scrollBottom\'s index');
assert.equal(term.buffer.lines.get(4).loadCell(0, new CellData()).getChars(), 'e');
assert.equal(term.buffer.lines.get(0)!.loadCell(0, new CellData()).getChars(), 'b');
assert.equal(term.buffer.lines.get(1)!.loadCell(0, new CellData()).getChars(), 'c');
assert.equal(term.buffer.lines.get(2)!.loadCell(0, new CellData()).getChars(), 'd');
assert.equal(term.buffer.lines.get(3)!.loadCell(0, new CellData()).getChars(), '', 'a blank line should be added at scrollBottom\'s index');
assert.equal(term.buffer.lines.get(4)!.loadCell(0, new CellData()).getChars(), 'e');
});
it('should properly scroll inside a scroll region (scrollTop and scrollBottom set)', () => {
term.buffer.lines.get(0).setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
term.buffer.lines.get(1).setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
term.buffer.lines.get(2).setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
term.buffer.lines.get(3).setCell(0, CellData.fromCharData([0, 'd', 0, 'd'.charCodeAt(0)]));
term.buffer.lines.get(4).setCell(0, CellData.fromCharData([0, 'e', 0, 'e'.charCodeAt(0)]));
term.buffer.lines.get(0)!.setCell(0, CellData.fromCharData([0, 'a', 0, 'a'.charCodeAt(0)]));
term.buffer.lines.get(1)!.setCell(0, CellData.fromCharData([0, 'b', 0, 'b'.charCodeAt(0)]));
term.buffer.lines.get(2)!.setCell(0, CellData.fromCharData([0, 'c', 0, 'c'.charCodeAt(0)]));
term.buffer.lines.get(3)!.setCell(0, CellData.fromCharData([0, 'd', 0, 'd'.charCodeAt(0)]));
term.buffer.lines.get(4)!.setCell(0, CellData.fromCharData([0, 'e', 0, 'e'.charCodeAt(0)]));
term.buffer.y = INIT_ROWS - 1; // Move cursor to last line
term.buffer.scrollTop = 1;
term.buffer.scrollBottom = 3;
term.scroll(DEFAULT_ATTR_DATA.clone());
assert.equal(term.buffer.lines.length, INIT_ROWS);
assert.equal(term.buffer.lines.get(0).loadCell(0, new CellData()).getChars(), 'a');
assert.equal(term.buffer.lines.get(1).loadCell(0, new CellData()).getChars(), 'c', '\'b\' should be removed from the buffer');
assert.equal(term.buffer.lines.get(2).loadCell(0, new CellData()).getChars(), 'd');
assert.equal(term.buffer.lines.get(3).loadCell(0, new CellData()).getChars(), '', 'a blank line should be added at scrollBottom\'s index');
assert.equal(term.buffer.lines.get(4).loadCell(0, new CellData()).getChars(), 'e');
assert.equal(term.buffer.lines.get(0)!.loadCell(0, new CellData()).getChars(), 'a');
assert.equal(term.buffer.lines.get(1)!.loadCell(0, new CellData()).getChars(), 'c', '\'b\' should be removed from the buffer');
assert.equal(term.buffer.lines.get(2)!.loadCell(0, new CellData()).getChars(), 'd');
assert.equal(term.buffer.lines.get(3)!.loadCell(0, new CellData()).getChars(), '', 'a blank line should be added at scrollBottom\'s index');
assert.equal(term.buffer.lines.get(4)!.loadCell(0, new CellData()).getChars(), 'e');
});
});
});
@@ -531,7 +530,6 @@ describe('Terminal', () => {
let evKeyPress: any;
beforeEach(() => {
term.showCursor = () => { };
term.clearSelection = () => { };
// term.compositionHelper = {
// isComposing: false,
@@ -724,11 +722,11 @@ describe('Terminal', () => {
const cell = new CellData();
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
term.writeSync(high + String.fromCharCode(i));
const tchar = term.buffer.lines.get(0).loadCell(0, cell);
const tchar = term.buffer.lines.get(0)!.loadCell(0, cell);
expect(tchar.getChars()).eql(high + String.fromCharCode(i));
expect(tchar.getChars().length).eql(2);
expect(tchar.getWidth()).eql(1);
expect(term.buffer.lines.get(0).loadCell(1, cell).getChars()).eql('');
expect(term.buffer.lines.get(0)!.loadCell(1, cell).getChars()).eql('');
term.reset();
}
});
@@ -738,9 +736,9 @@ describe('Terminal', () => {
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
term.buffer.x = term.cols - 1;
term.writeSync(high + String.fromCharCode(i));
expect(term.buffer.lines.get(0).loadCell(term.buffer.x - 1, cell).getChars()).eql(high + String.fromCharCode(i));
expect(term.buffer.lines.get(0).loadCell(term.buffer.x - 1, cell).getChars().length).eql(2);
expect(term.buffer.lines.get(1).loadCell(0, cell).getChars()).eql('');
expect(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars()).eql(high + String.fromCharCode(i));
expect(term.buffer.lines.get(0)!.loadCell(term.buffer.x - 1, cell).getChars().length).eql(2);
expect(term.buffer.lines.get(1)!.loadCell(0, cell).getChars()).eql('');
term.reset();
}
});
@@ -751,10 +749,10 @@ describe('Terminal', () => {
term.buffer.x = term.cols - 1;
term.writeSync('a' + high + String.fromCharCode(i));
expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars()).eql('a');
expect(term.buffer.lines.get(1).loadCell(0, cell).getChars()).eql(high + String.fromCharCode(i));
expect(term.buffer.lines.get(1).loadCell(0, cell).getChars().length).eql(2);
expect(term.buffer.lines.get(1).loadCell(1, cell).getChars()).eql('');
expect(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars()).eql('a');
expect(term.buffer.lines.get(1)!.loadCell(0, cell).getChars()).eql(high + String.fromCharCode(i));
expect(term.buffer.lines.get(1)!.loadCell(0, cell).getChars().length).eql(2);
expect(term.buffer.lines.get(1)!.loadCell(1, cell).getChars()).eql('');
term.reset();
}
});
@@ -770,9 +768,9 @@ describe('Terminal', () => {
}
term.writeSync('a' + high + String.fromCharCode(i));
// auto wraparound mode should cut off the rest of the line
expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars()).eql(high + String.fromCharCode(i));
expect(term.buffer.lines.get(0).loadCell(term.cols - 1, cell).getChars().length).eql(2);
expect(term.buffer.lines.get(1).loadCell(1, cell).getChars()).eql('');
expect(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars()).eql(high + String.fromCharCode(i));
expect(term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell).getChars().length).eql(2);
expect(term.buffer.lines.get(1)!.loadCell(1, cell).getChars()).eql('');
term.reset();
}
});
@@ -782,11 +780,11 @@ describe('Terminal', () => {
for (let i = 0xDC00; i <= 0xDCFF; ++i) {
term.writeSync(high);
term.writeSync(String.fromCharCode(i));
const tchar = term.buffer.lines.get(0).loadCell(0, cell);
const tchar = term.buffer.lines.get(0)!.loadCell(0, cell);
expect(tchar.getChars()).eql(high + String.fromCharCode(i));
expect(tchar.getChars().length).eql(2);
expect(tchar.getWidth()).eql(1);
expect(term.buffer.lines.get(0).loadCell(1, cell).getChars()).eql('');
expect(term.buffer.lines.get(0)!.loadCell(1, cell).getChars()).eql('');
term.reset();
}
});
@@ -796,7 +794,7 @@ describe('Terminal', () => {
const cell = new CellData();
it('café', () => {
term.writeSync('cafe\u0301');
term.buffer.lines.get(0).loadCell(3, cell);
term.buffer.lines.get(0)!.loadCell(3, cell);
expect(cell.getChars()).eql('e\u0301');
expect(cell.getChars().length).eql(2);
expect(cell.getWidth()).eql(1);
@@ -804,11 +802,11 @@ describe('Terminal', () => {
it('café - end of line', () => {
term.buffer.x = term.cols - 1 - 3;
term.writeSync('cafe\u0301');
term.buffer.lines.get(0).loadCell(term.cols - 1, cell);
term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell);
expect(cell.getChars()).eql('e\u0301');
expect(cell.getChars().length).eql(2);
expect(cell.getWidth()).eql(1);
term.buffer.lines.get(0).loadCell(1, cell);
term.buffer.lines.get(0)!.loadCell(1, cell);
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
expect(cell.getWidth()).eql(1);
@@ -816,12 +814,12 @@ describe('Terminal', () => {
it('multiple combined é', () => {
term.writeSync(Array(100).join('e\u0301'));
for (let i = 0; i < term.cols; ++i) {
term.buffer.lines.get(0).loadCell(i, cell);
term.buffer.lines.get(0)!.loadCell(i, cell);
expect(cell.getChars()).eql('e\u0301');
expect(cell.getChars().length).eql(2);
expect(cell.getWidth()).eql(1);
}
term.buffer.lines.get(1).loadCell(0, cell);
term.buffer.lines.get(1)!.loadCell(0, cell);
expect(cell.getChars()).eql('e\u0301');
expect(cell.getChars().length).eql(2);
expect(cell.getWidth()).eql(1);
@@ -829,12 +827,12 @@ describe('Terminal', () => {
it('multiple surrogate with combined', () => {
term.writeSync(Array(100).join('\uD800\uDC00\u0301'));
for (let i = 0; i < term.cols; ++i) {
term.buffer.lines.get(0).loadCell(i, cell);
term.buffer.lines.get(0)!.loadCell(i, cell);
expect(cell.getChars()).eql('\uD800\uDC00\u0301');
expect(cell.getChars().length).eql(3);
expect(cell.getWidth()).eql(1);
}
term.buffer.lines.get(1).loadCell(0, cell);
term.buffer.lines.get(1)!.loadCell(0, cell);
expect(cell.getChars()).eql('\uD800\uDC00\u0301');
expect(cell.getChars().length).eql(3);
expect(cell.getWidth()).eql(1);
@@ -857,7 +855,7 @@ describe('Terminal', () => {
it('line of ¥ even', () => {
term.writeSync(Array(50).join('¥'));
for (let i = 0; i < term.cols; ++i) {
term.buffer.lines.get(0).loadCell(i, cell);
term.buffer.lines.get(0)!.loadCell(i, cell);
if (i % 2) {
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
@@ -868,7 +866,7 @@ describe('Terminal', () => {
expect(cell.getWidth()).eql(2);
}
}
term.buffer.lines.get(1).loadCell(0, cell);
term.buffer.lines.get(1)!.loadCell(0, cell);
expect(cell.getChars()).eql('¥');
expect(cell.getChars().length).eql(1);
expect(cell.getWidth()).eql(2);
@@ -877,7 +875,7 @@ describe('Terminal', () => {
term.buffer.x = 1;
term.writeSync(Array(50).join('¥'));
for (let i = 1; i < term.cols - 1; ++i) {
term.buffer.lines.get(0).loadCell(i, cell);
term.buffer.lines.get(0)!.loadCell(i, cell);
if (!(i % 2)) {
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
@@ -888,11 +886,11 @@ describe('Terminal', () => {
expect(cell.getWidth()).eql(2);
}
}
term.buffer.lines.get(0).loadCell(term.cols - 1, cell);
term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell);
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
expect(cell.getWidth()).eql(1);
term.buffer.lines.get(1).loadCell(0, cell);
term.buffer.lines.get(1)!.loadCell(0, cell);
expect(cell.getChars()).eql('¥');
expect(cell.getChars().length).eql(1);
expect(cell.getWidth()).eql(2);
@@ -901,7 +899,7 @@ describe('Terminal', () => {
term.buffer.x = 1;
term.writeSync(Array(50).join('¥\u0301'));
for (let i = 1; i < term.cols - 1; ++i) {
term.buffer.lines.get(0).loadCell(i, cell);
term.buffer.lines.get(0)!.loadCell(i, cell);
if (!(i % 2)) {
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
@@ -912,11 +910,11 @@ describe('Terminal', () => {
expect(cell.getWidth()).eql(2);
}
}
term.buffer.lines.get(0).loadCell(term.cols - 1, cell);
term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell);
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
expect(cell.getWidth()).eql(1);
term.buffer.lines.get(1).loadCell(0, cell);
term.buffer.lines.get(1)!.loadCell(0, cell);
expect(cell.getChars()).eql('¥\u0301');
expect(cell.getChars().length).eql(2);
expect(cell.getWidth()).eql(2);
@@ -924,7 +922,7 @@ describe('Terminal', () => {
it('line of ¥ with combining even', () => {
term.writeSync(Array(50).join('¥\u0301'));
for (let i = 0; i < term.cols; ++i) {
term.buffer.lines.get(0).loadCell(i, cell);
term.buffer.lines.get(0)!.loadCell(i, cell);
if (i % 2) {
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
@@ -935,7 +933,7 @@ describe('Terminal', () => {
expect(cell.getWidth()).eql(2);
}
}
term.buffer.lines.get(1).loadCell(0, cell);
term.buffer.lines.get(1)!.loadCell(0, cell);
expect(cell.getChars()).eql('¥\u0301');
expect(cell.getChars().length).eql(2);
expect(cell.getWidth()).eql(2);
@@ -944,7 +942,7 @@ describe('Terminal', () => {
term.buffer.x = 1;
term.writeSync(Array(50).join('\ud843\ude6d\u0301'));
for (let i = 1; i < term.cols - 1; ++i) {
term.buffer.lines.get(0).loadCell(i, cell);
term.buffer.lines.get(0)!.loadCell(i, cell);
if (!(i % 2)) {
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
@@ -955,11 +953,11 @@ describe('Terminal', () => {
expect(cell.getWidth()).eql(2);
}
}
term.buffer.lines.get(0).loadCell(term.cols - 1, cell);
term.buffer.lines.get(0)!.loadCell(term.cols - 1, cell);
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
expect(cell.getWidth()).eql(1);
term.buffer.lines.get(1).loadCell(0, cell);
term.buffer.lines.get(1)!.loadCell(0, cell);
expect(cell.getChars()).eql('\ud843\ude6d\u0301');
expect(cell.getChars().length).eql(3);
expect(cell.getWidth()).eql(2);
@@ -967,7 +965,7 @@ describe('Terminal', () => {
it('line of surrogate fullwidth with combining even', () => {
term.writeSync(Array(50).join('\ud843\ude6d\u0301'));
for (let i = 0; i < term.cols; ++i) {
term.buffer.lines.get(0).loadCell(i, cell);
term.buffer.lines.get(0)!.loadCell(i, cell);
if (i % 2) {
expect(cell.getChars()).eql('');
expect(cell.getChars().length).eql(0);
@@ -978,7 +976,7 @@ describe('Terminal', () => {
expect(cell.getWidth()).eql(2);
}
}
term.buffer.lines.get(1).loadCell(0, cell);
term.buffer.lines.get(1)!.loadCell(0, cell);
expect(cell.getChars()).eql('\ud843\ude6d\u0301');
expect(cell.getChars().length).eql(3);
expect(cell.getWidth()).eql(2);
@@ -991,42 +989,42 @@ describe('Terminal', () => {
term.writeSync(Array(9).join('0123456789').slice(-80));
term.buffer.x = 10;
term.buffer.y = 0;
term.insertMode = true;
term.write('\x1b[4h');
term.writeSync('abcde');
expect(term.buffer.lines.get(0).length).eql(term.cols);
expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('a');
expect(term.buffer.lines.get(0).loadCell(14, cell).getChars()).eql('e');
expect(term.buffer.lines.get(0).loadCell(15, cell).getChars()).eql('0');
expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql('4');
expect(term.buffer.lines.get(0)!.length).eql(term.cols);
expect(term.buffer.lines.get(0)!.loadCell(10, cell).getChars()).eql('a');
expect(term.buffer.lines.get(0)!.loadCell(14, cell).getChars()).eql('e');
expect(term.buffer.lines.get(0)!.loadCell(15, cell).getChars()).eql('0');
expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql('4');
});
it('fullwidth - insert', () => {
term.writeSync(Array(9).join('0123456789').slice(-80));
term.buffer.x = 10;
term.buffer.y = 0;
term.insertMode = true;
term.write('\x1b[4h');
term.writeSync('¥¥¥');
expect(term.buffer.lines.get(0).length).eql(term.cols);
expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('¥');
expect(term.buffer.lines.get(0).loadCell(11, cell).getChars()).eql('');
expect(term.buffer.lines.get(0).loadCell(14, cell).getChars()).eql('¥');
expect(term.buffer.lines.get(0).loadCell(15, cell).getChars()).eql('');
expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql('3');
expect(term.buffer.lines.get(0)!.length).eql(term.cols);
expect(term.buffer.lines.get(0)!.loadCell(10, cell).getChars()).eql('¥');
expect(term.buffer.lines.get(0)!.loadCell(11, cell).getChars()).eql('');
expect(term.buffer.lines.get(0)!.loadCell(14, cell).getChars()).eql('¥');
expect(term.buffer.lines.get(0)!.loadCell(15, cell).getChars()).eql('');
expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql('3');
});
it('fullwidth - right border', () => {
term.writeSync(Array(41).join('¥'));
term.buffer.x = 10;
term.buffer.y = 0;
term.insertMode = true;
term.write('\x1b[4h');
term.writeSync('a');
expect(term.buffer.lines.get(0).length).eql(term.cols);
expect(term.buffer.lines.get(0).loadCell(10, cell).getChars()).eql('a');
expect(term.buffer.lines.get(0).loadCell(11, cell).getChars()).eql('¥');
expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql(''); // fullwidth char got replaced
expect(term.buffer.lines.get(0)!.length).eql(term.cols);
expect(term.buffer.lines.get(0)!.loadCell(10, cell).getChars()).eql('a');
expect(term.buffer.lines.get(0)!.loadCell(11, cell).getChars()).eql('¥');
expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql(''); // fullwidth char got replaced
term.writeSync('b');
expect(term.buffer.lines.get(0).length).eql(term.cols);
expect(term.buffer.lines.get(0).loadCell(11, cell).getChars()).eql('b');
expect(term.buffer.lines.get(0).loadCell(12, cell).getChars()).eql('¥');
expect(term.buffer.lines.get(0).loadCell(79, cell).getChars()).eql(''); // empty cell after fullwidth
expect(term.buffer.lines.get(0)!.length).eql(term.cols);
expect(term.buffer.lines.get(0)!.loadCell(11, cell).getChars()).eql('b');
expect(term.buffer.lines.get(0)!.loadCell(12, cell).getChars()).eql('¥');
expect(term.buffer.lines.get(0)!.loadCell(79, cell).getChars()).eql(''); // empty cell after fullwidth
});
});
@@ -1271,9 +1269,9 @@ describe('Terminal', () => {
terminal.writeSync(input);
const s = terminal.buffer.iterator(true).next().content;
assert.equal(input, s);
const stringIndex = s.match(/😃/).index;
const stringIndex = s.match(/😃/)!.index!;
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, stringIndex);
assert(terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars(), '😃');
assert(terminal.buffer.lines.get(bufferIndex[0])!.loadCell(bufferIndex[1], new CellData()).getChars(), '😃');
});
it('multiline fullwidth chars with offset 1 (currently tests for broken behavior)', () => {
@@ -1300,7 +1298,7 @@ describe('Terminal', () => {
assert.equal(input, s);
for (let i = 0; i < input.length; ++i) {
const bufferIndex = terminal.buffer.stringIndexToBufferIndex(0, i, true);
assert.equal(input[i], terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars());
assert.equal(input[i], terminal.buffer.lines.get(bufferIndex[0])!.loadCell(bufferIndex[1], new CellData()).getChars());
}
});
@@ -1318,7 +1316,7 @@ describe('Terminal', () => {
: (i % 3 === 1)
? input.substr(i, 2)
: input.substr(i - 1, 2),
terminal.buffer.lines.get(bufferIndex[0]).loadCell(bufferIndex[1], new CellData()).getChars());
terminal.buffer.lines.get(bufferIndex[0])!.loadCell(bufferIndex[1], new CellData()).getChars());
}
});
@@ -1372,15 +1370,15 @@ describe('Terminal', () => {
const normalTerminal = new TestTerminal({rows: 5, cols: 10, windowsMode: false});
normalTerminal.writeSync(data.join(''));
assert.equal(normalTerminal.buffer.lines.get(0).isWrapped, false);
assert.equal(normalTerminal.buffer.lines.get(1).isWrapped, false);
assert.equal(normalTerminal.buffer.lines.get(2).isWrapped, false);
assert.equal(normalTerminal.buffer.lines.get(0)!.isWrapped, false);
assert.equal(normalTerminal.buffer.lines.get(1)!.isWrapped, false);
assert.equal(normalTerminal.buffer.lines.get(2)!.isWrapped, false);
const windowsModeTerminal = new TestTerminal({rows: 5, cols: 10, windowsMode: true});
windowsModeTerminal.writeSync(data.join(''));
assert.equal(windowsModeTerminal.buffer.lines.get(0).isWrapped, false);
assert.equal(windowsModeTerminal.buffer.lines.get(1).isWrapped, true, 'This line should wrap in Windows mode as the previous line ends in a non-null character');
assert.equal(windowsModeTerminal.buffer.lines.get(2).isWrapped, false);
assert.equal(windowsModeTerminal.buffer.lines.get(0)!.isWrapped, false);
assert.equal(windowsModeTerminal.buffer.lines.get(1)!.isWrapped, true, 'This line should wrap in Windows mode as the previous line ends in a non-null character');
assert.equal(windowsModeTerminal.buffer.lines.get(2)!.isWrapped, false);
});
it('should mark lines as wrapped when the line ends in a non-null character after a CUP', () => {
@@ -1392,15 +1390,99 @@ describe('Terminal', () => {
const normalTerminal = new TestTerminal({rows: 5, cols: 10, windowsMode: false});
normalTerminal.writeSync(data.join(''));
assert.equal(normalTerminal.buffer.lines.get(0).isWrapped, false);
assert.equal(normalTerminal.buffer.lines.get(1).isWrapped, false);
assert.equal(normalTerminal.buffer.lines.get(2).isWrapped, false);
assert.equal(normalTerminal.buffer.lines.get(0)!.isWrapped, false);
assert.equal(normalTerminal.buffer.lines.get(1)!.isWrapped, false);
assert.equal(normalTerminal.buffer.lines.get(2)!.isWrapped, false);
const windowsModeTerminal = new TestTerminal({rows: 5, cols: 10, windowsMode: true});
windowsModeTerminal.writeSync(data.join(''));
assert.equal(windowsModeTerminal.buffer.lines.get(0).isWrapped, false);
assert.equal(windowsModeTerminal.buffer.lines.get(1).isWrapped, true, 'This line should wrap in Windows mode as the previous line ends in a non-null character');
assert.equal(windowsModeTerminal.buffer.lines.get(2).isWrapped, false);
assert.equal(windowsModeTerminal.buffer.lines.get(0)!.isWrapped, false);
assert.equal(windowsModeTerminal.buffer.lines.get(1)!.isWrapped, true, 'This line should wrap in Windows mode as the previous line ends in a non-null character');
assert.equal(windowsModeTerminal.buffer.lines.get(2)!.isWrapped, false);
});
});
it('convertEol setting', function(): void {
// not converting
const termNotConverting = new TestTerminal({cols: 15, rows: 10});
termNotConverting.writeSync('Hello\nWorld');
expect(termNotConverting.buffer.lines.get(0)!.translateToString(false)).equals('Hello ');
expect(termNotConverting.buffer.lines.get(1)!.translateToString(false)).equals(' World ');
expect(termNotConverting.buffer.lines.get(0)!.translateToString(true)).equals('Hello');
expect(termNotConverting.buffer.lines.get(1)!.translateToString(true)).equals(' World');
// converting
const termConverting = new TestTerminal({cols: 15, rows: 10, convertEol: true});
termConverting.writeSync('Hello\nWorld');
expect(termConverting.buffer.lines.get(0)!.translateToString(false)).equals('Hello ');
expect(termConverting.buffer.lines.get(1)!.translateToString(false)).equals('World ');
expect(termConverting.buffer.lines.get(0)!.translateToString(true)).equals('Hello');
expect(termConverting.buffer.lines.get(1)!.translateToString(true)).equals('World');
});
describe('Terminal InputHandler integration', () => {
function getLines(term: TestTerminal, limit: number = term.rows): string[] {
const res: string[] = [];
for (let i = 0; i < limit; ++i) {
res.push(term.buffer.lines.get(i)!.translateToString(true));
}
return res;
}
// This suite cannot live in InputHandler unless Terminal.scroll moved into IBufferService
describe('SL/SR/DECIC/DECDC', () => {
let term: TestTerminal;
beforeEach(() => {
term = new TestTerminal({cols: 5, rows: 5, scrollback: 1});
});
it('SL (scrollLeft)', () => {
term.writeSync('12345'.repeat(6));
term.writeSync('\x1b[ @');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '2345', '2345', '2345', '2345', '2345']);
term.writeSync('\x1b[0 @');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '345', '345', '345', '345', '345']);
term.writeSync('\x1b[2 @');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '5', '5', '5', '5', '5']);
});
it('SR (scrollRight)', () => {
term.writeSync('12345'.repeat(6));
term.writeSync('\x1b[ A');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 1234', ' 1234', ' 1234', ' 1234', ' 1234']);
term.writeSync('\x1b[0 A');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 123', ' 123', ' 123', ' 123', ' 123']);
term.writeSync('\x1b[2 A');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', ' 1', ' 1', ' 1', ' 1', ' 1']);
});
it('insertColumns (DECIC)', () => {
term.writeSync('12345'.repeat(6));
term.writeSync('\x1b[3;3H');
term.writeSync('\x1b[\'}');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']);
term.reset();
term.writeSync('12345'.repeat(6));
term.writeSync('\x1b[3;3H');
term.writeSync('\x1b[1\'}');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 34', '12 34', '12 34', '12 34', '12 34']);
term.reset();
term.writeSync('12345'.repeat(6));
term.writeSync('\x1b[3;3H');
term.writeSync('\x1b[2\'}');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '12 3', '12 3', '12 3', '12 3', '12 3']);
});
it('deleteColumns (DECDC)', () => {
term.writeSync('12345'.repeat(6));
term.writeSync('\x1b[3;3H');
term.writeSync('\x1b[\'~');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '1245', '1245', '1245', '1245', '1245']);
term.reset();
term.writeSync('12345'.repeat(6));
term.writeSync('\x1b[3;3H');
term.writeSync('\x1b[1\'~');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '1245', '1245', '1245', '1245', '1245']);
term.reset();
term.writeSync('12345'.repeat(6));
term.writeSync('\x1b[3;3H');
term.writeSync('\x1b[2\'~');
assert.deepEqual(getLines(term, term.rows + 1), ['12345', '125', '125', '125', '125', '125']);
});
});
});
});
File diff suppressed because it is too large Load Diff
@@ -8,14 +8,14 @@ import * as path from 'path';
import * as os from 'os';
import * as fs from 'fs';
import * as pty from 'node-pty';
import { Terminal } from './Terminal';
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 TESTFILES = glob.sync('**/escape_sequence_files/*.in', { cwd: path.join(__dirname, '../..')});
const SKIP_FILES = [
't0084-CBT.in',
't0101-NLM.in',
@@ -124,7 +124,7 @@ 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);
lineText = term.buffer.lines.get(line)!.translateToString(true);
// rtrim empty cells as xterm does
lineText = lineText.replace(/\s+$/, '');
result += lineText;
+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;
@@ -42,9 +42,9 @@ describe('AddonManager', () => {
public activate(): void {}
public dispose(): void { called++; }
}
manager.loadAddon(null, new Addon());
manager.loadAddon(null, new Addon());
manager.loadAddon(null, new Addon());
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);
@@ -4,12 +4,12 @@
*/
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 '../Types';
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 '../browser/LocalizableStrings';
import * as Strings from '../LocalizableStrings';
import { IEvent, EventEmitter } from 'common/EventEmitter';
import { AddonManager } from './AddonManager';
import { IParams } from 'common/parser/Types';
@@ -17,7 +17,7 @@ import { IParams } from 'common/parser/Types';
export class Terminal implements ITerminalApi {
private _core: ITerminal;
private _addonManager: AddonManager;
private _parser: IParser;
private _parser: IParser | undefined;
constructor(options?: ITerminalOptions) {
this._core = new TerminalCore(options);
@@ -81,11 +81,11 @@ export class Terminal implements ITerminalApi {
public deregisterCharacterJoiner(joinerId: number): void {
this._core.deregisterCharacterJoiner(joinerId);
}
public registerMarker(cursorYOffset: number): IMarker {
public registerMarker(cursorYOffset: number): IMarker | undefined {
this._verifyIntegers(cursorYOffset);
return this._core.addMarker(cursorYOffset);
}
public addMarker(cursorYOffset: number): IMarker {
public addMarker(cursorYOffset: number): IMarker | undefined {
return this.registerMarker(cursorYOffset);
}
public hasSelection(): boolean {
+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" }
]
+336
View File
@@ -0,0 +1,336 @@
/**
* Copyright (c) 2014-2020 The xterm.js authors. All rights reserved.
* Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
* @license MIT
*
* Originally forked from (with the author's permission):
* Fabrice Bellard's javascript vt100 for jslinux:
* http://bellard.org/jslinux/
* Copyright (c) 2011 Fabrice Bellard
* The original design remains. The terminal itself
* has been extended to include xterm CSI codes, among
* other features.
*
* Terminal Emulation References:
* http://vt100.net/
* http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt
* http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
* http://invisible-island.net/vttest/
* http://www.inwap.com/pdp10/ansicode.txt
* http://linux.die.net/man/4/console_codes
* http://linux.die.net/man/7/urxvt
*/
import { Disposable } from 'common/Lifecycle';
import { IInstantiationService, IOptionsService, IBufferService, ILogService, ICharsetService, ICoreService, ICoreMouseService, IUnicodeService, IDirtyRowService } from 'common/services/Services';
import { InstantiationService } from 'common/services/InstantiationService';
import { LogService } from 'common/services/LogService';
import { BufferService, MINIMUM_COLS, MINIMUM_ROWS } from 'common/services/BufferService';
import { OptionsService } from 'common/services/OptionsService';
import { ITerminalOptions, IDisposable, IBufferLine, IAttributeData, ICoreTerminal } from 'common/Types';
import { CoreService } from 'common/services/CoreService';
import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';
import { CoreMouseService } from 'common/services/CoreMouseService';
import { DirtyRowService } from 'common/services/DirtyRowService';
import { UnicodeService } from 'common/services/UnicodeService';
import { CharsetService } from 'common/services/CharsetService';
import { updateWindowsModeWrappedState } from 'common/WindowsMode';
import { IFunctionIdentifier, IParams } from 'common/parser/Types';
import { IBufferSet } from 'common/buffer/Types';
import { InputHandler } from 'common/InputHandler';
import { WriteBuffer } from 'common/input/WriteBuffer';
export abstract class CoreTerminal extends Disposable implements ICoreTerminal {
protected readonly _instantiationService: IInstantiationService;
protected readonly _bufferService: IBufferService;
protected readonly _logService: ILogService;
protected readonly _coreService: ICoreService;
protected readonly _charsetService: ICharsetService;
protected readonly _coreMouseService: ICoreMouseService;
protected readonly _dirtyRowService: IDirtyRowService;
public readonly unicodeService: IUnicodeService;
public readonly optionsService: IOptionsService;
protected _inputHandler: InputHandler;
private _writeBuffer: WriteBuffer;
private _windowsMode: IDisposable | undefined;
/** An IBufferline to clone/copy from for new blank lines */
private _cachedBlankLine: IBufferLine | undefined;
private _onBinary = new EventEmitter<string>();
public get onBinary(): IEvent<string> { return this._onBinary.event; }
private _onData = new EventEmitter<string>();
public get onData(): IEvent<string> { return this._onData.event; }
protected _onLineFeed = new EventEmitter<void>();
public get onLineFeed(): IEvent<void> { return this._onLineFeed.event; }
private _onResize = new EventEmitter<{ cols: number, rows: number }>();
public get onResize(): IEvent<{ cols: number, rows: number }> { return this._onResize.event; }
protected _onScroll = new EventEmitter<number>();
public get onScroll(): IEvent<number> { return this._onScroll.event; }
public get cols(): number { return this._bufferService.cols; }
public get rows(): number { return this._bufferService.rows; }
public get buffers(): IBufferSet { return this._bufferService.buffers; }
constructor(
options: ITerminalOptions
) {
super();
// Setup and initialize services
this._instantiationService = new InstantiationService();
this.optionsService = new OptionsService(options);
this._instantiationService.setService(IOptionsService, this.optionsService);
this._bufferService = this._instantiationService.createInstance(BufferService);
this._instantiationService.setService(IBufferService, this._bufferService);
this._logService = this._instantiationService.createInstance(LogService);
this._instantiationService.setService(ILogService, this._logService);
this._coreService = this._instantiationService.createInstance(CoreService, () => this.scrollToBottom());
this._instantiationService.setService(ICoreService, this._coreService);
this._coreMouseService = this._instantiationService.createInstance(CoreMouseService);
this._instantiationService.setService(ICoreMouseService, this._coreMouseService);
this._dirtyRowService = this._instantiationService.createInstance(DirtyRowService);
this._instantiationService.setService(IDirtyRowService, this._dirtyRowService);
this.unicodeService = this._instantiationService.createInstance(UnicodeService);
this._instantiationService.setService(IUnicodeService, this.unicodeService);
this._charsetService = this._instantiationService.createInstance(CharsetService);
this._instantiationService.setService(ICharsetService, this._charsetService);
// Register input handler and handle/forward events
this._inputHandler = new InputHandler(this._bufferService, this._charsetService, this._coreService, this._dirtyRowService, this._logService, this.optionsService, this._coreMouseService, this.unicodeService);
this.register(forwardEvent(this._inputHandler.onLineFeed, this._onLineFeed));
this.register(this._inputHandler);
// Setup listeners
this.register(forwardEvent(this._bufferService.onResize, this._onResize));
this.register(forwardEvent(this._coreService.onData, this._onData));
this.register(forwardEvent(this._coreService.onBinary, this._onBinary));
this.register(this.optionsService.onOptionChange(key => this._updateOptions(key)));
// Setup WriteBuffer
this._writeBuffer = new WriteBuffer(data => this._inputHandler.parse(data));
}
public dispose(): void {
if (this._isDisposed) {
return;
}
super.dispose();
this._windowsMode?.dispose();
this._windowsMode = undefined;
}
public write(data: string | Uint8Array, callback?: () => void): void {
this._writeBuffer.write(data, callback);
}
public writeSync(data: string | Uint8Array): void {
this._writeBuffer.writeSync(data);
}
public resize(x: number, y: number): void {
if (isNaN(x) || isNaN(y)) {
return;
}
x = Math.max(x, MINIMUM_COLS);
y = Math.max(y, MINIMUM_ROWS);
this._bufferService.resize(x, y);
}
/**
* Scroll the terminal down 1 row, creating a blank line.
* @param isWrapped Whether the new line is wrapped from the previous line.
*/
public scroll(eraseAttr: IAttributeData, isWrapped: boolean = false): void {
const buffer = this._bufferService.buffer;
let newLine: IBufferLine | undefined;
newLine = this._cachedBlankLine;
if (!newLine || newLine.length !== this.cols || newLine.getFg(0) !== eraseAttr.fg || newLine.getBg(0) !== eraseAttr.bg) {
newLine = buffer.getBlankLine(eraseAttr, isWrapped);
this._cachedBlankLine = newLine;
}
newLine.isWrapped = isWrapped;
const topRow = buffer.ybase + buffer.scrollTop;
const bottomRow = buffer.ybase + buffer.scrollBottom;
if (buffer.scrollTop === 0) {
// Determine whether the buffer is going to be trimmed after insertion.
const willBufferBeTrimmed = buffer.lines.isFull;
// Insert the line using the fastest method
if (bottomRow === buffer.lines.length - 1) {
if (willBufferBeTrimmed) {
buffer.lines.recycle().copyFrom(newLine);
} else {
buffer.lines.push(newLine.clone());
}
} else {
buffer.lines.splice(bottomRow + 1, 0, newLine.clone());
}
// Only adjust ybase and ydisp when the buffer is not trimmed
if (!willBufferBeTrimmed) {
buffer.ybase++;
// Only scroll the ydisp with ybase if the user has not scrolled up
if (!this._bufferService.isUserScrolling) {
buffer.ydisp++;
}
} else {
// When the buffer is full and the user has scrolled up, keep the text
// stable unless ydisp is right at the top
if (this._bufferService.isUserScrolling) {
buffer.ydisp = Math.max(buffer.ydisp - 1, 0);
}
}
} else {
// scrollTop is non-zero which means no line will be going to the
// scrollback, instead we can just shift them in-place.
const scrollRegionHeight = bottomRow - topRow + 1 /* as it's zero-based */;
buffer.lines.shiftElements(topRow + 1, scrollRegionHeight - 1, -1);
buffer.lines.set(bottomRow, newLine.clone());
}
// Move the viewport to the bottom of the buffer unless the user is
// scrolling.
if (!this._bufferService.isUserScrolling) {
buffer.ydisp = buffer.ybase;
}
// Flag rows that need updating
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
this._onScroll.fire(buffer.ydisp);
}
/**
* Scroll the display of the terminal
* @param disp The number of lines to scroll down (negative scroll up).
* @param suppressScrollEvent Don't emit the scroll event as scrollLines. This is used
* to avoid unwanted events being handled by the viewport when the event was triggered from the
* viewport originally.
*/
public scrollLines(disp: number, suppressScrollEvent?: boolean): void {
const buffer = this._bufferService.buffer;
if (disp < 0) {
if (buffer.ydisp === 0) {
return;
}
this._bufferService.isUserScrolling = true;
} else if (disp + buffer.ydisp >= buffer.ybase) {
this._bufferService.isUserScrolling = false;
}
const oldYdisp = buffer.ydisp;
buffer.ydisp = Math.max(Math.min(buffer.ydisp + disp, buffer.ybase), 0);
// No change occurred, don't trigger scroll/refresh
if (oldYdisp === buffer.ydisp) {
return;
}
if (!suppressScrollEvent) {
this._onScroll.fire(buffer.ydisp);
}
}
/**
* Scroll the display of the terminal by a number of pages.
* @param pageCount The number of pages to scroll (negative scrolls up).
*/
public scrollPages(pageCount: number): void {
this.scrollLines(pageCount * (this.rows - 1));
}
/**
* Scrolls the display of the terminal to the top.
*/
public scrollToTop(): void {
this.scrollLines(-this._bufferService.buffer.ydisp);
}
/**
* Scrolls the display of the terminal to the bottom.
*/
public scrollToBottom(): void {
this.scrollLines(this._bufferService.buffer.ybase - this._bufferService.buffer.ydisp);
}
public scrollToLine(line: number): void {
const scrollAmount = line - this._bufferService.buffer.ydisp;
if (scrollAmount !== 0) {
this.scrollLines(scrollAmount);
}
}
/** Add handler for ESC escape sequence. See xterm.d.ts for details. */
public addEscHandler(id: IFunctionIdentifier, callback: () => boolean): IDisposable {
return this._inputHandler.addEscHandler(id, callback);
}
/** Add handler for DCS escape sequence. See xterm.d.ts for details. */
public addDcsHandler(id: IFunctionIdentifier, callback: (data: string, param: IParams) => boolean): IDisposable {
return this._inputHandler.addDcsHandler(id, callback);
}
/** Add handler for CSI escape sequence. See xterm.d.ts for details. */
public addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable {
return this._inputHandler.addCsiHandler(id, callback);
}
/** Add handler for OSC escape sequence. See xterm.d.ts for details. */
public addOscHandler(ident: number, callback: (data: string) => boolean): IDisposable {
return this._inputHandler.addOscHandler(ident, callback);
}
protected _setup(): void {
if (this.optionsService.options.windowsMode) {
this._enableWindowsMode();
}
}
public reset(): void {
this._inputHandler.reset();
this._bufferService.reset();
this._charsetService.reset();
this._coreService.reset();
this._coreMouseService.reset();
}
protected _updateOptions(key: string): void {
// TODO: These listeners should be owned by individual components
switch (key) {
case 'scrollback':
this.buffers.resize(this.cols, this.rows);
break;
case 'windowsMode':
if (this.optionsService.options.windowsMode) {
this._enableWindowsMode();
} else {
this._windowsMode?.dispose();
this._windowsMode = undefined;
}
break;
}
}
protected _enableWindowsMode(): void {
if (!this._windowsMode) {
const disposables: IDisposable[] = [];
disposables.push(this.onLineFeed(updateWindowsModeWrappedState.bind(null, this._bufferService)));
disposables.push(this.addCsiHandler({ final: 'H' }, () => {
updateWindowsModeWrappedState(this._bufferService);
return false;
}));
this._windowsMode = {
dispose: () => {
disposables.forEach(d => d.dispose());
}
};
}
}
}
+4
View File
@@ -63,3 +63,7 @@ export class EventEmitter<T, U = void> implements IEventEmitter<T, U> {
this._disposed = true;
}
}
export function forwardEvent<T>(from: IEvent<T>, to: IEventEmitter<T>): IDisposable {
return from(e => to.fire(e));
}
File diff suppressed because it is too large Load Diff
@@ -4,7 +4,7 @@
* @license MIT
*/
import { IInputHandler, IInputHandlingTerminal } from './Types';
import { IInputHandler, IAttributeData, IDisposable, IWindowOptions } from 'common/Types';
import { C0, C1 } from 'common/data/EscapeSequences';
import { CHARSETS, DEFAULT_CHARSET } from 'common/data/Charsets';
import { EscapeSequenceParser } from 'common/parser/EscapeSequenceParser';
@@ -17,11 +17,9 @@ import { IParsingState, IDcsHandler, IEscapeSequenceParser, IParams, IFunctionId
import { NULL_CELL_CODE, NULL_CELL_WIDTH, Attributes, FgFlags, BgFlags, Content } from 'common/buffer/Constants';
import { CellData } from 'common/buffer/CellData';
import { AttributeData } from 'common/buffer/AttributeData';
import { IAttributeData, IDisposable, IWindowOptions } from 'common/Types';
import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService, ICoreMouseService, ICharsetService, IUnicodeService, IInstantiationService } from 'common/services/Services';
import { ICoreService, IBufferService, IOptionsService, ILogService, IDirtyRowService, ICoreMouseService, ICharsetService, IUnicodeService } from 'common/services/Services';
import { OscHandler } from 'common/parser/OscParser';
import { DcsHandler } from 'common/parser/DcsParser';
import { IRenderService } from 'browser/services/Services';
/**
* Map collect to glevel. Used in `selectCharset`.
@@ -94,7 +92,10 @@ function paramToWindowOption(n: number, opts: IWindowOptions): boolean {
return false;
}
export enum WindowsOptionsReportType {
GET_WIN_SIZE_PIXELS = 0,
GET_CELL_SIZE_PIXELS = 1
}
/**
* DCS subparser implementations
@@ -218,27 +219,39 @@ export class InputHandler extends Disposable implements IInputHandler {
private _workCell: CellData = new CellData();
private _windowTitle = '';
private _iconName = '';
private _windowTitleStack: string[] = [];
private _iconNameStack: string[] = [];
protected _windowTitleStack: string[] = [];
protected _iconNameStack: string[] = [];
private _curAttrData: IAttributeData = DEFAULT_ATTR_DATA.clone();
private _eraseAttrDataInternal: IAttributeData = DEFAULT_ATTR_DATA.clone();
private _onRequestBell = new EventEmitter<void>();
public get onRequestBell(): IEvent<void> { return this._onRequestBell.event; }
private _onRequestRefreshRows = new EventEmitter<number, number>();
public get onRequestRefreshRows(): IEvent<number, number> { return this._onRequestRefreshRows.event; }
private _onRequestReset = new EventEmitter<void>();
public get onRequestReset(): IEvent<void> { return this._onRequestReset.event; }
private _onRequestBell = new EventEmitter<void>();
public get onRequestBell(): IEvent<void> { return this._onRequestBell.event; }
private _onRequestScroll = new EventEmitter<IAttributeData, boolean | void>();
public get onRequestScroll(): IEvent<IAttributeData, boolean | void> { return this._onRequestScroll.event; }
private _onRequestSyncScrollBar = new EventEmitter<void>();
public get onRequestSyncScrollBar(): IEvent<void> { return this._onRequestSyncScrollBar.event; }
private _onRequestWindowsOptionsReport = new EventEmitter<WindowsOptionsReportType>();
public get onRequestWindowsOptionsReport(): IEvent<WindowsOptionsReportType> { return this._onRequestWindowsOptionsReport.event; }
private _onA11yChar = new EventEmitter<string>();
public get onA11yChar(): IEvent<string> { return this._onA11yChar.event; }
private _onA11yTab = new EventEmitter<number>();
public get onA11yTab(): IEvent<number> { return this._onA11yTab.event; }
private _onCursorMove = new EventEmitter<void>();
public get onCursorMove(): IEvent<void> { return this._onCursorMove.event; }
private _onLineFeed = new EventEmitter<void>();
public get onLineFeed(): IEvent<void> { return this._onLineFeed.event; }
private _onScroll = new EventEmitter<number>();
public get onScroll(): IEvent<number> { return this._onScroll.event; }
private _onTitleChange = new EventEmitter<string>();
public get onTitleChange(): IEvent<string> { return this._onTitleChange.event; }
constructor(
private _terminal: IInputHandlingTerminal,
private readonly _bufferService: IBufferService,
private readonly _charsetService: ICharsetService,
private readonly _coreService: ICoreService,
@@ -247,9 +260,8 @@ export class InputHandler extends Disposable implements IInputHandler {
private readonly _optionsService: IOptionsService,
private readonly _coreMouseService: ICoreMouseService,
private readonly _unicodeService: IUnicodeService,
private readonly _instantiationService: IInstantiationService,
private readonly _parser: IEscapeSequenceParser = new EscapeSequenceParser())
{
private readonly _parser: IEscapeSequenceParser = new EscapeSequenceParser()
) {
super();
this.register(this._parser);
@@ -489,9 +501,9 @@ export class InputHandler extends Disposable implements IInputHandler {
const screenReaderMode = this._optionsService.options.screenReaderMode;
const cols = this._bufferService.cols;
const wraparoundMode = this._coreService.decPrivateModes.wraparound;
const insertMode = this._terminal.insertMode;
const insertMode = this._coreService.modes.insertMode;
const curAttr = this._curAttrData;
let bufferRow = buffer.lines.get(buffer.y + buffer.ybase);
let bufferRow = buffer.lines.get(buffer.ybase + buffer.y)!;
this._dirtyRowService.markDirty(buffer.y);
@@ -518,7 +530,7 @@ export class InputHandler extends Disposable implements IInputHandler {
}
if (screenReaderMode) {
this._terminal.onA11yCharEmitter.fire(stringFromCodePoint(code));
this._onA11yChar.fire(stringFromCodePoint(code));
}
// insert combining char at last cursor position
@@ -552,17 +564,17 @@ export class InputHandler extends Disposable implements IInputHandler {
buffer.y++;
if (buffer.y === buffer.scrollBottom + 1) {
buffer.y--;
this._terminal.scroll(this._eraseAttrData(), true);
this._onRequestScroll.fire(this._eraseAttrData(), true);
} else {
if (buffer.y >= this._bufferService.rows) {
buffer.y = this._bufferService.rows - 1;
}
// The line already exists (eg. the initial viewport), mark it as a
// wrapped line
buffer.lines.get(buffer.y).isWrapped = true;
buffer.lines.get(buffer.ybase + buffer.y)!.isWrapped = true;
}
// row changed, get it again
bufferRow = buffer.lines.get(buffer.y + buffer.ybase);
bufferRow = buffer.lines.get(buffer.ybase + buffer.y)!;
} else {
buffer.x = cols - 1;
if (chWidth === 2) {
@@ -691,7 +703,7 @@ export class InputHandler extends Disposable implements IInputHandler {
buffer.y++;
if (buffer.y === buffer.scrollBottom + 1) {
buffer.y--;
this._terminal.scroll(this._eraseAttrData());
this._onRequestScroll.fire(this._eraseAttrData());
} else if (buffer.y >= this._bufferService.rows) {
buffer.y = this._bufferService.rows - 1;
}
@@ -756,16 +768,16 @@ export class InputHandler extends Disposable implements IInputHandler {
if (buffer.x === 0
&& buffer.y > buffer.scrollTop
&& buffer.y <= buffer.scrollBottom
&& buffer.lines.get(buffer.ybase + buffer.y).isWrapped)
&& buffer.lines.get(buffer.ybase + buffer.y)?.isWrapped)
{
buffer.lines.get(buffer.ybase + buffer.y).isWrapped = false;
buffer.lines.get(buffer.ybase + buffer.y)!.isWrapped = false;
buffer.y--;
buffer.x = this._bufferService.cols - 1;
// find last taken cell - last cell can have 3 different states:
// - hasContent(true) + hasWidth(1): narrow char - we are done
// - hasWidth(0): second part of wide char - we are done
// - hasContent(false) + hasWidth(1): empty cell due to early wrapping wide char, go one cell further back
const line = buffer.lines.get(buffer.ybase + buffer.y);
const line = buffer.lines.get(buffer.ybase + buffer.y)!;
if (line.hasWidth(buffer.x) && !line.hasContent(buffer.x)) {
buffer.x--;
// We do this only once, since width=1 + hasContent=false currently happens only once before
@@ -790,7 +802,7 @@ export class InputHandler extends Disposable implements IInputHandler {
const originalX = this._bufferService.buffer.x;
this._bufferService.buffer.x = this._bufferService.buffer.nextStop();
if (this._optionsService.options.screenReaderMode) {
this._terminal.onA11yTabEmitter.fire(this._bufferService.buffer.x - originalX);
this._onA11yTab.fire(this._bufferService.buffer.x - originalX);
}
}
@@ -1078,7 +1090,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* @param end end - 1 is last erased cell
*/
private _eraseInBufferLine(y: number, start: number, end: number, clearWrap: boolean = false): void {
const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y);
const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y)!;
line.replaceCells(
start,
end,
@@ -1096,7 +1108,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* @param y row index
*/
private _resetBufferLine(y: number): void {
const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y);
const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + y)!;
line.fill(this._bufferService.buffer.getNullCell(this._eraseAttrData()));
line.isWrapped = false;
}
@@ -1145,7 +1157,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this._eraseInBufferLine(j, 0, this._bufferService.buffer.x + 1, true);
if (this._bufferService.buffer.x + 1 >= this._bufferService.cols) {
// Deleted entire previous line. This next line can no longer be wrapped.
this._bufferService.buffer.lines.get(j + 1).isWrapped = false;
this._bufferService.buffer.lines.get(j + 1)!.isWrapped = false;
}
while (j--) {
this._resetBufferLine(j);
@@ -1232,7 +1244,7 @@ export class InputHandler extends Disposable implements IInputHandler {
return;
}
const row: number = buffer.y + buffer.ybase;
const row: number = buffer.ybase + buffer.y;
const scrollBottomRowsOffset = this._bufferService.rows - 1 - buffer.scrollBottom;
const scrollBottomAbsolute = this._bufferService.rows - 1 + buffer.ybase - scrollBottomRowsOffset + 1;
@@ -1267,7 +1279,7 @@ export class InputHandler extends Disposable implements IInputHandler {
return;
}
const row: number = buffer.y + buffer.ybase;
const row: number = buffer.ybase + buffer.y;
let j: number;
j = this._bufferService.rows - 1 - buffer.scrollBottom;
@@ -1296,7 +1308,7 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public insertChars(params: IParams): void {
this._restrictCursor();
const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.y + this._bufferService.buffer.ybase);
const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + this._bufferService.buffer.y);
if (line) {
line.insertCells(
this._bufferService.buffer.x,
@@ -1321,7 +1333,7 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public deleteChars(params: IParams): void {
this._restrictCursor();
const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.y + this._bufferService.buffer.ybase);
const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + this._bufferService.buffer.y);
if (line) {
line.deleteCells(
this._bufferService.buffer.x,
@@ -1397,7 +1409,7 @@ export class InputHandler extends Disposable implements IInputHandler {
}
const param = params.params[0] || 1;
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
const line = buffer.lines.get(buffer.ybase + y);
const line = buffer.lines.get(buffer.ybase + y)!;
line.deleteCells(0, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
line.isWrapped = false;
}
@@ -1430,7 +1442,7 @@ export class InputHandler extends Disposable implements IInputHandler {
}
const param = params.params[0] || 1;
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
const line = buffer.lines.get(buffer.ybase + y);
const line = buffer.lines.get(buffer.ybase + y)!;
line.insertCells(0, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
line.isWrapped = false;
}
@@ -1453,7 +1465,7 @@ export class InputHandler extends Disposable implements IInputHandler {
}
const param = params.params[0] || 1;
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
const line = this._bufferService.buffer.lines.get(buffer.ybase + y);
const line = this._bufferService.buffer.lines.get(buffer.ybase + y)!;
line.insertCells(buffer.x, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
line.isWrapped = false;
}
@@ -1476,7 +1488,7 @@ export class InputHandler extends Disposable implements IInputHandler {
}
const param = params.params[0] || 1;
for (let y = buffer.scrollTop; y <= buffer.scrollBottom; ++y) {
const line = buffer.lines.get(buffer.ybase + y);
const line = buffer.lines.get(buffer.ybase + y)!;
line.deleteCells(buffer.x, param, buffer.getNullCell(this._eraseAttrData()), this._eraseAttrData());
line.isWrapped = false;
}
@@ -1493,7 +1505,7 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public eraseChars(params: IParams): void {
this._restrictCursor();
const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.y + this._bufferService.buffer.ybase);
const line = this._bufferService.buffer.lines.get(this._bufferService.buffer.ybase + this._bufferService.buffer.y);
if (line) {
line.replaceCells(
this._bufferService.buffer.x,
@@ -1574,9 +1586,9 @@ export class InputHandler extends Disposable implements IInputHandler {
if (params.params[0] > 0) {
return;
}
if (this._terminal.is('xterm') || this._terminal.is('rxvt-unicode') || this._terminal.is('screen')) {
if (this._is('xterm') || this._is('rxvt-unicode') || this._is('screen')) {
this._coreService.triggerDataEvent(C0.ESC + '[?1;2c');
} else if (this._terminal.is('linux')) {
} else if (this._is('linux')) {
this._coreService.triggerDataEvent(C0.ESC + '[?6c');
}
}
@@ -1612,19 +1624,27 @@ export class InputHandler extends Disposable implements IInputHandler {
// xterm and urxvt
// seem to spit this
// out around ~370 times (?).
if (this._terminal.is('xterm')) {
if (this._is('xterm')) {
this._coreService.triggerDataEvent(C0.ESC + '[>0;276;0c');
} else if (this._terminal.is('rxvt-unicode')) {
} else if (this._is('rxvt-unicode')) {
this._coreService.triggerDataEvent(C0.ESC + '[>85;95;0c');
} else if (this._terminal.is('linux')) {
} else if (this._is('linux')) {
// not supported by linux console.
// linux console echoes parameters.
this._coreService.triggerDataEvent(params.params[0] + 'c');
} else if (this._terminal.is('screen')) {
} else if (this._is('screen')) {
this._coreService.triggerDataEvent(C0.ESC + '[>83;40003;0c');
}
}
/**
* Evaluate if the current terminal is the given argument.
* @param term The terminal name to evaluate
*/
private _is(term: string): boolean {
return (this._optionsService.options.termName + '').indexOf(term) === 0;
}
/**
* CSI Pm h Set Mode (SM).
* Ps = 2 -> Keyboard Action Mode (AM).
@@ -1641,15 +1661,12 @@ export class InputHandler extends Disposable implements IInputHandler {
* | 4 | Insert Mode (IRM). | #Y |
* | 12 | Send/receive (SRM). Always off. | #N |
* | 20 | Automatic Newline (LNM). Always off. | #N |
*
*
* FIXME: why is LNM commented out?
*/
public setMode(params: IParams): void {
for (let i = 0; i < params.length; i++) {
switch (params.params[i]) {
case 4:
this._terminal.insertMode = true;
this._coreService.modes.insertMode = true;
break;
case 20:
// this._t.convertEol = true;
@@ -1791,7 +1808,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* through `options.windowsOptions`.
*/
if (this._optionsService.options.windowOptions.setWinLines) {
this._terminal.resize(132, this._bufferService.rows);
this._bufferService.resize(132, this._bufferService.rows);
this._onRequestReset.fire();
}
break;
@@ -1811,7 +1828,7 @@ export class InputHandler extends Disposable implements IInputHandler {
case 66:
this._logService.debug('Serial port requested application keypad.');
this._coreService.decPrivateModes.applicationKeypad = true;
this._terminal.viewport?.syncScrollArea();
this._onRequestSyncScrollBar.fire();
break;
case 9: // X10 Mouse
// no release, no motion, no wheel, no modifiers.
@@ -1832,7 +1849,7 @@ export class InputHandler extends Disposable implements IInputHandler {
case 1004: // send focusin/focusout events
// focusin: ^[[I
// focusout: ^[[O
this._terminal.sendFocus = true;
this._coreService.decPrivateModes.sendFocus = true;
break;
case 1005: // utf8 ext mode mouse - removed in #2507
this._logService.debug('DECSET 1005 not supported (see #2507)');
@@ -1855,12 +1872,12 @@ export class InputHandler extends Disposable implements IInputHandler {
case 47: // alt screen buffer
case 1047: // alt screen buffer
this._bufferService.buffers.activateAltBuffer(this._eraseAttrData());
this._coreService.isCursorInitialized = true;
this._onRequestRefreshRows.fire(0, this._bufferService.rows - 1);
this._terminal.viewport?.syncScrollArea();
this._terminal.showCursor();
this._onRequestSyncScrollBar.fire();
break;
case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)
this._terminal.bracketedPasteMode = true;
this._coreService.decPrivateModes.bracketedPasteMode = true;
break;
}
}
@@ -1891,7 +1908,7 @@ export class InputHandler extends Disposable implements IInputHandler {
for (let i = 0; i < params.length; i++) {
switch (params.params[i]) {
case 4:
this._terminal.insertMode = false;
this._coreService.modes.insertMode = false;
break;
case 20:
// this._t.convertEol = false;
@@ -2022,7 +2039,7 @@ export class InputHandler extends Disposable implements IInputHandler {
* through `options.windowsOptions`.
*/
if (this._optionsService.options.windowOptions.setWinLines) {
this._terminal.resize(80, this._bufferService.rows);
this._bufferService.resize(80, this._bufferService.rows);
this._onRequestReset.fire();
}
break;
@@ -2042,7 +2059,7 @@ export class InputHandler extends Disposable implements IInputHandler {
case 66:
this._logService.debug('Switching back to normal keypad.');
this._coreService.decPrivateModes.applicationKeypad = false;
this._terminal.viewport?.syncScrollArea();
this._onRequestSyncScrollBar.fire();
break;
case 9: // X10 Mouse
case 1000: // vt200 mouse
@@ -2051,7 +2068,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this._coreMouseService.activeProtocol = 'NONE';
break;
case 1004: // send focusin/focusout events
this._terminal.sendFocus = false;
this._coreService.decPrivateModes.sendFocus = false;
break;
case 1005: // utf8 ext mode mouse - removed in #2507
this._logService.debug('DECRST 1005 not supported (see #2507)');
@@ -2077,12 +2094,12 @@ export class InputHandler extends Disposable implements IInputHandler {
if (params.params[i] === 1049) {
this.restoreCursor();
}
this._coreService.isCursorInitialized = true;
this._onRequestRefreshRows.fire(0, this._bufferService.rows - 1);
this._terminal.viewport?.syncScrollArea();
this._terminal.showCursor();
this._onRequestSyncScrollBar.fire();
break;
case 2004: // bracketed paste mode (https://cirw.in/blog/bracketed-paste)
this._terminal.bracketedPasteMode = false;
this._coreService.decPrivateModes.bracketedPasteMode = false;
break;
}
}
@@ -2108,7 +2125,7 @@ export class InputHandler extends Disposable implements IInputHandler {
do {
accu[advance + cSpace] = params.params[pos + advance];
if (params.hasSubParams(pos + advance)) {
const subparams = params.getSubParams(pos + advance);
const subparams = params.getSubParams(pos + advance)!;
let i = 0;
do {
if (accu[1] === 5) {
@@ -2418,8 +2435,7 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public softReset(params: IParams): void {
this._coreService.isCursorHidden = false;
this._terminal.insertMode = false;
this._terminal.viewport?.syncScrollArea();
this._onRequestSyncScrollBar.fire();
this._bufferService.buffer.scrollTop = 0;
this._bufferService.buffer.scrollBottom = this._bufferService.rows - 1;
this._curAttrData = DEFAULT_ATTR_DATA.clone();
@@ -2533,22 +2549,14 @@ export class InputHandler extends Disposable implements IInputHandler {
return;
}
const second = (params.length > 1) ? params.params[1] : 0;
const rs = this._instantiationService.getService(IRenderService);
switch (params.params[0]) {
case 14: // GetWinSizePixels, returns CSI 4 ; height ; width t
if (rs && second !== 2) {
console.log(rs.dimensions);
const w = rs.dimensions.scaledCanvasWidth.toFixed(0);
const h = rs.dimensions.scaledCanvasHeight.toFixed(0);
this._coreService.triggerDataEvent(`${C0.ESC}[4;${h};${w}t`);
if (second !== 2) {
this._onRequestWindowsOptionsReport.fire(WindowsOptionsReportType.GET_WIN_SIZE_PIXELS);
}
break;
case 16: // GetCellSizePixels, returns CSI 6 ; height ; width t
if (rs) {
const w = rs.dimensions.scaledCellWidth.toFixed(0);
const h = rs.dimensions.scaledCellHeight.toFixed(0);
this._coreService.triggerDataEvent(`${C0.ESC}[6;${h};${w}t`);
}
this._onRequestWindowsOptionsReport.fire(WindowsOptionsReportType.GET_CELL_SIZE_PIXELS);
break;
case 18: // GetWinSizeChars, returns CSI 8 ; height ; width t
if (this._bufferService) {
@@ -2572,12 +2580,12 @@ export class InputHandler extends Disposable implements IInputHandler {
case 23: // PopTitle
if (second === 0 || second === 2) {
if (this._windowTitleStack.length) {
this.setTitle(this._windowTitleStack.pop());
this.setTitle(this._windowTitleStack.pop()!);
}
}
if (second === 0 || second === 1) {
if (this._iconNameStack.length) {
this.setIconName(this._iconNameStack.pop());
this.setIconName(this._iconNameStack.pop()!);
}
}
break;
@@ -2635,7 +2643,7 @@ export class InputHandler extends Disposable implements IInputHandler {
*/
public setTitle(data: string): void {
this._windowTitle = data;
this._terminal.handleTitle(data);
this._onTitleChange.fire(data);
}
/**
@@ -2668,7 +2676,7 @@ export class InputHandler extends Disposable implements IInputHandler {
public keypadApplicationMode(): void {
this._logService.debug('Serial port requested application keypad.');
this._coreService.decPrivateModes.applicationKeypad = true;
this._terminal.viewport?.syncScrollArea();
this._onRequestSyncScrollBar.fire();
}
/**
@@ -2679,7 +2687,7 @@ export class InputHandler extends Disposable implements IInputHandler {
public keypadNumericMode(): void {
this._logService.debug('Switching back to normal keypad.');
this._coreService.decPrivateModes.applicationKeypad = false;
this._terminal.viewport?.syncScrollArea();
this._onRequestSyncScrollBar.fire();
}
/**
@@ -2736,7 +2744,7 @@ export class InputHandler extends Disposable implements IInputHandler {
this._bufferService.buffer.y++;
if (buffer.y === buffer.scrollBottom + 1) {
buffer.y--;
this._terminal.scroll(this._eraseAttrData());
this._onRequestScroll.fire(this._eraseAttrData());
} else if (buffer.y >= this._bufferService.rows) {
buffer.y = this._bufferService.rows - 1;
}
@@ -2774,8 +2782,8 @@ export class InputHandler extends Disposable implements IInputHandler {
// test: echo -ne '\e[1;1H\e[44m\eM\e[0m'
// blankLine(true) is xterm/linux behavior
const scrollRegionHeight = buffer.scrollBottom - buffer.scrollTop;
buffer.lines.shiftElements(buffer.y + buffer.ybase, scrollRegionHeight, 1);
buffer.lines.set(buffer.y + buffer.ybase, buffer.getBlankLine(this._eraseAttrData()));
buffer.lines.shiftElements(buffer.ybase + buffer.y, scrollRegionHeight, 1);
buffer.lines.set(buffer.ybase + buffer.y, buffer.getBlankLine(this._eraseAttrData()));
this._dirtyRowService.markRangeDirty(buffer.scrollTop, buffer.scrollBottom);
} else {
buffer.y--;
@@ -2840,9 +2848,12 @@ export class InputHandler extends Disposable implements IInputHandler {
this._setCursor(0, 0);
for (let yOffset = 0; yOffset < this._bufferService.rows; ++yOffset) {
const row = buffer.y + buffer.ybase + yOffset;
buffer.lines.get(row).fill(cell);
buffer.lines.get(row).isWrapped = false;
const row = buffer.ybase + buffer.y + yOffset;
const line = buffer.lines.get(row);
if (line) {
line.fill(cell);
line.isWrapped = false;
}
}
this._dirtyRowService.markAllDirty();
this._setCursor(0, 0);
+9 -2
View File
@@ -9,13 +9,15 @@ import { clone } from 'common/Clone';
import { DEFAULT_OPTIONS } from 'common/services/OptionsService';
import { IBufferSet, IBuffer } from 'common/buffer/Types';
import { BufferSet } from 'common/buffer/BufferSet';
import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEventType, ICharset } from 'common/Types';
import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEventType, ICharset, IModes } from 'common/Types';
import { UnicodeV6 } from 'common/input/UnicodeV6';
export class MockBufferService implements IBufferService {
public serviceBrand: any;
public get buffer(): IBuffer { return this.buffers.active; }
public buffers: IBufferSet = {} as any;
public onResize: IEvent<{ cols: number, rows: number }> = new EventEmitter<{ cols: number, rows: number }>().event;
public isUserScrolling: boolean = false;
constructor(
public cols: number,
public rows: number,
@@ -31,6 +33,7 @@ export class MockBufferService implements IBufferService {
}
export class MockCoreMouseService implements ICoreMouseService {
public areMouseEventsActive: boolean = false;
public activeEncoding: string = '';
public activeProtocol: string = '';
public addEncoding(name: string): void {}
@@ -47,7 +50,6 @@ export class MockCharsetService implements ICharsetService {
public serviceBrand: any;
public charset: ICharset | undefined;
public glevel: number = 0;
public charsets: ReadonlyArray<ICharset> = [];
public reset(): void {}
public setgLevel(g: number): void {}
public setgCharset(g: number, charset: ICharset): void {}
@@ -58,11 +60,16 @@ export class MockCoreService implements ICoreService {
public isCursorInitialized: boolean = false;
public isCursorHidden: boolean = false;
public isFocused: boolean = false;
public modes: IModes = {
insertMode: false
};
public decPrivateModes: IDecPrivateModes = {
applicationCursorKeys: false,
applicationKeypad: false,
bracketedPasteMode: false,
origin: false,
reverseWraparound: false,
sendFocus: false,
wraparound: true
};
public onData: IEvent<string> = new EventEmitter<string>().event;
+107 -1
View File
@@ -3,13 +3,29 @@
* @license MIT
*/
import { ITerminalOptions as IPublicTerminalOptions } from 'xterm';
import { IEvent, IEventEmitter } from 'common/EventEmitter';
import { IDeleteEvent, IInsertEvent } from 'common/CircularList';
import { IParams } from 'common/parser/Types';
import { IOptionsService, IUnicodeService } from 'common/services/Services';
export interface ICoreTerminal {
optionsService: IOptionsService;
unicodeService: IUnicodeService;
}
export interface IDisposable {
dispose(): void;
}
// TODO: The options that are not in the public API should be reviewed
export interface ITerminalOptions extends IPublicTerminalOptions {
[key: string]: any;
cancelEvents?: boolean;
convertEol?: boolean;
termName?: string;
}
export type XtermListener = (...args: any[]) => void;
/**
@@ -41,7 +57,7 @@ export interface ICircularList<T> {
get(index: number): T | undefined;
set(index: number, value: T): void;
push(value: T): void;
recycle(): T | undefined;
recycle(): T;
pop(): T | undefined;
splice(start: number, deleteCount: number, ...items: T[]): void;
trimStart(count: number): void;
@@ -150,12 +166,17 @@ export interface IMarker extends IDisposable {
readonly isDisposed: boolean;
readonly line: number;
}
export interface IModes {
insertMode: boolean;
}
export interface IDecPrivateModes {
applicationCursorKeys: boolean;
applicationKeypad: boolean;
bracketedPasteMode: boolean;
origin: boolean;
reverseWraparound: boolean;
sendFocus: boolean;
wraparound: boolean; // defaults: xterm - true, vt100 - false
}
@@ -287,3 +308,88 @@ export interface IWindowOptions {
popTitle?: boolean;
setWinLines?: boolean;
}
/**
* Calls the parser and handles actions generated by the parser.
*/
export interface IInputHandler {
onTitleChange: IEvent<string>;
onRequestScroll: IEvent<IAttributeData, boolean | void>;
parse(data: string | Uint8Array): void;
print(data: Uint32Array, start: number, end: number): void;
/** C0 BEL */ bell(): void;
/** C0 LF */ lineFeed(): void;
/** C0 CR */ carriageReturn(): void;
/** C0 BS */ backspace(): void;
/** C0 HT */ tab(): void;
/** C0 SO */ shiftOut(): void;
/** C0 SI */ shiftIn(): void;
/** CSI @ */ insertChars(params: IParams): void;
/** CSI SP @ */ scrollLeft(params: IParams): void;
/** CSI A */ cursorUp(params: IParams): void;
/** CSI SP A */ scrollRight(params: IParams): void;
/** CSI B */ cursorDown(params: IParams): void;
/** CSI C */ cursorForward(params: IParams): void;
/** CSI D */ cursorBackward(params: IParams): void;
/** CSI E */ cursorNextLine(params: IParams): void;
/** CSI F */ cursorPrecedingLine(params: IParams): void;
/** CSI G */ cursorCharAbsolute(params: IParams): void;
/** CSI H */ cursorPosition(params: IParams): void;
/** CSI I */ cursorForwardTab(params: IParams): void;
/** CSI J */ eraseInDisplay(params: IParams): void;
/** CSI K */ eraseInLine(params: IParams): void;
/** CSI L */ insertLines(params: IParams): void;
/** CSI M */ deleteLines(params: IParams): void;
/** CSI P */ deleteChars(params: IParams): void;
/** CSI S */ scrollUp(params: IParams): void;
/** CSI T */ scrollDown(params: IParams, collect?: string): void;
/** CSI X */ eraseChars(params: IParams): void;
/** CSI Z */ cursorBackwardTab(params: IParams): void;
/** CSI ` */ charPosAbsolute(params: IParams): void;
/** CSI a */ hPositionRelative(params: IParams): void;
/** CSI b */ repeatPrecedingCharacter(params: IParams): void;
/** CSI c */ sendDeviceAttributesPrimary(params: IParams): void;
/** CSI > c */ sendDeviceAttributesSecondary(params: IParams): void;
/** CSI d */ linePosAbsolute(params: IParams): void;
/** CSI e */ vPositionRelative(params: IParams): void;
/** CSI f */ hVPosition(params: IParams): void;
/** CSI g */ tabClear(params: IParams): void;
/** CSI h */ setMode(params: IParams, collect?: string): void;
/** CSI l */ resetMode(params: IParams, collect?: string): void;
/** CSI m */ charAttributes(params: IParams): void;
/** CSI n */ deviceStatus(params: IParams, collect?: string): void;
/** CSI p */ softReset(params: IParams, collect?: string): void;
/** CSI q */ setCursorStyle(params: IParams, collect?: string): void;
/** CSI r */ setScrollRegion(params: IParams, collect?: string): void;
/** CSI s */ saveCursor(params: IParams): void;
/** CSI u */ restoreCursor(params: IParams): void;
/** CSI ' } */ insertColumns(params: IParams): void;
/** CSI ' ~ */ deleteColumns(params: IParams): void;
/** OSC 0
OSC 2 */ setTitle(data: string): void;
/** ESC E */ nextLine(): void;
/** ESC = */ keypadApplicationMode(): void;
/** ESC > */ keypadNumericMode(): void;
/** ESC % G
ESC % @ */ selectDefaultCharset(): void;
/** ESC ( C
ESC ) C
ESC * C
ESC + C
ESC - C
ESC . C
ESC / C */ selectCharset(collectAndFlag: string): void;
/** ESC D */ index(): void;
/** ESC H */ tabSet(): void;
/** ESC M */ reverseIndex(): void;
/** ESC c */ fullReset(): void;
/** ESC n
ESC o
ESC |
ESC }
ESC ~ */ setgLevel(level: number): void;
/** ESC # 8 */ screenAlignmentPattern(): void;
}
+1 -1
View File
@@ -36,7 +36,7 @@ export class Buffer implements IBuffer {
public savedY: number = 0;
public savedX: number = 0;
public savedCurAttrData = DEFAULT_ATTR_DATA.clone();
public savedCharset: ICharset | null = DEFAULT_CHARSET;
public savedCharset: ICharset | undefined = DEFAULT_CHARSET;
public markers: Marker[] = [];
private _nullCell: ICellData = CellData.fromCharData([0, NULL_CELL_CHAR, NULL_CELL_WIDTH, NULL_CELL_CODE]);
private _whitespaceCell: ICellData = CellData.fromCharData([0, WHITESPACE_CELL_CHAR, WHITESPACE_CELL_WIDTH, WHITESPACE_CELL_CODE]);
+1 -1
View File
@@ -31,7 +31,7 @@ export interface IBuffer {
hasScrollback: boolean;
savedY: number;
savedX: number;
savedCharset: ICharset | null;
savedCharset: ICharset | undefined;
savedCurAttrData: IAttributeData;
isCursorInViewport: boolean;
markers: IMarker[];
+3 -3
View File
@@ -10,12 +10,12 @@ import { ICharset } from 'common/Types';
* to be represented within the terminal with only 8-bit encoding. See ISO 2022
* for a discussion on character sets. Only VT100 character sets are supported.
*/
export const CHARSETS: { [key: string]: ICharset | null } = {};
export const CHARSETS: { [key: string]: ICharset | undefined } = {};
/**
* The default character set, US.
*/
export const DEFAULT_CHARSET: ICharset | null = CHARSETS['B'];
export const DEFAULT_CHARSET: ICharset | undefined = CHARSETS['B'];
/**
* DEC Special Character and Line Drawing Set.
@@ -74,7 +74,7 @@ CHARSETS['A'] = {
* United States character set
* ESC (B
*/
CHARSETS['B'] = null;
CHARSETS['B'] = undefined;
/**
* Dutch character set
+10
View File
@@ -6,6 +6,7 @@
import { IBufferService, IOptionsService } from 'common/services/Services';
import { BufferSet } from 'common/buffer/BufferSet';
import { IBufferSet, IBuffer } from 'common/buffer/Types';
import { EventEmitter, IEvent } from 'common/EventEmitter';
export const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars
export const MINIMUM_ROWS = 1;
@@ -16,6 +17,11 @@ export class BufferService implements IBufferService {
public cols: number;
public rows: number;
public buffers: IBufferSet;
/** Whether the user is scrolling (locks the scroll position) */
public isUserScrolling: boolean = false;
private _onResize = new EventEmitter<{ cols: number, rows: number }>();
public get onResize(): IEvent<{ cols: number, rows: number }> { return this._onResize.event; }
public get buffer(): IBuffer { return this.buffers.active; }
@@ -30,9 +36,13 @@ export class BufferService implements IBufferService {
public resize(cols: number, rows: number): void {
this.cols = cols;
this.rows = rows;
this.buffers.resize(cols, rows);
this.buffers.setupTabStops(this.cols);
this._onResize.fire({ cols, rows });
}
public reset(): void {
this.buffers = new BufferSet(this._optionsService, this);
this.isUserScrolling = false;
}
}
+6 -5
View File
@@ -10,22 +10,23 @@ export class CharsetService implements ICharsetService {
public serviceBrand: any;
public charset: ICharset | undefined;
public charsets: ICharset[] = [];
public glevel: number = 0;
private _charsets: (ICharset | undefined)[] = [];
public reset(): void {
this.charset = undefined;
this.charsets = [];
this._charsets = [];
this.glevel = 0;
}
public setgLevel(g: number): void {
this.glevel = g;
this.charset = this.charsets[g];
this.charset = this._charsets[g];
}
public setgCharset(g: number, charset: ICharset): void {
this.charsets[g] = charset;
public setgCharset(g: number, charset: ICharset | undefined): void {
this._charsets[g] = charset;
if (this.glevel === g) {
this.charset = charset;
}
+4
View File
@@ -192,6 +192,10 @@ export class CoreMouseService implements ICoreMouseService {
return this._activeProtocol;
}
public get areMouseEventsActive(): boolean {
return this._protocols[this._activeProtocol].events !== 0;
}
public set activeProtocol(name: string) {
if (!this._protocols[name]) {
throw new Error(`unknown protocol "${name}"`);
+10 -1
View File
@@ -5,14 +5,20 @@
import { ICoreService, ILogService, IOptionsService, IBufferService } from 'common/services/Services';
import { EventEmitter, IEvent } from 'common/EventEmitter';
import { IDecPrivateModes } from 'common/Types';
import { IDecPrivateModes, IModes } from 'common/Types';
import { clone } from 'common/Clone';
const DEFAULT_MODES: IModes = Object.freeze({
insertMode: false
});
const DEFAULT_DEC_PRIVATE_MODES: IDecPrivateModes = Object.freeze({
applicationCursorKeys: false,
applicationKeypad: false,
bracketedPasteMode: false,
origin: false,
reverseWraparound: false,
sendFocus: false,
wraparound: true // defaults: xterm - true, vt100 - false
});
@@ -21,6 +27,7 @@ export class CoreService implements ICoreService {
public isCursorInitialized: boolean = false;
public isCursorHidden: boolean = false;
public modes: IModes;
public decPrivateModes: IDecPrivateModes;
private _onData = new EventEmitter<string>();
@@ -37,10 +44,12 @@ export class CoreService implements ICoreService {
@ILogService private readonly _logService: ILogService,
@IOptionsService private readonly _optionsService: IOptionsService
) {
this.modes = clone(DEFAULT_MODES);
this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES);
}
public reset(): void {
this.modes = clone(DEFAULT_MODES);
this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES);
}
+29 -56
View File
@@ -5,19 +5,20 @@
import { IEvent } from 'common/EventEmitter';
import { IBuffer, IBufferSet } from 'common/buffer/Types';
import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEncoding, ICoreMouseProtocol, CoreMouseEventType, ICharset, IWindowOptions } from 'common/Types';
import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEncoding, ICoreMouseProtocol, CoreMouseEventType, ICharset, IWindowOptions, IModes } from 'common/Types';
import { createDecorator } from 'common/services/ServiceRegistry';
export const IBufferService = createDecorator<IBufferService>('BufferService');
export interface IBufferService {
serviceBrand: any;
serviceBrand: undefined;
readonly cols: number;
readonly rows: number;
readonly buffer: IBuffer;
readonly buffers: IBufferSet;
isUserScrolling: boolean;
// TODO: Move resize event here
onResize: IEvent<{ cols: number, rows: number }>;
resize(cols: number, rows: number): void;
reset(): void;
@@ -27,6 +28,7 @@ export const ICoreMouseService = createDecorator<ICoreMouseService>('CoreMouseSe
export interface ICoreMouseService {
activeProtocol: string;
activeEncoding: string;
areMouseEventsActive: boolean;
addProtocol(name: string, protocol: ICoreMouseProtocol): void;
addEncoding(name: string, encoding: CoreMouseEncoding): void;
reset(): void;
@@ -51,12 +53,12 @@ export interface ICoreMouseService {
/**
* Human readable version of mouse events.
*/
explainEvents(events: CoreMouseEventType): {[event: string]: boolean};
explainEvents(events: CoreMouseEventType): { [event: string]: boolean };
}
export const ICoreService = createDecorator<ICoreService>('CoreService');
export interface ICoreService {
serviceBrand: any;
serviceBrand: undefined;
/**
* Initially the cursor will not be visible until the first time the terminal
@@ -65,6 +67,7 @@ export interface ICoreService {
isCursorInitialized: boolean;
isCursorHidden: boolean;
readonly modes: IModes;
readonly decPrivateModes: IDecPrivateModes;
readonly onData: IEvent<string>;
@@ -92,11 +95,10 @@ export interface ICoreService {
export const ICharsetService = createDecorator<ICharsetService>('CharsetService');
export interface ICharsetService {
serviceBrand: any;
serviceBrand: undefined;
charset: ICharset | undefined;
readonly glevel: number;
readonly charsets: ReadonlyArray<ICharset>;
reset(): void;
@@ -111,12 +113,12 @@ export interface ICharsetService {
* @param g
* @param charset
*/
setgCharset(g: number, charset: ICharset): void;
setgCharset(g: number, charset: ICharset | undefined): void;
}
export const IDirtyRowService = createDecorator<IDirtyRowService>('DirtyRowService');
export interface IDirtyRowService {
serviceBrand: any;
serviceBrand: undefined;
readonly start: number;
readonly end: number;
@@ -132,61 +134,32 @@ export interface IServiceIdentifier<T> {
type: T;
}
export interface IConstructorSignature0<T> {
new(...services: { serviceBrand: any }[]): T;
}
export interface IBrandedService {
serviceBrand: undefined;
};
export interface IConstructorSignature1<A1, T> {
new(first: A1, ...services: { serviceBrand: any }[]): T;
}
export interface IConstructorSignature2<A1, A2, T> {
new(first: A1, second: A2, ...services: { serviceBrand: any }[]): T;
}
export interface IConstructorSignature3<A1, A2, A3, T> {
new(first: A1, second: A2, third: A3, ...services: { serviceBrand: any }[]): T;
}
export interface IConstructorSignature4<A1, A2, A3, A4, T> {
new(first: A1, second: A2, third: A3, fourth: A4, ...services: { serviceBrand: any }[]): T;
}
export interface IConstructorSignature5<A1, A2, A3, A4, A5, T> {
new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, ...services: { serviceBrand: any }[]): T;
}
export interface IConstructorSignature6<A1, A2, A3, A4, A5, A6, T> {
new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, ...services: { serviceBrand: any }[]): T;
}
export interface IConstructorSignature7<A1, A2, A3, A4, A5, A6, A7, T> {
new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, ...services: { serviceBrand: any }[]): T;
}
export interface IConstructorSignature8<A1, A2, A3, A4, A5, A6, A7, A8, T> {
new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8, ...services: { serviceBrand: any }[]): T;
}
type GetLeadingNonServiceArgs<Args> =
Args extends [...IBrandedService[]] ? []
: Args extends [infer A1, ...IBrandedService[]] ? [A1]
: Args extends [infer A1, infer A2, ...IBrandedService[]] ? [A1, A2]
: Args extends [infer A1, infer A2, infer A3, ...IBrandedService[]] ? [A1, A2, A3]
: Args extends [infer A1, infer A2, infer A3, infer A4, ...IBrandedService[]] ? [A1, A2, A3, A4]
: Args extends [infer A1, infer A2, infer A3, infer A4, infer A5, ...IBrandedService[]] ? [A1, A2, A3, A4, A5]
: Args extends [infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, ...IBrandedService[]] ? [A1, A2, A3, A4, A5, A6]
: Args extends [infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, infer A7, ...IBrandedService[]] ? [A1, A2, A3, A4, A5, A6, A7]
: Args extends [infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, infer A7, infer A8, ...IBrandedService[]] ? [A1, A2, A3, A4, A5, A6, A7, A8]
: never;
export const IInstantiationService = createDecorator<IInstantiationService>('InstantiationService');
export interface IInstantiationService {
setService<T>(id: IServiceIdentifier<T>, instance: T): void;
getService<T>(id: IServiceIdentifier<T>): T | undefined;
createInstance<T>(ctor: IConstructorSignature0<T>): T;
createInstance<A1, T>(ctor: IConstructorSignature1<A1, T>, first: A1): T;
createInstance<A1, A2, T>(ctor: IConstructorSignature2<A1, A2, T>, first: A1, second: A2): T;
createInstance<A1, A2, A3, T>(ctor: IConstructorSignature3<A1, A2, A3, T>, first: A1, second: A2, third: A3): T;
createInstance<A1, A2, A3, A4, T>(ctor: IConstructorSignature4<A1, A2, A3, A4, T>, first: A1, second: A2, third: A3, fourth: A4): T;
createInstance<A1, A2, A3, A4, A5, T>(ctor: IConstructorSignature5<A1, A2, A3, A4, A5, T>, first: A1, second: A2, third: A3, fourth: A4, fifth: A5): T;
createInstance<A1, A2, A3, A4, A5, A6, T>(ctor: IConstructorSignature6<A1, A2, A3, A4, A5, A6, T>, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6): T;
createInstance<A1, A2, A3, A4, A5, A6, A7, T>(ctor: IConstructorSignature7<A1, A2, A3, A4, A5, A6, A7, T>, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7): T;
createInstance<A1, A2, A3, A4, A5, A6, A7, A8, T>(ctor: IConstructorSignature8<A1, A2, A3, A4, A5, A6, A7, A8, T>, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8): T;
createInstance<Ctor extends new (...args: any[]) => any, R extends InstanceType<Ctor>>(t: Ctor, ...args: GetLeadingNonServiceArgs<ConstructorParameters<Ctor>>): R;
}
export const ILogService = createDecorator<ILogService>('LogService');
export interface ILogService {
serviceBrand: any;
serviceBrand: undefined;
debug(message: any, ...optionalParams: any[]): void;
info(message: any, ...optionalParams: any[]): void;
@@ -196,7 +169,7 @@ export interface ILogService {
export const IOptionsService = createDecorator<IOptionsService>('OptionsService');
export interface IOptionsService {
serviceBrand: any;
serviceBrand: undefined;
readonly options: ITerminalOptions;
@@ -311,7 +284,7 @@ export interface ITheme {
export const IUnicodeService = createDecorator<IUnicodeService>('UnicodeService');
export interface IUnicodeService {
serviceBrand: any;
serviceBrand: undefined;
/** Register an Unicode version provider. */
register(provider: IUnicodeVersionProvider): void;
/** Registered Unicode versions. */
+4 -1
View File
@@ -10,5 +10,8 @@
],
"baseUrl": ".."
},
"include": [ "./**/*" ]
"include": [
"./**/*",
"../../typings/xterm.d.ts"
]
}
+2 -1
View File
@@ -8,6 +8,7 @@
"removeComments": true,
"pretty": true,
"incremental": true
"incremental": true,
"experimentalDecorators": true
}
}
-34
View File
@@ -1,34 +0,0 @@
{
"extends": "./tsconfig-base",
"compilerOptions": {
"module": "commonjs",
"lib": [
"dom",
"es5",
"es6",
"scripthost",
"es2015.promise"
],
"rootDir": ".",
"outDir": "../out",
"baseUrl": ".",
"paths": {
"common/*": [ "./common/*" ],
"browser/*": [ "./browser/*" ]
},
"noUnusedLocals": true,
"noImplicitAny": true
},
"include": [
"./**/*",
"../typings/xterm.d.ts"
],
"exclude": [
"./addons/**/*"
],
"references": [
{ "path": "./common" },
{ "path": "./browser" }
]
}