@xterm/xterm 6.1.0-beta.25 → 6.1.0-beta.250

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 (163) 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 +25 -22
  8. package/src/browser/AccessibilityManager.ts +11 -6
  9. package/src/browser/Clipboard.ts +6 -3
  10. package/src/browser/CoreBrowserTerminal.ts +149 -319
  11. package/src/browser/Dom.ts +178 -0
  12. package/src/browser/Linkifier.ts +11 -11
  13. package/src/browser/OscLinkProvider.ts +82 -14
  14. package/src/browser/RenderDebouncer.ts +2 -2
  15. package/src/browser/TimeBasedDebouncer.ts +2 -2
  16. package/src/browser/Types.ts +12 -11
  17. package/src/browser/Viewport.ts +55 -20
  18. package/src/browser/decorations/BufferDecorationRenderer.ts +1 -1
  19. package/src/browser/decorations/OverviewRulerRenderer.ts +33 -17
  20. package/src/browser/input/CompositionHelper.ts +44 -8
  21. package/src/browser/public/Terminal.ts +25 -28
  22. package/src/browser/renderer/dom/DomRenderer.ts +242 -76
  23. package/src/browser/renderer/dom/DomRendererRowFactory.ts +19 -13
  24. package/src/browser/renderer/dom/WidthCache.ts +54 -52
  25. package/src/browser/renderer/shared/Constants.ts +7 -0
  26. package/src/browser/renderer/shared/TextBlinkStateManager.ts +97 -0
  27. package/src/browser/renderer/shared/Types.ts +8 -2
  28. package/src/browser/scrollable/abstractScrollbar.ts +300 -0
  29. package/src/browser/scrollable/fastDomNode.ts +126 -0
  30. package/src/browser/scrollable/globalPointerMoveMonitor.ts +90 -0
  31. package/src/browser/scrollable/horizontalScrollbar.ts +85 -0
  32. package/src/browser/scrollable/mouseEvent.ts +292 -0
  33. package/src/browser/scrollable/scrollable.ts +486 -0
  34. package/src/browser/scrollable/scrollableElement.ts +581 -0
  35. package/src/browser/scrollable/scrollableElementOptions.ts +161 -0
  36. package/src/browser/scrollable/scrollbarArrow.ts +110 -0
  37. package/src/browser/scrollable/scrollbarState.ts +246 -0
  38. package/src/browser/scrollable/scrollbarVisibilityController.ts +113 -0
  39. package/src/browser/scrollable/touch.ts +485 -0
  40. package/src/browser/scrollable/verticalScrollbar.ts +143 -0
  41. package/src/browser/scrollable/widget.ts +23 -0
  42. package/src/browser/services/CharSizeService.ts +2 -2
  43. package/src/browser/services/CoreBrowserService.ts +7 -5
  44. package/src/browser/services/KeyboardService.ts +67 -0
  45. package/src/browser/services/LinkProviderService.ts +1 -1
  46. package/src/browser/services/MouseCoordsService.ts +47 -0
  47. package/src/browser/services/MouseService.ts +518 -25
  48. package/src/browser/services/RenderService.ts +28 -16
  49. package/src/browser/services/SelectionService.ts +45 -39
  50. package/src/browser/services/Services.ts +40 -17
  51. package/src/browser/services/ThemeService.ts +2 -2
  52. package/src/common/Async.ts +141 -0
  53. package/src/common/CircularList.ts +2 -2
  54. package/src/common/Color.ts +8 -0
  55. package/src/common/CoreTerminal.ts +32 -22
  56. package/src/common/Event.ts +118 -0
  57. package/src/common/InputHandler.ts +286 -87
  58. package/src/common/Lifecycle.ts +113 -0
  59. package/src/common/Platform.ts +13 -3
  60. package/src/common/SortedList.ts +7 -3
  61. package/src/common/StringBuilder.ts +67 -0
  62. package/src/common/TaskQueue.ts +14 -5
  63. package/src/common/Types.ts +51 -31
  64. package/src/common/Version.ts +9 -0
  65. package/src/common/buffer/Buffer.ts +34 -19
  66. package/src/common/buffer/BufferLine.ts +140 -68
  67. package/src/common/buffer/BufferLineStringCache.ts +69 -0
  68. package/src/common/buffer/BufferReflow.ts +4 -1
  69. package/src/common/buffer/BufferSet.ts +11 -6
  70. package/src/common/buffer/CellData.ts +57 -0
  71. package/src/common/buffer/Marker.ts +2 -2
  72. package/src/common/buffer/Types.ts +6 -2
  73. package/src/common/data/EscapeSequences.ts +71 -70
  74. package/src/common/input/Keyboard.ts +14 -7
  75. package/src/common/input/KittyKeyboard.ts +526 -0
  76. package/src/common/input/Win32InputMode.ts +297 -0
  77. package/src/common/input/WriteBuffer.ts +107 -38
  78. package/src/common/input/XParseColor.ts +2 -2
  79. package/src/common/parser/ApcParser.ts +196 -0
  80. package/src/common/parser/Constants.ts +14 -4
  81. package/src/common/parser/DcsParser.ts +11 -12
  82. package/src/common/parser/EscapeSequenceParser.ts +205 -63
  83. package/src/common/parser/OscParser.ts +11 -12
  84. package/src/common/parser/Params.ts +27 -8
  85. package/src/common/parser/Types.ts +36 -2
  86. package/src/common/public/BufferLineApiView.ts +2 -2
  87. package/src/common/public/BufferNamespaceApi.ts +3 -3
  88. package/src/common/public/ParserApi.ts +3 -0
  89. package/src/common/services/BufferService.ts +14 -9
  90. package/src/common/services/CharsetService.ts +4 -0
  91. package/src/common/services/CoreService.ts +22 -9
  92. package/src/common/services/DecorationService.ts +255 -8
  93. package/src/common/services/LogService.ts +1 -31
  94. package/src/common/services/{CoreMouseService.ts → MouseStateService.ts} +21 -132
  95. package/src/common/services/OptionsService.ts +13 -4
  96. package/src/common/services/ServiceRegistry.ts +9 -7
  97. package/src/common/services/Services.ts +49 -40
  98. package/src/common/services/UnicodeService.ts +1 -1
  99. package/typings/xterm.d.ts +318 -34
  100. package/src/common/Clone.ts +0 -23
  101. package/src/common/TypedArrayUtils.ts +0 -17
  102. package/src/vs/base/browser/browser.ts +0 -141
  103. package/src/vs/base/browser/canIUse.ts +0 -49
  104. package/src/vs/base/browser/dom.ts +0 -2369
  105. package/src/vs/base/browser/fastDomNode.ts +0 -316
  106. package/src/vs/base/browser/globalPointerMoveMonitor.ts +0 -112
  107. package/src/vs/base/browser/iframe.ts +0 -135
  108. package/src/vs/base/browser/keyboardEvent.ts +0 -213
  109. package/src/vs/base/browser/mouseEvent.ts +0 -229
  110. package/src/vs/base/browser/touch.ts +0 -372
  111. package/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +0 -303
  112. package/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +0 -114
  113. package/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +0 -720
  114. package/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +0 -165
  115. package/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +0 -114
  116. package/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +0 -243
  117. package/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +0 -118
  118. package/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +0 -116
  119. package/src/vs/base/browser/ui/widget.ts +0 -57
  120. package/src/vs/base/browser/window.ts +0 -14
  121. package/src/vs/base/common/arrays.ts +0 -887
  122. package/src/vs/base/common/arraysFind.ts +0 -202
  123. package/src/vs/base/common/assert.ts +0 -71
  124. package/src/vs/base/common/async.ts +0 -1992
  125. package/src/vs/base/common/cancellation.ts +0 -148
  126. package/src/vs/base/common/charCode.ts +0 -450
  127. package/src/vs/base/common/collections.ts +0 -140
  128. package/src/vs/base/common/decorators.ts +0 -130
  129. package/src/vs/base/common/equals.ts +0 -146
  130. package/src/vs/base/common/errors.ts +0 -303
  131. package/src/vs/base/common/event.ts +0 -1778
  132. package/src/vs/base/common/functional.ts +0 -32
  133. package/src/vs/base/common/hash.ts +0 -316
  134. package/src/vs/base/common/iterator.ts +0 -159
  135. package/src/vs/base/common/keyCodes.ts +0 -526
  136. package/src/vs/base/common/keybindings.ts +0 -284
  137. package/src/vs/base/common/lazy.ts +0 -47
  138. package/src/vs/base/common/lifecycle.ts +0 -801
  139. package/src/vs/base/common/linkedList.ts +0 -142
  140. package/src/vs/base/common/map.ts +0 -202
  141. package/src/vs/base/common/numbers.ts +0 -98
  142. package/src/vs/base/common/observable.ts +0 -76
  143. package/src/vs/base/common/observableInternal/api.ts +0 -31
  144. package/src/vs/base/common/observableInternal/autorun.ts +0 -281
  145. package/src/vs/base/common/observableInternal/base.ts +0 -489
  146. package/src/vs/base/common/observableInternal/debugName.ts +0 -145
  147. package/src/vs/base/common/observableInternal/derived.ts +0 -428
  148. package/src/vs/base/common/observableInternal/lazyObservableValue.ts +0 -146
  149. package/src/vs/base/common/observableInternal/logging.ts +0 -328
  150. package/src/vs/base/common/observableInternal/promise.ts +0 -209
  151. package/src/vs/base/common/observableInternal/utils.ts +0 -610
  152. package/src/vs/base/common/platform.ts +0 -281
  153. package/src/vs/base/common/scrollable.ts +0 -522
  154. package/src/vs/base/common/sequence.ts +0 -34
  155. package/src/vs/base/common/stopwatch.ts +0 -43
  156. package/src/vs/base/common/strings.ts +0 -557
  157. package/src/vs/base/common/symbols.ts +0 -9
  158. package/src/vs/base/common/uint.ts +0 -59
  159. package/src/vs/patches/nls.ts +0 -90
  160. package/src/vs/typings/base-common.d.ts +0 -20
  161. package/src/vs/typings/require.d.ts +0 -42
  162. package/src/vs/typings/vscode-globals-nls.d.ts +0 -36
  163. package/src/vs/typings/vscode-globals-product.d.ts +0 -33
