diff options
Diffstat (limited to 'node_modules/xterm/src/common/services')
11 files changed, 1452 insertions, 0 deletions
diff --git a/node_modules/xterm/src/common/services/BufferService.ts b/node_modules/xterm/src/common/services/BufferService.ts new file mode 100644 index 0000000..bba60dd --- /dev/null +++ b/node_modules/xterm/src/common/services/BufferService.ts @@ -0,0 +1,185 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +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'; +import { Disposable } from 'common/Lifecycle'; +import { IAttributeData, IBufferLine, ScrollSource } from 'common/Types'; + +export const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars +export const MINIMUM_ROWS = 1; + +export class BufferService extends Disposable implements IBufferService { + public serviceBrand: any; + + 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; } + private _onScroll = new EventEmitter<number>(); + public get onScroll(): IEvent<number> { return this._onScroll.event; } + + public get buffer(): IBuffer { return this.buffers.active; } + + /** An IBufferline to clone/copy from for new blank lines */ + private _cachedBlankLine: IBufferLine | undefined; + + constructor( + @IOptionsService private _optionsService: IOptionsService + ) { + super(); + this.cols = Math.max(_optionsService.rawOptions.cols || 0, MINIMUM_COLS); + this.rows = Math.max(_optionsService.rawOptions.rows || 0, MINIMUM_ROWS); + this.buffers = new BufferSet(_optionsService, this); + } + + public dispose(): void { + super.dispose(); + this.buffers.dispose(); + } + + 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.reset(); + this.isUserScrolling = false; + } + + /** + * 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.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.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.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.isUserScrolling) { + buffer.ydisp = buffer.ybase; + } + + 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, source?: ScrollSource): void { + const buffer = this.buffer; + if (disp < 0) { + if (buffer.ydisp === 0) { + return; + } + this.isUserScrolling = true; + } else if (disp + buffer.ydisp >= buffer.ybase) { + this.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.buffer.ydisp); + } + + /** + * Scrolls the display of the terminal to the bottom. + */ + public scrollToBottom(): void { + this.scrollLines(this.buffer.ybase - this.buffer.ydisp); + } + + public scrollToLine(line: number): void { + const scrollAmount = line - this.buffer.ydisp; + if (scrollAmount !== 0) { + this.scrollLines(scrollAmount); + } + } +} diff --git a/node_modules/xterm/src/common/services/CharsetService.ts b/node_modules/xterm/src/common/services/CharsetService.ts new file mode 100644 index 0000000..c538106 --- /dev/null +++ b/node_modules/xterm/src/common/services/CharsetService.ts @@ -0,0 +1,34 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { ICharsetService } from 'common/services/Services'; +import { ICharset } from 'common/Types'; + +export class CharsetService implements ICharsetService { + public serviceBrand: any; + + public charset: ICharset | undefined; + public glevel: number = 0; + + private _charsets: (ICharset | undefined)[] = []; + + public reset(): void { + this.charset = undefined; + this._charsets = []; + this.glevel = 0; + } + + public setgLevel(g: number): void { + this.glevel = g; + this.charset = this._charsets[g]; + } + + public setgCharset(g: number, charset: ICharset | undefined): void { + this._charsets[g] = charset; + if (this.glevel === g) { + this.charset = charset; + } + } +} diff --git a/node_modules/xterm/src/common/services/CoreMouseService.ts b/node_modules/xterm/src/common/services/CoreMouseService.ts new file mode 100644 index 0000000..0b0dc36 --- /dev/null +++ b/node_modules/xterm/src/common/services/CoreMouseService.ts @@ -0,0 +1,309 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ +import { IBufferService, ICoreService, ICoreMouseService } from 'common/services/Services'; +import { EventEmitter, IEvent } from 'common/EventEmitter'; +import { ICoreMouseProtocol, ICoreMouseEvent, CoreMouseEncoding, CoreMouseEventType, CoreMouseButton, CoreMouseAction } from 'common/Types'; + +/** + * Supported default protocols. + */ +const DEFAULT_PROTOCOLS: {[key: string]: ICoreMouseProtocol} = { + /** + * NONE + * Events: none + * Modifiers: none + */ + NONE: { + events: CoreMouseEventType.NONE, + restrict: () => false + }, + /** + * X10 + * Events: mousedown + * Modifiers: none + */ + X10: { + events: CoreMouseEventType.DOWN, + restrict: (e: ICoreMouseEvent) => { + // no wheel, no move, no up + if (e.button === CoreMouseButton.WHEEL || e.action !== CoreMouseAction.DOWN) { + return false; + } + // no modifiers + e.ctrl = false; + e.alt = false; + e.shift = false; + return true; + } + }, + /** + * VT200 + * Events: mousedown / mouseup / wheel + * Modifiers: all + */ + VT200: { + events: CoreMouseEventType.DOWN | CoreMouseEventType.UP | CoreMouseEventType.WHEEL, + restrict: (e: ICoreMouseEvent) => { + // no move + if (e.action === CoreMouseAction.MOVE) { + return false; + } + return true; + } + }, + /** + * DRAG + * Events: mousedown / mouseup / wheel / mousedrag + * Modifiers: all + */ + DRAG: { + events: CoreMouseEventType.DOWN | CoreMouseEventType.UP | CoreMouseEventType.WHEEL | CoreMouseEventType.DRAG, + restrict: (e: ICoreMouseEvent) => { + // no move without button + if (e.action === CoreMouseAction.MOVE && e.button === CoreMouseButton.NONE) { + return false; + } + return true; + } + }, + /** + * ANY + * Events: all mouse related events + * Modifiers: all + */ + ANY: { + events: + CoreMouseEventType.DOWN | CoreMouseEventType.UP | CoreMouseEventType.WHEEL + | CoreMouseEventType.DRAG | CoreMouseEventType.MOVE, + restrict: (e: ICoreMouseEvent) => true + } +}; + +const enum Modifiers { + SHIFT = 4, + ALT = 8, + CTRL = 16 +} + +// helper for default encoders to generate the event code. +function eventCode(e: ICoreMouseEvent, isSGR: boolean): number { + let code = (e.ctrl ? Modifiers.CTRL : 0) | (e.shift ? Modifiers.SHIFT : 0) | (e.alt ? Modifiers.ALT : 0); + if (e.button === CoreMouseButton.WHEEL) { + code |= 64; + code |= e.action; + } else { + code |= e.button & 3; + if (e.button & 4) { + code |= 64; + } + if (e.button & 8) { + code |= 128; + } + if (e.action === CoreMouseAction.MOVE) { + code |= CoreMouseAction.MOVE; + } else if (e.action === CoreMouseAction.UP && !isSGR) { + // special case - only SGR can report button on release + // all others have to go with NONE + code |= CoreMouseButton.NONE; + } + } + return code; +} + +const S = String.fromCharCode; + +/** + * Supported default encodings. + */ +const DEFAULT_ENCODINGS: {[key: string]: CoreMouseEncoding} = { + /** + * DEFAULT - CSI M Pb Px Py + * Single byte encoding for coords and event code. + * Can encode values up to 223 (1-based). + */ + DEFAULT: (e: ICoreMouseEvent) => { + const params = [eventCode(e, false) + 32, e.col + 32, e.row + 32]; + // supress mouse report if we exceed addressible range + // Note this is handled differently by emulators + // - xterm: sends 0;0 coords instead + // - vte, konsole: no report + if (params[0] > 255 || params[1] > 255 || params[2] > 255) { + return ''; + } + return `\x1b[M${S(params[0])}${S(params[1])}${S(params[2])}`; + }, + /** + * SGR - CSI < Pb ; Px ; Py M|m + * No encoding limitation. + * Can report button on release and works with a well formed sequence. + */ + SGR: (e: ICoreMouseEvent) => { + const final = (e.action === CoreMouseAction.UP && e.button !== CoreMouseButton.WHEEL) ? 'm' : 'M'; + return `\x1b[<${eventCode(e, true)};${e.col};${e.row}${final}`; + } +}; + +/** + * CoreMouseService + * + * Provides mouse tracking reports with different protocols and encodings. + * - protocols: NONE (default), X10, VT200, DRAG, ANY + * - encodings: DEFAULT, SGR (UTF8, URXVT removed in #2507) + * + * Custom protocols/encodings can be added by `addProtocol` / `addEncoding`. + * To activate a protocol/encoding, set `activeProtocol` / `activeEncoding`. + * Switching a protocol will send a notification event `onProtocolChange` + * with a list of needed events to track. + * + * The service handles the mouse tracking state and decides whether to send + * a tracking report to the backend based on protocol and encoding limitations. + * To send a mouse event call `triggerMouseEvent`. + */ +export class CoreMouseService implements ICoreMouseService { + private _protocols: {[name: string]: ICoreMouseProtocol} = {}; + private _encodings: {[name: string]: CoreMouseEncoding} = {}; + private _activeProtocol: string = ''; + private _activeEncoding: string = ''; + private _onProtocolChange = new EventEmitter<CoreMouseEventType>(); + private _lastEvent: ICoreMouseEvent | null = null; + + constructor( + @IBufferService private readonly _bufferService: IBufferService, + @ICoreService private readonly _coreService: ICoreService + ) { + // register default protocols and encodings + for (const name of Object.keys(DEFAULT_PROTOCOLS)) this.addProtocol(name, DEFAULT_PROTOCOLS[name]); + for (const name of Object.keys(DEFAULT_ENCODINGS)) this.addEncoding(name, DEFAULT_ENCODINGS[name]); + // call reset to set defaults + this.reset(); + } + + public addProtocol(name: string, protocol: ICoreMouseProtocol): void { + this._protocols[name] = protocol; + } + + public addEncoding(name: string, encoding: CoreMouseEncoding): void { + this._encodings[name] = encoding; + } + + public get activeProtocol(): string { + 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}"`); + } + this._activeProtocol = name; + this._onProtocolChange.fire(this._protocols[name].events); + } + + public get activeEncoding(): string { + return this._activeEncoding; + } + + public set activeEncoding(name: string) { + if (!this._encodings[name]) { + throw new Error(`unknown encoding "${name}"`); + } + this._activeEncoding = name; + } + + public reset(): void { + this.activeProtocol = 'NONE'; + this.activeEncoding = 'DEFAULT'; + this._lastEvent = null; + } + + /** + * Event to announce changes in mouse tracking. + */ + public get onProtocolChange(): IEvent<CoreMouseEventType> { + return this._onProtocolChange.event; + } + + /** + * Triggers a mouse event to be sent. + * + * Returns true if the event passed all protocol restrictions and a report + * was sent, otherwise false. The return value may be used to decide whether + * the default event action in the bowser component should be omitted. + * + * Note: The method will change values of the given event object + * to fullfill protocol and encoding restrictions. + */ + public triggerMouseEvent(e: ICoreMouseEvent): boolean { + // range check for col/row + if (e.col < 0 || e.col >= this._bufferService.cols + || e.row < 0 || e.row >= this._bufferService.rows) { + return false; + } + + // filter nonsense combinations of button + action + if (e.button === CoreMouseButton.WHEEL && e.action === CoreMouseAction.MOVE) { + return false; + } + if (e.button === CoreMouseButton.NONE && e.action !== CoreMouseAction.MOVE) { + return false; + } + if (e.button !== CoreMouseButton.WHEEL && (e.action === CoreMouseAction.LEFT || e.action === CoreMouseAction.RIGHT)) { + return false; + } + + // report 1-based coords + e.col++; + e.row++; + + // debounce move at grid level + if (e.action === CoreMouseAction.MOVE && this._lastEvent && this._compareEvents(this._lastEvent, e)) { + return false; + } + + // apply protocol restrictions + if (!this._protocols[this._activeProtocol].restrict(e)) { + return false; + } + + // encode report and send + const report = this._encodings[this._activeEncoding](e); + if (report) { + // always send DEFAULT as binary data + if (this._activeEncoding === 'DEFAULT') { + this._coreService.triggerBinaryEvent(report); + } else { + this._coreService.triggerDataEvent(report, true); + } + } + + this._lastEvent = e; + + return true; + } + + public explainEvents(events: CoreMouseEventType): {[event: string]: boolean} { + return { + down: !!(events & CoreMouseEventType.DOWN), + up: !!(events & CoreMouseEventType.UP), + drag: !!(events & CoreMouseEventType.DRAG), + move: !!(events & CoreMouseEventType.MOVE), + wheel: !!(events & CoreMouseEventType.WHEEL) + }; + } + + private _compareEvents(e1: ICoreMouseEvent, e2: ICoreMouseEvent): boolean { + if (e1.col !== e2.col) return false; + if (e1.row !== e2.row) return false; + if (e1.button !== e2.button) return false; + if (e1.action !== e2.action) return false; + if (e1.ctrl !== e2.ctrl) return false; + if (e1.alt !== e2.alt) return false; + if (e1.shift !== e2.shift) return false; + return true; + } +} diff --git a/node_modules/xterm/src/common/services/CoreService.ts b/node_modules/xterm/src/common/services/CoreService.ts new file mode 100644 index 0000000..20a3460 --- /dev/null +++ b/node_modules/xterm/src/common/services/CoreService.ts @@ -0,0 +1,92 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { ICoreService, ILogService, IOptionsService, IBufferService } from 'common/services/Services'; +import { EventEmitter, IEvent } from 'common/EventEmitter'; +import { IDecPrivateModes, IModes } from 'common/Types'; +import { clone } from 'common/Clone'; +import { Disposable } from 'common/Lifecycle'; + +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 +}); + +export class CoreService extends Disposable implements ICoreService { + public serviceBrand: any; + + public isCursorInitialized: boolean = false; + public isCursorHidden: boolean = false; + public modes: IModes; + public decPrivateModes: IDecPrivateModes; + + // Circular dependency, this must be unset or memory will leak after Terminal.dispose + private _scrollToBottom: (() => void) | undefined; + + private _onData = this.register(new EventEmitter<string>()); + public get onData(): IEvent<string> { return this._onData.event; } + private _onUserInput = this.register(new EventEmitter<void>()); + public get onUserInput(): IEvent<void> { return this._onUserInput.event; } + private _onBinary = this.register(new EventEmitter<string>()); + public get onBinary(): IEvent<string> { return this._onBinary.event; } + + constructor( + // TODO: Move this into a service + scrollToBottom: () => void, + @IBufferService private readonly _bufferService: IBufferService, + @ILogService private readonly _logService: ILogService, + @IOptionsService private readonly _optionsService: IOptionsService + ) { + super(); + this._scrollToBottom = scrollToBottom; + this.register({ dispose: () => this._scrollToBottom = undefined }); + 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); + } + + public triggerDataEvent(data: string, wasUserInput: boolean = false): void { + // Prevents all events to pty process if stdin is disabled + if (this._optionsService.rawOptions.disableStdin) { + return; + } + + // Input is being sent to the terminal, the terminal should focus the prompt. + const buffer = this._bufferService.buffer; + if (buffer.ybase !== buffer.ydisp) { + this._scrollToBottom!(); + } + + // Fire onUserInput so listeners can react as well (eg. clear selection) + if (wasUserInput) { + this._onUserInput.fire(); + } + + // Fire onData API + this._logService.debug(`sending data "${data}"`, () => data.split('').map(e => e.charCodeAt(0))); + this._onData.fire(data); + } + + public triggerBinaryEvent(data: string): void { + if (this._optionsService.rawOptions.disableStdin) { + return; + } + this._logService.debug(`sending binary "${data}"`, () => data.split('').map(e => e.charCodeAt(0))); + this._onBinary.fire(data); + } +} diff --git a/node_modules/xterm/src/common/services/DirtyRowService.ts b/node_modules/xterm/src/common/services/DirtyRowService.ts new file mode 100644 index 0000000..1c43b67 --- /dev/null +++ b/node_modules/xterm/src/common/services/DirtyRowService.ts @@ -0,0 +1,53 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { IBufferService, IDirtyRowService } from 'common/services/Services'; + +export class DirtyRowService implements IDirtyRowService { + public serviceBrand: any; + + private _start!: number; + private _end!: number; + + public get start(): number { return this._start; } + public get end(): number { return this._end; } + + constructor( + @IBufferService private readonly _bufferService: IBufferService + ) { + this.clearRange(); + } + + public clearRange(): void { + this._start = this._bufferService.buffer.y; + this._end = this._bufferService.buffer.y; + } + + public markDirty(y: number): void { + if (y < this._start) { + this._start = y; + } else if (y > this._end) { + this._end = y; + } + } + + public markRangeDirty(y1: number, y2: number): void { + if (y1 > y2) { + const temp = y1; + y1 = y2; + y2 = temp; + } + if (y1 < this._start) { + this._start = y1; + } + if (y2 > this._end) { + this._end = y2; + } + } + + public markAllDirty(): void { + this.markRangeDirty(0, this._bufferService.rows - 1); + } +} diff --git a/node_modules/xterm/src/common/services/InstantiationService.ts b/node_modules/xterm/src/common/services/InstantiationService.ts new file mode 100644 index 0000000..8280948 --- /dev/null +++ b/node_modules/xterm/src/common/services/InstantiationService.ts @@ -0,0 +1,83 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + * + * This was heavily inspired from microsoft/vscode's dependency injection system (MIT). + */ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IInstantiationService, IServiceIdentifier } from 'common/services/Services'; +import { getServiceDependencies } from 'common/services/ServiceRegistry'; + +export class ServiceCollection { + + private _entries = new Map<IServiceIdentifier<any>, any>(); + + constructor(...entries: [IServiceIdentifier<any>, any][]) { + for (const [id, service] of entries) { + this.set(id, service); + } + } + + public set<T>(id: IServiceIdentifier<T>, instance: T): T { + const result = this._entries.get(id); + this._entries.set(id, instance); + return result; + } + + public forEach(callback: (id: IServiceIdentifier<any>, instance: any) => any): void { + this._entries.forEach((value, key) => callback(key, value)); + } + + public has(id: IServiceIdentifier<any>): boolean { + return this._entries.has(id); + } + + public get<T>(id: IServiceIdentifier<T>): T | undefined { + return this._entries.get(id); + } +} + +export class InstantiationService implements IInstantiationService { + public serviceBrand: undefined; + + private readonly _services: ServiceCollection = new ServiceCollection(); + + constructor() { + this._services.set(IInstantiationService, this); + } + + public setService<T>(id: IServiceIdentifier<T>, instance: T): void { + this._services.set(id, instance); + } + + public getService<T>(id: IServiceIdentifier<T>): T | undefined { + return this._services.get(id); + } + + public createInstance<T>(ctor: any, ...args: any[]): T { + const serviceDependencies = getServiceDependencies(ctor).sort((a, b) => a.index - b.index); + + const serviceArgs: any[] = []; + for (const dependency of serviceDependencies) { + const service = this._services.get(dependency.id); + if (!service) { + throw new Error(`[createInstance] ${ctor.name} depends on UNKNOWN service ${dependency.id}.`); + } + serviceArgs.push(service); + } + + const firstServiceArgPos = serviceDependencies.length > 0 ? serviceDependencies[0].index : args.length; + + // check for argument mismatches, adjust static args if needed + if (args.length !== firstServiceArgPos) { + throw new Error(`[createInstance] First service dependency of ${ctor.name} at position ${firstServiceArgPos + 1} conflicts with ${args.length} static arguments`); + } + + // now create the instance + return new ctor(...[...args, ...serviceArgs]); + } +} diff --git a/node_modules/xterm/src/common/services/LogService.ts b/node_modules/xterm/src/common/services/LogService.ts new file mode 100644 index 0000000..d356656 --- /dev/null +++ b/node_modules/xterm/src/common/services/LogService.ts @@ -0,0 +1,88 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { ILogService, IOptionsService, LogLevelEnum } from 'common/services/Services'; + +type LogType = (message?: any, ...optionalParams: any[]) => void; + +interface IConsole { + log: LogType; + error: LogType; + info: LogType; + trace: LogType; + warn: LogType; +} + +// console is available on both node.js and browser contexts but the common +// module doesn't depend on them so we need to explicitly declare it. +declare const console: IConsole; + +const optionsKeyToLogLevel: { [key: string]: LogLevelEnum } = { + debug: LogLevelEnum.DEBUG, + info: LogLevelEnum.INFO, + warn: LogLevelEnum.WARN, + error: LogLevelEnum.ERROR, + off: LogLevelEnum.OFF +}; + +const LOG_PREFIX = 'xterm.js: '; + +export class LogService implements ILogService { + public serviceBrand: any; + + public logLevel: LogLevelEnum = LogLevelEnum.OFF; + + constructor( + @IOptionsService private readonly _optionsService: IOptionsService + ) { + this._updateLogLevel(); + this._optionsService.onOptionChange(key => { + if (key === 'logLevel') { + this._updateLogLevel(); + } + }); + } + + private _updateLogLevel(): void { + this.logLevel = optionsKeyToLogLevel[this._optionsService.rawOptions.logLevel]; + } + + private _evalLazyOptionalParams(optionalParams: any[]): void { + for (let i = 0; i < optionalParams.length; i++) { + if (typeof optionalParams[i] === 'function') { + optionalParams[i] = optionalParams[i](); + } + } + } + + private _log(type: LogType, message: string, optionalParams: any[]): void { + this._evalLazyOptionalParams(optionalParams); + type.call(console, LOG_PREFIX + message, ...optionalParams); + } + + public debug(message: string, ...optionalParams: any[]): void { + if (this.logLevel <= LogLevelEnum.DEBUG) { + this._log(console.log, message, optionalParams); + } + } + + public info(message: string, ...optionalParams: any[]): void { + if (this.logLevel <= LogLevelEnum.INFO) { + this._log(console.info, message, optionalParams); + } + } + + public warn(message: string, ...optionalParams: any[]): void { + if (this.logLevel <= LogLevelEnum.WARN) { + this._log(console.warn, message, optionalParams); + } + } + + public error(message: string, ...optionalParams: any[]): void { + if (this.logLevel <= LogLevelEnum.ERROR) { + this._log(console.error, message, optionalParams); + } + } +} diff --git a/node_modules/xterm/src/common/services/OptionsService.ts b/node_modules/xterm/src/common/services/OptionsService.ts new file mode 100644 index 0000000..43fe998 --- /dev/null +++ b/node_modules/xterm/src/common/services/OptionsService.ts @@ -0,0 +1,177 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { IOptionsService, ITerminalOptions, FontWeight } from 'common/services/Services'; +import { EventEmitter, IEvent } from 'common/EventEmitter'; +import { isMac } from 'common/Platform'; + +// Source: https://freesound.org/people/altemark/sounds/45759/ +// This sound is released under the Creative Commons Attribution 3.0 Unported +// (CC BY 3.0) license. It was created by 'altemark'. No modifications have been +// made, apart from the conversion to base64. +export const DEFAULT_BELL_SOUND = 'data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjMyLjEwNAAAAAAAAAAAAAAA//tQxAADB8AhSmxhIIEVCSiJrDCQBTcu3UrAIwUdkRgQbFAZC1CQEwTJ9mjRvBA4UOLD8nKVOWfh+UlK3z/177OXrfOdKl7pyn3Xf//WreyTRUoAWgBgkOAGbZHBgG1OF6zM82DWbZaUmMBptgQhGjsyYqc9ae9XFz280948NMBWInljyzsNRFLPWdnZGWrddDsjK1unuSrVN9jJsK8KuQtQCtMBjCEtImISdNKJOopIpBFpNSMbIHCSRpRR5iakjTiyzLhchUUBwCgyKiweBv/7UsQbg8isVNoMPMjAAAA0gAAABEVFGmgqK////9bP/6XCykxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq'; + +export const DEFAULT_OPTIONS: Readonly<ITerminalOptions> = { + cols: 80, + rows: 24, + cursorBlink: false, + cursorStyle: 'block', + cursorWidth: 1, + customGlyphs: true, + bellSound: DEFAULT_BELL_SOUND, + bellStyle: 'none', + drawBoldTextInBrightColors: true, + fastScrollModifier: 'alt', + fastScrollSensitivity: 5, + fontFamily: 'courier-new, courier, monospace', + fontSize: 15, + fontWeight: 'normal', + fontWeightBold: 'bold', + lineHeight: 1.0, + linkTooltipHoverDuration: 500, + letterSpacing: 0, + logLevel: 'info', + scrollback: 1000, + scrollSensitivity: 1, + screenReaderMode: false, + macOptionIsMeta: false, + macOptionClickForcesSelection: false, + minimumContrastRatio: 1, + disableStdin: false, + allowProposedApi: true, + allowTransparency: false, + tabStopWidth: 8, + theme: {}, + rightClickSelectsWord: isMac, + rendererType: 'canvas', + windowOptions: {}, + windowsMode: false, + wordSeparator: ' ()[]{}\',"`', + altClickMovesCursor: true, + convertEol: false, + termName: 'xterm', + cancelEvents: false +}; + +const FONT_WEIGHT_OPTIONS: Extract<FontWeight, string>[] = ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900']; + +export class OptionsService implements IOptionsService { + public serviceBrand: any; + + public readonly rawOptions: ITerminalOptions; + public options: ITerminalOptions; + + private _onOptionChange = new EventEmitter<string>(); + public get onOptionChange(): IEvent<string> { return this._onOptionChange.event; } + + constructor(options: Partial<ITerminalOptions>) { + // set the default value of each option + const defaultOptions = { ...DEFAULT_OPTIONS }; + for (const key in options) { + if (key in defaultOptions) { + try { + const newValue = options[key]; + defaultOptions[key] = this._sanitizeAndValidateOption(key, newValue); + } catch (e) { + console.error(e); + } + } + } + + // set up getters and setters for each option + this.rawOptions = defaultOptions; + this.options = { ... defaultOptions }; + this._setupOptions(); + } + + private _setupOptions(): void { + const getter = (propName: string): any => { + if (!(propName in DEFAULT_OPTIONS)) { + throw new Error(`No option with key "${propName}"`); + } + return this.rawOptions[propName]; + }; + + const setter = (propName: string, value: any): void => { + if (!(propName in DEFAULT_OPTIONS)) { + throw new Error(`No option with key "${propName}"`); + } + + value = this._sanitizeAndValidateOption(propName, value); + // Don't fire an option change event if they didn't change + if (this.rawOptions[propName] !== value) { + this.rawOptions[propName] = value; + this._onOptionChange.fire(propName); + } + }; + + for (const propName in this.rawOptions) { + const desc = { + get: getter.bind(this, propName), + set: setter.bind(this, propName) + }; + Object.defineProperty(this.options, propName, desc); + } + } + + public setOption(key: string, value: any): void { + this.options[key] = value; + } + + private _sanitizeAndValidateOption(key: string, value: any): any { + switch (key) { + case 'bellStyle': + case 'cursorStyle': + case 'rendererType': + case 'wordSeparator': + if (!value) { + value = DEFAULT_OPTIONS[key]; + } + break; + case 'fontWeight': + case 'fontWeightBold': + if (typeof value === 'number' && 1 <= value && value <= 1000) { + // already valid numeric value + break; + } + value = FONT_WEIGHT_OPTIONS.includes(value) ? value : DEFAULT_OPTIONS[key]; + break; + case 'cursorWidth': + value = Math.floor(value); + // Fall through for bounds check + case 'lineHeight': + case 'tabStopWidth': + if (value < 1) { + throw new Error(`${key} cannot be less than 1, value: ${value}`); + } + break; + case 'minimumContrastRatio': + value = Math.max(1, Math.min(21, Math.round(value * 10) / 10)); + break; + case 'scrollback': + value = Math.min(value, 4294967295); + if (value < 0) { + throw new Error(`${key} cannot be less than 0, value: ${value}`); + } + break; + case 'fastScrollSensitivity': + case 'scrollSensitivity': + if (value <= 0) { + throw new Error(`${key} cannot be less than or equal to 0, value: ${value}`); + } + case 'rows': + case 'cols': + if (!value && value !== 0) { + throw new Error(`${key} must be numeric, value: ${value}`); + } + break; + } + return value; + } + + public getOption(key: string): any { + return this.options[key]; + } +} diff --git a/node_modules/xterm/src/common/services/ServiceRegistry.ts b/node_modules/xterm/src/common/services/ServiceRegistry.ts new file mode 100644 index 0000000..6510fb8 --- /dev/null +++ b/node_modules/xterm/src/common/services/ServiceRegistry.ts @@ -0,0 +1,49 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + * + * This was heavily inspired from microsoft/vscode's dependency injection system (MIT). + */ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IServiceIdentifier } from 'common/services/Services'; + +const DI_TARGET = 'di$target'; +const DI_DEPENDENCIES = 'di$dependencies'; + +export const serviceRegistry: Map<string, IServiceIdentifier<any>> = new Map(); + +export function getServiceDependencies(ctor: any): { id: IServiceIdentifier<any>, index: number, optional: boolean }[] { + return ctor[DI_DEPENDENCIES] || []; +} + +export function createDecorator<T>(id: string): IServiceIdentifier<T> { + if (serviceRegistry.has(id)) { + return serviceRegistry.get(id)!; + } + + const decorator: any = function (target: Function, key: string, index: number): any { + if (arguments.length !== 3) { + throw new Error('@IServiceName-decorator can only be used to decorate a parameter'); + } + + storeServiceDependency(decorator, target, index); + }; + + decorator.toString = () => id; + + serviceRegistry.set(id, decorator); + return decorator; +} + +function storeServiceDependency(id: Function, target: Function, index: number): void { + if ((target as any)[DI_TARGET] === target) { + (target as any)[DI_DEPENDENCIES].push({ id, index }); + } else { + (target as any)[DI_DEPENDENCIES] = [{ id, index }]; + (target as any)[DI_TARGET] = target; + } +} diff --git a/node_modules/xterm/src/common/services/Services.ts b/node_modules/xterm/src/common/services/Services.ts new file mode 100644 index 0000000..90dca98 --- /dev/null +++ b/node_modules/xterm/src/common/services/Services.ts @@ -0,0 +1,300 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ + +import { IEvent } from 'common/EventEmitter'; +import { IBuffer, IBufferSet } from 'common/buffer/Types'; +import { IDecPrivateModes, ICoreMouseEvent, CoreMouseEncoding, ICoreMouseProtocol, CoreMouseEventType, ICharset, IWindowOptions, IModes, IAttributeData, ScrollSource } from 'common/Types'; +import { createDecorator } from 'common/services/ServiceRegistry'; + +export const IBufferService = createDecorator<IBufferService>('BufferService'); +export interface IBufferService { + serviceBrand: undefined; + + readonly cols: number; + readonly rows: number; + readonly buffer: IBuffer; + readonly buffers: IBufferSet; + isUserScrolling: boolean; + onResize: IEvent<{ cols: number, rows: number }>; + onScroll: IEvent<number>; + scroll(eraseAttr: IAttributeData, isWrapped?: boolean): void; + scrollToBottom(): void; + scrollToTop(): void; + scrollToLine(line: number): void; + scrollLines(disp: number, suppressScrollEvent?: boolean, source?: ScrollSource): void; + scrollPages(pageCount: number): void; + resize(cols: number, rows: number): void; + reset(): void; +} + +export const ICoreMouseService = createDecorator<ICoreMouseService>('CoreMouseService'); +export interface ICoreMouseService { + activeProtocol: string; + activeEncoding: string; + areMouseEventsActive: boolean; + addProtocol(name: string, protocol: ICoreMouseProtocol): void; + addEncoding(name: string, encoding: CoreMouseEncoding): void; + reset(): void; + + /** + * Triggers a mouse event to be sent. + * + * Returns true if the event passed all protocol restrictions and a report + * was sent, otherwise false. The return value may be used to decide whether + * the default event action in the bowser component should be omitted. + * + * Note: The method will change values of the given event object + * to fullfill protocol and encoding restrictions. + */ + triggerMouseEvent(event: ICoreMouseEvent): boolean; + + /** + * Event to announce changes in mouse tracking. + */ + onProtocolChange: IEvent<CoreMouseEventType>; + + /** + * Human readable version of mouse events. + */ + explainEvents(events: CoreMouseEventType): { [event: string]: boolean }; +} + +export const ICoreService = createDecorator<ICoreService>('CoreService'); +export interface ICoreService { + serviceBrand: undefined; + + /** + * Initially the cursor will not be visible until the first time the terminal + * is focused. + */ + isCursorInitialized: boolean; + isCursorHidden: boolean; + + readonly modes: IModes; + readonly decPrivateModes: IDecPrivateModes; + + readonly onData: IEvent<string>; + readonly onUserInput: IEvent<void>; + readonly onBinary: IEvent<string>; + + reset(): void; + + /** + * Triggers the onData event in the public API. + * @param data The data that is being emitted. + * @param wasFromUser Whether the data originated from the user (as opposed to + * resulting from parsing incoming data). When true this will also: + * - Scroll to the bottom of the buffer.s + * - Fire the `onUserInput` event (so selection can be cleared). + */ + triggerDataEvent(data: string, wasUserInput?: boolean): void; + + /** + * Triggers the onBinary event in the public API. + * @param data The data that is being emitted. + */ + triggerBinaryEvent(data: string): void; +} + +export const ICharsetService = createDecorator<ICharsetService>('CharsetService'); +export interface ICharsetService { + serviceBrand: undefined; + + charset: ICharset | undefined; + readonly glevel: number; + + reset(): void; + + /** + * Set the G level of the terminal. + * @param g + */ + setgLevel(g: number): void; + + /** + * Set the charset for the given G level of the terminal. + * @param g + * @param charset + */ + setgCharset(g: number, charset: ICharset | undefined): void; +} + +export const IDirtyRowService = createDecorator<IDirtyRowService>('DirtyRowService'); +export interface IDirtyRowService { + serviceBrand: undefined; + + readonly start: number; + readonly end: number; + + clearRange(): void; + markDirty(y: number): void; + markRangeDirty(y1: number, y2: number): void; + markAllDirty(): void; +} + +export interface IServiceIdentifier<T> { + (...args: any[]): void; + type: T; +} + +export interface IBrandedService { + serviceBrand: undefined; +} + +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 { + serviceBrand: undefined; + + setService<T>(id: IServiceIdentifier<T>, instance: T): void; + getService<T>(id: IServiceIdentifier<T>): T | undefined; + createInstance<Ctor extends new (...args: any[]) => any, R extends InstanceType<Ctor>>(t: Ctor, ...args: GetLeadingNonServiceArgs<ConstructorParameters<Ctor>>): R; +} + +export enum LogLevelEnum { + DEBUG = 0, + INFO = 1, + WARN = 2, + ERROR = 3, + OFF = 4 +} + +export const ILogService = createDecorator<ILogService>('LogService'); +export interface ILogService { + serviceBrand: undefined; + + logLevel: LogLevelEnum; + + debug(message: any, ...optionalParams: any[]): void; + info(message: any, ...optionalParams: any[]): void; + warn(message: any, ...optionalParams: any[]): void; + error(message: any, ...optionalParams: any[]): void; +} + +export const IOptionsService = createDecorator<IOptionsService>('OptionsService'); +export interface IOptionsService { + serviceBrand: undefined; + + /** + * Read only access to the raw options object, this is an internal-only fast path for accessing + * single options without any validation as we trust TypeScript to enforce correct usage + * internally. + */ + readonly rawOptions: Readonly<ITerminalOptions>; + readonly options: ITerminalOptions; + + readonly onOptionChange: IEvent<string>; + + setOption<T>(key: string, value: T): void; + getOption<T>(key: string): T | undefined; +} + +export type FontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900' | number; +export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'off'; + +export type RendererType = 'dom' | 'canvas'; + +export interface ITerminalOptions { + allowProposedApi: boolean; + allowTransparency: boolean; + altClickMovesCursor: boolean; + bellSound: string; + bellStyle: 'none' | 'sound' /* | 'visual' | 'both' */; + cols: number; + convertEol: boolean; + cursorBlink: boolean; + cursorStyle: 'block' | 'underline' | 'bar'; + cursorWidth: number; + customGlyphs: boolean; + disableStdin: boolean; + drawBoldTextInBrightColors: boolean; + fastScrollModifier: 'alt' | 'ctrl' | 'shift' | undefined; + fastScrollSensitivity: number; + fontSize: number; + fontFamily: string; + fontWeight: FontWeight; + fontWeightBold: FontWeight; + letterSpacing: number; + lineHeight: number; + linkTooltipHoverDuration: number; + logLevel: LogLevel; + macOptionIsMeta: boolean; + macOptionClickForcesSelection: boolean; + minimumContrastRatio: number; + rendererType: RendererType; + rightClickSelectsWord: boolean; + rows: number; + screenReaderMode: boolean; + scrollback: number; + scrollSensitivity: number; + tabStopWidth: number; + theme: ITheme; + windowsMode: boolean; + windowOptions: IWindowOptions; + wordSeparator: string; + + [key: string]: any; + cancelEvents: boolean; + termName: string; +} + +export interface ITheme { + foreground?: string; + background?: string; + cursor?: string; + cursorAccent?: string; + selection?: string; + black?: string; + red?: string; + green?: string; + yellow?: string; + blue?: string; + magenta?: string; + cyan?: string; + white?: string; + brightBlack?: string; + brightRed?: string; + brightGreen?: string; + brightYellow?: string; + brightBlue?: string; + brightMagenta?: string; + brightCyan?: string; + brightWhite?: string; +} + +export const IUnicodeService = createDecorator<IUnicodeService>('UnicodeService'); +export interface IUnicodeService { + serviceBrand: undefined; + /** Register an Unicode version provider. */ + register(provider: IUnicodeVersionProvider): void; + /** Registered Unicode versions. */ + readonly versions: string[]; + /** Currently active version. */ + activeVersion: string; + /** Event triggered, when activate version changed. */ + readonly onChange: IEvent<string>; + + /** + * Unicode version dependent + */ + wcwidth(codepoint: number): number; + getStringCellWidth(s: string): number; +} + +export interface IUnicodeVersionProvider { + readonly version: string; + wcwidth(ucs: number): 0 | 1 | 2; +} diff --git a/node_modules/xterm/src/common/services/UnicodeService.ts b/node_modules/xterm/src/common/services/UnicodeService.ts new file mode 100644 index 0000000..e96b757 --- /dev/null +++ b/node_modules/xterm/src/common/services/UnicodeService.ts @@ -0,0 +1,82 @@ +/** + * Copyright (c) 2019 The xterm.js authors. All rights reserved. + * @license MIT + */ +import { IUnicodeService, IUnicodeVersionProvider } from 'common/services/Services'; +import { EventEmitter, IEvent } from 'common/EventEmitter'; +import { UnicodeV6 } from 'common/input/UnicodeV6'; + + +export class UnicodeService implements IUnicodeService { + public serviceBrand: any; + + private _providers: {[key: string]: IUnicodeVersionProvider} = Object.create(null); + private _active: string = ''; + private _activeProvider: IUnicodeVersionProvider; + private _onChange = new EventEmitter<string>(); + public get onChange(): IEvent<string> { return this._onChange.event; } + + constructor() { + const defaultProvider = new UnicodeV6(); + this.register(defaultProvider); + this._active = defaultProvider.version; + this._activeProvider = defaultProvider; + } + + public get versions(): string[] { + return Object.keys(this._providers); + } + + public get activeVersion(): string { + return this._active; + } + + public set activeVersion(version: string) { + if (!this._providers[version]) { + throw new Error(`unknown Unicode version "${version}"`); + } + this._active = version; + this._activeProvider = this._providers[version]; + this._onChange.fire(version); + } + + public register(provider: IUnicodeVersionProvider): void { + this._providers[provider.version] = provider; + } + + /** + * Unicode version dependent interface. + */ + public wcwidth(num: number): number { + return this._activeProvider.wcwidth(num); + } + + public getStringCellWidth(s: string): number { + let result = 0; + const length = s.length; + for (let i = 0; i < length; ++i) { + let code = s.charCodeAt(i); + // surrogate pair first + if (0xD800 <= code && code <= 0xDBFF) { + if (++i >= length) { + // this should not happen with strings retrieved from + // Buffer.translateToString as it converts from UTF-32 + // and therefore always should contain the second part + // for any other string we still have to handle it somehow: + // simply treat the lonely surrogate first as a single char (UCS-2 behavior) + return result + this.wcwidth(code); + } + const second = s.charCodeAt(i); + // convert surrogate pair to high codepoint only for valid second part (UTF-16) + // otherwise treat them independently (UCS-2 behavior) + if (0xDC00 <= second && second <= 0xDFFF) { + code = (code - 0xD800) * 0x400 + second - 0xDC00 + 0x10000; + } else { + result += this.wcwidth(second); + } + } + result += this.wcwidth(code); + } + return result; + } +} |