@xterm/xterm 5.4.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +235 -0
  3. package/css/xterm.css +209 -0
  4. package/lib/xterm.js +2 -0
  5. package/lib/xterm.js.map +1 -0
  6. package/package.json +101 -0
  7. package/src/browser/AccessibilityManager.ts +278 -0
  8. package/src/browser/Clipboard.ts +93 -0
  9. package/src/browser/ColorContrastCache.ts +34 -0
  10. package/src/browser/Lifecycle.ts +33 -0
  11. package/src/browser/Linkifier2.ts +416 -0
  12. package/src/browser/LocalizableStrings.ts +12 -0
  13. package/src/browser/OscLinkProvider.ts +128 -0
  14. package/src/browser/RenderDebouncer.ts +83 -0
  15. package/src/browser/Terminal.ts +1317 -0
  16. package/src/browser/TimeBasedDebouncer.ts +86 -0
  17. package/src/browser/Types.d.ts +181 -0
  18. package/src/browser/Viewport.ts +401 -0
  19. package/src/browser/decorations/BufferDecorationRenderer.ts +134 -0
  20. package/src/browser/decorations/ColorZoneStore.ts +117 -0
  21. package/src/browser/decorations/OverviewRulerRenderer.ts +218 -0
  22. package/src/browser/input/CompositionHelper.ts +246 -0
  23. package/src/browser/input/Mouse.ts +54 -0
  24. package/src/browser/input/MoveToCell.ts +249 -0
  25. package/src/browser/public/Terminal.ts +260 -0
  26. package/src/browser/renderer/dom/DomRenderer.ts +509 -0
  27. package/src/browser/renderer/dom/DomRendererRowFactory.ts +526 -0
  28. package/src/browser/renderer/dom/WidthCache.ts +160 -0
  29. package/src/browser/renderer/shared/CellColorResolver.ts +137 -0
  30. package/src/browser/renderer/shared/CharAtlasCache.ts +96 -0
  31. package/src/browser/renderer/shared/CharAtlasUtils.ts +75 -0
  32. package/src/browser/renderer/shared/Constants.ts +14 -0
  33. package/src/browser/renderer/shared/CursorBlinkStateManager.ts +146 -0
  34. package/src/browser/renderer/shared/CustomGlyphs.ts +687 -0
  35. package/src/browser/renderer/shared/DevicePixelObserver.ts +41 -0
  36. package/src/browser/renderer/shared/README.md +1 -0
  37. package/src/browser/renderer/shared/RendererUtils.ts +58 -0
  38. package/src/browser/renderer/shared/SelectionRenderModel.ts +91 -0
  39. package/src/browser/renderer/shared/TextureAtlas.ts +1082 -0
  40. package/src/browser/renderer/shared/Types.d.ts +173 -0
  41. package/src/browser/selection/SelectionModel.ts +144 -0
  42. package/src/browser/selection/Types.d.ts +15 -0
  43. package/src/browser/services/CharSizeService.ts +102 -0
  44. package/src/browser/services/CharacterJoinerService.ts +339 -0
  45. package/src/browser/services/CoreBrowserService.ts +137 -0
  46. package/src/browser/services/MouseService.ts +46 -0
  47. package/src/browser/services/RenderService.ts +279 -0
  48. package/src/browser/services/SelectionService.ts +1031 -0
  49. package/src/browser/services/Services.ts +147 -0
  50. package/src/browser/services/ThemeService.ts +237 -0
  51. package/src/common/CircularList.ts +241 -0
  52. package/src/common/Clone.ts +23 -0
  53. package/src/common/Color.ts +357 -0
  54. package/src/common/CoreTerminal.ts +284 -0
  55. package/src/common/EventEmitter.ts +78 -0
  56. package/src/common/InputHandler.ts +3461 -0
  57. package/src/common/Lifecycle.ts +108 -0
  58. package/src/common/MultiKeyMap.ts +42 -0
  59. package/src/common/Platform.ts +44 -0
  60. package/src/common/SortedList.ts +118 -0
  61. package/src/common/TaskQueue.ts +166 -0
  62. package/src/common/TypedArrayUtils.ts +17 -0
  63. package/src/common/Types.d.ts +553 -0
  64. package/src/common/WindowsMode.ts +27 -0
  65. package/src/common/buffer/AttributeData.ts +196 -0
  66. package/src/common/buffer/Buffer.ts +654 -0
  67. package/src/common/buffer/BufferLine.ts +524 -0
  68. package/src/common/buffer/BufferRange.ts +13 -0
  69. package/src/common/buffer/BufferReflow.ts +223 -0
  70. package/src/common/buffer/BufferSet.ts +134 -0
  71. package/src/common/buffer/CellData.ts +94 -0
  72. package/src/common/buffer/Constants.ts +149 -0
  73. package/src/common/buffer/Marker.ts +43 -0
  74. package/src/common/buffer/Types.d.ts +52 -0
  75. package/src/common/data/Charsets.ts +256 -0
  76. package/src/common/data/EscapeSequences.ts +153 -0
  77. package/src/common/input/Keyboard.ts +398 -0
  78. package/src/common/input/TextDecoder.ts +346 -0
  79. package/src/common/input/UnicodeV6.ts +145 -0
  80. package/src/common/input/WriteBuffer.ts +246 -0
  81. package/src/common/input/XParseColor.ts +80 -0
  82. package/src/common/parser/Constants.ts +58 -0
  83. package/src/common/parser/DcsParser.ts +192 -0
  84. package/src/common/parser/EscapeSequenceParser.ts +792 -0
  85. package/src/common/parser/OscParser.ts +238 -0
  86. package/src/common/parser/Params.ts +229 -0
  87. package/src/common/parser/Types.d.ts +275 -0
  88. package/src/common/public/AddonManager.ts +53 -0
  89. package/src/common/public/BufferApiView.ts +35 -0
  90. package/src/common/public/BufferLineApiView.ts +29 -0
  91. package/src/common/public/BufferNamespaceApi.ts +36 -0
  92. package/src/common/public/ParserApi.ts +37 -0
  93. package/src/common/public/UnicodeApi.ts +27 -0
  94. package/src/common/services/BufferService.ts +151 -0
  95. package/src/common/services/CharsetService.ts +34 -0
  96. package/src/common/services/CoreMouseService.ts +318 -0
  97. package/src/common/services/CoreService.ts +87 -0
  98. package/src/common/services/DecorationService.ts +140 -0
  99. package/src/common/services/InstantiationService.ts +85 -0
  100. package/src/common/services/LogService.ts +124 -0
  101. package/src/common/services/OptionsService.ts +202 -0
  102. package/src/common/services/OscLinkService.ts +115 -0
  103. package/src/common/services/ServiceRegistry.ts +49 -0
  104. package/src/common/services/Services.ts +373 -0
  105. package/src/common/services/UnicodeService.ts +111 -0
  106. package/src/headless/Terminal.ts +136 -0
  107. package/src/headless/public/Terminal.ts +195 -0
  108. package/typings/xterm.d.ts +1857 -0
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Copyright (c) 2019 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { Disposable } from 'common/Lifecycle';
7
+ import { ILogService, IOptionsService, LogLevelEnum } from 'common/services/Services';
8
+
9
+ type LogType = (message?: any, ...optionalParams: any[]) => void;
10
+
11
+ interface IConsole {
12
+ log: LogType;
13
+ error: LogType;
14
+ info: LogType;
15
+ trace: LogType;
16
+ warn: LogType;
17
+ }
18
+
19
+ // console is available on both node.js and browser contexts but the common
20
+ // module doesn't depend on them so we need to explicitly declare it.
21
+ declare const console: IConsole;
22
+
23
+ const optionsKeyToLogLevel: { [key: string]: LogLevelEnum } = {
24
+ trace: LogLevelEnum.TRACE,
25
+ debug: LogLevelEnum.DEBUG,
26
+ info: LogLevelEnum.INFO,
27
+ warn: LogLevelEnum.WARN,
28
+ error: LogLevelEnum.ERROR,
29
+ off: LogLevelEnum.OFF
30
+ };
31
+
32
+ const LOG_PREFIX = 'xterm.js: ';
33
+
34
+ export class LogService extends Disposable implements ILogService {
35
+ public serviceBrand: any;
36
+
37
+ private _logLevel: LogLevelEnum = LogLevelEnum.OFF;
38
+ public get logLevel(): LogLevelEnum { return this._logLevel; }
39
+
40
+ constructor(
41
+ @IOptionsService private readonly _optionsService: IOptionsService
42
+ ) {
43
+ super();
44
+ this._updateLogLevel();
45
+ this.register(this._optionsService.onSpecificOptionChange('logLevel', () => this._updateLogLevel()));
46
+
47
+ // For trace logging, assume the latest created log service is valid
48
+ traceLogger = this;
49
+ }
50
+
51
+ private _updateLogLevel(): void {
52
+ this._logLevel = optionsKeyToLogLevel[this._optionsService.rawOptions.logLevel];
53
+ }
54
+
55
+ private _evalLazyOptionalParams(optionalParams: any[]): void {
56
+ for (let i = 0; i < optionalParams.length; i++) {
57
+ if (typeof optionalParams[i] === 'function') {
58
+ optionalParams[i] = optionalParams[i]();
59
+ }
60
+ }
61
+ }
62
+
63
+ private _log(type: LogType, message: string, optionalParams: any[]): void {
64
+ this._evalLazyOptionalParams(optionalParams);
65
+ type.call(console, (this._optionsService.options.logger ? '' : LOG_PREFIX) + message, ...optionalParams);
66
+ }
67
+
68
+ public trace(message: string, ...optionalParams: any[]): void {
69
+ if (this._logLevel <= LogLevelEnum.TRACE) {
70
+ this._log(this._optionsService.options.logger?.trace.bind(this._optionsService.options.logger) ?? console.log, message, optionalParams);
71
+ }
72
+ }
73
+
74
+ public debug(message: string, ...optionalParams: any[]): void {
75
+ if (this._logLevel <= LogLevelEnum.DEBUG) {
76
+ this._log(this._optionsService.options.logger?.debug.bind(this._optionsService.options.logger) ?? console.log, message, optionalParams);
77
+ }
78
+ }
79
+
80
+ public info(message: string, ...optionalParams: any[]): void {
81
+ if (this._logLevel <= LogLevelEnum.INFO) {
82
+ this._log(this._optionsService.options.logger?.info.bind(this._optionsService.options.logger) ?? console.info, message, optionalParams);
83
+ }
84
+ }
85
+
86
+ public warn(message: string, ...optionalParams: any[]): void {
87
+ if (this._logLevel <= LogLevelEnum.WARN) {
88
+ this._log(this._optionsService.options.logger?.warn.bind(this._optionsService.options.logger) ?? console.warn, message, optionalParams);
89
+ }
90
+ }
91
+
92
+ public error(message: string, ...optionalParams: any[]): void {
93
+ if (this._logLevel <= LogLevelEnum.ERROR) {
94
+ this._log(this._optionsService.options.logger?.error.bind(this._optionsService.options.logger) ?? console.error, message, optionalParams);
95
+ }
96
+ }
97
+ }
98
+
99
+ let traceLogger: ILogService;
100
+ export function setTraceLogger(logger: ILogService): void {
101
+ traceLogger = logger;
102
+ }
103
+
104
+ /**
105
+ * A decorator that can be used to automatically log trace calls to the decorated function.
106
+ */
107
+ export function traceCall(_target: any, key: string, descriptor: any): any {
108
+ if (typeof descriptor.value !== 'function') {
109
+ throw new Error('not supported');
110
+ }
111
+ const fnKey = 'value';
112
+ const fn = descriptor.value;
113
+ descriptor[fnKey] = function (...args: any[]) {
114
+ // Early exit
115
+ if (traceLogger.logLevel !== LogLevelEnum.TRACE) {
116
+ return fn.apply(this, args);
117
+ }
118
+
119
+ traceLogger.trace(`GlyphRenderer#${fn.name}(${args.map(e => JSON.stringify(e)).join(', ')})`);
120
+ const result = fn.apply(this, args);
121
+ traceLogger.trace(`GlyphRenderer#${fn.name} return`, result);
122
+ return result;
123
+ };
124
+ }
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Copyright (c) 2019 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { EventEmitter } from 'common/EventEmitter';
7
+ import { Disposable } from 'common/Lifecycle';
8
+ import { isMac } from 'common/Platform';
9
+ import { CursorStyle, IDisposable } from 'common/Types';
10
+ import { FontWeight, IOptionsService, ITerminalOptions } from 'common/services/Services';
11
+
12
+ export const DEFAULT_OPTIONS: Readonly<Required<ITerminalOptions>> = {
13
+ cols: 80,
14
+ rows: 24,
15
+ cursorBlink: false,
16
+ cursorStyle: 'block',
17
+ cursorWidth: 1,
18
+ cursorInactiveStyle: 'outline',
19
+ customGlyphs: true,
20
+ drawBoldTextInBrightColors: true,
21
+ documentOverride: null,
22
+ fastScrollModifier: 'alt',
23
+ fastScrollSensitivity: 5,
24
+ fontFamily: 'courier-new, courier, monospace',
25
+ fontSize: 15,
26
+ fontWeight: 'normal',
27
+ fontWeightBold: 'bold',
28
+ ignoreBracketedPasteMode: false,
29
+ lineHeight: 1.0,
30
+ letterSpacing: 0,
31
+ linkHandler: null,
32
+ logLevel: 'info',
33
+ logger: null,
34
+ scrollback: 1000,
35
+ scrollOnUserInput: true,
36
+ scrollSensitivity: 1,
37
+ screenReaderMode: false,
38
+ smoothScrollDuration: 0,
39
+ macOptionIsMeta: false,
40
+ macOptionClickForcesSelection: false,
41
+ minimumContrastRatio: 1,
42
+ disableStdin: false,
43
+ allowProposedApi: false,
44
+ allowTransparency: false,
45
+ tabStopWidth: 8,
46
+ theme: {},
47
+ rightClickSelectsWord: isMac,
48
+ windowOptions: {},
49
+ windowsMode: false,
50
+ windowsPty: {},
51
+ wordSeparator: ' ()[]{}\',"`',
52
+ altClickMovesCursor: true,
53
+ convertEol: false,
54
+ termName: 'xterm',
55
+ cancelEvents: false,
56
+ overviewRulerWidth: 0
57
+ };
58
+
59
+ const FONT_WEIGHT_OPTIONS: Extract<FontWeight, string>[] = ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'];
60
+
61
+ export class OptionsService extends Disposable implements IOptionsService {
62
+ public serviceBrand: any;
63
+
64
+ public readonly rawOptions: Required<ITerminalOptions>;
65
+ public options: Required<ITerminalOptions>;
66
+
67
+ private readonly _onOptionChange = this.register(new EventEmitter<keyof ITerminalOptions>());
68
+ public readonly onOptionChange = this._onOptionChange.event;
69
+
70
+ constructor(options: Partial<ITerminalOptions>) {
71
+ super();
72
+ // set the default value of each option
73
+ const defaultOptions = { ...DEFAULT_OPTIONS };
74
+ for (const key in options) {
75
+ if (key in defaultOptions) {
76
+ try {
77
+ const newValue = options[key];
78
+ defaultOptions[key] = this._sanitizeAndValidateOption(key, newValue);
79
+ } catch (e) {
80
+ console.error(e);
81
+ }
82
+ }
83
+ }
84
+
85
+ // set up getters and setters for each option
86
+ this.rawOptions = defaultOptions;
87
+ this.options = { ... defaultOptions };
88
+ this._setupOptions();
89
+ }
90
+
91
+ // eslint-disable-next-line @typescript-eslint/naming-convention
92
+ public onSpecificOptionChange<T extends keyof ITerminalOptions>(key: T, listener: (value: ITerminalOptions[T]) => any): IDisposable {
93
+ return this.onOptionChange(eventKey => {
94
+ if (eventKey === key) {
95
+ listener(this.rawOptions[key]);
96
+ }
97
+ });
98
+ }
99
+
100
+ // eslint-disable-next-line @typescript-eslint/naming-convention
101
+ public onMultipleOptionChange(keys: (keyof ITerminalOptions)[], listener: () => any): IDisposable {
102
+ return this.onOptionChange(eventKey => {
103
+ if (keys.indexOf(eventKey) !== -1) {
104
+ listener();
105
+ }
106
+ });
107
+ }
108
+
109
+ private _setupOptions(): void {
110
+ const getter = (propName: string): any => {
111
+ if (!(propName in DEFAULT_OPTIONS)) {
112
+ throw new Error(`No option with key "${propName}"`);
113
+ }
114
+ return this.rawOptions[propName];
115
+ };
116
+
117
+ const setter = (propName: string, value: any): void => {
118
+ if (!(propName in DEFAULT_OPTIONS)) {
119
+ throw new Error(`No option with key "${propName}"`);
120
+ }
121
+
122
+ value = this._sanitizeAndValidateOption(propName, value);
123
+ // Don't fire an option change event if they didn't change
124
+ if (this.rawOptions[propName] !== value) {
125
+ this.rawOptions[propName] = value;
126
+ this._onOptionChange.fire(propName);
127
+ }
128
+ };
129
+
130
+ for (const propName in this.rawOptions) {
131
+ const desc = {
132
+ get: getter.bind(this, propName),
133
+ set: setter.bind(this, propName)
134
+ };
135
+ Object.defineProperty(this.options, propName, desc);
136
+ }
137
+ }
138
+
139
+ private _sanitizeAndValidateOption(key: string, value: any): any {
140
+ switch (key) {
141
+ case 'cursorStyle':
142
+ if (!value) {
143
+ value = DEFAULT_OPTIONS[key];
144
+ }
145
+ if (!isCursorStyle(value)) {
146
+ throw new Error(`"${value}" is not a valid value for ${key}`);
147
+ }
148
+ break;
149
+ case 'wordSeparator':
150
+ if (!value) {
151
+ value = DEFAULT_OPTIONS[key];
152
+ }
153
+ break;
154
+ case 'fontWeight':
155
+ case 'fontWeightBold':
156
+ if (typeof value === 'number' && 1 <= value && value <= 1000) {
157
+ // already valid numeric value
158
+ break;
159
+ }
160
+ value = FONT_WEIGHT_OPTIONS.includes(value) ? value : DEFAULT_OPTIONS[key];
161
+ break;
162
+ case 'cursorWidth':
163
+ value = Math.floor(value);
164
+ // Fall through for bounds check
165
+ case 'lineHeight':
166
+ case 'tabStopWidth':
167
+ if (value < 1) {
168
+ throw new Error(`${key} cannot be less than 1, value: ${value}`);
169
+ }
170
+ break;
171
+ case 'minimumContrastRatio':
172
+ value = Math.max(1, Math.min(21, Math.round(value * 10) / 10));
173
+ break;
174
+ case 'scrollback':
175
+ value = Math.min(value, 4294967295);
176
+ if (value < 0) {
177
+ throw new Error(`${key} cannot be less than 0, value: ${value}`);
178
+ }
179
+ break;
180
+ case 'fastScrollSensitivity':
181
+ case 'scrollSensitivity':
182
+ if (value <= 0) {
183
+ throw new Error(`${key} cannot be less than or equal to 0, value: ${value}`);
184
+ }
185
+ break;
186
+ case 'rows':
187
+ case 'cols':
188
+ if (!value && value !== 0) {
189
+ throw new Error(`${key} must be numeric, value: ${value}`);
190
+ }
191
+ break;
192
+ case 'windowsPty':
193
+ value = value ?? {};
194
+ break;
195
+ }
196
+ return value;
197
+ }
198
+ }
199
+
200
+ function isCursorStyle(value: unknown): value is CursorStyle {
201
+ return value === 'block' || value === 'underline' || value === 'bar';
202
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Copyright (c) 2022 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+ import { IBufferService, IOscLinkService } from 'common/services/Services';
6
+ import { IMarker, IOscLinkData } from 'common/Types';
7
+
8
+ export class OscLinkService implements IOscLinkService {
9
+ public serviceBrand: any;
10
+
11
+ private _nextId = 1;
12
+
13
+ /**
14
+ * A map of the link key to link entry. This is used to add additional lines to links with ids.
15
+ */
16
+ private _entriesWithId: Map<string, IOscLinkEntryWithId> = new Map();
17
+
18
+ /**
19
+ * A map of the link id to the link entry. The "link id" (number) which is the numberic
20
+ * representation of a unique link should not be confused with "id" (string) which comes in with
21
+ * `id=` in the OSC link's properties.
22
+ */
23
+ private _dataByLinkId: Map<number, IOscLinkEntryNoId | IOscLinkEntryWithId> = new Map();
24
+
25
+ constructor(
26
+ @IBufferService private readonly _bufferService: IBufferService
27
+ ) {
28
+ }
29
+
30
+ public registerLink(data: IOscLinkData): number {
31
+ const buffer = this._bufferService.buffer;
32
+
33
+ // Links with no id will only ever be registered a single time
34
+ if (data.id === undefined) {
35
+ const marker = buffer.addMarker(buffer.ybase + buffer.y);
36
+ const entry: IOscLinkEntryNoId = {
37
+ data,
38
+ id: this._nextId++,
39
+ lines: [marker]
40
+ };
41
+ marker.onDispose(() => this._removeMarkerFromLink(entry, marker));
42
+ this._dataByLinkId.set(entry.id, entry);
43
+ return entry.id;
44
+ }
45
+
46
+ // Add the line to the link if it already exists
47
+ const castData = data as Required<IOscLinkData>;
48
+ const key = this._getEntryIdKey(castData);
49
+ const match = this._entriesWithId.get(key);
50
+ if (match) {
51
+ this.addLineToLink(match.id, buffer.ybase + buffer.y);
52
+ return match.id;
53
+ }
54
+
55
+ // Create the link
56
+ const marker = buffer.addMarker(buffer.ybase + buffer.y);
57
+ const entry: IOscLinkEntryWithId = {
58
+ id: this._nextId++,
59
+ key: this._getEntryIdKey(castData),
60
+ data: castData,
61
+ lines: [marker]
62
+ };
63
+ marker.onDispose(() => this._removeMarkerFromLink(entry, marker));
64
+ this._entriesWithId.set(entry.key, entry);
65
+ this._dataByLinkId.set(entry.id, entry);
66
+ return entry.id;
67
+ }
68
+
69
+ public addLineToLink(linkId: number, y: number): void {
70
+ const entry = this._dataByLinkId.get(linkId);
71
+ if (!entry) {
72
+ return;
73
+ }
74
+ if (entry.lines.every(e => e.line !== y)) {
75
+ const marker = this._bufferService.buffer.addMarker(y);
76
+ entry.lines.push(marker);
77
+ marker.onDispose(() => this._removeMarkerFromLink(entry, marker));
78
+ }
79
+ }
80
+
81
+ public getLinkData(linkId: number): IOscLinkData | undefined {
82
+ return this._dataByLinkId.get(linkId)?.data;
83
+ }
84
+
85
+ private _getEntryIdKey(linkData: Required<IOscLinkData>): string {
86
+ return `${linkData.id};;${linkData.uri}`;
87
+ }
88
+
89
+ private _removeMarkerFromLink(entry: IOscLinkEntryNoId | IOscLinkEntryWithId, marker: IMarker): void {
90
+ const index = entry.lines.indexOf(marker);
91
+ if (index === -1) {
92
+ return;
93
+ }
94
+ entry.lines.splice(index, 1);
95
+ if (entry.lines.length === 0) {
96
+ if (entry.data.id !== undefined) {
97
+ this._entriesWithId.delete((entry as IOscLinkEntryWithId).key);
98
+ }
99
+ this._dataByLinkId.delete(entry.id);
100
+ }
101
+ }
102
+ }
103
+
104
+ interface IOscLinkEntry<T extends IOscLinkData> {
105
+ data: T;
106
+ id: number;
107
+ lines: IMarker[];
108
+ }
109
+
110
+ interface IOscLinkEntryNoId extends IOscLinkEntry<IOscLinkData> {
111
+ }
112
+
113
+ interface IOscLinkEntryWithId extends IOscLinkEntry<Required<IOscLinkData>> {
114
+ key: string;
115
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Copyright (c) 2019 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ *
5
+ * This was heavily inspired from microsoft/vscode's dependency injection system (MIT).
6
+ */
7
+ /*---------------------------------------------------------------------------------------------
8
+ * Copyright (c) Microsoft Corporation. All rights reserved.
9
+ * Licensed under the MIT License. See License.txt in the project root for license information.
10
+ *--------------------------------------------------------------------------------------------*/
11
+
12
+ import { IServiceIdentifier } from 'common/services/Services';
13
+
14
+ const DI_TARGET = 'di$target';
15
+ const DI_DEPENDENCIES = 'di$dependencies';
16
+
17
+ export const serviceRegistry: Map<string, IServiceIdentifier<any>> = new Map();
18
+
19
+ export function getServiceDependencies(ctor: any): { id: IServiceIdentifier<any>, index: number, optional: boolean }[] {
20
+ return ctor[DI_DEPENDENCIES] || [];
21
+ }
22
+
23
+ export function createDecorator<T>(id: string): IServiceIdentifier<T> {
24
+ if (serviceRegistry.has(id)) {
25
+ return serviceRegistry.get(id)!;
26
+ }
27
+
28
+ const decorator: any = function (target: Function, key: string, index: number): any {
29
+ if (arguments.length !== 3) {
30
+ throw new Error('@IServiceName-decorator can only be used to decorate a parameter');
31
+ }
32
+
33
+ storeServiceDependency(decorator, target, index);
34
+ };
35
+
36
+ decorator.toString = () => id;
37
+
38
+ serviceRegistry.set(id, decorator);
39
+ return decorator;
40
+ }
41
+
42
+ function storeServiceDependency(id: Function, target: Function, index: number): void {
43
+ if ((target as any)[DI_TARGET] === target) {
44
+ (target as any)[DI_DEPENDENCIES].push({ id, index });
45
+ } else {
46
+ (target as any)[DI_DEPENDENCIES] = [{ id, index }];
47
+ (target as any)[DI_TARGET] = target;
48
+ }
49
+ }