@xterm/xterm 5.6.0-beta.8 → 5.6.0-beta.81

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 (122) hide show
  1. package/README.md +6 -3
  2. package/css/xterm.css +71 -4
  3. package/lib/xterm.js +1 -1
  4. package/lib/xterm.js.map +1 -1
  5. package/lib/xterm.mjs +53 -0
  6. package/lib/xterm.mjs.map +7 -0
  7. package/package.json +43 -33
  8. package/src/browser/AccessibilityManager.ts +53 -25
  9. package/src/browser/{Terminal.ts → CoreBrowserTerminal.ts} +139 -148
  10. package/src/browser/Linkifier.ts +26 -14
  11. package/src/browser/LocalizableStrings.ts +15 -4
  12. package/src/browser/{Types.d.ts → Types.ts} +67 -15
  13. package/src/browser/Viewport.ts +143 -370
  14. package/src/browser/decorations/BufferDecorationRenderer.ts +14 -9
  15. package/src/browser/decorations/OverviewRulerRenderer.ts +40 -44
  16. package/src/browser/public/Terminal.ts +25 -19
  17. package/src/browser/renderer/dom/DomRenderer.ts +22 -19
  18. package/src/browser/renderer/dom/DomRendererRowFactory.ts +35 -15
  19. package/src/browser/renderer/shared/CharAtlasUtils.ts +4 -0
  20. package/src/browser/renderer/shared/CustomGlyphs.ts +6 -0
  21. package/src/browser/renderer/shared/DevicePixelObserver.ts +1 -2
  22. package/src/browser/renderer/shared/TextureAtlas.ts +3 -3
  23. package/src/browser/renderer/shared/{Types.d.ts → Types.ts} +4 -4
  24. package/src/browser/services/CharSizeService.ts +6 -6
  25. package/src/browser/services/CoreBrowserService.ts +15 -15
  26. package/src/browser/services/LinkProviderService.ts +2 -2
  27. package/src/browser/services/RenderService.ts +20 -20
  28. package/src/browser/services/SelectionService.ts +8 -8
  29. package/src/browser/services/Services.ts +13 -13
  30. package/src/browser/services/ThemeService.ts +19 -58
  31. package/src/browser/shared/Constants.ts +8 -0
  32. package/src/common/CircularList.ts +5 -5
  33. package/src/common/CoreTerminal.ts +35 -41
  34. package/src/common/InputHandler.ts +63 -51
  35. package/src/common/{Types.d.ts → Types.ts} +13 -17
  36. package/src/common/buffer/Buffer.ts +15 -7
  37. package/src/common/buffer/BufferReflow.ts +9 -6
  38. package/src/common/buffer/BufferSet.ts +5 -5
  39. package/src/common/buffer/Marker.ts +4 -4
  40. package/src/common/buffer/{Types.d.ts → Types.ts} +2 -2
  41. package/src/common/input/WriteBuffer.ts +3 -3
  42. package/src/common/parser/EscapeSequenceParser.ts +4 -4
  43. package/src/common/public/BufferNamespaceApi.ts +3 -3
  44. package/src/common/services/BufferService.ts +7 -7
  45. package/src/common/services/CoreMouseService.ts +5 -3
  46. package/src/common/services/CoreService.ts +8 -6
  47. package/src/common/services/DecorationService.ts +8 -9
  48. package/src/common/services/InstantiationService.ts +1 -1
  49. package/src/common/services/LogService.ts +2 -2
  50. package/src/common/services/OptionsService.ts +6 -5
  51. package/src/common/services/ServiceRegistry.ts +1 -1
  52. package/src/common/services/Services.ts +26 -17
  53. package/src/common/services/UnicodeService.ts +2 -2
  54. package/src/vs/base/browser/browser.ts +141 -0
  55. package/src/vs/base/browser/canIUse.ts +49 -0
  56. package/src/vs/base/browser/dom.ts +2369 -0
  57. package/src/vs/base/browser/fastDomNode.ts +316 -0
  58. package/src/vs/base/browser/globalPointerMoveMonitor.ts +112 -0
  59. package/src/vs/base/browser/iframe.ts +135 -0
  60. package/src/vs/base/browser/keyboardEvent.ts +213 -0
  61. package/src/vs/base/browser/mouseEvent.ts +229 -0
  62. package/src/vs/base/browser/touch.ts +372 -0
  63. package/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +303 -0
  64. package/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +114 -0
  65. package/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +720 -0
  66. package/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +165 -0
  67. package/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +114 -0
  68. package/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +243 -0
  69. package/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +118 -0
  70. package/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +116 -0
  71. package/src/vs/base/browser/ui/widget.ts +57 -0
  72. package/src/vs/base/browser/window.ts +14 -0
  73. package/src/vs/base/common/arrays.ts +887 -0
  74. package/src/vs/base/common/arraysFind.ts +202 -0
  75. package/src/vs/base/common/assert.ts +71 -0
  76. package/src/vs/base/common/async.ts +1992 -0
  77. package/src/vs/base/common/cancellation.ts +148 -0
  78. package/src/vs/base/common/charCode.ts +450 -0
  79. package/src/vs/base/common/collections.ts +140 -0
  80. package/src/vs/base/common/decorators.ts +130 -0
  81. package/src/vs/base/common/equals.ts +146 -0
  82. package/src/vs/base/common/errors.ts +303 -0
  83. package/src/vs/base/common/event.ts +1778 -0
  84. package/src/vs/base/common/functional.ts +32 -0
  85. package/src/vs/base/common/hash.ts +316 -0
  86. package/src/vs/base/common/iterator.ts +159 -0
  87. package/src/vs/base/common/keyCodes.ts +526 -0
  88. package/src/vs/base/common/keybindings.ts +284 -0
  89. package/src/vs/base/common/lazy.ts +47 -0
  90. package/src/vs/base/common/lifecycle.ts +801 -0
  91. package/src/vs/base/common/linkedList.ts +142 -0
  92. package/src/vs/base/common/map.ts +202 -0
  93. package/src/vs/base/common/numbers.ts +98 -0
  94. package/src/vs/base/common/observable.ts +76 -0
  95. package/src/vs/base/common/observableInternal/api.ts +31 -0
  96. package/src/vs/base/common/observableInternal/autorun.ts +281 -0
  97. package/src/vs/base/common/observableInternal/base.ts +489 -0
  98. package/src/vs/base/common/observableInternal/debugName.ts +145 -0
  99. package/src/vs/base/common/observableInternal/derived.ts +428 -0
  100. package/src/vs/base/common/observableInternal/lazyObservableValue.ts +146 -0
  101. package/src/vs/base/common/observableInternal/logging.ts +328 -0
  102. package/src/vs/base/common/observableInternal/promise.ts +209 -0
  103. package/src/vs/base/common/observableInternal/utils.ts +610 -0
  104. package/src/vs/base/common/platform.ts +281 -0
  105. package/src/vs/base/common/scrollable.ts +522 -0
  106. package/src/vs/base/common/sequence.ts +34 -0
  107. package/src/vs/base/common/stopwatch.ts +43 -0
  108. package/src/vs/base/common/strings.ts +557 -0
  109. package/src/vs/base/common/symbols.ts +9 -0
  110. package/src/vs/base/common/uint.ts +59 -0
  111. package/src/vs/patches/nls.ts +90 -0
  112. package/src/vs/typings/base-common.d.ts +20 -0
  113. package/src/vs/typings/require.d.ts +42 -0
  114. package/src/vs/typings/thenable.d.ts +12 -0
  115. package/src/vs/typings/vscode-globals-nls.d.ts +36 -0
  116. package/src/vs/typings/vscode-globals-product.d.ts +33 -0
  117. package/typings/xterm.d.ts +66 -15
  118. package/src/browser/Lifecycle.ts +0 -33
  119. package/src/common/EventEmitter.ts +0 -78
  120. package/src/common/Lifecycle.ts +0 -108
  121. /package/src/browser/selection/{Types.d.ts → Types.ts} +0 -0
  122. /package/src/common/parser/{Types.d.ts → Types.ts} +0 -0