@@ -32,6 +32,7 @@ export interface IParams {
32
32
  clone(): IParams;
33
33
  toArray(): ParamsArray;
34
34
  reset(): void;
35
+ resetZdm(): void;
35
36
  addParam(value: number): void;
36
37
  addSubParam(value: number): void;
37
38
  hasSubParams(idx: number): boolean;
@@ -107,7 +108,7 @@ export type EscFallbackHandlerType = (identifier: number) => void;
107
108
  /**
108
109
  * EXECUTE handler types.
109
110
  */
110
- export type ExecuteHandlerType = () => boolean;
111
+ export type ExecuteHandlerType = (ident?: number) => boolean;
111
112
  export type ExecuteFallbackHandlerType = (ident: number) => void;
112
113
 
113
114
  /**
@@ -134,6 +135,29 @@ export interface IOscHandler {
134
135
  }
135
136
  export type OscFallbackHandlerType = (ident: number, action: 'START' | 'PUT' | 'END', payload?: any) => void;
136
137
 
138
+ /**
139
+ * APC handler types.
140
+ */
141
+ export interface IApcHandler {
142
+ /**
143
+ * Announces start of this APC command.
144
+ * Prepare needed data structures here.
145
+ */
146
+ start(): void;
147
+ /**
148
+ * Incoming data chunk.
149
+ */
150
+ put(data: Uint32Array, start: number, end: number): void;
151
+ /**
152
+ * End of APC command. `success` indicates whether the
153
+ * command finished normally or got aborted, thus final
154
+ * execution of the command should depend on `success`.
155
+ * To save memory also cleanup data structures here.
156
+ */
157
+ end(success: boolean): boolean | Promise<boolean>;
158
+ }
159
+ export type ApcFallbackHandlerType = (ident: number, action: 'START' | 'PUT' | 'END', payload?: any) => void;
160
+
137
161
  /**
138
162
  * PRINT handler types.
139
163
  */
@@ -196,6 +220,10 @@ export interface IEscapeSequenceParser extends IDisposable {
196
220
  clearOscHandler(ident: number): void;
197
221
  setOscHandlerFallback(handler: OscFallbackHandlerType): void;
198
222
 
223
+ registerApcHandler(id: IFunctionIdentifier, handler: IApcHandler): IDisposable;
224
+ clearApcHandler(id: IFunctionIdentifier): void;
225
+ setApcHandlerFallback(handler: ApcFallbackHandlerType): void;
226
+
199
227
  setErrorHandler(handler: (state: IParsingState) => IParsingState): void;
200
228
  clearErrorHandler(): void;
201
229
  }
@@ -223,6 +251,11 @@ export interface IDcsParser extends ISubParser<IDcsHandler, DcsFallbackHandlerTy
223
251
  unhook(success: boolean, promiseResult?: boolean): void | Promise<boolean>;
224
252
  }
