@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,137 @@
1
+ import { ISelectionRenderModel } from 'browser/renderer/shared/Types';
2
+ import { ICoreBrowserService, IThemeService } from 'browser/services/Services';
3
+ import { ReadonlyColorSet } from 'browser/Types';
4
+ import { Attributes, BgFlags, FgFlags } from 'common/buffer/Constants';
5
+ import { IDecorationService } from 'common/services/Services';
6
+ import { ICellData } from 'common/Types';
7
+ import { Terminal } from '@xterm/xterm';
8
+
9
+ // Work variables to avoid garbage collection
10
+ let $fg = 0;
11
+ let $bg = 0;
12
+ let $hasFg = false;
13
+ let $hasBg = false;
14
+ let $isSelected = false;
15
+ let $colors: ReadonlyColorSet | undefined;
16
+
17
+ export class CellColorResolver {
18
+ /**
19
+ * The shared result of the {@link resolve} call. This is only safe to use immediately after as
20
+ * any other calls will share object.
21
+ */
22
+ public readonly result: { fg: number, bg: number, ext: number } = {
23
+ fg: 0,
24
+ bg: 0,
25
+ ext: 0
26
+ };
27
+
28
+ constructor(
29
+ private readonly _terminal: Terminal,
30
+ private readonly _selectionRenderModel: ISelectionRenderModel,
31
+ private readonly _decorationService: IDecorationService,
32
+ private readonly _coreBrowserService: ICoreBrowserService,
33
+ private readonly _themeService: IThemeService
34
+ ) {
35
+ }
36
+
37
+ /**
38
+ * Resolves colors for the cell, putting the result into the shared {@link result}. This resolves
39
+ * overrides, inverse and selection for the cell which can then be used to feed into the renderer.
40
+ */
41
+ public resolve(cell: ICellData, x: number, y: number): void {
42
+ this.result.bg = cell.bg;
43
+ this.result.fg = cell.fg;
44
+ this.result.ext = cell.bg & BgFlags.HAS_EXTENDED ? cell.extended.ext : 0;
45
+ // Get any foreground/background overrides, this happens on the model to avoid spreading
46
+ // override logic throughout the different sub-renderers
47
+
48
+ // Reset overrides work variables
49
+ $bg = 0;
50
+ $fg = 0;
51
+ $hasBg = false;
52
+ $hasFg = false;
53
+ $isSelected = false;
54
+ $colors = this._themeService.colors;
55
+
56
+ // Apply decorations on the bottom layer
57
+ this._decorationService.forEachDecorationAtCell(x, y, 'bottom', d => {
58
+ if (d.backgroundColorRGB) {
59
+ $bg = d.backgroundColorRGB.rgba >> 8 & 0xFFFFFF;
60
+ $hasBg = true;
61
+ }
62
+ if (d.foregroundColorRGB) {
63
+ $fg = d.foregroundColorRGB.rgba >> 8 & 0xFFFFFF;
64
+ $hasFg = true;
65
+ }
66
+ });
67
+
68
+ // Apply the selection color if needed
69
+ $isSelected = this._selectionRenderModel.isCellSelected(this._terminal, x, y);
70
+ if ($isSelected) {
71
+ $bg = (this._coreBrowserService.isFocused ? $colors.selectionBackgroundOpaque : $colors.selectionInactiveBackgroundOpaque).rgba >> 8 & 0xFFFFFF;
72
+ $hasBg = true;
73
+ if ($colors.selectionForeground) {
74
+ $fg = $colors.selectionForeground.rgba >> 8 & 0xFFFFFF;
75
+ $hasFg = true;
76
+ }
77
+ }
78
+
79
+ // Apply decorations on the top layer
80
+ this._decorationService.forEachDecorationAtCell(x, y, 'top', d => {
81
+ if (d.backgroundColorRGB) {
82
+ $bg = d.backgroundColorRGB.rgba >> 8 & 0xFFFFFF;
83
+ $hasBg = true;
84
+ }
85
+ if (d.foregroundColorRGB) {
86
+ $fg = d.foregroundColorRGB.rgba >> 8 & 0xFFFFFF;
87
+ $hasFg = true;
88
+ }
89
+ });
90
+
91
+ // Convert any overrides from rgba to the fg/bg packed format. This resolves the inverse flag
92
+ // ahead of time in order to use the correct cache key
93
+ if ($hasBg) {
94
+ if ($isSelected) {
95
+ // Non-RGB attributes from model + force non-dim + override + force RGB color mode
96
+ $bg = (cell.bg & ~Attributes.RGB_MASK & ~BgFlags.DIM) | $bg | Attributes.CM_RGB;
97
+ } else {
98
+ // Non-RGB attributes from model + override + force RGB color mode
99
+ $bg = (cell.bg & ~Attributes.RGB_MASK) | $bg | Attributes.CM_RGB;
100
+ }
101
+ }
102
+ if ($hasFg) {
103
+ // Non-RGB attributes from model + force disable inverse + override + force RGB color mode
104
+ $fg = (cell.fg & ~Attributes.RGB_MASK & ~FgFlags.INVERSE) | $fg | Attributes.CM_RGB;
105
+ }
106
+
107
+ // Handle case where inverse was specified by only one of bg override or fg override was set,
108
+ // resolving the other inverse color and setting the inverse flag if needed.
109
+ if (this.result.fg & FgFlags.INVERSE) {
110
+ if ($hasBg && !$hasFg) {
111
+ // Resolve bg color type (default color has a different meaning in fg vs bg)
112
+ if ((this.result.bg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) {
113
+ $fg = (this.result.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | (($colors.background.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB;
114
+ } else {
115
+ $fg = (this.result.fg & ~(Attributes.RGB_MASK | FgFlags.INVERSE | Attributes.CM_MASK)) | this.result.bg & (Attributes.RGB_MASK | Attributes.CM_MASK);
116
+ }
117
+ $hasFg = true;
118
+ }
119
+ if (!$hasBg && $hasFg) {
120
+ // Resolve bg color type (default color has a different meaning in fg vs bg)
121
+ if ((this.result.fg & Attributes.CM_MASK) === Attributes.CM_DEFAULT) {
122
+ $bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | (($colors.foreground.rgba >> 8 & 0xFFFFFF) & Attributes.RGB_MASK) | Attributes.CM_RGB;
123
+ } else {
124
+ $bg = (this.result.bg & ~(Attributes.RGB_MASK | Attributes.CM_MASK)) | this.result.fg & (Attributes.RGB_MASK | Attributes.CM_MASK);
125
+ }
126
+ $hasBg = true;
127
+ }
128
+ }
129
+
130
+ // Release object
131
+ $colors = undefined;
132
+
133
+ // Use the override if it exists
134
+ this.result.bg = $hasBg ? $bg : this.result.bg;
135
+ this.result.fg = $hasFg ? $fg : this.result.fg;
136
+ }
137
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Copyright (c) 2017 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { TextureAtlas } from 'browser/renderer/shared/TextureAtlas';
7
+ import { ITerminalOptions, Terminal } from '@xterm/xterm';
8
+ import { ITerminal, ReadonlyColorSet } from 'browser/Types';
9
+ import { ICharAtlasConfig, ITextureAtlas } from 'browser/renderer/shared/Types';
10
+ import { generateConfig, configEquals } from 'browser/renderer/shared/CharAtlasUtils';
11
+
12
+ interface ITextureAtlasCacheEntry {
13
+ atlas: ITextureAtlas;
14
+ config: ICharAtlasConfig;
15
+ // N.B. This implementation potentially holds onto copies of the terminal forever, so
16
+ // this may cause memory leaks.
17
+ ownedBy: Terminal[];
18
+ }
19
+
20
+ const charAtlasCache: ITextureAtlasCacheEntry[] = [];
21
+
22
+ /**
23
+ * Acquires a char atlas, either generating a new one or returning an existing
24
+ * one that is in use by another terminal.
25
+ */
26
+ export function acquireTextureAtlas(
27
+ terminal: Terminal,
28
+ options: Required<ITerminalOptions>,
29
+ colors: ReadonlyColorSet,
30
+ deviceCellWidth: number,
31
+ deviceCellHeight: number,
32
+ deviceCharWidth: number,
33
+ deviceCharHeight: number,
34
+ devicePixelRatio: number
35
+ ): ITextureAtlas {
36
+ const newConfig = generateConfig(deviceCellWidth, deviceCellHeight, deviceCharWidth, deviceCharHeight, options, colors, devicePixelRatio);
37
+
38
+ // Check to see if the terminal already owns this config
39
+ for (let i = 0; i < charAtlasCache.length; i++) {
40
+ const entry = charAtlasCache[i];
41
+ const ownedByIndex = entry.ownedBy.indexOf(terminal);
42
+ if (ownedByIndex >= 0) {
43
+ if (configEquals(entry.config, newConfig)) {
44
+ return entry.atlas;
45
+ }
46
+ // The configs differ, release the terminal from the entry
47
+ if (entry.ownedBy.length === 1) {
48
+ entry.atlas.dispose();
49
+ charAtlasCache.splice(i, 1);
50
+ } else {
51
+ entry.ownedBy.splice(ownedByIndex, 1);
52
+ }
53
+ break;
54
+ }
55
+ }
56
+
57
+ // Try match a char atlas from the cache
58
+ for (let i = 0; i < charAtlasCache.length; i++) {
59
+ const entry = charAtlasCache[i];
60
+ if (configEquals(entry.config, newConfig)) {
61
+ // Add the terminal to the cache entry and return
62
+ entry.ownedBy.push(terminal);
63
+ return entry.atlas;
64
+ }
65
+ }
66
+
67
+ const core: ITerminal = (terminal as any)._core;
68
+ const newEntry: ITextureAtlasCacheEntry = {
69
+ atlas: new TextureAtlas(document, newConfig, core.unicodeService),
70
+ config: newConfig,
71
+ ownedBy: [terminal]
72
+ };
73
+ charAtlasCache.push(newEntry);
74
+ return newEntry.atlas;
75
+ }
76
+
77
+ /**
78
+ * Removes a terminal reference from the cache, allowing its memory to be freed.
79
+ * @param terminal The terminal to remove.
80
+ */
81
+ export function removeTerminalFromCache(terminal: Terminal): void {
82
+ for (let i = 0; i < charAtlasCache.length; i++) {
83
+ const index = charAtlasCache[i].ownedBy.indexOf(terminal);
84
+ if (index !== -1) {
85
+ if (charAtlasCache[i].ownedBy.length === 1) {
86
+ // Remove the cache entry if it's the only terminal
87
+ charAtlasCache[i].atlas.dispose();
88
+ charAtlasCache.splice(i, 1);
89
+ } else {
90
+ // Remove the reference from the cache entry
91
+ charAtlasCache[i].ownedBy.splice(index, 1);
92
+ }
93
+ break;
94
+ }
95
+ }
96
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Copyright (c) 2017 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { ICharAtlasConfig } from './Types';
7
+ import { Attributes } from 'common/buffer/Constants';
8
+ import { ITerminalOptions } from '@xterm/xterm';
9
+ import { IColorSet, ReadonlyColorSet } from 'browser/Types';
10
+ import { NULL_COLOR } from 'common/Color';
11
+
12
+ export function generateConfig(deviceCellWidth: number, deviceCellHeight: number, deviceCharWidth: number, deviceCharHeight: number, options: Required<ITerminalOptions>, colors: ReadonlyColorSet, devicePixelRatio: number): ICharAtlasConfig {
13
+ // null out some fields that don't matter
14
+ const clonedColors: IColorSet = {
15
+ foreground: colors.foreground,
16
+ background: colors.background,
17
+ cursor: NULL_COLOR,
18
+ cursorAccent: NULL_COLOR,
19
+ selectionForeground: NULL_COLOR,
20
+ selectionBackgroundTransparent: NULL_COLOR,
21
+ selectionBackgroundOpaque: NULL_COLOR,
22
+ selectionInactiveBackgroundTransparent: NULL_COLOR,
23
+ selectionInactiveBackgroundOpaque: NULL_COLOR,
24
+ // For the static char atlas, we only use the first 16 colors, but we need all 256 for the
25
+ // dynamic character atlas.
26
+ ansi: colors.ansi.slice(),
27
+ contrastCache: colors.contrastCache,
28
+ halfContrastCache: colors.halfContrastCache
29
+ };
30
+ return {
31
+ customGlyphs: options.customGlyphs,
32
+ devicePixelRatio,
33
+ letterSpacing: options.letterSpacing,
34
+ lineHeight: options.lineHeight,
35
+ deviceCellWidth: deviceCellWidth,
36
+ deviceCellHeight: deviceCellHeight,
37
+ deviceCharWidth: deviceCharWidth,
38
+ deviceCharHeight: deviceCharHeight,
39
+ fontFamily: options.fontFamily,
40
+ fontSize: options.fontSize,
41
+ fontWeight: options.fontWeight,
42
+ fontWeightBold: options.fontWeightBold,
43
+ allowTransparency: options.allowTransparency,
44
+ drawBoldTextInBrightColors: options.drawBoldTextInBrightColors,
45
+ minimumContrastRatio: options.minimumContrastRatio,
46
+ colors: clonedColors
47
+ };
48
+ }
49
+
50
+ export function configEquals(a: ICharAtlasConfig, b: ICharAtlasConfig): boolean {
51
+ for (let i = 0; i < a.colors.ansi.length; i++) {
52
+ if (a.colors.ansi[i].rgba !== b.colors.ansi[i].rgba) {
53
+ return false;
54
+ }
55
+ }
56
+ return a.devicePixelRatio === b.devicePixelRatio &&
57
+ a.customGlyphs === b.customGlyphs &&
58
+ a.lineHeight === b.lineHeight &&
59
+ a.letterSpacing === b.letterSpacing &&
60
+ a.fontFamily === b.fontFamily &&
61
+ a.fontSize === b.fontSize &&
62
+ a.fontWeight === b.fontWeight &&
63
+ a.fontWeightBold === b.fontWeightBold &&
64
+ a.allowTransparency === b.allowTransparency &&
65
+ a.deviceCharWidth === b.deviceCharWidth &&
66
+ a.deviceCharHeight === b.deviceCharHeight &&
67
+ a.drawBoldTextInBrightColors === b.drawBoldTextInBrightColors &&
68
+ a.minimumContrastRatio === b.minimumContrastRatio &&
69
+ a.colors.foreground.rgba === b.colors.foreground.rgba &&
70
+ a.colors.background.rgba === b.colors.background.rgba;
71
+ }
72
+
73
+ export function is256Color(colorCode: number): boolean {
74
+ return (colorCode & Attributes.CM_MASK) === Attributes.CM_P16 || (colorCode & Attributes.CM_MASK) === Attributes.CM_P256;
75
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Copyright (c) 2017 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { isFirefox, isLegacyEdge } from 'common/Platform';
7
+
8
+ export const INVERTED_DEFAULT_COLOR = 257;
9
+
10
+ export const DIM_OPACITY = 0.5;
11
+ // The text baseline is set conditionally by browser. Using 'ideographic' for Firefox or Legacy Edge
12
+ // would result in truncated text (Issue 3353). Using 'bottom' for Chrome would result in slightly
13
+ // unaligned Powerline fonts (PR 3356#issuecomment-850928179).
14
+ export const TEXT_BASELINE: CanvasTextBaseline = isFirefox || isLegacyEdge ? 'bottom' : 'ideographic';
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Copyright (c) 2017 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { ICoreBrowserService } from 'browser/services/Services';
7
+
8
+ /**
9
+ * The time between cursor blinks.
10
+ */
11
+ const BLINK_INTERVAL = 600;
12
+
13
+ export class CursorBlinkStateManager {
14
+ public isCursorVisible: boolean;
15
+
16
+ private _animationFrame: number | undefined;
17
+ private _blinkStartTimeout: number | undefined;
18
+ private _blinkInterval: number | undefined;
19
+
20
+ /**
21
+ * The time at which the animation frame was restarted, this is used on the
22
+ * next render to restart the timers so they don't need to restart the timers
23
+ * multiple times over a short period.
24
+ */
25
+ private _animationTimeRestarted: number | undefined;
26
+
27
+ constructor(
28
+ private _renderCallback: () => void,
29
+ private _coreBrowserService: ICoreBrowserService
30
+ ) {
31
+ this.isCursorVisible = true;
32
+ if (this._coreBrowserService.isFocused) {
33
+ this._restartInterval();
34
+ }
35
+ }
36
+
37
+ public get isPaused(): boolean { return !(this._blinkStartTimeout || this._blinkInterval); }
38
+
39
+ public dispose(): void {
40
+ if (this._blinkInterval) {
41
+ this._coreBrowserService.window.clearInterval(this._blinkInterval);
42
+ this._blinkInterval = undefined;
43
+ }
44
+ if (this._blinkStartTimeout) {
45
+ this._coreBrowserService.window.clearTimeout(this._blinkStartTimeout);
46
+ this._blinkStartTimeout = undefined;
47
+ }
48
+ if (this._animationFrame) {
49
+ this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame);
50
+ this._animationFrame = undefined;
51
+ }
52
+ }
53
+
54
+ public restartBlinkAnimation(): void {
55
+ if (this.isPaused) {
56
+ return;
57
+ }
58
+ // Save a timestamp so that the restart can be done on the next interval
59
+ this._animationTimeRestarted = Date.now();
60
+ // Force a cursor render to ensure it's visible and in the correct position
61
+ this.isCursorVisible = true;
62
+ if (!this._animationFrame) {
63
+ this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => {
64
+ this._renderCallback();
65
+ this._animationFrame = undefined;
66
+ });
67
+ }
68
+ }
69
+
70
+ private _restartInterval(timeToStart: number = BLINK_INTERVAL): void {
71
+ // Clear any existing interval
72
+ if (this._blinkInterval) {
73
+ this._coreBrowserService.window.clearInterval(this._blinkInterval);
74
+ this._blinkInterval = undefined;
75
+ }
76
+
77
+ // Setup the initial timeout which will hide the cursor, this is done before
78
+ // the regular interval is setup in order to support restarting the blink
79
+ // animation in a lightweight way (without thrashing clearInterval and
80
+ // setInterval).
81
+ this._blinkStartTimeout = this._coreBrowserService.window.setTimeout(() => {
82
+ // Check if another animation restart was requested while this was being
83
+ // started
84
+ if (this._animationTimeRestarted) {
85
+ const time = BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted);
86
+ this._animationTimeRestarted = undefined;
87
+ if (time > 0) {
88
+ this._restartInterval(time);
89
+ return;
90
+ }
91
+ }
92
+
93
+ // Hide the cursor
94
+ this.isCursorVisible = false;
95
+ this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => {
96
+ this._renderCallback();
97
+ this._animationFrame = undefined;
98
+ });
99
+
100
+ // Setup the blink interval
101
+ this._blinkInterval = this._coreBrowserService.window.setInterval(() => {
102
+ // Adjust the animation time if it was restarted
103
+ if (this._animationTimeRestarted) {
104
+ // calc time diff
105
+ // Make restart interval do a setTimeout initially?
106
+ const time = BLINK_INTERVAL - (Date.now() - this._animationTimeRestarted);
107
+ this._animationTimeRestarted = undefined;
108
+ this._restartInterval(time);
109
+ return;
110
+ }
111
+
112
+ // Invert visibility and render
113
+ this.isCursorVisible = !this.isCursorVisible;
114
+ this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => {
115
+ this._renderCallback();
116
+ this._animationFrame = undefined;
117
+ });
118
+ }, BLINK_INTERVAL);
119
+ }, timeToStart);
120
+ }
121
+
122
+ public pause(): void {
123
+ this.isCursorVisible = true;
124
+ if (this._blinkInterval) {
125
+ this._coreBrowserService.window.clearInterval(this._blinkInterval);
126
+ this._blinkInterval = undefined;
127
+ }
128
+ if (this._blinkStartTimeout) {
129
+ this._coreBrowserService.window.clearTimeout(this._blinkStartTimeout);
130
+ this._blinkStartTimeout = undefined;
131
+ }
132
+ if (this._animationFrame) {
133
+ this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame);
134
+ this._animationFrame = undefined;
135
+ }
136
+ }
137
+
138
+ public resume(): void {
139
+ // Clear out any existing timers just in case
140
+ this.pause();
141
+
142
+ this._animationTimeRestarted = undefined;
143
+ this._restartInterval();
144
+ this.restartBlinkAnimation();
145
+ }
146
+ }