@@ -1,414 +1,187 @@
1
1
  /**
2
- * Copyright (c) 2016 The xterm.js authors. All rights reserved.
2
+ * Copyright (c) 2024 The xterm.js authors. All rights reserved.
3
3
  * @license MIT
4
4
  */
5
5
 
6
- import { addDisposableDomListener } from 'browser/Lifecycle';
7
- import { IViewport, ReadonlyColorSet } from 'browser/Types';
8
- import { IRenderDimensions } from 'browser/renderer/shared/Types';
9
- import { ICharSizeService, ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
10
- import { EventEmitter } from 'common/EventEmitter';
11
- import { Disposable } from 'common/Lifecycle';
12
- import { IBuffer } from 'common/buffer/Types';
13
- import { IBufferService, IOptionsService } from 'common/services/Services';
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';
14
16
 
15
- const FALLBACK_SCROLL_BAR_WIDTH = 15;
17
+ export class Viewport extends Disposable {
16
18
 
17
- interface ISmoothScrollState {
18
- startTime: number;
19
- origin: number;
20
- target: number;
21
- }
22
-
23
- /**
24
- * Represents the viewport of a terminal, the visible area within the larger buffer of output.
25
- * Logic for the virtual scroll bar is included in this object.
26
- */
27
- export class Viewport extends Disposable implements IViewport {
28
- public scrollBarWidth: number = 0;
29
- private _currentRowHeight: number = 0;
30
- private _currentDeviceCellHeight: number = 0;
31
- private _lastRecordedBufferLength: number = 0;
32
- private _lastRecordedViewportHeight: number = 0;
33
- private _lastRecordedBufferHeight: number = 0;
34
- private _lastTouchY: number = 0;
35
- private _lastScrollTop: number = 0;
36
- private _activeBuffer: IBuffer;
37
- private _renderDimensions: IRenderDimensions;
38
-
39
- private _smoothScrollAnimationFrame: number = 0;
40
-
41
- // Stores a partial line amount when scrolling, this is used to keep track of how much of a line
42
- // is scrolled so we can "scroll" over partial lines and feel natural on touchpads. This is a
43
- // quick fix and could have a more robust solution in place that reset the value when needed.
44
- private _wheelPartialScroll: number = 0;
19
+ protected _onRequestScrollLines = this._register(new Emitter<number>());
20
+ public readonly onRequestScrollLines = this._onRequestScrollLines.event;
45
21
 
46
- private _refreshAnimationFrame: number | null = null;
47
- private _ignoreNextScrollEvent: boolean = false;
48
- private _smoothScrollState: ISmoothScrollState = {
49
- startTime: 0,
50
- origin: -1,
51
- target: -1
52
- };
22
+ private _scrollableElement: SmoothScrollableElement;
23
+ private _styleElement: HTMLStyleElement;
53
24
 
54
- private _ensureTimeout: number;
55
-
56
- private readonly _onRequestScrollLines = this.register(new EventEmitter<{ amount: number, suppressScrollEvent: boolean }>());
57
- public readonly onRequestScrollLines = this._onRequestScrollLines.event;
25
+ private _queuedAnimationFrame?: number;
26
+ private _latestYDisp?: number;
27
+ private _isSyncing: boolean = false;
28
+ private _isHandlingScroll: boolean = false;
29
+ private _suppressOnScrollHandler: boolean = false;
58
30
 
59
31
  constructor(
60
- private readonly _viewportElement: HTMLElement,
61
- private readonly _scrollArea: HTMLElement,
32
+ element: HTMLElement,
33
+ screenElement: HTMLElement,
62
34
  @IBufferService private readonly _bufferService: IBufferService,
35
+ @ICoreBrowserService coreBrowserService: ICoreBrowserService,
36
+ @ICoreMouseService coreMouseService: ICoreMouseService,
37
+ @IThemeService themeService: IThemeService,
63
38
  @IOptionsService private readonly _optionsService: IOptionsService,
64
- @ICharSizeService private readonly _charSizeService: ICharSizeService,
65
- @IRenderService private readonly _renderService: IRenderService,
66
- @ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService,
67
- @IThemeService themeService: IThemeService
39
+ @IRenderService private readonly _renderService: IRenderService
68
40
  ) {
69
41
  super();
70
42
 
71
- // Measure the width of the scrollbar. If it is 0 we can assume it's an OSX overlay scrollbar.
72
- // Unfortunately the overlay scrollbar would be hidden underneath the screen element in that
73
- // case, therefore we account for a standard amount to make it visible
74
- this.scrollBarWidth = (this._viewportElement.offsetWidth - this._scrollArea.offsetWidth) || FALLBACK_SCROLL_BAR_WIDTH;
75
- this.register(addDisposableDomListener(this._viewportElement, 'scroll', this._handleScroll.bind(this)));
76
-
77
- // Track properties used in performance critical code manually to avoid using slow getters
78
- this._activeBuffer = this._bufferService.buffer;
79
- this.register(this._bufferService.buffers.onBufferActivate(e => this._activeBuffer = e.activeBuffer));
80
- this._renderDimensions = this._renderService.dimensions;
81
- this.register(this._renderService.onDimensionsChange(e => this._renderDimensions = e));
82
-
83
- this._handleThemeChange(themeService.colors);
84
- this.register(themeService.onChangeColors(e => this._handleThemeChange(e)));
85
- this.register(this._optionsService.onSpecificOptionChange('scrollback', () => this.syncScrollArea()));
86
-
87
- // Perform this async to ensure the ICharSizeService is ready.
88
- this._ensureTimeout = window.setTimeout(() => this.syncScrollArea());
43
+ const scrollable = this._register(new Scrollable({
44
+ forceIntegerValues: false,
45
+ smoothScrollDuration: this._optionsService.rawOptions.smoothScrollDuration,
46
+ // This is used over `IRenderService.addRefreshCallback` since it can be canceled
47
+ scheduleAtNextAnimationFrame: cb => scheduleAtNextAnimationFrame(coreBrowserService.window, cb)
48
+ }));
49
+ this._register(this._optionsService.onSpecificOptionChange('smoothScrollDuration', () => {
50
+ scrollable.setSmoothScrollDuration(this._optionsService.rawOptions.smoothScrollDuration);
51
+ }));
52
+
53
+ this._scrollableElement = this._register(new SmoothScrollableElement(screenElement, {
54
+ vertical: ScrollbarVisibility.Auto,
55
+ horizontal: ScrollbarVisibility.Hidden,
56
+ useShadows: false,
57
+ mouseWheelSmoothScroll: true,
58
+ ...this._getChangeOptions()
59
+ }, scrollable));
60
+ this._register(this._optionsService.onMultipleOptionChange([
61
+ 'scrollSensitivity',
62
+ 'fastScrollSensitivity',
63
+ 'overviewRuler'
64
+ ], () => this._scrollableElement.updateOptions(this._getChangeOptions())));
65
+ // Don't handle mouse wheel if wheel events are supported by the current mouse prototcol
66
+ this._register(coreMouseService.onProtocolChange(type => {
67
+ this._scrollableElement.updateOptions({
68
+ handleMouseWheel: !(type & CoreMouseEventType.WHEEL)
69
+ });
70
+ }));
71
+
72
+ this._scrollableElement.setScrollDimensions({ height: 0, scrollHeight: 0 });
73
+ this._register(Event.runAndSubscribe(themeService.onChangeColors, () => {
74
+ this._scrollableElement.getDomNode().style.backgroundColor = themeService.colors.background.css;
75
+ }));
76
+ element.appendChild(this._scrollableElement.getDomNode());
77
+ this._register(toDisposable(() => this._scrollableElement.getDomNode().remove()));
78
+
79
+ this._styleElement = coreBrowserService.mainDocument.createElement('style');
80
+ screenElement.appendChild(this._styleElement);
81
+ this._register(toDisposable(() => this._styleElement.remove()));
82
+ this._register(Event.runAndSubscribe(themeService.onChangeColors, () => {
83
+ this._styleElement.textContent = [
84
+ `.xterm .xterm-scrollable-element > .scrollbar > .slider {`,
85
+ ` background: ${themeService.colors.scrollbarSliderBackground.css};`,
86
+ `}`,
87
+ `.xterm .xterm-scrollable-element > .scrollbar > .slider:hover {`,
88
+ ` background: ${themeService.colors.scrollbarSliderHoverBackground.css};`,
89
+ `}`,
90
+ `.xterm .xterm-scrollable-element > .scrollbar > .slider.active {`,
91
+ ` background: ${themeService.colors.scrollbarSliderActiveBackground.css};`,
92
+ `}`
93
+ ].join('\n');
94
+ }));
95
+
96
+ this._register(this._bufferService.onResize(() => this._queueSync()));
97
+ this._register(this._bufferService.buffers.onBufferActivate(() => this._queueSync()));
98
+ this._register(this._bufferService.onScroll(() => this._sync()));
99
+
100
+ this._register(this._scrollableElement.onScroll(e => this._handleScroll(e)));
89
101
  }
90
102
 
91
- private _handleThemeChange(colors: ReadonlyColorSet): void {
92
- this._viewportElement.style.backgroundColor = colors.background.css;
93
- }
94
-
95
- public reset(): void {
96
- this._currentRowHeight = 0;
97
- this._currentDeviceCellHeight = 0;
98
- this._lastRecordedBufferLength = 0;
99
- this._lastRecordedViewportHeight = 0;
100
- this._lastRecordedBufferHeight = 0;
101
- this._lastTouchY = 0;
102
- this._lastScrollTop = 0;
103
- // Sync on next animation frame to ensure the new terminal state is used
104
- this._coreBrowserService.window.requestAnimationFrame(() => this.syncScrollArea());
105
- }
106
-
107
- /**
108
- * Refreshes row height, setting line-height, viewport height and scroll area height if
109
- * necessary.
110
- */
111
- private _refresh(immediate: boolean): void {
112
- if (immediate) {
113
- this._innerRefresh();
114
- if (this._refreshAnimationFrame !== null) {
115
- this._coreBrowserService.window.cancelAnimationFrame(this._refreshAnimationFrame);
116
- }
117
- return;
118
- }
119
- if (this._refreshAnimationFrame === null) {
120
- this._refreshAnimationFrame = this._coreBrowserService.window.requestAnimationFrame(() => this._innerRefresh());
121
- }
103
+ public scrollLines(disp: number): void {
104
+ const pos = this._scrollableElement.getScrollPosition();
105
+ this._scrollableElement.setScrollPosition({
106
+ reuseAnimation: true,
107
+ scrollTop: pos.scrollTop + disp * this._renderService.dimensions.css.cell.height
108
+ });
122
109
  }
123
110
 
124
- private _innerRefresh(): void {
125
- if (this._charSizeService.height > 0) {
126
- this._currentRowHeight = this._renderDimensions.device.cell.height / this._coreBrowserService.dpr;
127
- this._currentDeviceCellHeight = this._renderDimensions.device.cell.height;
128
- this._lastRecordedViewportHeight = this._viewportElement.offsetHeight;
129
- const newBufferHeight = Math.round(this._currentRowHeight * this._lastRecordedBufferLength) + (this._lastRecordedViewportHeight - this._renderDimensions.css.canvas.height);
130
- if (this._lastRecordedBufferHeight !== newBufferHeight) {
131
- this._lastRecordedBufferHeight = newBufferHeight;
132
- this._scrollArea.style.height = this._lastRecordedBufferHeight + 'px';
133
- }
134
- }
135
-
136
- // Sync scrollTop
137
- const scrollTop = this._bufferService.buffer.ydisp * this._currentRowHeight;
138
- if (this._viewportElement.scrollTop !== scrollTop) {
139
- // Ignore the next scroll event which will be triggered by setting the scrollTop as we do not
140
- // want this event to scroll the terminal
141
- this._ignoreNextScrollEvent = true;
142
- this._viewportElement.scrollTop = scrollTop;
111
+ public scrollToLine(line: number, disableSmoothScroll?: boolean): void {
112
+ if (disableSmoothScroll) {
113
+ this._latestYDisp = line;
143
114
  }
144
-
145
- this._refreshAnimationFrame = null;
115
+ this._scrollableElement.setScrollPosition({
116
+ reuseAnimation: !disableSmoothScroll,
117
+ scrollTop: line * this._renderService.dimensions.css.cell.height
118
+ });
146
119
  }
147
120
 
148
- /**
149
- * Updates dimensions and synchronizes the scroll area if necessary.
150
- */
151
- public syncScrollArea(immediate: boolean = false): void {
152
- // If buffer height changed
153
- if (this._lastRecordedBufferLength !== this._bufferService.buffer.lines.length) {
154
- this._lastRecordedBufferLength = this._bufferService.buffer.lines.length;
155
- this._refresh(immediate);
156
- return;
157
- }
158
-
159
- // If viewport height changed
160
- if (this._lastRecordedViewportHeight !== this._renderService.dimensions.css.canvas.height) {
161
- this._refresh(immediate);
162
- return;
163
- }
164
-
165
- // If the buffer position doesn't match last scroll top
166
- if (this._lastScrollTop !== this._activeBuffer.ydisp * this._currentRowHeight) {
167
- this._refresh(immediate);
168
- return;
169
- }
170
-
171
- // If row height changed
172
- if (this._renderDimensions.device.cell.height !== this._currentDeviceCellHeight) {
173
- this._refresh(immediate);
174
- return;
175
- }
121
+ private _getChangeOptions(): ScrollableElementChangeOptions {
122
+ return {
123
+ mouseWheelScrollSensitivity: this._optionsService.rawOptions.scrollSensitivity,
124
+ fastScrollSensitivity: this._optionsService.rawOptions.fastScrollSensitivity,
125
+ verticalScrollbarSize: this._optionsService.rawOptions.overviewRuler?.width || ViewportConstants.DEFAULT_SCROLL_BAR_WIDTH
126
+ };
176
127
  }
177
128
 
178
- /**
179
- * Handles scroll events on the viewport, calculating the new viewport and requesting the
180
- * terminal to scroll to it.
181
- * @param ev The scroll event.
182
- */
183
- private _handleScroll(ev: Event): void {
184
- // Record current scroll top position
185
- this._lastScrollTop = this._viewportElement.scrollTop;
186
-
187
- // Don't attempt to scroll if the element is not visible, otherwise scrollTop will be corrupt
188
- // which causes the terminal to scroll the buffer to the top
189
- if (!this._viewportElement.offsetParent) {
190
- return;
129
+ private _queueSync(ydisp?: number): void {
130
+ // Update state
131
+ if (ydisp !== undefined) {
132
+ this._latestYDisp = ydisp;
191
133
  }
192
134
 
193
- // Ignore the event if it was flagged to ignore (when the source of the event is from Viewport)
194
- if (this._ignoreNextScrollEvent) {
195
- this._ignoreNextScrollEvent = false;
196
- // Still trigger the scroll so lines get refreshed
197
- this._onRequestScrollLines.fire({ amount: 0, suppressScrollEvent: true });
135
+ // Don't queue more than one callback
136
+ if (this._queuedAnimationFrame !== undefined) {
198
137
  return;
199
138
  }
200
-
201
- const newRow = Math.round(this._lastScrollTop / this._currentRowHeight);
202
- const diff = newRow - this._bufferService.buffer.ydisp;
203
- this._onRequestScrollLines.fire({ amount: diff, suppressScrollEvent: true });
139
+ this._queuedAnimationFrame = this._renderService.addRefreshCallback(() => {
140
+ this._queuedAnimationFrame = undefined;
141
+ this._sync(this._latestYDisp);
142
+ });
204
143
  }
205
144
 
206
- private _smoothScroll(): void {
207
- // Check valid state
208
- if (this._isDisposed || this._smoothScrollState.origin === -1 || this._smoothScrollState.target === -1) {
145
+ private _sync(ydisp: number = this._bufferService.buffer.ydisp): void {
146
+ if (!this._renderService || this._isSyncing) {
209
147
  return;
210
148
  }
149
+ this._isSyncing = true;
211
150
 
212
- // Calculate position complete
213
- const percent = this._smoothScrollPercent();
214
- this._viewportElement.scrollTop = this._smoothScrollState.origin + Math.round(percent * (this._smoothScrollState.target - this._smoothScrollState.origin));
151
+ // Ignore any onScroll event that happens as a result of dimensions changing as this should
152
+ // never cause a scrollLines call, only setScrollPosition can do that.
153
+ this._suppressOnScrollHandler = true;
154
+ this._scrollableElement.setScrollDimensions({
155
+ height: this._renderService.dimensions.css.canvas.height,
156
+ scrollHeight: this._renderService.dimensions.css.cell.height * this._bufferService.buffer.lines.length
157
+ });
158
+ this._suppressOnScrollHandler = false;
215
159
 
216
- // Continue or finish smooth scroll
217
- if (percent < 1) {
218
- if (!this._smoothScrollAnimationFrame) {
219
- this._smoothScrollAnimationFrame = this._coreBrowserService.window.requestAnimationFrame(() => {
220
- this._smoothScrollAnimationFrame = 0;
221
- this._smoothScroll();
222
- });
223
- }
224
- } else {
225
- this._clearSmoothScrollState();
160
+ // If ydisp has been changed by some other copmonent (input/buffer), then stop animating smooth
161
+ // scroll and scroll there immediately.
162
+ if (ydisp !== this._latestYDisp) {
163
+ this._scrollableElement.setScrollPosition({
164
+ scrollTop: ydisp * this._renderService.dimensions.css.cell.height
165
+ });
226
166
  }
227
- }
228
-
229
- private _smoothScrollPercent(): number {
230
- if (!this._optionsService.rawOptions.smoothScrollDuration || !this._smoothScrollState.startTime) {
231
- return 1;
232
- }
233
- return Math.max(Math.min((Date.now() - this._smoothScrollState.startTime) / this._optionsService.rawOptions.smoothScrollDuration, 1), 0);
234
- }
235
-
236
- private _clearSmoothScrollState(): void {
237
- this._smoothScrollState.startTime = 0;
238
- this._smoothScrollState.origin = -1;
239
- this._smoothScrollState.target = -1;
240
- }
241
167
 
242
- /**
243
- * Handles bubbling of scroll event in case the viewport has reached top or bottom
244
- * @param ev The scroll event.
245
- * @param amount The amount scrolled
246
- */
247
- private _bubbleScroll(ev: Event, amount: number): boolean {
248
- const scrollPosFromTop = this._viewportElement.scrollTop + this._lastRecordedViewportHeight;
249
- if ((amount < 0 && this._viewportElement.scrollTop !== 0) ||
250
- (amount > 0 && scrollPosFromTop < this._lastRecordedBufferHeight)) {
251
- if (ev.cancelable) {
252
- ev.preventDefault();
253
- }
254
- return false;
255
- }
256
- return true;
168
+ this._isSyncing = false;
257
169
  }
258
170
 
259
- /**
260
- * Handles mouse wheel events by adjusting the viewport's scrollTop and delegating the actual
261
- * scrolling to `onScroll`, this event needs to be attached manually by the consumer of
262
- * `Viewport`.
263
- * @param ev The mouse wheel event.
264
- */
265
- public handleWheel(ev: WheelEvent): boolean {
266
- const amount = this._getPixelsScrolled(ev);
267
- if (amount === 0) {
268
- return false;
269
- }
270
- if (!this._optionsService.rawOptions.smoothScrollDuration) {
271
- this._viewportElement.scrollTop += amount;
272
- } else {
273
- this._smoothScrollState.startTime = Date.now();
274
- if (this._smoothScrollPercent() < 1) {
275
- this._smoothScrollState.origin = this._viewportElement.scrollTop;
276
- if (this._smoothScrollState.target === -1) {
277
- this._smoothScrollState.target = this._viewportElement.scrollTop + amount;
278
- } else {
279
- this._smoothScrollState.target += amount;
280
- }
281
- this._smoothScrollState.target = Math.max(Math.min(this._smoothScrollState.target, this._viewportElement.scrollHeight), 0);
282
- this._smoothScroll();
283
- } else {
284
- this._clearSmoothScrollState();
285
- }
286
- }
287
- return this._bubbleScroll(ev, amount);
288
- }
289
-
290
- public scrollLines(disp: number): void {
291
- if (disp === 0) {
171
+ private _handleScroll(e: ScrollEvent): void {
172
+ if (!this._renderService) {
292
173
  return;
293
174
  }
294
- if (!this._optionsService.rawOptions.smoothScrollDuration) {
295
- this._onRequestScrollLines.fire({ amount: disp, suppressScrollEvent: false });
296
- } else {
297
- const amount = disp * this._currentRowHeight;
298
- this._smoothScrollState.startTime = Date.now();
299
- if (this._smoothScrollPercent() < 1) {
300
- this._smoothScrollState.origin = this._viewportElement.scrollTop;
301
- this._smoothScrollState.target = this._smoothScrollState.origin + amount;
302
- this._smoothScrollState.target = Math.max(Math.min(this._smoothScrollState.target, this._viewportElement.scrollHeight), 0);
303
- this._smoothScroll();
304
- } else {
305
- this._clearSmoothScrollState();
306
- }
307
- }
308
- }
309
-
310
- private _getPixelsScrolled(ev: WheelEvent): number {
311
- // Do nothing if it's not a vertical scroll event
312
- if (ev.deltaY === 0 || ev.shiftKey) {
313
- return 0;
314
- }
315
-
316
- // Fallback to WheelEvent.DOM_DELTA_PIXEL
317
- let amount = this._applyScrollModifier(ev.deltaY, ev);
318
- if (ev.deltaMode === WheelEvent.DOM_DELTA_LINE) {
319
- amount *= this._currentRowHeight;
320
- } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
321
- amount *= this._currentRowHeight * this._bufferService.rows;
322
- }
323
- return amount;
324
- }
325
-
326
-
327
- public getBufferElements(startLine: number, endLine?: number): { bufferElements: HTMLElement[], cursorElement?: HTMLElement } {
328
- let currentLine: string = '';
329
- let cursorElement: HTMLElement | undefined;
330
- const bufferElements: HTMLElement[] = [];
331
- const end = endLine ?? this._bufferService.buffer.lines.length;
332
- const lines = this._bufferService.buffer.lines;
333
- for (let i = startLine; i < end; i++) {
334
- const line = lines.get(i);
335
- if (!line) {
336
- continue;
337
- }
338
- const isWrapped = lines.get(i + 1)?.isWrapped;
339
- currentLine += line.translateToString(!isWrapped);
340
- if (!isWrapped || i === lines.length - 1) {
341
- const div = document.createElement('div');
342
- div.textContent = currentLine;
343
- bufferElements.push(div);
344
- if (currentLine.length > 0) {
345
- cursorElement = div;
346
- }
347
- currentLine = '';
348
- }
349
- }
350
- return { bufferElements, cursorElement };
351
- }
352
-
353
- /**
354
- * Gets the number of pixels scrolled by the mouse event taking into account what type of delta
355
- * is being used.
356
- * @param ev The mouse wheel event.
357
- */
358
- public getLinesScrolled(ev: WheelEvent): number {
359
- // Do nothing if it's not a vertical scroll event
360
- if (ev.deltaY === 0 || ev.shiftKey) {
361
- return 0;
362
- }
363
-
364
- // Fallback to WheelEvent.DOM_DELTA_LINE
365
- let amount = this._applyScrollModifier(ev.deltaY, ev);
366
- if (ev.deltaMode === WheelEvent.DOM_DELTA_PIXEL) {
367
- amount /= this._currentRowHeight + 0.0; // Prevent integer division
368
- this._wheelPartialScroll += amount;
369
- amount = Math.floor(Math.abs(this._wheelPartialScroll)) * (this._wheelPartialScroll > 0 ? 1 : -1);
370
- this._wheelPartialScroll %= 1;
371
- } else if (ev.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
372
- amount *= this._bufferService.rows;
373
- }
374
- return amount;
375
- }
376
-
377
- private _applyScrollModifier(amount: number, ev: WheelEvent): number {
378
- const modifier = this._optionsService.rawOptions.fastScrollModifier;
379
- // Multiply the scroll speed when the modifier is down
380
- if ((modifier === 'alt' && ev.altKey) ||
381
- (modifier === 'ctrl' && ev.ctrlKey) ||
382
- (modifier === 'shift' && ev.shiftKey)) {
383
- return amount * this._optionsService.rawOptions.fastScrollSensitivity * this._optionsService.rawOptions.scrollSensitivity;
175
+ if (this._isHandlingScroll || this._suppressOnScrollHandler) {
176
+ return;
384
177
  }
385
-
386
- return amount * this._optionsService.rawOptions.scrollSensitivity;
387
- }
388
-
389
- /**
390
- * Handles the touchstart event, recording the touch occurred.
391
- * @param ev The touch event.
392
- */
393
- public handleTouchStart(ev: TouchEvent): void {
394
- this._lastTouchY = ev.touches[0].pageY;
395
- }
396
-
397
- /**
398
- * Handles the touchmove event, scrolling the viewport if the position shifted.
399
- * @param ev The touch event.
400
- */
401
- public handleTouchMove(ev: TouchEvent): boolean {
402
- const deltaY = this._lastTouchY - ev.touches[0].pageY;
403
- this._lastTouchY = ev.touches[0].pageY;
404
- if (deltaY === 0) {
405
- return false;
178
+ this._isHandlingScroll = true;
179
+ const newRow = Math.round(e.scrollTop / this._renderService.dimensions.css.cell.height);
180
+ const diff = newRow - this._bufferService.buffer.ydisp;
181
+ if (diff !== 0) {
182
+ this._latestYDisp = newRow;
183
+ this._onRequestScrollLines.fire(diff);
406
184
  }
407
- this._viewportElement.scrollTop += deltaY;
408
- return this._bubbleScroll(ev, deltaY);
409
- }
410
-
411
- public dispose(): void {
412
- clearTimeout(this._ensureTimeout);
185
+ this._isHandlingScroll = false;
413
186
  }
414
187
  }
@@ -4,7 +4,7 @@
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
 
6
6
  import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
7
- import { Disposable, toDisposable } from 'common/Lifecycle';
7
+ import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
8
8
  import { IBufferService, IDecorationService, IInternalDecoration } from 'common/services/Services';
9
9
 
10
10
  export class BufferDecorationRenderer extends Disposable {
@@ -28,18 +28,18 @@ export class BufferDecorationRenderer extends Disposable {
28
28
  this._container.classList.add('xterm-decoration-container');
29
29
  this._screenElement.appendChild(this._container);
30
30
 
31
- this.register(this._renderService.onRenderedViewportChange(() => this._doRefreshDecorations()));
32
- this.register(this._renderService.onDimensionsChange(() => {
31
+ this._register(this._renderService.onRenderedViewportChange(() => this._doRefreshDecorations()));
32
+ this._register(this._renderService.onDimensionsChange(() => {
33
33
  this._dimensionsChanged = true;
34
34
  this._queueRefresh();
35
35
  }));
36
- this.register(this._coreBrowserService.onDprChange(() => this._queueRefresh()));
37
- this.register(this._bufferService.buffers.onBufferActivate(() => {
36
+ this._register(this._coreBrowserService.onDprChange(() => this._queueRefresh()));
37
+ this._register(this._bufferService.buffers.onBufferActivate(() => {
38
38
  this._altBufferIsActive = this._bufferService.buffer === this._bufferService.buffers.alt;
39
39
  }));
40
- this.register(this._decorationService.onDecorationRegistered(() => this._queueRefresh()));
41
- this.register(this._decorationService.onDecorationRemoved(decoration => this._removeDecoration(decoration)));
42
- this.register(toDisposable(() => {
40
+ this._register(this._decorationService.onDecorationRegistered(() => this._queueRefresh()));
41
+ this._register(this._decorationService.onDecorationRemoved(decoration => this._removeDecoration(decoration)));
42
+ this._register(toDisposable(() => {
43
43
  this._container.remove();
44
44
  this._decorationElements.clear();
45
45
  }));
@@ -108,8 +108,13 @@ export class BufferDecorationRenderer extends Disposable {
108
108
  element!.remove();
109
109
  });
110
110
  }
111
- element.style.top = `${line * this._renderService.dimensions.css.cell.height}px`;
112
111
  element.style.display = this._altBufferIsActive ? 'none' : 'block';
112
+ if (!this._altBufferIsActive) {
113
+ element.style.width = `${Math.round((decoration.options.width || 1) * this._renderService.dimensions.css.cell.width)}px`;
114
+ element.style.height = `${(decoration.options.height || 1) * this._renderService.dimensions.css.cell.height}px`;
115
+ element.style.top = `${line * this._renderService.dimensions.css.cell.height}px`;
116
+ element.style.lineHeight = `${this._renderService.dimensions.css.cell.height}px`;
117
+ }
113
118
  decoration.onRenderEmitter.fire(element);
114
119
  }
115
120
  }