225
253
 
254
+ export interface IApcParser extends ISubParser<IApcHandler, ApcFallbackHandlerType> {
255
+ start(ident: number): void;
256
+ end(success: boolean, promiseResult?: boolean): void | Promise<boolean>;
257
+ }
258
+
226
259
  /**
227
260
  * Interface to denote a specific ESC, CSI or DCS handler slot.
228
261
  * The values are used to create an integer respresentation during handler
@@ -252,7 +285,8 @@ export const enum ParserStackType {
252
285
  CSI,
253
286
  ESC,
254
287
  OSC,
255
- DCS
288
+ DCS,
289
+ APC
256
290
  }
257
291
 
258
292
  // aggregate of resumable handler lists
@@ -18,10 +18,10 @@ export class BufferLineApiView implements IBufferLineApi {
18
18
  }
19
19
 
20
20
  if (cell) {
21
- this._line.loadCell(x, cell as ICellData);
21
+ this._line.loadCell(x, cell as unknown as ICellData);
22
22
  return cell;
23
23
  }
24
- return this._line.loadCell(x, new CellData());
24
+ return this._line.loadCell(x, new CellData()) as unknown as IBufferCellApi;
25
25
  }
26
26
  public translateToString(trimRight?: boolean, startColumn?: number, endColumn?: number): string {
27
27
  return this._line.translateToString(trimRight, startColumn, endColumn);
@@ -6,8 +6,8 @@
6
6
  import { IBuffer as IBufferApi, IBufferNamespace as IBufferNamespaceApi } from '@xterm/xterm';
7
7
  import { BufferApiView } from 'common/public/BufferApiView';
8
8
  import { ICoreTerminal } from 'common/Types';
9
- import { Disposable } from 'vs/base/common/lifecycle';
10
- import { Emitter } from 'vs/base/common/event';
9
+ import { Disposable } from 'common/Lifecycle';
10
+ import { Emitter } from 'common/Event';
11
11
 
12
12
  export class BufferNamespaceApi extends Disposable implements IBufferNamespaceApi {
13
13
  private _normal: BufferApiView;
@@ -20,7 +20,7 @@ export class BufferNamespaceApi extends Disposable implements IBufferNamespaceAp
20
20
  super();
21
21
  this._normal = new BufferApiView(this._core.buffers.normal, 'normal');
22
22
  this._alternate = new BufferApiView(this._core.buffers.alt, 'alternate');
23
- this._core.buffers.onBufferActivate(() => this._onBufferChange.fire(this.active));
23
+ this._register(this._core.buffers.onBufferActivate(() => this._onBufferChange.fire(this.active)));
24
24
  }
25
25
  public get active(): IBufferApi {
26
26
  if (this._core.buffers.active === this._core.buffers.normal) { return this.normal; }
@@ -34,4 +34,7 @@ export class ParserApi implements IParser {
34
34
  public addOscHandler(ident: number, callback: (data: string) => boolean | Promise<boolean>): IDisposable {
35
35
  return this.registerOscHandler(ident, callback);
36
36
  }
37
+ public registerApcHandler(id: IFunctionIdentifier, callback: (data: string) => boolean | Promise<boolean>): IDisposable {
38
+ return this._core.registerApcHandler(id, callback);
39
+ }
37
40
  }
@@ -3,15 +3,17 @@
3
3
  * @license MIT
4
4
  */
