@xterm/xterm 6.1.0-beta.28 → 6.1.0-beta.280

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 (181) hide show
  1. package/README.md +62 -38
  2. package/css/xterm.css +29 -22
  3. package/lib/xterm.js +1 -1
  4. package/lib/xterm.js.map +1 -1
  5. package/lib/xterm.mjs +8 -34
  6. package/lib/xterm.mjs.map +4 -4
  7. package/package.json +56 -45
  8. package/src/browser/AccessibilityManager.ts +18 -13
  9. package/src/browser/Clipboard.ts +8 -5
  10. package/src/browser/ColorContrastCache.ts +3 -3
  11. package/src/browser/CoreBrowserTerminal.ts +179 -348
  12. package/src/browser/Dom.ts +178 -0
  13. package/src/browser/Linkifier.ts +14 -14
  14. package/src/browser/OscLinkProvider.ts +86 -18
  15. package/src/browser/RenderDebouncer.ts +7 -9
  16. package/src/browser/TimeBasedDebouncer.ts +12 -5
  17. package/src/browser/Types.ts +16 -14
  18. package/src/browser/Viewport.ts +58 -23
  19. package/src/browser/decorations/BufferDecorationRenderer.ts +3 -3
  20. package/src/browser/decorations/ColorZoneStore.ts +1 -1
  21. package/src/browser/decorations/OverviewRulerRenderer.ts +36 -20
  22. package/src/browser/input/CompositionHelper.ts +47 -11
  23. package/src/browser/input/Mouse.ts +5 -9
  24. package/src/browser/input/MoveToCell.ts +3 -3
  25. package/src/browser/public/Terminal.ts +33 -36
  26. package/src/browser/renderer/dom/DomRenderer.ts +265 -89
  27. package/src/browser/renderer/dom/DomRendererRowFactory.ts +34 -27
  28. package/src/browser/renderer/dom/WidthCache.ts +57 -55
  29. package/src/browser/renderer/shared/Constants.ts +7 -0
  30. package/src/browser/renderer/shared/RendererUtils.ts +1 -1
  31. package/src/browser/renderer/shared/SelectionRenderModel.ts +2 -2
  32. package/src/browser/renderer/shared/TextBlinkStateManager.ts +97 -0
  33. package/src/browser/renderer/shared/Types.ts +10 -4
  34. package/src/browser/scrollable/abstractScrollbar.ts +300 -0
  35. package/src/browser/scrollable/fastDomNode.ts +126 -0
  36. package/src/browser/scrollable/globalPointerMoveMonitor.ts +90 -0
  37. package/src/browser/scrollable/horizontalScrollbar.ts +85 -0
  38. package/src/browser/scrollable/mouseEvent.ts +292 -0
  39. package/src/browser/scrollable/scrollable.ts +486 -0
  40. package/src/browser/scrollable/scrollableElement.ts +581 -0
  41. package/src/browser/scrollable/scrollableElementOptions.ts +161 -0
  42. package/src/browser/scrollable/scrollbarArrow.ts +110 -0
  43. package/src/browser/scrollable/scrollbarState.ts +246 -0
  44. package/src/browser/scrollable/scrollbarVisibilityController.ts +113 -0
  45. package/src/browser/scrollable/touch.ts +485 -0
  46. package/src/browser/scrollable/verticalScrollbar.ts +143 -0
  47. package/src/browser/scrollable/widget.ts +23 -0
  48. package/src/browser/selection/SelectionModel.ts +1 -1
  49. package/src/browser/services/CharSizeService.ts +4 -4
  50. package/src/browser/services/CharacterJoinerService.ts +13 -11
  51. package/src/browser/services/CoreBrowserService.ts +7 -5
  52. package/src/browser/services/KeyboardService.ts +67 -0
  53. package/src/browser/services/LinkProviderService.ts +3 -3
  54. package/src/browser/services/MouseCoordsService.ts +47 -0
  55. package/src/browser/services/MouseService.ts +619 -23
  56. package/src/browser/services/RenderService.ts +33 -21
  57. package/src/browser/services/SelectionService.ts +72 -54
  58. package/src/browser/services/Services.ts +44 -21
  59. package/src/browser/services/ThemeService.ts +9 -9
  60. package/src/common/Async.ts +141 -0
  61. package/src/common/CircularList.ts +24 -3
  62. package/src/common/Color.ts +13 -5
  63. package/src/common/CoreTerminal.ts +61 -35
  64. package/src/common/Event.ts +118 -0
  65. package/src/common/InputHandler.ts +286 -105
  66. package/src/common/Lifecycle.ts +113 -0
  67. package/src/common/Platform.ts +15 -5
  68. package/src/common/SortedList.ts +8 -4
  69. package/src/common/StringBuilder.ts +67 -0
  70. package/src/common/TaskQueue.ts +17 -8
  71. package/src/common/Types.ts +68 -206
  72. package/src/common/Version.ts +1 -1
  73. package/src/common/WindowsMode.ts +2 -2
  74. package/src/common/buffer/AttributeData.ts +3 -2
  75. package/src/common/buffer/Buffer.ts +47 -32
  76. package/src/common/buffer/BufferLine.ts +175 -100
  77. package/src/common/buffer/BufferLineStringCache.ts +69 -0
  78. package/src/common/buffer/BufferReflow.ts +7 -4
  79. package/src/common/buffer/BufferSet.ts +13 -9
  80. package/src/common/buffer/CellData.ts +61 -4
  81. package/src/common/buffer/Constants.ts +13 -13
  82. package/src/common/buffer/Marker.ts +4 -4
  83. package/src/common/buffer/Types.ts +153 -3
  84. package/src/common/data/Charsets.ts +4 -7
  85. package/src/common/data/EscapeSequences.ts +71 -70
  86. package/src/common/input/Keyboard.ts +17 -10
  87. package/src/common/input/KittyKeyboard.ts +526 -0
  88. package/src/common/input/TextDecoder.ts +3 -3
  89. package/src/common/input/UnicodeV6.ts +3 -2
  90. package/src/common/input/Win32InputMode.ts +297 -0
  91. package/src/common/input/WriteBuffer.ts +108 -39
  92. package/src/common/input/XParseColor.ts +2 -2
  93. package/src/common/parser/ApcParser.ts +196 -0
  94. package/src/common/parser/Constants.ts +14 -4
  95. package/src/common/parser/DcsParser.ts +15 -16
  96. package/src/common/parser/EscapeSequenceParser.ts +214 -72
  97. package/src/common/parser/OscParser.ts +14 -15
  98. package/src/common/parser/Params.ts +31 -12
  99. package/src/common/parser/Types.ts +40 -30
  100. package/src/common/public/BufferApiView.ts +3 -3
  101. package/src/common/public/BufferLineApiView.ts +4 -4
  102. package/src/common/public/BufferNamespaceApi.ts +5 -5
  103. package/src/common/public/ParserApi.ts +5 -2
  104. package/src/common/public/UnicodeApi.ts +1 -1
  105. package/src/common/services/BufferService.ts +17 -13
  106. package/src/common/services/CharsetService.ts +6 -2
  107. package/src/common/services/CoreService.ts +23 -10
  108. package/src/common/services/DecorationService.ts +260 -15
  109. package/src/common/services/InstantiationService.ts +2 -2
  110. package/src/common/services/LogService.ts +2 -32
  111. package/src/common/services/{CoreMouseService.ts → MouseStateService.ts} +22 -133
  112. package/src/common/services/OptionsService.ts +17 -7
  113. package/src/common/services/OscLinkService.ts +3 -2
  114. package/src/common/services/ServiceRegistry.ts +14 -8
  115. package/src/common/services/Services.ts +54 -50
  116. package/src/common/services/UnicodeService.ts +6 -11
  117. package/typings/xterm.d.ts +329 -34
  118. package/src/common/Clone.ts +0 -23
  119. package/src/common/TypedArrayUtils.ts +0 -17
  120. package/src/vs/base/browser/browser.ts +0 -141
  121. package/src/vs/base/browser/canIUse.ts +0 -49
  122. package/src/vs/base/browser/dom.ts +0 -2369
  123. package/src/vs/base/browser/fastDomNode.ts +0 -316
  124. package/src/vs/base/browser/globalPointerMoveMonitor.ts +0 -112
  125. package/src/vs/base/browser/iframe.ts +0 -135
  126. package/src/vs/base/browser/keyboardEvent.ts +0 -213
  127. package/src/vs/base/browser/mouseEvent.ts +0 -229
  128. package/src/vs/base/browser/touch.ts +0 -372
  129. package/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +0 -303
  130. package/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +0 -114
  131. package/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +0 -720
  132. package/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +0 -165
  133. package/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +0 -114
  134. package/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +0 -243
  135. package/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +0 -118
  136. package/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +0 -116
  137. package/src/vs/base/browser/ui/widget.ts +0 -57
  138. package/src/vs/base/browser/window.ts +0 -14
  139. package/src/vs/base/common/arrays.ts +0 -887
  140. package/src/vs/base/common/arraysFind.ts +0 -202
  141. package/src/vs/base/common/assert.ts +0 -71
  142. package/src/vs/base/common/async.ts +0 -1992
  143. package/src/vs/base/common/cancellation.ts +0 -148
  144. package/src/vs/base/common/charCode.ts +0 -450
  145. package/src/vs/base/common/collections.ts +0 -140
  146. package/src/vs/base/common/decorators.ts +0 -130
  147. package/src/vs/base/common/equals.ts +0 -146
  148. package/src/vs/base/common/errors.ts +0 -303
  149. package/src/vs/base/common/event.ts +0 -1778
  150. package/src/vs/base/common/functional.ts +0 -32
  151. package/src/vs/base/common/hash.ts +0 -316
  152. package/src/vs/base/common/iterator.ts +0 -159
  153. package/src/vs/base/common/keyCodes.ts +0 -526
  154. package/src/vs/base/common/keybindings.ts +0 -284
  155. package/src/vs/base/common/lazy.ts +0 -47
  156. package/src/vs/base/common/lifecycle.ts +0 -801
  157. package/src/vs/base/common/linkedList.ts +0 -142
  158. package/src/vs/base/common/map.ts +0 -202
  159. package/src/vs/base/common/numbers.ts +0 -98
  160. package/src/vs/base/common/observable.ts +0 -76
  161. package/src/vs/base/common/observableInternal/api.ts +0 -31
  162. package/src/vs/base/common/observableInternal/autorun.ts +0 -281
  163. package/src/vs/base/common/observableInternal/base.ts +0 -489
  164. package/src/vs/base/common/observableInternal/debugName.ts +0 -145
  165. package/src/vs/base/common/observableInternal/derived.ts +0 -428
  166. package/src/vs/base/common/observableInternal/lazyObservableValue.ts +0 -146
  167. package/src/vs/base/common/observableInternal/logging.ts +0 -328
  168. package/src/vs/base/common/observableInternal/promise.ts +0 -209
  169. package/src/vs/base/common/observableInternal/utils.ts +0 -610
  170. package/src/vs/base/common/platform.ts +0 -281
  171. package/src/vs/base/common/scrollable.ts +0 -522
  172. package/src/vs/base/common/sequence.ts +0 -34
  173. package/src/vs/base/common/stopwatch.ts +0 -43
  174. package/src/vs/base/common/strings.ts +0 -557
  175. package/src/vs/base/common/symbols.ts +0 -9
  176. package/src/vs/base/common/uint.ts +0 -59
  177. package/src/vs/patches/nls.ts +0 -90
  178. package/src/vs/typings/base-common.d.ts +0 -20
  179. package/src/vs/typings/require.d.ts +0 -42
  180. package/src/vs/typings/vscode-globals-nls.d.ts +0 -36
  181. package/src/vs/typings/vscode-globals-product.d.ts +0 -33
