@xterm/xterm 6.1.0-beta.22 → 6.1.0-beta.221

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 (160) 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 +147 -318
  11. package/src/browser/Dom.ts +178 -0
  12. package/src/browser/Linkifier.ts +11 -11
  13. package/src/browser/OscLinkProvider.ts +3 -1
  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 +22 -15
  49. package/src/browser/services/SelectionService.ts +42 -35
  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 +31 -21
  56. package/src/common/Event.ts +118 -0
  57. package/src/common/InputHandler.ts +283 -81
  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/TaskQueue.ts +14 -5
  62. package/src/common/Types.ts +35 -15
  63. package/src/common/Version.ts +9 -0
  64. package/src/common/buffer/Buffer.ts +34 -19
  65. package/src/common/buffer/BufferLine.ts +133 -65
  66. package/src/common/buffer/BufferLineStringCache.ts +69 -0
  67. package/src/common/buffer/BufferSet.ts +11 -6
  68. package/src/common/buffer/CellData.ts +57 -0
  69. package/src/common/buffer/Marker.ts +2 -2
  70. package/src/common/buffer/Types.ts +6 -2
  71. package/src/common/data/EscapeSequences.ts +71 -70
  72. package/src/common/input/Keyboard.ts +14 -7
  73. package/src/common/input/KittyKeyboard.ts +526 -0
  74. package/src/common/input/Win32InputMode.ts +297 -0
  75. package/src/common/input/WriteBuffer.ts +64 -32
  76. package/src/common/input/XParseColor.ts +2 -2
  77. package/src/common/parser/ApcParser.ts +197 -0
  78. package/src/common/parser/Constants.ts +14 -4
  79. package/src/common/parser/DcsParser.ts +5 -5
  80. package/src/common/parser/EscapeSequenceParser.ts +205 -63
  81. package/src/common/parser/OscParser.ts +5 -5
  82. package/src/common/parser/Params.ts +27 -8
  83. package/src/common/parser/Types.ts +36 -2
  84. package/src/common/public/BufferLineApiView.ts +2 -2
  85. package/src/common/public/BufferNamespaceApi.ts +2 -2
  86. package/src/common/public/ParserApi.ts +3 -0
  87. package/src/common/services/BufferService.ts +14 -9
  88. package/src/common/services/CharsetService.ts +4 -0
  89. package/src/common/services/CoreService.ts +18 -4
  90. package/src/common/services/DecorationService.ts +258 -8
  91. package/src/common/services/LogService.ts +1 -31
  92. package/src/common/services/{CoreMouseService.ts → MouseStateService.ts} +21 -132
  93. package/src/common/services/OptionsService.ts +13 -4
  94. package/src/common/services/ServiceRegistry.ts +9 -7
  95. package/src/common/services/Services.ts +49 -40
  96. package/src/common/services/UnicodeService.ts +1 -1
  97. package/typings/xterm.d.ts +318 -34
  98. package/src/common/TypedArrayUtils.ts +0 -17
  99. package/src/vs/base/browser/browser.ts +0 -141
  100. package/src/vs/base/browser/canIUse.ts +0 -49
  101. package/src/vs/base/browser/dom.ts +0 -2369
  102. package/src/vs/base/browser/fastDomNode.ts +0 -316
  103. package/src/vs/base/browser/globalPointerMoveMonitor.ts +0 -112
  104. package/src/vs/base/browser/iframe.ts +0 -135
  105. package/src/vs/base/browser/keyboardEvent.ts +0 -213
  106. package/src/vs/base/browser/mouseEvent.ts +0 -229
  107. package/src/vs/base/browser/touch.ts +0 -372
  108. package/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +0 -303
  109. package/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +0 -114
  110. package/src/vs/base/browser/ui/scrollbar/scrollableElement.ts +0 -720
  111. package/src/vs/base/browser/ui/scrollbar/scrollableElementOptions.ts +0 -165
  112. package/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +0 -114
  113. package/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +0 -243
  114. package/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +0 -118
  115. package/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +0 -116
  116. package/src/vs/base/browser/ui/widget.ts +0 -57
  117. package/src/vs/base/browser/window.ts +0 -14
  118. package/src/vs/base/common/arrays.ts +0 -887
  119. package/src/vs/base/common/arraysFind.ts +0 -202
  120. package/src/vs/base/common/assert.ts +0 -71
  121. package/src/vs/base/common/async.ts +0 -1992
  122. package/src/vs/base/common/cancellation.ts +0 -148
  123. package/src/vs/base/common/charCode.ts +0 -450
  124. package/src/vs/base/common/collections.ts +0 -140
  125. package/src/vs/base/common/decorators.ts +0 -130
  126. package/src/vs/base/common/equals.ts +0 -146
  127. package/src/vs/base/common/errors.ts +0 -303
  128. package/src/vs/base/common/event.ts +0 -1778
  129. package/src/vs/base/common/functional.ts +0 -32
  130. package/src/vs/base/common/hash.ts +0 -316
  131. package/src/vs/base/common/iterator.ts +0 -159
  132. package/src/vs/base/common/keyCodes.ts +0 -526
  133. package/src/vs/base/common/keybindings.ts +0 -284
  134. package/src/vs/base/common/lazy.ts +0 -47
  135. package/src/vs/base/common/lifecycle.ts +0 -801
  136. package/src/vs/base/common/linkedList.ts +0 -142
  137. package/src/vs/base/common/map.ts +0 -202
  138. package/src/vs/base/common/numbers.ts +0 -98
  139. package/src/vs/base/common/observable.ts +0 -76
  140. package/src/vs/base/common/observableInternal/api.ts +0 -31
  141. package/src/vs/base/common/observableInternal/autorun.ts +0 -281
  142. package/src/vs/base/common/observableInternal/base.ts +0 -489
  143. package/src/vs/base/common/observableInternal/debugName.ts +0 -145
  144. package/src/vs/base/common/observableInternal/derived.ts +0 -428
  145. package/src/vs/base/common/observableInternal/lazyObservableValue.ts +0 -146
  146. package/src/vs/base/common/observableInternal/logging.ts +0 -328
  147. package/src/vs/base/common/observableInternal/promise.ts +0 -209
  148. package/src/vs/base/common/observableInternal/utils.ts +0 -610
  149. package/src/vs/base/common/platform.ts +0 -281
  150. package/src/vs/base/common/scrollable.ts +0 -522
  151. package/src/vs/base/common/sequence.ts +0 -34
  152. package/src/vs/base/common/stopwatch.ts +0 -43
  153. package/src/vs/base/common/strings.ts +0 -557
  154. package/src/vs/base/common/symbols.ts +0 -9
  155. package/src/vs/base/common/uint.ts +0 -59
  156. package/src/vs/patches/nls.ts +0 -90
  157. package/src/vs/typings/base-common.d.ts +0 -20
  158. package/src/vs/typings/require.d.ts +0 -42
  159. package/src/vs/typings/vscode-globals-nls.d.ts +0 -36
  160. 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;