5
5
 
6
- import { Disposable } from 'vs/base/common/lifecycle';
6
+ import { Disposable } from 'common/Lifecycle';
7
7
  import { IAttributeData, IBufferLine } from 'common/Types';
8
8
  import { BufferSet } from 'common/buffer/BufferSet';
9
9
  import { IBuffer, IBufferSet } from 'common/buffer/Types';
10
- import { IBufferService, IOptionsService, type IBufferResizeEvent } from 'common/services/Services';
11
- import { Emitter } from 'vs/base/common/event';
10
+ import { IBufferService, ILogService, IOptionsService, type IBufferResizeEvent } from 'common/services/Services';
11
+ import { Emitter } from 'common/Event';
12
12
 
13
- export const MINIMUM_COLS = 2; // Less than 2 can mess with wide chars
14
- export const MINIMUM_ROWS = 1;
13
+ export const enum BufferServiceConstants {
14
+ MINIMUM_COLS = 2, // Less than 2 can mess with wide chars
15
+ MINIMUM_ROWS = 1
16
+ }
15
17
 
16
18
  export class BufferService extends Disposable implements IBufferService {
17
19
  public serviceBrand: any;
@@ -32,11 +34,14 @@ export class BufferService extends Disposable implements IBufferService {
32
34
  /** An IBufferline to clone/copy from for new blank lines */
33
35
  private _cachedBlankLine: IBufferLine | undefined;
34
36
 
35
- constructor(@IOptionsService optionsService: IOptionsService) {
37
+ constructor(
38
+ @IOptionsService optionsService: IOptionsService,
39
+ @ILogService logService: ILogService
40
+ ) {
36
41
  super();
37
- this.cols = Math.max(optionsService.rawOptions.cols || 0, MINIMUM_COLS);
38
- this.rows = Math.max(optionsService.rawOptions.rows || 0, MINIMUM_ROWS);
39
- this.buffers = this._register(new BufferSet(optionsService, this));
42
+ this.cols = Math.max(optionsService.rawOptions.cols || 0, BufferServiceConstants.MINIMUM_COLS);
43
+ this.rows = Math.max(optionsService.rawOptions.rows || 0, BufferServiceConstants.MINIMUM_ROWS);
44
+ this.buffers = this._register(new BufferSet(optionsService, this, logService));
40
45
  this._register(this.buffers.onBufferActivate(e => {
41
46
  this._onScroll.fire(e.activeBuffer.ydisp);
42
47
  }));
@@ -14,6 +14,10 @@ export class CharsetService implements ICharsetService {
14
14
 
15
15
  private _charsets: (ICharset | undefined)[] = [];
16
16
 
17
+ public get charsets(): (ICharset | undefined)[] {
18
+ return this._charsets;
19
+ }
20
+
17
21
  public reset(): void {
18
22
  this.charset = undefined;
19
23
  this._charsets = [];
@@ -3,11 +3,10 @@
3
3
  * @license MIT
4
4
  */
5
5
 
6
- import { clone } from 'common/Clone';
7
- import { Disposable } from 'vs/base/common/lifecycle';
8
- import { IDecPrivateModes, IModes } from 'common/Types';
6
+ import { Disposable } from 'common/Lifecycle';
7
+ import { IDecPrivateModes, IKittyKeyboardState, IModes } from 'common/Types';
9
8
  import { IBufferService, ICoreService, ILogService, IOptionsService } from 'common/services/Services';
10
- import { Emitter } from 'vs/base/common/event';
9
+ import { Emitter } from 'common/Event';
11
10
 
12
11
  const DEFAULT_MODES: IModes = Object.freeze({
13
12
  insertMode: false
@@ -17,22 +16,33 @@ const DEFAULT_DEC_PRIVATE_MODES: IDecPrivateModes = Object.freeze({
17
16
  applicationCursorKeys: false,
18
17
  applicationKeypad: false,
19
18
  bracketedPasteMode: false,
19
+ colorSchemeUpdates: false,
20
20
  cursorBlink: undefined,
21
21
  cursorStyle: undefined,
22
22
  origin: false,
23
23
  reverseWraparound: false,
24
24
  sendFocus: false,
25
25
  synchronizedOutput: false,
26
+ win32InputMode: false,
26
27
  wraparound: true // defaults: xterm - true, vt100 - false
27
28
  });
28
29
 
30
+ const DEFAULT_KITTY_KEYBOARD_STATE = (): IKittyKeyboardState => ({
31
+ flags: 0,
32
+ mainFlags: 0,
33
+ altFlags: 0,
34
+ mainStack: [],
35
+ altStack: []
36
+ });
37
+
29
38
  export class CoreService extends Disposable implements ICoreService {
30
39
  public serviceBrand: any;
31
40
 
32
- public isCursorInitialized: boolean = false;
41
+ public isCursorInitialized: boolean;
33
42
  public isCursorHidden: boolean = false;
34
43
  public modes: IModes;
35
44
  public decPrivateModes: IDecPrivateModes;
45
+ public kittyKeyboard: IKittyKeyboardState;
36
46
 
37
47
  private readonly _onData = this._register(new Emitter<string>());
38
48
  public readonly onData = this._onData.event;
@@ -49,13 +59,16 @@ export class CoreService extends Disposable implements ICoreService {
49
59
  @IOptionsService private readonly _optionsService: IOptionsService
50
60
  ) {
51
61
  super();
52
- this.modes = clone(DEFAULT_MODES);
53
- this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES);
62
+ this.isCursorInitialized = _optionsService.rawOptions.showCursorImmediately ?? false;
63
+ this.modes = structuredClone(DEFAULT_MODES);
64
+ this.decPrivateModes = structuredClone(DEFAULT_DEC_PRIVATE_MODES);
65
+ this.kittyKeyboard = DEFAULT_KITTY_KEYBOARD_STATE();
54
66
  }
55
67
 
56
68
  public reset(): void {
57
- this.modes = clone(DEFAULT_MODES);
58
- this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES);
69
+ this.modes = structuredClone(DEFAULT_MODES);
70
+ this.decPrivateModes = structuredClone(DEFAULT_DEC_PRIVATE_MODES);
71
+ this.kittyKeyboard = DEFAULT_KITTY_KEYBOARD_STATE();
59
72
  }
60
73
 
61
74
  public triggerDataEvent(data: string, wasUserInput: boolean = false): void {
@@ -3,13 +3,15 @@
3
3
  * @license MIT
4
4
  */
5
5
 
6
+ import type { IDeleteEvent, IInsertEvent } from 'common/CircularList';
7
+ import { MicrotaskTimer } from 'common/Async';
6
8
  import { css } from 'common/Color';
7
- import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
8
- import { IDecorationService, IInternalDecoration } from 'common/services/Services';
9
+ import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'common/Lifecycle';
10
+ import { IBufferService, IDecorationService, IInternalDecoration, ILogService } from 'common/services/Services';
9
11
  import { SortedList } from 'common/SortedList';
10
- import { IColor } from 'common/Types';
12
+ import { IColor, ICircularList } from 'common/Types';
11
13
  import { IDecoration, IDecorationOptions, IMarker } from '@xterm/xterm';
12
- import { Emitter } from 'vs/base/common/event';
14
+ import { Emitter } from 'common/Event';
13
15
 
14
16
  // Work variables to avoid garbage collection
15
17
  let $xmin = 0;
@@ -23,7 +25,9 @@ export class DecorationService extends Disposable implements IDecorationService
23
25
  * while marker line values do change, they should all change by the same amount so this should
24
26
  * never become out of order.
25
27
  */
26
- private readonly _decorations: SortedList<IInternalDecoration> = new SortedList(e => e?.marker.line);
28
+ private readonly _decorations: SortedList<IInternalDecoration>;
29
+
30
+ private readonly _lineCache = this._register(new DecorationLineCache());
27
31
 
28
32
  private readonly _onDecorationRegistered = this._register(new Emitter<IInternalDecoration>());
29
33
  public readonly onDecorationRegistered = this._onDecorationRegistered.event;
@@ -32,10 +36,19 @@ export class DecorationService extends Disposable implements IDecorationService
32
36
 
33
37
  public get decorations(): IterableIterator<IInternalDecoration> { return this._decorations.values(); }
34
38
 
35
- constructor() {
39
+ constructor(
40
+ @ILogService private readonly _logService: ILogService,
41
+ @IBufferService private readonly _bufferService: IBufferService
42
+ ) {
36
43
  super();
37
44
 
45
+ this._decorations = new SortedList(e => e?.marker.line, this._logService);
46
+
38
47
  this._register(toDisposable(() => this.reset()));
48
+ this._register(this._bufferService.buffers.onBufferActivate(() => {
49
+ this._lineCache.attachToBufferLines(this._bufferService.buffer.lines);
50
+ }));
51
+ this._lineCache.attachToBufferLines(this._bufferService.buffer.lines);
39
52
  }
40
53
 
41
54
  public registerDecoration(options: IDecorationOptions): IDecoration | undefined {
@@ -49,12 +62,14 @@ export class DecorationService extends Disposable implements IDecorationService
49
62
  listener.dispose();
50
63
  if (decoration) {
51
64
  if (this._decorations.delete(decoration)) {
65
+ this._lineCache.remove(decoration);
52
66
  this._onDecorationRemoved.fire(decoration);
53
67
  }
54
68
  markerDispose.dispose();
55
69
  }
56
70
  });
57
71
  this._decorations.insert(decoration);
72
+ this._lineCache.add(decoration);
58
73
  this._onDecorationRegistered.fire(decoration);
59
74
  }
60
75
  return decoration;
@@ -65,12 +80,17 @@ export class DecorationService extends Disposable implements IDecorationService
65
80
  d.dispose();
66
81
  }
67
82
  this._decorations.clear();
83
+ this._lineCache.clear();
68
84
  }
69
85
 
70
86
  public *getDecorationsAtCell(x: number, line: number, layer?: 'bottom' | 'top'): IterableIterator<IInternalDecoration> {
87
+ const bucket = this._lineCache.getDecorationsOnLine(line);
88
+ if (!bucket) {
89
+ return;
90
+ }
71
91
  let xmin = 0;
72
92
  let xmax = 0;
73
- for (const d of this._decorations.getKeyIterator(line)) {
93
+ for (const d of bucket) {
74
94
  xmin = d.options.x ?? 0;
75
95
  xmax = xmin + (d.options.width ?? 1);
76
96
  if (x >= xmin && x < xmax && (!layer || (d.options.layer ?? 'bottom') === layer)) {
@@ -80,20 +100,246 @@ export class DecorationService extends Disposable implements IDecorationService
80
100
  }
81
101
 
82
102
  public forEachDecorationAtCell(x: number, line: number, layer: 'bottom' | 'top' | undefined, callback: (decoration: IInternalDecoration) => void): void {
83
- this._decorations.forEachByKey(line, d => {
103
+ const bucket = this._lineCache.getDecorationsOnLine(line);
104
+ if (!bucket) {
105
+ return;
106
+ }
107
+ for (const d of bucket) {
84
108
  $xmin = d.options.x ?? 0;
85
109
  $xmax = $xmin + (d.options.width ?? 1);
86
110
  if (x >= $xmin && x < $xmax && (!layer || (d.options.layer ?? 'bottom') === layer)) {
87
111
  callback(d);
88
112
  }
113
+ }
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Per-logical-line index of decorations for fast cell lookup.
119
+ *
120
+ * Keys are marker.line coordinates (logical buffer lines), not CircularList ring slots.
121
+ * Multi-line decorations appear in every line bucket they span. The index is kept aligned
122
+ * with marker.line updates via buffer line trim/insert/delete events.
123
+ */
124
+ export class DecorationLineCache extends Disposable {
125
+ private readonly _decorationsByLine: Map<number, IInternalDecoration[]> = new Map();
126
+ private readonly _decorations = new Set<IInternalDecoration>();
127
+ private readonly _bufferLineListeners = this._register(new MutableDisposable<DisposableStore>());
128
+ private readonly _lineIndexSyncTimer = this._register(new MicrotaskTimer());
129
+ private _lineIndexSyncCallbacks: (() => void)[] = [];
130
+
131
+ public clear(): void {
132
+ this._lineIndexSyncCallbacks.length = 0;
133
+ this._lineIndexSyncTimer.cancel();
134
+ this._decorationsByLine.clear();
135
+ this._decorations.clear();
136
+ }
137
+
138
+ public add(decoration: IInternalDecoration): void {
139
+ this._decorations.add(decoration);
140
+ this._addToLineBuckets(decoration);
141
+ }
142
+
143
+ public remove(decoration: IInternalDecoration): void {
144
+ this._decorations.delete(decoration);
145
+ this._removeFromLineBuckets(decoration);
146
+ }
147
+
148
+ public getDecorationsOnLine(line: number): ReadonlyArray<IInternalDecoration> | undefined {
149
+ return this._decorationsByLine.get(line);
150
+ }
151
+
152
+ public attachToBufferLines(lines: ICircularList<unknown>): void {
153
+ const store = new DisposableStore();
154
+ this._bufferLineListeners.value = store;
155
+ store.add(lines.onTrim(amount => this._handleBufferLinesTrim(amount)));
156
+ store.add(lines.onInsert(event => this._handleBufferLinesInsert(event)));
157
+ store.add(lines.onDelete(event => this._handleBufferLinesDelete(event)));
158
+ }
159
+
160
+ private _getDecorationHeight(decoration: IInternalDecoration): number {
161
+ return decoration.options.height ?? 1;
162
+ }
163
+
164
+ private _addToLineBuckets(decoration: IInternalDecoration): void {
165
+ const start = decoration.marker.line;
166
+ if (start < 0) {
167
+ return;
168
+ }
169
+ decoration._indexedStartLine = start;
170
+ const height = this._getDecorationHeight(decoration);
171
+ for (let line = start; line < start + height; line++) {
172
+ let bucket = this._decorationsByLine.get(line);
173
+ if (!bucket) {
174
+ bucket = [];
175
+ this._decorationsByLine.set(line, bucket);
176
+ }
177
+ bucket.push(decoration);
178
+ }
179
+ }
180
+
181
+ private _removeFromLineBuckets(decoration: IInternalDecoration): void {
182
+ const start = decoration._indexedStartLine;
183
+ const height = this._getDecorationHeight(decoration);
184
+ for (let line = start; line < start + height; line++) {
185
+ const bucket = this._decorationsByLine.get(line);
186
+ if (!bucket) {
187
+ continue;
188
+ }
189
+ const index = bucket.indexOf(decoration);
190
+ if (index !== -1) {
191
+ bucket.splice(index, 1);
192
+ }
193
+ if (bucket.length === 0) {
194
+ this._decorationsByLine.delete(line);
195
+ }
196
+ }
197
+ }
198
+
199
+ private _reindexDecoration(decoration: IInternalDecoration): void {
200
+ this._removeFromLineBuckets(decoration);
201
+ if (!decoration.marker.isDisposed && decoration.marker.line >= 0) {
202
+ this._addToLineBuckets(decoration);
203
+ }
204
+ }
205
+
206
+ /** Re-index after marker line updates (buffer listeners may run before markers). */
207
+ private _scheduleLineIndexSync(callback: () => void): void {
208
+ this._lineIndexSyncCallbacks.push(callback);
209
+ this._lineIndexSyncTimer.set(() => {
210
+ const callbacks = this._lineIndexSyncCallbacks;
211
+ this._lineIndexSyncCallbacks = [];
212
+ for (const cb of callbacks) {
213
+ cb();
214
+ }
89
215
  });
90
216
  }
217
+
218
+ private _handleBufferLinesTrim(amount: number): void {
219
+ if (amount <= 0) {
220
+ return;
221
+ }
222
+ const newMap = new Map<number, IInternalDecoration[]>();
223
+ for (const [line, bucket] of this._decorationsByLine) {
224
+ const newLine = line - amount;
225
+ if (newLine < 0) {
226
+ continue;
227
+ }
228
+ this._mergeLineBucket(newMap, newLine, bucket);
229
+ }
230
+ this._decorationsByLine.clear();
231
+ for (const [line, bucket] of newMap) {
232
+ this._decorationsByLine.set(line, bucket);
233
+ }
234
+ for (const d of this._decorations) {
235
+ if (!d.marker.isDisposed) {
236
+ d._indexedStartLine -= amount;
237
+ }
238
+ }
239
+ }
240
+
241
+ private _handleBufferLinesInsert(event: IInsertEvent): void {
242
+ this._scheduleLineIndexSync(() => this._applyBufferLinesInsert(event));
243
+ }
244
+
245
+ private _handleBufferLinesDelete(event: IDeleteEvent): void {
246
+ this._scheduleLineIndexSync(() => this._applyBufferLinesDelete(event));
247
+ }
248
+
249
+ private _mergeLineBucket(newMap: Map<number, IInternalDecoration[]>, line: number, bucket: IInternalDecoration[]): void {
250
+ const existing = newMap.get(line);
251
+ if (existing) {
252
+ for (let i = 0, len = bucket.length; i < len; i++) {
253
+ existing.push(bucket[i]);
254
+ }
255
+ } else {
256
+ newMap.set(line, bucket.slice());
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Shift indexed line keys and sync start lines. O(unique indexed lines), not O(decoration count).
262
+ * Decorations that span the insert point are re-indexed individually (rare vs single-line hits).
263
+ */
264
+ private _applyBufferLinesInsert(event: IInsertEvent): void {
265
+ const { index, amount } = event;
266
+ const spanCrossers: IInternalDecoration[] = [];
267
+ for (const d of this._decorations) {
268
+ if (d.marker.isDisposed) {
269
+ continue;
270
+ }
271
+ const start = d._indexedStartLine;
272
+ if (start < index && start + this._getDecorationHeight(d) > index) {
273
+ spanCrossers.push(d);
274
+ this._removeFromLineBuckets(d);
275
+ }
276
+ }
277
+ const newMap = new Map<number, IInternalDecoration[]>();
278
+ for (const [line, bucket] of this._decorationsByLine) {
279
+ const newLine = line >= index ? line + amount : line;
280
+ this._mergeLineBucket(newMap, newLine, bucket);
281
+ }
282
+ this._decorationsByLine.clear();
283
+ for (const [line, bucket] of newMap) {
284
+ this._decorationsByLine.set(line, bucket);
285
+ }
286
+ for (const d of this._decorations) {
287
+ if (d.marker.isDisposed) {
288
+ continue;
289
+ }
290
+ if (d._indexedStartLine >= index) {
291
+ d._indexedStartLine = d.marker.line;
292
+ }
293
+ }
294
+ for (const d of spanCrossers) {
295
+ this._addToLineBuckets(d);
296
+ }
297
+ }
298
+
299
+ /**
300
+ * Drop deleted line keys, shift keys below, sync start lines. Full re-index only when a
301
+ * multi-line decoration spans across the deleted range but survives.
302
+ */
303
+ private _applyBufferLinesDelete(event: IDeleteEvent): void {
304
+ const deleteEnd = event.index + event.amount;
305
+ const newMap = new Map<number, IInternalDecoration[]>();
306
+ for (const [line, bucket] of this._decorationsByLine) {
307
+ if (line >= event.index && line < deleteEnd) {
308
+ continue;
309
+ }
310
+ const newLine = line >= deleteEnd ? line - event.amount : line;
311
+ this._mergeLineBucket(newMap, newLine, bucket);
312
+ }
313
+ this._decorationsByLine.clear();
314
+ for (const [line, bucket] of newMap) {
315
+ this._decorationsByLine.set(line, bucket);
316
+ }
317
+ const toReindex: IInternalDecoration[] = [];
318
+ for (const d of this._decorations) {
319
+ if (d.marker.isDisposed) {
320
+ continue;
321
+ }
322
+ const start = d._indexedStartLine;
323
+ const height = this._getDecorationHeight(d);
324
+ if (start >= deleteEnd) {
325
+ d._indexedStartLine = d.marker.line;
326
+ } else if (start < event.index && start + height > deleteEnd) {
327
+ toReindex.push(d);
328
+ }
329
+ }
330
+ for (const d of toReindex) {
331
+ this._reindexDecoration(d);
332
+ }
333
+ }
91
334
  }
92
335
 
93
336
  class Decoration extends DisposableStore implements IInternalDecoration {
94
337
  public readonly marker: IMarker;
95
338
  public element: HTMLElement | undefined;
96
339
 
340
+ /** Start line used for line-index removal when marker.line is cleared on dispose. */
341
+ public _indexedStartLine: number;
342
+
97
343
  public readonly onRenderEmitter = this.add(new Emitter<HTMLElement>());
98
344
  public readonly onRender = this.onRenderEmitter.event;
99
345
  private readonly _onDispose = this.add(new Emitter<void>());
@@ -128,6 +374,7 @@ class Decoration extends DisposableStore implements IInternalDecoration {
128
374
  ) {
129
375
  super();
130
376
  this.marker = options.marker;
377
+ this._indexedStartLine = options.marker.line;
131
378
  if (this.options.overviewRulerOptions && !this.options.overviewRulerOptions.position) {
132
379
  this.options.overviewRulerOptions.position = 'full';
133
380
  }
@@ -3,7 +3,7 @@
3
3
  * @license MIT
4
4
  */
5
5
 
6
- import { Disposable } from 'vs/base/common/lifecycle';
6
+ import { Disposable } from 'common/Lifecycle';
7
7
  import { ILogService, IOptionsService, LogLevelEnum } from 'common/services/Services';
8
8
 
9
9
  type LogType = (message?: any, ...optionalParams: any[]) => void;
@@ -43,9 +43,6 @@ export class LogService extends Disposable implements ILogService {
43
43
  super();
44
44
  this._updateLogLevel();
45
45
  this._register(this._optionsService.onSpecificOptionChange('logLevel', () => this._updateLogLevel()));
46
-
47
- // For trace logging, assume the latest created log service is valid
48
- traceLogger = this;
49
46
  }
50
47
 
51
48
  private _updateLogLevel(): void {
@@ -95,30 +92,3 @@ export class LogService extends Disposable implements ILogService {
95
92
  }
96
93
  }
97
94
  }
98
-
99
- let traceLogger: ILogService;
100
- export function setTraceLogger(logger: ILogService): void {
101
- traceLogger = logger;
102
- }
103
-
104
- /**
105
- * A decorator that can be used to automatically log trace calls to the decorated function.
106
- */
107
- export function traceCall(_target: any, key: string, descriptor: any): any {
108
- if (typeof descriptor.value !== 'function') {
109
- throw new Error('not supported');
110
- }
111
- const fnKey = 'value';
112
- const fn = descriptor.value;
113
- descriptor[fnKey] = function (...args: any[]) {
114
- // Early exit
115
- if (traceLogger.logLevel !== LogLevelEnum.TRACE) {
116
- return fn.apply(this, args);
117
- }
118
-
119
- traceLogger.trace(`GlyphRenderer#${fn.name}(${args.map(e => JSON.stringify(e)).join(', ')})`);
120
- const result = fn.apply(this, args);
121
- traceLogger.trace(`GlyphRenderer#${fn.name} return`, result);
122
- return result;
123
- };
124
- }