@@ -3,16 +3,16 @@
3
3
  * @license MIT
4
4
  */
5
5
 
6
- import { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
7
- import { ViewportConstants } from 'browser/shared/Constants';
8
- import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
9
- import { IBufferService, ICoreMouseService, IOptionsService } from 'common/services/Services';
10
- import { CoreMouseEventType } from 'common/Types';
11
- import { scheduleAtNextAnimationFrame } from 'vs/base/browser/dom';
12
- import { SmoothScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
13
- import type { ScrollableElementChangeOptions } from 'vs/base/browser/ui/scrollbar/scrollableElementOptions';
14
- import { Emitter, Event } from 'vs/base/common/event';
15
- import { Scrollable, ScrollbarVisibility, type ScrollEvent } from 'vs/base/common/scrollable';
6
+ import { ICoreBrowserService, IRenderService, IThemeService } from './services/Services';
7
+ import { ViewportConstants } from './shared/Constants';
8
+ import { Disposable, toDisposable } from '../common/Lifecycle';
9
+ import { IBufferService, ICoreService, IMouseStateService, IOptionsService } from '../common/services/Services';
10
+ import { CoreMouseEventType } from '../common/Types';
11
+ import { scheduleAtNextAnimationFrame } from './Dom';
12
+ import { SmoothScrollableElement } from './scrollable/scrollableElement';
13
+ import type { IScrollableElementChangeOptions } from './scrollable/scrollableElementOptions';
14
+ import { Emitter, EventUtils } from '../common/Event';
15
+ import { Scrollable, ScrollbarVisibility, type IScrollEvent } from './scrollable/scrollable';
16
16
 
17
17
  export class Viewport extends Disposable {
18
18
 
@@ -27,13 +27,15 @@ export class Viewport extends Disposable {
27
27
  private _isSyncing: boolean = false;
28
28
  private _isHandlingScroll: boolean = false;
29
29
  private _suppressOnScrollHandler: boolean = false;
30
+ private _needsSyncOnRender: boolean = false;
30
31
 
31
32
  constructor(
32
33
  element: HTMLElement,
33
34
  screenElement: HTMLElement,
34
35
  @IBufferService private readonly _bufferService: IBufferService,
35
36
  @ICoreBrowserService coreBrowserService: ICoreBrowserService,
36
- @ICoreMouseService coreMouseService: ICoreMouseService,
37
+ @ICoreService private readonly _coreService: ICoreService,
38
+ @IMouseStateService mouseStateService: IMouseStateService,
37
39
  @IThemeService themeService: IThemeService,
38
40
  @IOptionsService private readonly _optionsService: IOptionsService,
39
41
  @IRenderService private readonly _renderService: IRenderService
@@ -51,26 +53,28 @@ export class Viewport extends Disposable {
51
53
  }));
52
54
 
53
55
  this._scrollableElement = this._register(new SmoothScrollableElement(screenElement, {
54
- vertical: ScrollbarVisibility.Auto,
55
- horizontal: ScrollbarVisibility.Hidden,
56
+ vertical: ScrollbarVisibility.AUTO,
57
+ horizontal: ScrollbarVisibility.HIDDEN,
56
58
  useShadows: false,
57
59
  mouseWheelSmoothScroll: true,
60
+ verticalHasArrows: this._optionsService.rawOptions.scrollbar?.showArrows ?? false,
58
61
  ...this._getChangeOptions()
59
62
  }, scrollable));
60
63
  this._register(this._optionsService.onMultipleOptionChange([
61
64
  'scrollSensitivity',
62
65
  'fastScrollSensitivity',
63
- 'overviewRuler'
66
+ 'scrollbar'
64
67
  ], () => this._scrollableElement.updateOptions(this._getChangeOptions())));
65
68
  // Don't handle mouse wheel if wheel events are supported by the current mouse prototcol
66
- this._register(coreMouseService.onProtocolChange(type => {
69
+ this._register(mouseStateService.onProtocolChange(type => {
67
70
  this._scrollableElement.updateOptions({
68
71
  handleMouseWheel: !(type & CoreMouseEventType.WHEEL)
69
72
  });
70
73
  }));
71
74
 
72
75
  this._scrollableElement.setScrollDimensions({ height: 0, scrollHeight: 0 });
73
- this._register(Event.runAndSubscribe(themeService.onChangeColors, () => {
76
+ this._register(EventUtils.runAndSubscribe(themeService.onChangeColors, () => {
77
+ element.style.backgroundColor = themeService.colors.background.css;
74
78
  this._scrollableElement.getDomNode().style.backgroundColor = themeService.colors.background.css;
75
79
  }));
76
80
  element.appendChild(this._scrollableElement.getDomNode());
@@ -79,15 +83,15 @@ export class Viewport extends Disposable {
79
83
  this._styleElement = coreBrowserService.mainDocument.createElement('style');
80
84
  screenElement.appendChild(this._styleElement);
81
85
  this._register(toDisposable(() => this._styleElement.remove()));
82
- this._register(Event.runAndSubscribe(themeService.onChangeColors, () => {
86
+ this._register(EventUtils.runAndSubscribe(themeService.onChangeColors, () => {
83
87
  this._styleElement.textContent = [
84
- `.xterm .xterm-scrollable-element > .scrollbar > .slider {`,
88
+ `.xterm .xterm-scrollable-element > .xterm-scrollbar > .xterm-slider {`,
85
89
  ` background: ${themeService.colors.scrollbarSliderBackground.css};`,
86
90
  `}`,
87
- `.xterm .xterm-scrollable-element > .scrollbar > .slider:hover {`,
91
+ `.xterm .xterm-scrollable-element > .xterm-scrollbar > .xterm-slider:hover {`,
88
92
  ` background: ${themeService.colors.scrollbarSliderHoverBackground.css};`,
89
93
  `}`,
90
- `.xterm .xterm-scrollable-element > .scrollbar > .slider.active {`,
94
+ `.xterm .xterm-scrollable-element > .xterm-scrollbar > .xterm-slider.xterm-active {`,
91
95
  ` background: ${themeService.colors.scrollbarSliderActiveBackground.css};`,
92
96
  `}`
93
97
  ].join('\n');
@@ -102,7 +106,18 @@ export class Viewport extends Disposable {
102
106
  }));
103
107
  this._register(this._bufferService.onScroll(() => this._sync()));
104
108
 
109
+ // Flush deferred viewport sync after a render completes (e.g. after ESU ends
110
+ // synchronized output mode). This ensures DOM scroll position updates atomically
111
+ // with the canvas render.
112
+ this._register(this._renderService.onRender(() => {
113
+ if (this._needsSyncOnRender) {
114
+ this._needsSyncOnRender = false;
115
+ this._sync();
116
+ }
117
+ }));
118
+
105
119
  this._register(this._scrollableElement.onScroll(e => this._handleScroll(e)));
120
+
106
121
  }
107
122
 
108
123
  public scrollLines(disp: number): void {
@@ -123,11 +138,18 @@ export class Viewport extends Disposable {
123
138
  });
124
139
  }
125
140
 
126
- private _getChangeOptions(): ScrollableElementChangeOptions {
141
+ private _getChangeOptions(): IScrollableElementChangeOptions {
142
+ const showScrollbar = this._optionsService.rawOptions.scrollbar?.showScrollbar ?? true;
143
+ const showArrows = this._optionsService.rawOptions.scrollbar?.showArrows ?? false;
144
+ const verticalScrollbarSize = showScrollbar
145
+ ? (this._optionsService.rawOptions.scrollbar?.width ?? ViewportConstants.DEFAULT_SCROLL_BAR_WIDTH)
146
+ : 0;
127
147
  return {
128
148
  mouseWheelScrollSensitivity: this._optionsService.rawOptions.scrollSensitivity,
129
149
  fastScrollSensitivity: this._optionsService.rawOptions.fastScrollSensitivity,
130
- verticalScrollbarSize: this._optionsService.rawOptions.overviewRuler?.width || ViewportConstants.DEFAULT_SCROLL_BAR_WIDTH
150
+ vertical: showScrollbar ? ScrollbarVisibility.AUTO : ScrollbarVisibility.HIDDEN,
151
+ verticalScrollbarSize,
152
+ verticalHasArrows: showArrows
131
153
  };
132
154
  }
133
155
 
@@ -151,6 +173,12 @@ export class Viewport extends Disposable {
151
173
  if (!this._renderService || this._isSyncing) {
152
174
  return;
153
175
  }
176
+ // Defer DOM scroll updates during synchronized output to prevent visible
177
+ // scroll position flickering while the canvas content is frozen.
178
+ if (this._coreService.decPrivateModes.synchronizedOutput) {
179
+ this._needsSyncOnRender = true;
180
+ return;
181
+ }
154
182
  this._isSyncing = true;
155
183
 
156
184
  // Ignore any onScroll event that happens as a result of dimensions changing as this should
@@ -173,7 +201,7 @@ export class Viewport extends Disposable {
173
201
  this._isSyncing = false;
174
202
  }
175
203
 
176
- private _handleScroll(e: ScrollEvent): void {
204
+ private _handleScroll(e: IScrollEvent): void {
177
205
  if (!this._renderService) {
178
206
  return;
179
207
  }
@@ -189,4 +217,11 @@ export class Viewport extends Disposable {
189
217
  }
190
218
  this._isHandlingScroll = false;
191
219
  }
220
+
221
+ public handleTouchScroll(translationY: number): void {
222
+ const pos = this._scrollableElement.getScrollPosition();
223
+ this._scrollableElement.setScrollPosition({
224
+ scrollTop: pos.scrollTop - translationY
225
+ });
226
+ }
192
227
  }
@@ -3,9 +3,9 @@
3
3
  * Licensed under the MIT License. See License.txt in the project root for license information.
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
 
6
- import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
7
- import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
8
- import { IBufferService, IDecorationService, IInternalDecoration } from 'common/services/Services';
6
+ import { ICoreBrowserService, IRenderService } from '../services/Services';
7
+ import { Disposable, toDisposable } from '../../common/Lifecycle';
8
+ import { IBufferService, IDecorationService, IInternalDecoration } from '../../common/services/Services';
9
9
 
10
10
  export class BufferDecorationRenderer extends Disposable {
11
11
  private readonly _container: HTMLElement;
@@ -3,7 +3,7 @@
3
3
  * Licensed under the MIT License. See License.txt in the project root for license information.
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
 
6
- import { IInternalDecoration } from 'common/services/Services';
6
+ import { IInternalDecoration } from '../../common/services/Services';
7
7
 
8
8
  export interface IColorZoneStore {
9
9
  readonly zones: IColorZone[];
@@ -3,10 +3,10 @@
3
3
  * Licensed under the MIT License. See License.txt in the project root for license information.
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
 
6
- import { ColorZoneStore, IColorZone, IColorZoneStore } from 'browser/decorations/ColorZoneStore';
7
- import { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
8
- import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
9
- import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
6
+ import { ColorZoneStore, IColorZone, IColorZoneStore } from './ColorZoneStore';
7
+ import { ICoreBrowserService, IRenderService, IThemeService } from '../services/Services';
8
+ import { Disposable, toDisposable } from '../../common/Lifecycle';
9
+ import { IBufferService, IDecorationService, IOptionsService } from '../../common/services/Services';
10
10
 
11
11
  const enum Constants {
12
12
  OVERVIEW_RULER_BORDER_WIDTH = 1
@@ -38,7 +38,12 @@ export class OverviewRulerRenderer extends Disposable {
38
38
  private readonly _ctx: CanvasRenderingContext2D;
39
39
  private readonly _colorZoneStore: IColorZoneStore = new ColorZoneStore();
40
40
  private get _width(): number {
41
- return this._optionsService.options.overviewRuler?.width || 0;
41
+ const scrollbar = this._optionsService.rawOptions.scrollbar;
42
+ const showScrollbar = scrollbar?.showScrollbar ?? true;
43
+ if (!showScrollbar) {
44
+ return 0;
45
+ }
46
+ return scrollbar?.width ?? 0;
42
47
  }
43
48
  private _animationFrame: number | undefined;
44
49
 
@@ -46,8 +51,6 @@ export class OverviewRulerRenderer extends Disposable {
46
51
  private _shouldUpdateAnchor: boolean | undefined = true;
47
52
  private _lastKnownBufferLength: number = 0;
48
53
 
49
- private _containerHeight: number | undefined;
50
-
51
54
  constructor(
52
55
  private readonly _viewportElement: HTMLElement,
53
56
  private readonly _screenElement: HTMLElement,
@@ -86,17 +89,17 @@ export class OverviewRulerRenderer extends Disposable {
86
89
  }
87
90
  }));
88
91
 
89
- // Container height changed
90
- this._register(this._renderService.onRender((): void => {
91
- if (!this._containerHeight || this._containerHeight !== this._screenElement.clientHeight) {
92
- this._queueRefresh(true);
93
- this._containerHeight = this._screenElement.clientHeight;
94
- }
95
- }));
92
+ this._register(this._renderService.onDimensionsChange(() => this._queueRefresh(true)));
96
93
 
97
94
  this._register(this._coreBrowserService.onDprChange(() => this._queueRefresh(true)));
98
- this._register(this._optionsService.onSpecificOptionChange('overviewRuler', () => this._queueRefresh(true)));
95
+ this._register(this._optionsService.onSpecificOptionChange('scrollbar', () => this._queueRefresh(true)));
99
96
  this._register(this._themeService.onChangeColors(() => this._queueRefresh()));
97
+ this._register(toDisposable(() => {
98
+ if (this._animationFrame !== undefined) {
99
+ this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame);
100
+ this._animationFrame = undefined;
101
+ }
102
+ }));
100
103
  this._queueRefresh(true);
101
104
  }
102
105
 
@@ -139,15 +142,23 @@ export class OverviewRulerRenderer extends Disposable {
139
142
  }
140
143
 
141
144
  private _refreshCanvasDimensions(): void {
145
+ if (this._store.isDisposed || !this._renderService.hasRenderer()) {
146
+ return;
147
+ }
148
+ const cssCanvasHeight = this._renderService.dimensions.css.canvas.height;
149
+ const deviceCanvasHeight = this._renderService.dimensions.device.canvas.height;
142
150
  this._canvas.style.width = `${this._width}px`;
143
151
  this._canvas.width = Math.round(this._width * this._coreBrowserService.dpr);
144
- this._canvas.style.height = `${this._screenElement.clientHeight}px`;
145
- this._canvas.height = Math.round(this._screenElement.clientHeight * this._coreBrowserService.dpr);
152
+ this._canvas.style.height = `${cssCanvasHeight}px`;
153
+ this._canvas.height = deviceCanvasHeight;
146
154
  this._refreshDrawConstants();
147
155
  this._refreshColorZonePadding();
148
156
  }
149
157
 
150
158
  private _refreshDecorations(): void {
159
+ if (this._store.isDisposed || !this._renderService.hasRenderer()) {
160
+ return;
161
+ }
151
162
  if (this._shouldUpdateDimensions) {
152
163
  this._refreshCanvasDimensions();
153
164
  }
@@ -176,10 +187,10 @@ export class OverviewRulerRenderer extends Disposable {
176
187
  private _renderRulerOutline(): void {
177
188
  this._ctx.fillStyle = this._themeService.colors.overviewRulerBorder.css;
178
189
  this._ctx.fillRect(0, 0, Constants.OVERVIEW_RULER_BORDER_WIDTH, this._canvas.height);
179
- if (this._optionsService.rawOptions.overviewRuler.showTopBorder) {
190
+ if (this._optionsService.rawOptions.scrollbar?.overviewRuler?.showTopBorder) {
180
191
  this._ctx.fillRect(Constants.OVERVIEW_RULER_BORDER_WIDTH, 0, this._canvas.width - Constants.OVERVIEW_RULER_BORDER_WIDTH, Constants.OVERVIEW_RULER_BORDER_WIDTH);
181
192
  }
182
- if (this._optionsService.rawOptions.overviewRuler.showBottomBorder) {
193
+ if (this._optionsService.rawOptions.scrollbar?.overviewRuler?.showBottomBorder) {
183
194
  this._ctx.fillRect(Constants.OVERVIEW_RULER_BORDER_WIDTH, this._canvas.height - Constants.OVERVIEW_RULER_BORDER_WIDTH, this._canvas.width - Constants.OVERVIEW_RULER_BORDER_WIDTH, this._canvas.height);
184
195
  }
185
196
  }
@@ -201,13 +212,18 @@ export class OverviewRulerRenderer extends Disposable {
201
212
  }
202
213
 
203
214
  private _queueRefresh(updateCanvasDimensions?: boolean, updateAnchor?: boolean): void {
215
+ if (this._store.isDisposed) {
216
+ return;
217
+ }
204
218
  this._shouldUpdateDimensions = updateCanvasDimensions || this._shouldUpdateDimensions;
205
219
  this._shouldUpdateAnchor = updateAnchor || this._shouldUpdateAnchor;
206
220
  if (this._animationFrame !== undefined) {
207
221
  return;
208
222
  }
209
223
  this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => {
210
- this._refreshDecorations();
224
+ if (!this._store.isDisposed) {
225
+ this._refreshDecorations();
226
+ }
211
227
  this._animationFrame = undefined;
212
228
  });
213
229
  }
@@ -3,9 +3,9 @@
3
3
  * @license MIT
4
4
  */
5
5
 
6
- import { IRenderService } from 'browser/services/Services';
7
- import { IBufferService, ICoreService, IOptionsService } from 'common/services/Services';
8
- import { C0 } from 'common/data/EscapeSequences';
6
+ import { IRenderService } from '../services/Services';
7
+ import { IBufferService, ICoreService, IOptionsService } from '../../common/services/Services';
8
+ import { C0 } from '../../common/data/EscapeSequences';
9
9
 
10
10
  interface IPosition {
11
11
  start: number;
@@ -30,6 +30,12 @@ export class CompositionHelper {
30
30
  */
31
31
  private _compositionPosition: IPosition;
32
32
 
33
+ /**
34
+ * Text that existed after the composing range when composition started.
35
+ * This is used to avoid treating existing trailing text as new input.
36
+ */
37
+ private _compositionSuffix: string;
38
+
33
39
  /**
34
40
  * Whether a composition is in the process of being sent, setting this to false will cancel any
35
41
  * in-progress composition.
@@ -41,6 +47,11 @@ export class CompositionHelper {
41
47
  */
42
48
  private _dataAlreadySent: string;
43
49
 
50
+ /**
51
+ * The pending textarea change timer, if any.
52
+ */
53
+ private _textareaChangeTimer?: number;
54
+
44
55
  constructor(
45
56
  private readonly _textarea: HTMLTextAreaElement,
46
57
  private readonly _compositionView: HTMLElement,
@@ -52,6 +63,7 @@ export class CompositionHelper {
52
63
  this._isComposing = false;
53
64
  this._isSendingComposition = false;
54
65
  this._compositionPosition = { start: 0, end: 0 };
66
+ this._compositionSuffix = '';
55
67
  this._dataAlreadySent = '';
56
68
  }
57
69
 
@@ -60,7 +72,13 @@ export class CompositionHelper {
60
72
  */
61
73
  public compositionstart(): void {
62
74
  this._isComposing = true;
63
- this._compositionPosition.start = this._textarea.value.length;
75
+ // It's important to use the selection here instead of textarea length to avoid conflicts with
76
+ // screen reader mode
77
+ const start = this._textarea.selectionStart ?? this._textarea.value.length;
78
+ const end = this._textarea.selectionEnd ?? start;
79
+ this._compositionPosition.start = Math.min(start, end);
80
+ this._compositionPosition.end = Math.max(start, end);
81
+ this._compositionSuffix = this._textarea.value.substring(this._compositionPosition.end);
64
82
  this._compositionView.textContent = '';
65
83
  this._dataAlreadySent = '';
66
84
  this._compositionView.classList.add('active');
@@ -71,10 +89,13 @@ export class CompositionHelper {
71
89
  * @param ev The event.
72
90
  */
73
91
  public compositionupdate(ev: Pick<CompositionEvent, 'data'>): void {
74
- this._compositionView.textContent = ev.data;
92
+ // Mark text as LTR, direction=rtl is used in CSS so the end of the text is followed for long
93
+ // compositions
94
+ this._compositionView.textContent = `\u200E${ev.data}\u200E`;
75
95
  this.updateCompositionElements();
76
96
  setTimeout(() => {
77
- this._compositionPosition.end = this._textarea.value.length;
97
+ const end = this._textarea.selectionEnd ?? this._textarea.value.length;
98
+ this._compositionPosition.end = Math.max( this._compositionPosition.start, end);
78
99
  }, 0);
79
100
  }
80
101
 
@@ -141,6 +162,7 @@ export class CompositionHelper {
141
162
  start: this._compositionPosition.start,
142
163
  end: this._compositionPosition.end
143
164
  };
165
+ const currentCompositionSuffix = this._compositionSuffix;
144
166
 
145
167
  // Since composition* events happen before the changes take place in the textarea on most
146
168
  // browsers, use a setTimeout with 0ms time to allow the native compositionend event to
@@ -164,10 +186,14 @@ export class CompositionHelper {
164
186
  // if a new composition has started.
165
187
  input = this._textarea.value.substring(currentCompositionPosition.start, this._compositionPosition.start);
166
188
  } else {
167
- // Don't use the end position here in order to pick up any characters after the
168
- // composition has finished, for example when typing a non-composition character
169
- // (eg. 2) after a composition character.
170
- input = this._textarea.value.substring(currentCompositionPosition.start);
189
+ // Keep support for non-composition characters typed immediately after composition end
190
+ // while avoiding re-sending the trailing text that was already present
191
+ // before composition started.
192
+ const value = this._textarea.value;
193
+ const valueEnd = currentCompositionSuffix.length > 0 && value.endsWith(currentCompositionSuffix)
194
+ ? value.length - currentCompositionSuffix.length
195
+ : value.length;
196
+ input = value.substring(currentCompositionPosition.start, Math.max(currentCompositionPosition.start, valueEnd));
171
197
  }
172
198
  if (input.length > 0) {
173
199
  this._coreService.triggerDataEvent(input, true);
@@ -184,8 +210,12 @@ export class CompositionHelper {
184
210
  * IME is active.
185
211
  */
186
212
  private _handleAnyTextareaChanges(): void {
213
+ if (this._textareaChangeTimer) {
214
+ return;
215
+ }
187
216
  const oldValue = this._textarea.value;
188
- setTimeout(() => {
217
+ this._textareaChangeTimer = window.setTimeout(() => {
218
+ this._textareaChangeTimer = undefined;
189
219
  // Ignore if a composition has started since the timeout
190
220
  if (!this._isComposing) {
191
221
  const newValue = this._textarea.value;
@@ -230,6 +260,12 @@ export class CompositionHelper {
230
260
  this._compositionView.style.lineHeight = cellHeight + 'px';
231
261
  this._compositionView.style.fontFamily = this._optionsService.rawOptions.fontFamily;
232
262
  this._compositionView.style.fontSize = this._optionsService.rawOptions.fontSize + 'px';
263
+ // Limit the composition view width to the space between the cursor and
264
+ // the terminal's right edge, preventing it from overflowing the terminal.
265
+ const maxWidth = this._bufferService.cols * this._renderService.dimensions.css.cell.width - cursorLeft;
266
+ this._compositionView.style.maxWidth = maxWidth + 'px';
267
+ this._compositionView.style.overflow = 'hidden';
268
+ this._compositionView.style.direction = 'rtl';
233
269
  // Sync the textarea to the exact position of the composition view so the IME knows where the
234
270
  // text is.
235
271
  const compositionViewBounds = this._compositionView.getBoundingClientRect();
@@ -6,8 +6,8 @@
6
6
  export function getCoordsRelativeToElement(window: Pick<Window, 'getComputedStyle'>, event: {clientX: number, clientY: number}, element: HTMLElement): [number, number] {
7
7
  const rect = element.getBoundingClientRect();
8
8
  const elementStyle = window.getComputedStyle(element);
9
- const leftPadding = parseInt(elementStyle.getPropertyValue('padding-left'));
10
- const topPadding = parseInt(elementStyle.getPropertyValue('padding-top'));
9
+ const leftPadding = parseInt(elementStyle.getPropertyValue('padding-left'), 10);
10
+ const topPadding = parseInt(elementStyle.getPropertyValue('padding-top'), 10);
11
11
  return [
12
12
  event.clientX - rect.left - leftPadding,
13
13
  event.clientY - rect.top - topPadding
@@ -22,7 +22,7 @@ export function getCoordsRelativeToElement(window: Pick<Window, 'getComputedStyl
22
22
  * @param event The mouse event.
23
23
  * @param element The terminal's container element.
24
24
  * @param colCount The number of columns in the terminal.
25
- * @param rowCount The number of rows n the terminal.
25
+ * @param rowCount The number of rows in the terminal.
26
26
  * @param hasValidCharSize Whether there is a valid character size available.
27
27
  * @param cssCellWidth The cell width device pixel render dimensions.
28
28
  * @param cssCellHeight The cell height device pixel render dimensions.
@@ -31,21 +31,17 @@ export function getCoordsRelativeToElement(window: Pick<Window, 'getComputedStyl
31
31
  * select that cell and the right half will select the next cell.
32
32
  */
33
33
  export function getCoords(window: Pick<Window, 'getComputedStyle'>, event: Pick<MouseEvent, 'clientX' | 'clientY'>, element: HTMLElement, colCount: number, rowCount: number, hasValidCharSize: boolean, cssCellWidth: number, cssCellHeight: number, isSelection?: boolean): [number, number] | undefined {
34
- // Coordinates cannot be measured if there are no valid
34
+ // Coordinates cannot be measured if there is no valid character size.
35
35
  if (!hasValidCharSize) {
36
36
  return undefined;
37
37
  }
38
38
 
39
39
  const coords = getCoordsRelativeToElement(window, event, element);
40
- if (!coords) {
41
- return undefined;
42
- }
43
-
44
40
  coords[0] = Math.ceil((coords[0] + (isSelection ? cssCellWidth / 2 : 0)) / cssCellWidth);
45
41
  coords[1] = Math.ceil(coords[1] / cssCellHeight);
46
42
 
47
43
  // Ensure coordinates are within the terminal viewport. Note that selections
48
- // need an addition point of precision to cover the end point (as characters
44
+ // need an additional point of precision to cover the end point (as characters
49
45
  // cover half of one char and half of the next).
50
46
  coords[0] = Math.min(Math.max(coords[0], 1), colCount + (isSelection ? 1 : 0));
51
47
  coords[1] = Math.min(Math.max(coords[1], 1), rowCount);
@@ -3,8 +3,8 @@
3
3
  * @license MIT
4
4
  */
5
5
 
6
- import { C0 } from 'common/data/EscapeSequences';
7
- import { IBufferService } from 'common/services/Services';
6
+ import { C0 } from '../../common/data/EscapeSequences';
7
+ import { IBufferService } from '../../common/services/Services';
8
8
 
9
9
  const enum Direction {
10
10
  UP = 'A',
@@ -156,7 +156,7 @@ function wrappedRowsForRow(currentRow: number, bufferService: IBufferService): n
156
156
  */
157
157
  function horizontalDirection(startX: number, startY: number, targetX: number, targetY: number, bufferService: IBufferService, applicationCursor: boolean): Direction {
158
158
  let startRow;
159
- if (moveToRequestedRow(targetX, targetY, bufferService, applicationCursor).length > 0) {
159
+ if (moveToRequestedRow(startY, targetY, bufferService, applicationCursor).length > 0) {
160
160
  startRow = targetY - wrappedRowsForRow(targetY, bufferService);
161
161
  } else {
162
162
  startRow = startY;
@@ -3,17 +3,17 @@
3
3
  * @license MIT
4
4
  */
5
5
 
6
- import * as Strings from 'browser/LocalizableStrings';
7
- import { CoreBrowserTerminal as TerminalCore } from 'browser/CoreBrowserTerminal';
8
- import { IBufferRange, ITerminal } from 'browser/Types';
9
- import { Disposable } from 'vs/base/common/lifecycle';
10
- import { ITerminalOptions } from 'common/Types';
11
- import { AddonManager } from 'common/public/AddonManager';
12
- import { BufferNamespaceApi } from 'common/public/BufferNamespaceApi';
13
- import { ParserApi } from 'common/public/ParserApi';
14
- import { UnicodeApi } from 'common/public/UnicodeApi';
15
- import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling } from '@xterm/xterm';
16
- import type { Event } from 'vs/base/common/event';
6
+ import * as Strings from '../LocalizableStrings';
7
+ import { CoreBrowserTerminal as TerminalCore } from '../CoreBrowserTerminal';
8
+ import { IBufferRange, ITerminal } from '../Types';
9
+ import { Disposable } from '../../common/Lifecycle';
10
+ import { ITerminalOptions } from '../../common/Types';
11
+ import { AddonManager } from '../../common/public/AddonManager';
12
+ import { BufferNamespaceApi } from '../../common/public/BufferNamespaceApi';
13
+ import { ParserApi } from '../../common/public/ParserApi';
14
+ import { UnicodeApi } from '../../common/public/UnicodeApi';
15
+ import { IBufferNamespace as IBufferNamespaceApi, IDecoration, IDecorationOptions, IDisposable, ILinkProvider, ILocalizableStrings, IMarker, IModes, IParser, IRenderDimensions, ITerminalAddon, Terminal as ITerminalApi, ITerminalInitOnlyOptions, IUnicodeHandling } from '@xterm/xterm';
16
+ import type { IEvent } from '../../common/Event';
17
17
 
18
18
  /**
19
19
  * The set of options that only have an effect when set in the Terminal constructor.
@@ -68,25 +68,24 @@ export class Terminal extends Disposable implements ITerminalApi {
68
68
  }
69
69
  }
70
70
 
71
- public get onBell(): Event<void> { return this._core.onBell; }
72
- public get onBinary(): Event<string> { return this._core.onBinary; }
73
- public get onCursorMove(): Event<void> { return this._core.onCursorMove; }
74
- public get onData(): Event<string> { return this._core.onData; }
75
- public get onKey(): Event<{ key: string, domEvent: KeyboardEvent }> { return this._core.onKey; }
76
- public get onLineFeed(): Event<void> { return this._core.onLineFeed; }
77
- public get onRender(): Event<{ start: number, end: number }> { return this._core.onRender; }
78
- public get onResize(): Event<{ cols: number, rows: number }> { return this._core.onResize; }
79
- public get onScroll(): Event<number> { return this._core.onScroll; }
80
- public get onSelectionChange(): Event<void> { return this._core.onSelectionChange; }
81
- public get onTitleChange(): Event<string> { return this._core.onTitleChange; }
82
- public get onWriteParsed(): Event<void> { return this._core.onWriteParsed; }
71
+ public get onBell(): IEvent<void> { return this._core.onBell; }
72
+ public get onBinary(): IEvent<string> { return this._core.onBinary; }
73
+ public get onCursorMove(): IEvent<void> { return this._core.onCursorMove; }
74
+ public get onData(): IEvent<string> { return this._core.onData; }
75
+ public get onKey(): IEvent<{ key: string, domEvent: KeyboardEvent }> { return this._core.onKey; }
76
+ public get onLineFeed(): IEvent<void> { return this._core.onLineFeed; }
77
+ public get onRender(): IEvent<{ start: number, end: number }> { return this._core.onRender; }
78
+ public get onResize(): IEvent<{ cols: number, rows: number }> { return this._core.onResize; }
79
+ public get onScroll(): IEvent<number> { return this._core.onScroll; }
80
+ public get onSelectionChange(): IEvent<void> { return this._core.onSelectionChange; }
81
+ public get onTitleChange(): IEvent<string> { return this._core.onTitleChange; }
82
+ public get onWriteParsed(): IEvent<void> { return this._core.onWriteParsed; }
83
+ public get onDimensionsChange(): IEvent<IRenderDimensions> { return this._core.onDimensionsChange; }
83
84
 
84
85
  public get element(): HTMLElement | undefined { return this._core.element; }
86
+ public get screenElement(): HTMLElement | undefined { return this._core.screenElement; }
85
87
  public get parser(): IParser {
86
- if (!this._parser) {
87
- this._parser = new ParserApi(this._core);
88
- }
89
- return this._parser;
88
+ return this._parser ??= new ParserApi(this._core);
90
89
  }
91
90
  public get unicode(): IUnicodeHandling {
92
91
  this._checkProposedApi();
@@ -96,19 +95,15 @@ export class Terminal extends Disposable implements ITerminalApi {
96
95
  public get rows(): number { return this._core.rows; }
97
96
  public get cols(): number { return this._core.cols; }
98
97
  public get buffer(): IBufferNamespaceApi {
99
- if (!this._buffer) {
100
- this._buffer = this._register(new BufferNamespaceApi(this._core));
101
- }
102
- return this._buffer;
98
+ return this._buffer ??= this._register(new BufferNamespaceApi(this._core));
103
99
  }
104
100
  public get markers(): ReadonlyArray<IMarker> {
105
- this._checkProposedApi();
106
101
  return this._core.markers;
107
102
  }
108
103
  public get modes(): IModes {
109
104
  const m = this._core.coreService.decPrivateModes;
110
105
  let mouseTrackingMode: 'none' | 'x10' | 'vt200' | 'drag' | 'any' = 'none';
111
- switch (this._core.coreMouseService.activeProtocol) {
106
+ switch (this._core.mouseStateService.activeProtocol) {
112
107
  case 'X10': mouseTrackingMode = 'x10'; break;
113
108
  case 'VT200': mouseTrackingMode = 'vt200'; break;
114
109
  case 'DRAG': mouseTrackingMode = 'drag'; break;
@@ -123,10 +118,15 @@ export class Terminal extends Disposable implements ITerminalApi {
123
118
  originMode: m.origin,
124
119
  reverseWraparoundMode: m.reverseWraparound,
125
120
  sendFocusMode: m.sendFocus,
121
+ showCursor: !this._core.coreService.isCursorHidden,
126
122
  synchronizedOutputMode: m.synchronizedOutput,
123
+ win32InputMode: m.win32InputMode,
127
124
  wraparoundMode: m.wraparound
128
125
  };
129
126
  }
127
+ public get dimensions(): IRenderDimensions | undefined {
128
+ return this._core.dimensions;
129
+ }
130
130
  public get options(): Required<ITerminalOptions> {
131
131
  return this._publicOptions;
132
132
  }
@@ -161,11 +161,9 @@ export class Terminal extends Disposable implements ITerminalApi {
161
161
  return this._core.registerLinkProvider(linkProvider);
162
162
  }
163
163
  public registerCharacterJoiner(handler: (text: string) => [number, number][]): number {
164
- this._checkProposedApi();
165
164
  return this._core.registerCharacterJoiner(handler);
166
165
  }
167
166
  public deregisterCharacterJoiner(joinerId: number): void {
168
- this._checkProposedApi();
169
167
  this._core.deregisterCharacterJoiner(joinerId);
170
168
  }
171
169
  public registerMarker(cursorYOffset: number = 0): IMarker {
@@ -173,7 +171,6 @@ export class Terminal extends Disposable implements ITerminalApi {
173
171
  return this._core.registerMarker(cursorYOffset);
174
172
  }
175
173
  public registerDecoration(decorationOptions: IDecorationOptions): IDecoration | undefined {
176
- this._checkProposedApi();
177
174
  this._verifyPositiveIntegers(decorationOptions.x ?? 0, decorationOptions.width ?? 0, decorationOptions.height ?? 0);
178
175
  return this._core.registerDecoration(decorationOptions);
179
176
  }