@@ -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 = [];
@@ -4,10 +4,10 @@
4
4
  */
5
5
 
6
6
  import { clone } from 'common/Clone';
7
- import { Disposable } from 'vs/base/common/lifecycle';
8
- import { IDecPrivateModes, IModes } from 'common/Types';
7
+ import { Disposable } from 'common/Lifecycle';
8
+ import { IDecPrivateModes, IKittyKeyboardState, IModes } from 'common/Types';
9
9
  import { IBufferService, ICoreService, ILogService, IOptionsService } from 'common/services/Services';
10
- import { Emitter } from 'vs/base/common/event';
10
+ import { Emitter } from 'common/Event';
11
11
 
12
12
  const DEFAULT_MODES: IModes = Object.freeze({
13
13
  insertMode: false
@@ -17,22 +17,33 @@ const DEFAULT_DEC_PRIVATE_MODES: IDecPrivateModes = Object.freeze({
17
17
  applicationCursorKeys: false,
18
18
  applicationKeypad: false,
19
19
  bracketedPasteMode: false,
20
+ colorSchemeUpdates: false,
20
21
  cursorBlink: undefined,
21
22
  cursorStyle: undefined,
22
23
  origin: false,
23
24
  reverseWraparound: false,
24
25
  sendFocus: false,
25
26
  synchronizedOutput: false,
27
+ win32InputMode: false,
26
28
  wraparound: true // defaults: xterm - true, vt100 - false
27
29
  });
28
30
 
31
+ const DEFAULT_KITTY_KEYBOARD_STATE = (): IKittyKeyboardState => ({
32
+ flags: 0,
33
+ mainFlags: 0,
34
+ altFlags: 0,
35
+ mainStack: [],
36
+ altStack: []
37
+ });
38
+
29
39
  export class CoreService extends Disposable implements ICoreService {
30
40
  public serviceBrand: any;
31
41
 
32
- public isCursorInitialized: boolean = false;
42
+ public isCursorInitialized: boolean;
33
43
  public isCursorHidden: boolean = false;
34
44
  public modes: IModes;
35
45
  public decPrivateModes: IDecPrivateModes;
46
+ public kittyKeyboard: IKittyKeyboardState;
36
47
 
37
48
  private readonly _onData = this._register(new Emitter<string>());
38
49
  public readonly onData = this._onData.event;
@@ -49,13 +60,16 @@ export class CoreService extends Disposable implements ICoreService {
49
60
  @IOptionsService private readonly _optionsService: IOptionsService
50
61
  ) {
51
62
  super();
63
+ this.isCursorInitialized = _optionsService.rawOptions.showCursorImmediately ?? false;
52
64
  this.modes = clone(DEFAULT_MODES);
53
65
  this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES);
66
+ this.kittyKeyboard = DEFAULT_KITTY_KEYBOARD_STATE();
54
67
  }
55
68
 
56
69
  public reset(): void {
57
70
  this.modes = clone(DEFAULT_MODES);
58
71
  this.decPrivateModes = clone(DEFAULT_DEC_PRIVATE_MODES);
72
+ this.kittyKeyboard = DEFAULT_KITTY_KEYBOARD_STATE();
59
73
  }
60
74
 
61
75
  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,249 @@ 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
+ const existing = newMap.get(newLine);
229
+ if (existing) {
230
+ existing.push(...bucket);
231
+ } else {
232
+ newMap.set(newLine, bucket.slice());
233
+ }
234
+ }
235
+ this._decorationsByLine.clear();
236
+ for (const [line, bucket] of newMap) {
237
+ this._decorationsByLine.set(line, bucket);
238
+ }
239
+ for (const d of this._decorations) {
240
+ if (!d.marker.isDisposed) {
241
+ d._indexedStartLine -= amount;
242
+ }
243
+ }
244
+ }
245
+
246
+ private _handleBufferLinesInsert(event: IInsertEvent): void {
247
+ this._scheduleLineIndexSync(() => this._applyBufferLinesInsert(event));
248
+ }
249
+
250
+ private _handleBufferLinesDelete(event: IDeleteEvent): void {
251
+ this._scheduleLineIndexSync(() => this._applyBufferLinesDelete(event));
252
+ }
253
+
254
+ private _mergeLineBucket(newMap: Map<number, IInternalDecoration[]>, line: number, bucket: IInternalDecoration[]): void {
255
+ const existing = newMap.get(line);
256
+ if (existing) {
257
+ existing.push(...bucket);
258
+ } else {
259
+ newMap.set(line, bucket.slice());
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Shift indexed line keys and sync start lines. O(unique indexed lines), not O(decoration count).
265
+ * Decorations that span the insert point are re-indexed individually (rare vs single-line hits).
266
+ */
267
+ private _applyBufferLinesInsert(event: IInsertEvent): void {
268
+ const { index, amount } = event;
269
+ const spanCrossers: IInternalDecoration[] = [];
270
+ for (const d of this._decorations) {
271
+ if (d.marker.isDisposed) {
272
+ continue;
273
+ }
274
+ const start = d._indexedStartLine;
275
+ if (start < index && start + this._getDecorationHeight(d) > index) {
276
+ spanCrossers.push(d);
277
+ this._removeFromLineBuckets(d);
278
+ }
279
+ }
280
+ const newMap = new Map<number, IInternalDecoration[]>();
281
+ for (const [line, bucket] of this._decorationsByLine) {
282
+ const newLine = line >= index ? line + amount : line;
283
+ this._mergeLineBucket(newMap, newLine, bucket);
284
+ }
285
+ this._decorationsByLine.clear();
286
+ for (const [line, bucket] of newMap) {
287
+ this._decorationsByLine.set(line, bucket);
288
+ }
289
+ for (const d of this._decorations) {
290
+ if (d.marker.isDisposed) {
291
+ continue;
292
+ }
293
+ if (d._indexedStartLine >= index) {
294
+ d._indexedStartLine = d.marker.line;
295
+ }
296
+ }
297
+ for (const d of spanCrossers) {
298
+ this._addToLineBuckets(d);
299
+ }
300
+ }
301
+
302
+ /**
303
+ * Drop deleted line keys, shift keys below, sync start lines. Full re-index only when a
304
+ * multi-line decoration spans across the deleted range but survives.
305
+ */
306
+ private _applyBufferLinesDelete(event: IDeleteEvent): void {
307
+ const deleteEnd = event.index + event.amount;
308
+ const newMap = new Map<number, IInternalDecoration[]>();
309
+ for (const [line, bucket] of this._decorationsByLine) {
310
+ if (line >= event.index && line < deleteEnd) {
311
+ continue;
312
+ }
313
+ const newLine = line >= deleteEnd ? line - event.amount : line;
314
+ this._mergeLineBucket(newMap, newLine, bucket);
315
+ }
316
+ this._decorationsByLine.clear();
317
+ for (const [line, bucket] of newMap) {
318
+ this._decorationsByLine.set(line, bucket);
319
+ }
320
+ const toReindex: IInternalDecoration[] = [];
321
+ for (const d of this._decorations) {
322
+ if (d.marker.isDisposed) {
323
+ continue;
324
+ }
325
+ const start = d._indexedStartLine;
326
+ const height = this._getDecorationHeight(d);
327
+ if (start >= deleteEnd) {
328
+ d._indexedStartLine = d.marker.line;
329
+ } else if (start < event.index && start + height > deleteEnd) {
330
+ toReindex.push(d);
331
+ }
332
+ }
333
+ for (const d of toReindex) {
334
+ this._reindexDecoration(d);
335
+ }
336
+ }
91
337
  }
92
338
 
93
339
  class Decoration extends DisposableStore implements IInternalDecoration {
94
340
  public readonly marker: IMarker;
95
341
  public element: HTMLElement | undefined;
96
342
 
343
+ /** Start line used for line-index removal when marker.line is cleared on dispose. */
344
+ public _indexedStartLine: number;
345
+
97
346
  public readonly onRenderEmitter = this.add(new Emitter<HTMLElement>());
98
347
  public readonly onRender = this.onRenderEmitter.event;
99
348
  private readonly _onDispose = this.add(new Emitter<void>());
@@ -128,6 +377,7 @@ class Decoration extends DisposableStore implements IInternalDecoration {
128
377
  ) {
129
378
  super();
130
379
  this.marker = options.marker;
380
+ this._indexedStartLine = options.marker.line;
131
381
  if (this.options.overviewRulerOptions && !this.options.overviewRulerOptions.position) {
132
382
  this.options.overviewRulerOptions.position = 'full';
133
383
  }
@@ -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
- }