@wendongfly/myhi 1.0.2 → 1.0.3

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 (135) hide show
  1. package/dist/index.js +1 -1
  2. package/dist/lib/xterm/LICENSE +21 -0
  3. package/dist/lib/xterm/README.md +225 -0
  4. package/dist/lib/xterm/css/xterm.css +190 -0
  5. package/dist/lib/xterm/lib/xterm.js +2 -0
  6. package/dist/lib/xterm/lib/xterm.js.map +1 -0
  7. package/dist/lib/xterm/package.json +90 -0
  8. package/dist/lib/xterm/src/browser/AccessibilityManager.ts +301 -0
  9. package/dist/lib/xterm/src/browser/Clipboard.ts +99 -0
  10. package/dist/lib/xterm/src/browser/ColorContrastCache.ts +39 -0
  11. package/dist/lib/xterm/src/browser/ColorManager.ts +268 -0
  12. package/dist/lib/xterm/src/browser/Dom.ts +10 -0
  13. package/dist/lib/xterm/src/browser/Lifecycle.ts +30 -0
  14. package/dist/lib/xterm/src/browser/Linkifier.ts +356 -0
  15. package/dist/lib/xterm/src/browser/Linkifier2.ts +397 -0
  16. package/dist/lib/xterm/src/browser/LocalizableStrings.ts +10 -0
  17. package/dist/lib/xterm/src/browser/MouseZoneManager.ts +236 -0
  18. package/dist/lib/xterm/src/browser/RenderDebouncer.ts +82 -0
  19. package/dist/lib/xterm/src/browser/ScreenDprMonitor.ts +69 -0
  20. package/dist/lib/xterm/src/browser/Terminal.ts +1447 -0
  21. package/dist/lib/xterm/src/browser/TimeBasedDebouncer.ts +86 -0
  22. package/dist/lib/xterm/src/browser/Types.d.ts +317 -0
  23. package/dist/lib/xterm/src/browser/Viewport.ts +276 -0
  24. package/dist/lib/xterm/src/browser/decorations/BufferDecorationRenderer.ts +131 -0
  25. package/dist/lib/xterm/src/browser/decorations/ColorZoneStore.ts +117 -0
  26. package/dist/lib/xterm/src/browser/decorations/OverviewRulerRenderer.ts +228 -0
  27. package/dist/lib/xterm/src/browser/input/CompositionHelper.ts +237 -0
  28. package/dist/lib/xterm/src/browser/input/Mouse.ts +64 -0
  29. package/dist/lib/xterm/src/browser/input/MoveToCell.ts +249 -0
  30. package/dist/lib/xterm/src/browser/public/Terminal.ts +298 -0
  31. package/dist/lib/xterm/src/browser/renderer/BaseRenderLayer.ts +582 -0
  32. package/dist/lib/xterm/src/browser/renderer/CursorRenderLayer.ts +378 -0
  33. package/dist/lib/xterm/src/browser/renderer/CustomGlyphs.ts +632 -0
  34. package/dist/lib/xterm/src/browser/renderer/GridCache.ts +33 -0
  35. package/dist/lib/xterm/src/browser/renderer/LinkRenderLayer.ts +84 -0
  36. package/dist/lib/xterm/src/browser/renderer/Renderer.ts +219 -0
  37. package/dist/lib/xterm/src/browser/renderer/RendererUtils.ts +26 -0
  38. package/dist/lib/xterm/src/browser/renderer/SelectionRenderLayer.ts +131 -0
  39. package/dist/lib/xterm/src/browser/renderer/TextRenderLayer.ts +344 -0
  40. package/dist/lib/xterm/src/browser/renderer/Types.d.ts +109 -0
  41. package/dist/lib/xterm/src/browser/renderer/atlas/BaseCharAtlas.ts +58 -0
  42. package/dist/lib/xterm/src/browser/renderer/atlas/CharAtlasCache.ts +95 -0
  43. package/dist/lib/xterm/src/browser/renderer/atlas/CharAtlasUtils.ts +54 -0
  44. package/dist/lib/xterm/src/browser/renderer/atlas/Constants.ts +15 -0
  45. package/dist/lib/xterm/src/browser/renderer/atlas/DynamicCharAtlas.ts +404 -0
  46. package/dist/lib/xterm/src/browser/renderer/atlas/LRUMap.ts +136 -0
  47. package/dist/lib/xterm/src/browser/renderer/atlas/Types.d.ts +29 -0
  48. package/dist/lib/xterm/src/browser/renderer/dom/DomRenderer.ts +403 -0
  49. package/dist/lib/xterm/src/browser/renderer/dom/DomRendererRowFactory.ts +344 -0
  50. package/dist/lib/xterm/src/browser/selection/SelectionModel.ts +144 -0
  51. package/dist/lib/xterm/src/browser/selection/Types.d.ts +15 -0
  52. package/dist/lib/xterm/src/browser/services/CharSizeService.ts +87 -0
  53. package/dist/lib/xterm/src/browser/services/CharacterJoinerService.ts +339 -0
  54. package/dist/lib/xterm/src/browser/services/CoreBrowserService.ts +20 -0
  55. package/dist/lib/xterm/src/browser/services/MouseService.ts +36 -0
  56. package/dist/lib/xterm/src/browser/services/RenderService.ts +237 -0
  57. package/dist/lib/xterm/src/browser/services/SelectionService.ts +1027 -0
  58. package/dist/lib/xterm/src/browser/services/Services.ts +123 -0
  59. package/dist/lib/xterm/src/browser/services/SoundService.ts +63 -0
  60. package/dist/lib/xterm/src/common/CircularList.ts +239 -0
  61. package/dist/lib/xterm/src/common/Clone.ts +23 -0
  62. package/dist/lib/xterm/src/common/Color.ts +285 -0
  63. package/dist/lib/xterm/src/common/CoreTerminal.ts +300 -0
  64. package/dist/lib/xterm/src/common/EventEmitter.ts +69 -0
  65. package/dist/lib/xterm/src/common/InputHandler.ts +3230 -0
  66. package/dist/lib/xterm/src/common/Lifecycle.ts +68 -0
  67. package/dist/lib/xterm/src/common/Platform.ts +31 -0
  68. package/dist/lib/xterm/src/common/SortedList.ts +88 -0
  69. package/dist/lib/xterm/src/common/TypedArrayUtils.ts +50 -0
  70. package/dist/lib/xterm/src/common/Types.d.ts +489 -0
  71. package/dist/lib/xterm/src/common/WindowsMode.ts +27 -0
  72. package/dist/lib/xterm/src/common/buffer/AttributeData.ts +148 -0
  73. package/dist/lib/xterm/src/common/buffer/Buffer.ts +711 -0
  74. package/dist/lib/xterm/src/common/buffer/BufferLine.ts +441 -0
  75. package/dist/lib/xterm/src/common/buffer/BufferRange.ts +13 -0
  76. package/dist/lib/xterm/src/common/buffer/BufferReflow.ts +220 -0
  77. package/dist/lib/xterm/src/common/buffer/BufferSet.ts +131 -0
  78. package/dist/lib/xterm/src/common/buffer/CellData.ts +94 -0
  79. package/dist/lib/xterm/src/common/buffer/Constants.ts +139 -0
  80. package/dist/lib/xterm/src/common/buffer/Marker.ts +37 -0
  81. package/dist/lib/xterm/src/common/buffer/Types.d.ts +64 -0
  82. package/dist/lib/xterm/src/common/data/Charsets.ts +256 -0
  83. package/dist/lib/xterm/src/common/data/EscapeSequences.ts +153 -0
  84. package/dist/lib/xterm/src/common/input/Keyboard.ts +398 -0
  85. package/dist/lib/xterm/src/common/input/TextDecoder.ts +346 -0
  86. package/dist/lib/xterm/src/common/input/UnicodeV6.ts +133 -0
  87. package/dist/lib/xterm/src/common/input/WriteBuffer.ts +229 -0
  88. package/dist/lib/xterm/src/common/input/XParseColor.ts +80 -0
  89. package/dist/lib/xterm/src/common/parser/Constants.ts +58 -0
  90. package/dist/lib/xterm/src/common/parser/DcsParser.ts +192 -0
  91. package/dist/lib/xterm/src/common/parser/EscapeSequenceParser.ts +796 -0
  92. package/dist/lib/xterm/src/common/parser/OscParser.ts +238 -0
  93. package/dist/lib/xterm/src/common/parser/Params.ts +229 -0
  94. package/dist/lib/xterm/src/common/parser/Types.d.ts +274 -0
  95. package/dist/lib/xterm/src/common/public/AddonManager.ts +56 -0
  96. package/dist/lib/xterm/src/common/public/BufferApiView.ts +35 -0
  97. package/dist/lib/xterm/src/common/public/BufferLineApiView.ts +29 -0
  98. package/dist/lib/xterm/src/common/public/BufferNamespaceApi.ts +33 -0
  99. package/dist/lib/xterm/src/common/public/ParserApi.ts +37 -0
  100. package/dist/lib/xterm/src/common/public/UnicodeApi.ts +27 -0
  101. package/dist/lib/xterm/src/common/services/BufferService.ts +185 -0
  102. package/dist/lib/xterm/src/common/services/CharsetService.ts +34 -0
  103. package/dist/lib/xterm/src/common/services/CoreMouseService.ts +309 -0
  104. package/dist/lib/xterm/src/common/services/CoreService.ts +92 -0
  105. package/dist/lib/xterm/src/common/services/DecorationService.ts +139 -0
  106. package/dist/lib/xterm/src/common/services/DirtyRowService.ts +53 -0
  107. package/dist/lib/xterm/src/common/services/InstantiationService.ts +83 -0
  108. package/dist/lib/xterm/src/common/services/LogService.ts +88 -0
  109. package/dist/lib/xterm/src/common/services/OptionsService.ts +178 -0
  110. package/dist/lib/xterm/src/common/services/ServiceRegistry.ts +49 -0
  111. package/dist/lib/xterm/src/common/services/Services.ts +323 -0
  112. package/dist/lib/xterm/src/common/services/UnicodeService.ts +82 -0
  113. package/dist/lib/xterm/src/headless/Terminal.ts +170 -0
  114. package/dist/lib/xterm/src/headless/Types.d.ts +31 -0
  115. package/dist/lib/xterm/src/headless/public/Terminal.ts +216 -0
  116. package/dist/lib/xterm/typings/xterm.d.ts +1872 -0
  117. package/dist/lib/xterm-fit/LICENSE +19 -0
  118. package/dist/lib/xterm-fit/README.md +24 -0
  119. package/dist/lib/xterm-fit/lib/xterm-addon-fit.js +2 -0
  120. package/dist/lib/xterm-fit/lib/xterm-addon-fit.js.map +1 -0
  121. package/dist/lib/xterm-fit/out/FitAddon.js +58 -0
  122. package/dist/lib/xterm-fit/out/FitAddon.js.map +1 -0
  123. package/dist/lib/xterm-fit/out-test/FitAddon.api.js.map +1 -0
  124. package/dist/lib/xterm-fit/package.json +21 -0
  125. package/dist/lib/xterm-fit/src/FitAddon.ts +86 -0
  126. package/dist/lib/xterm-fit/typings/xterm-addon-fit.d.ts +55 -0
  127. package/dist/lib/xterm-links/LICENSE +19 -0
  128. package/dist/lib/xterm-links/README.md +21 -0
  129. package/dist/lib/xterm-links/lib/xterm-addon-web-links.js +2 -0
  130. package/dist/lib/xterm-links/lib/xterm-addon-web-links.js.map +1 -0
  131. package/dist/lib/xterm-links/package.json +26 -0
  132. package/dist/lib/xterm-links/src/WebLinkProvider.ts +145 -0
  133. package/dist/lib/xterm-links/src/WebLinksAddon.ts +77 -0
  134. package/dist/lib/xterm-links/typings/xterm-addon-web-links.d.ts +58 -0
  135. package/package.json +1 -1
@@ -0,0 +1,1027 @@
1
+ /**
2
+ * Copyright (c) 2017 The xterm.js authors. All rights reserved.
3
+ * @license MIT
4
+ */
5
+
6
+ import { ISelectionRedrawRequestEvent, ISelectionRequestScrollLinesEvent } from 'browser/selection/Types';
7
+ import { IBuffer } from 'common/buffer/Types';
8
+ import { IBufferLine, IDisposable } from 'common/Types';
9
+ import * as Browser from 'common/Platform';
10
+ import { SelectionModel } from 'browser/selection/SelectionModel';
11
+ import { CellData } from 'common/buffer/CellData';
12
+ import { EventEmitter, IEvent } from 'common/EventEmitter';
13
+ import { IMouseService, ISelectionService, IRenderService } from 'browser/services/Services';
14
+ import { IBufferRange, ILinkifier2 } from 'browser/Types';
15
+ import { IBufferService, IOptionsService, ICoreService } from 'common/services/Services';
16
+ import { getCoordsRelativeToElement } from 'browser/input/Mouse';
17
+ import { moveToCellSequence } from 'browser/input/MoveToCell';
18
+ import { Disposable } from 'common/Lifecycle';
19
+ import { getRangeLength } from 'common/buffer/BufferRange';
20
+
21
+ /**
22
+ * The number of pixels the mouse needs to be above or below the viewport in
23
+ * order to scroll at the maximum speed.
24
+ */
25
+ const DRAG_SCROLL_MAX_THRESHOLD = 50;
26
+
27
+ /**
28
+ * The maximum scrolling speed
29
+ */
30
+ const DRAG_SCROLL_MAX_SPEED = 15;
31
+
32
+ /**
33
+ * The number of milliseconds between drag scroll updates.
34
+ */
35
+ const DRAG_SCROLL_INTERVAL = 50;
36
+
37
+ /**
38
+ * The maximum amount of time that can have elapsed for an alt click to move the
39
+ * cursor.
40
+ */
41
+ const ALT_CLICK_MOVE_CURSOR_TIME = 500;
42
+
43
+ const NON_BREAKING_SPACE_CHAR = String.fromCharCode(160);
44
+ const ALL_NON_BREAKING_SPACE_REGEX = new RegExp(NON_BREAKING_SPACE_CHAR, 'g');
45
+
46
+ /**
47
+ * Represents a position of a word on a line.
48
+ */
49
+ interface IWordPosition {
50
+ start: number;
51
+ length: number;
52
+ }
53
+
54
+ /**
55
+ * A selection mode, this drives how the selection behaves on mouse move.
56
+ */
57
+ export const enum SelectionMode {
58
+ NORMAL,
59
+ WORD,
60
+ LINE,
61
+ COLUMN
62
+ }
63
+
64
+ /**
65
+ * A class that manages the selection of the terminal. With help from
66
+ * SelectionModel, SelectionService handles with all logic associated with
67
+ * dealing with the selection, including handling mouse interaction, wide
68
+ * characters and fetching the actual text within the selection. Rendering is
69
+ * not handled by the SelectionService but the onRedrawRequest event is fired
70
+ * when the selection is ready to be redrawn (on an animation frame).
71
+ */
72
+ export class SelectionService extends Disposable implements ISelectionService {
73
+ public serviceBrand: undefined;
74
+
75
+ protected _model: SelectionModel;
76
+
77
+ /**
78
+ * The amount to scroll every drag scroll update (depends on how far the mouse
79
+ * drag is above or below the terminal).
80
+ */
81
+ private _dragScrollAmount: number = 0;
82
+
83
+ /**
84
+ * The current selection mode.
85
+ */
86
+ protected _activeSelectionMode: SelectionMode;
87
+
88
+ /**
89
+ * A setInterval timer that is active while the mouse is down whose callback
90
+ * scrolls the viewport when necessary.
91
+ */
92
+ private _dragScrollIntervalTimer: number | undefined;
93
+
94
+ /**
95
+ * The animation frame ID used for refreshing the selection.
96
+ */
97
+ private _refreshAnimationFrame: number | undefined;
98
+
99
+ /**
100
+ * Whether selection is enabled.
101
+ */
102
+ private _enabled = true;
103
+
104
+ private _mouseMoveListener: EventListener;
105
+ private _mouseUpListener: EventListener;
106
+ private _trimListener: IDisposable;
107
+ private _workCell: CellData = new CellData();
108
+
109
+ private _mouseDownTimeStamp: number = 0;
110
+ private _oldHasSelection: boolean = false;
111
+ private _oldSelectionStart: [number, number] | undefined = undefined;
112
+ private _oldSelectionEnd: [number, number] | undefined = undefined;
113
+
114
+ private _onLinuxMouseSelection = this.register(new EventEmitter<string>());
115
+ public get onLinuxMouseSelection(): IEvent<string> { return this._onLinuxMouseSelection.event; }
116
+ private _onRedrawRequest = this.register(new EventEmitter<ISelectionRedrawRequestEvent>());
117
+ public get onRequestRedraw(): IEvent<ISelectionRedrawRequestEvent> { return this._onRedrawRequest.event; }
118
+ private _onSelectionChange = this.register(new EventEmitter<void>());
119
+ public get onSelectionChange(): IEvent<void> { return this._onSelectionChange.event; }
120
+ private _onRequestScrollLines = this.register(new EventEmitter<ISelectionRequestScrollLinesEvent>());
121
+ public get onRequestScrollLines(): IEvent<ISelectionRequestScrollLinesEvent> { return this._onRequestScrollLines.event; }
122
+
123
+ constructor(
124
+ private readonly _element: HTMLElement,
125
+ private readonly _screenElement: HTMLElement,
126
+ private readonly _linkifier: ILinkifier2,
127
+ @IBufferService private readonly _bufferService: IBufferService,
128
+ @ICoreService private readonly _coreService: ICoreService,
129
+ @IMouseService private readonly _mouseService: IMouseService,
130
+ @IOptionsService private readonly _optionsService: IOptionsService,
131
+ @IRenderService private readonly _renderService: IRenderService
132
+ ) {
133
+ super();
134
+
135
+ // Init listeners
136
+ this._mouseMoveListener = event => this._onMouseMove(event as MouseEvent);
137
+ this._mouseUpListener = event => this._onMouseUp(event as MouseEvent);
138
+ this._coreService.onUserInput(() => {
139
+ if (this.hasSelection) {
140
+ this.clearSelection();
141
+ }
142
+ });
143
+ this._trimListener = this._bufferService.buffer.lines.onTrim(amount => this._onTrim(amount));
144
+ this.register(this._bufferService.buffers.onBufferActivate(e => this._onBufferActivate(e)));
145
+
146
+ this.enable();
147
+
148
+ this._model = new SelectionModel(this._bufferService);
149
+ this._activeSelectionMode = SelectionMode.NORMAL;
150
+ }
151
+
152
+ public dispose(): void {
153
+ this._removeMouseDownListeners();
154
+ }
155
+
156
+ public reset(): void {
157
+ this.clearSelection();
158
+ }
159
+
160
+ /**
161
+ * Disables the selection manager. This is useful for when terminal mouse
162
+ * are enabled.
163
+ */
164
+ public disable(): void {
165
+ this.clearSelection();
166
+ this._enabled = false;
167
+ }
168
+
169
+ /**
170
+ * Enable the selection manager.
171
+ */
172
+ public enable(): void {
173
+ this._enabled = true;
174
+ }
175
+
176
+ public get selectionStart(): [number, number] | undefined { return this._model.finalSelectionStart; }
177
+ public get selectionEnd(): [number, number] | undefined { return this._model.finalSelectionEnd; }
178
+
179
+ /**
180
+ * Gets whether there is an active text selection.
181
+ */
182
+ public get hasSelection(): boolean {
183
+ const start = this._model.finalSelectionStart;
184
+ const end = this._model.finalSelectionEnd;
185
+ if (!start || !end) {
186
+ return false;
187
+ }
188
+ return start[0] !== end[0] || start[1] !== end[1];
189
+ }
190
+
191
+ /**
192
+ * Gets the text currently selected.
193
+ */
194
+ public get selectionText(): string {
195
+ const start = this._model.finalSelectionStart;
196
+ const end = this._model.finalSelectionEnd;
197
+ if (!start || !end) {
198
+ return '';
199
+ }
200
+
201
+ const buffer = this._bufferService.buffer;
202
+ const result: string[] = [];
203
+
204
+ if (this._activeSelectionMode === SelectionMode.COLUMN) {
205
+ // Ignore zero width selections
206
+ if (start[0] === end[0]) {
207
+ return '';
208
+ }
209
+
210
+ // For column selection it's not enough to rely on final selection's swapping of reversed
211
+ // values, it also needs the x coordinates to swap independently of the y coordinate is needed
212
+ const startCol = start[0] < end[0] ? start[0] : end[0];
213
+ const endCol = start[0] < end[0] ? end[0] : start[0];
214
+ for (let i = start[1]; i <= end[1]; i++) {
215
+ const lineText = buffer.translateBufferLineToString(i, true, startCol, endCol);
216
+ result.push(lineText);
217
+ }
218
+ } else {
219
+ // Get first row
220
+ const startRowEndCol = start[1] === end[1] ? end[0] : undefined;
221
+ result.push(buffer.translateBufferLineToString(start[1], true, start[0], startRowEndCol));
222
+
223
+ // Get middle rows
224
+ for (let i = start[1] + 1; i <= end[1] - 1; i++) {
225
+ const bufferLine = buffer.lines.get(i);
226
+ const lineText = buffer.translateBufferLineToString(i, true);
227
+ if (bufferLine?.isWrapped) {
228
+ result[result.length - 1] += lineText;
229
+ } else {
230
+ result.push(lineText);
231
+ }
232
+ }
233
+
234
+ // Get final row
235
+ if (start[1] !== end[1]) {
236
+ const bufferLine = buffer.lines.get(end[1]);
237
+ const lineText = buffer.translateBufferLineToString(end[1], true, 0, end[0]);
238
+ if (bufferLine && bufferLine!.isWrapped) {
239
+ result[result.length - 1] += lineText;
240
+ } else {
241
+ result.push(lineText);
242
+ }
243
+ }
244
+ }
245
+
246
+ // Format string by replacing non-breaking space chars with regular spaces
247
+ // and joining the array into a multi-line string.
248
+ const formattedResult = result.map(line => {
249
+ return line.replace(ALL_NON_BREAKING_SPACE_REGEX, ' ');
250
+ }).join(Browser.isWindows ? '\r\n' : '\n');
251
+
252
+ return formattedResult;
253
+ }
254
+
255
+ /**
256
+ * Clears the current terminal selection.
257
+ */
258
+ public clearSelection(): void {
259
+ this._model.clearSelection();
260
+ this._removeMouseDownListeners();
261
+ this.refresh();
262
+ this._onSelectionChange.fire();
263
+ }
264
+
265
+ /**
266
+ * Queues a refresh, redrawing the selection on the next opportunity.
267
+ * @param isLinuxMouseSelection Whether the selection should be registered as a new
268
+ * selection on Linux.
269
+ */
270
+ public refresh(isLinuxMouseSelection?: boolean): void {
271
+ // Queue the refresh for the renderer
272
+ if (!this._refreshAnimationFrame) {
273
+ this._refreshAnimationFrame = window.requestAnimationFrame(() => this._refresh());
274
+ }
275
+
276
+ // If the platform is Linux and the refresh call comes from a mouse event,
277
+ // we need to update the selection for middle click to paste selection.
278
+ if (Browser.isLinux && isLinuxMouseSelection) {
279
+ const selectionText = this.selectionText;
280
+ if (selectionText.length) {
281
+ this._onLinuxMouseSelection.fire(this.selectionText);
282
+ }
283
+ }
284
+ }
285
+
286
+ /**
287
+ * Fires the refresh event, causing consumers to pick it up and redraw the
288
+ * selection state.
289
+ */
290
+ private _refresh(): void {
291
+ this._refreshAnimationFrame = undefined;
292
+ this._onRedrawRequest.fire({
293
+ start: this._model.finalSelectionStart,
294
+ end: this._model.finalSelectionEnd,
295
+ columnSelectMode: this._activeSelectionMode === SelectionMode.COLUMN
296
+ });
297
+ }
298
+
299
+ /**
300
+ * Checks if the current click was inside the current selection
301
+ * @param event The mouse event
302
+ */
303
+ private _isClickInSelection(event: MouseEvent): boolean {
304
+ const coords = this._getMouseBufferCoords(event);
305
+ const start = this._model.finalSelectionStart;
306
+ const end = this._model.finalSelectionEnd;
307
+
308
+ if (!start || !end || !coords) {
309
+ return false;
310
+ }
311
+
312
+ return this._areCoordsInSelection(coords, start, end);
313
+ }
314
+
315
+ public isCellInSelection(x: number, y: number): boolean {
316
+ const start = this._model.finalSelectionStart;
317
+ const end = this._model.finalSelectionEnd;
318
+ if (!start || !end) {
319
+ return false;
320
+ }
321
+ return this._areCoordsInSelection([x, y], start, end);
322
+ }
323
+
324
+ protected _areCoordsInSelection(coords: [number, number], start: [number, number], end: [number, number]): boolean {
325
+ return (coords[1] > start[1] && coords[1] < end[1]) ||
326
+ (start[1] === end[1] && coords[1] === start[1] && coords[0] >= start[0] && coords[0] < end[0]) ||
327
+ (start[1] < end[1] && coords[1] === end[1] && coords[0] < end[0]) ||
328
+ (start[1] < end[1] && coords[1] === start[1] && coords[0] >= start[0]);
329
+ }
330
+
331
+ /**
332
+ * Selects word at the current mouse event coordinates.
333
+ * @param event The mouse event.
334
+ */
335
+ private _selectWordAtCursor(event: MouseEvent, allowWhitespaceOnlySelection: boolean): boolean {
336
+ // Check if there is a link under the cursor first and select that if so
337
+ const range = this._linkifier.currentLink?.link?.range;
338
+ if (range) {
339
+ this._model.selectionStart = [range.start.x - 1, range.start.y - 1];
340
+ this._model.selectionStartLength = getRangeLength(range, this._bufferService.cols);
341
+ this._model.selectionEnd = undefined;
342
+ return true;
343
+ }
344
+
345
+ const coords = this._getMouseBufferCoords(event);
346
+ if (coords) {
347
+ this._selectWordAt(coords, allowWhitespaceOnlySelection);
348
+ this._model.selectionEnd = undefined;
349
+ return true;
350
+ }
351
+ return false;
352
+ }
353
+
354
+ /**
355
+ * Selects all text within the terminal.
356
+ */
357
+ public selectAll(): void {
358
+ this._model.isSelectAllActive = true;
359
+ this.refresh();
360
+ this._onSelectionChange.fire();
361
+ }
362
+
363
+ public selectLines(start: number, end: number): void {
364
+ this._model.clearSelection();
365
+ start = Math.max(start, 0);
366
+ end = Math.min(end, this._bufferService.buffer.lines.length - 1);
367
+ this._model.selectionStart = [0, start];
368
+ this._model.selectionEnd = [this._bufferService.cols, end];
369
+ this.refresh();
370
+ this._onSelectionChange.fire();
371
+ }
372
+
373
+ /**
374
+ * Handle the buffer being trimmed, adjust the selection position.
375
+ * @param amount The amount the buffer is being trimmed.
376
+ */
377
+ private _onTrim(amount: number): void {
378
+ const needsRefresh = this._model.onTrim(amount);
379
+ if (needsRefresh) {
380
+ this.refresh();
381
+ }
382
+ }
383
+
384
+ /**
385
+ * Gets the 0-based [x, y] buffer coordinates of the current mouse event.
386
+ * @param event The mouse event.
387
+ */
388
+ private _getMouseBufferCoords(event: MouseEvent): [number, number] | undefined {
389
+ const coords = this._mouseService.getCoords(event, this._screenElement, this._bufferService.cols, this._bufferService.rows, true);
390
+ if (!coords) {
391
+ return undefined;
392
+ }
393
+
394
+ // Convert to 0-based
395
+ coords[0]--;
396
+ coords[1]--;
397
+
398
+ // Convert viewport coords to buffer coords
399
+ coords[1] += this._bufferService.buffer.ydisp;
400
+ return coords;
401
+ }
402
+
403
+ /**
404
+ * Gets the amount the viewport should be scrolled based on how far out of the
405
+ * terminal the mouse is.
406
+ * @param event The mouse event.
407
+ */
408
+ private _getMouseEventScrollAmount(event: MouseEvent): number {
409
+ let offset = getCoordsRelativeToElement(window, event, this._screenElement)[1];
410
+ const terminalHeight = this._renderService.dimensions.canvasHeight;
411
+ if (offset >= 0 && offset <= terminalHeight) {
412
+ return 0;
413
+ }
414
+ if (offset > terminalHeight) {
415
+ offset -= terminalHeight;
416
+ }
417
+
418
+ offset = Math.min(Math.max(offset, -DRAG_SCROLL_MAX_THRESHOLD), DRAG_SCROLL_MAX_THRESHOLD);
419
+ offset /= DRAG_SCROLL_MAX_THRESHOLD;
420
+ return (offset / Math.abs(offset)) + Math.round(offset * (DRAG_SCROLL_MAX_SPEED - 1));
421
+ }
422
+
423
+ /**
424
+ * Returns whether the selection manager should force selection, regardless of
425
+ * whether the terminal is in mouse events mode.
426
+ * @param event The mouse event.
427
+ */
428
+ public shouldForceSelection(event: MouseEvent): boolean {
429
+ if (Browser.isMac) {
430
+ return event.altKey && this._optionsService.rawOptions.macOptionClickForcesSelection;
431
+ }
432
+
433
+ return event.shiftKey;
434
+ }
435
+
436
+ /**
437
+ * Handles te mousedown event, setting up for a new selection.
438
+ * @param event The mousedown event.
439
+ */
440
+ public onMouseDown(event: MouseEvent): void {
441
+ this._mouseDownTimeStamp = event.timeStamp;
442
+ // If we have selection, we want the context menu on right click even if the
443
+ // terminal is in mouse mode.
444
+ if (event.button === 2 && this.hasSelection) {
445
+ return;
446
+ }
447
+
448
+ // Only action the primary button
449
+ if (event.button !== 0) {
450
+ return;
451
+ }
452
+
453
+ // Allow selection when using a specific modifier key, even when disabled
454
+ if (!this._enabled) {
455
+ if (!this.shouldForceSelection(event)) {
456
+ return;
457
+ }
458
+
459
+ // Don't send the mouse down event to the current process, we want to select
460
+ event.stopPropagation();
461
+ }
462
+
463
+ // Tell the browser not to start a regular selection
464
+ event.preventDefault();
465
+
466
+ // Reset drag scroll state
467
+ this._dragScrollAmount = 0;
468
+
469
+ if (this._enabled && event.shiftKey) {
470
+ this._onIncrementalClick(event);
471
+ } else {
472
+ if (event.detail === 1) {
473
+ this._onSingleClick(event);
474
+ } else if (event.detail === 2) {
475
+ this._onDoubleClick(event);
476
+ } else if (event.detail === 3) {
477
+ this._onTripleClick(event);
478
+ }
479
+ }
480
+
481
+ this._addMouseDownListeners();
482
+ this.refresh(true);
483
+ }
484
+
485
+ /**
486
+ * Adds listeners when mousedown is triggered.
487
+ */
488
+ private _addMouseDownListeners(): void {
489
+ // Listen on the document so that dragging outside of viewport works
490
+ if (this._screenElement.ownerDocument) {
491
+ this._screenElement.ownerDocument.addEventListener('mousemove', this._mouseMoveListener);
492
+ this._screenElement.ownerDocument.addEventListener('mouseup', this._mouseUpListener);
493
+ }
494
+ this._dragScrollIntervalTimer = window.setInterval(() => this._dragScroll(), DRAG_SCROLL_INTERVAL);
495
+ }
496
+
497
+ /**
498
+ * Removes the listeners that are registered when mousedown is triggered.
499
+ */
500
+ private _removeMouseDownListeners(): void {
501
+ if (this._screenElement.ownerDocument) {
502
+ this._screenElement.ownerDocument.removeEventListener('mousemove', this._mouseMoveListener);
503
+ this._screenElement.ownerDocument.removeEventListener('mouseup', this._mouseUpListener);
504
+ }
505
+ clearInterval(this._dragScrollIntervalTimer);
506
+ this._dragScrollIntervalTimer = undefined;
507
+ }
508
+
509
+ /**
510
+ * Performs an incremental click, setting the selection end position to the mouse
511
+ * position.
512
+ * @param event The mouse event.
513
+ */
514
+ private _onIncrementalClick(event: MouseEvent): void {
515
+ if (this._model.selectionStart) {
516
+ this._model.selectionEnd = this._getMouseBufferCoords(event);
517
+ }
518
+ }
519
+
520
+ /**
521
+ * Performs a single click, resetting relevant state and setting the selection
522
+ * start position.
523
+ * @param event The mouse event.
524
+ */
525
+ private _onSingleClick(event: MouseEvent): void {
526
+ this._model.selectionStartLength = 0;
527
+ this._model.isSelectAllActive = false;
528
+ this._activeSelectionMode = this.shouldColumnSelect(event) ? SelectionMode.COLUMN : SelectionMode.NORMAL;
529
+
530
+ // Initialize the new selection
531
+ this._model.selectionStart = this._getMouseBufferCoords(event);
532
+ if (!this._model.selectionStart) {
533
+ return;
534
+ }
535
+ this._model.selectionEnd = undefined;
536
+
537
+ // Ensure the line exists
538
+ const line = this._bufferService.buffer.lines.get(this._model.selectionStart[1]);
539
+ if (!line) {
540
+ return;
541
+ }
542
+
543
+ // Return early if the click event is not in the buffer (eg. in scroll bar)
544
+ if (line.length === this._model.selectionStart[0]) {
545
+ return;
546
+ }
547
+
548
+ // If the mouse is over the second half of a wide character, adjust the
549
+ // selection to cover the whole character
550
+ if (line.hasWidth(this._model.selectionStart[0]) === 0) {
551
+ this._model.selectionStart[0]++;
552
+ }
553
+ }
554
+
555
+ /**
556
+ * Performs a double click, selecting the current word.
557
+ * @param event The mouse event.
558
+ */
559
+ private _onDoubleClick(event: MouseEvent): void {
560
+ if (this._selectWordAtCursor(event, true)) {
561
+ this._activeSelectionMode = SelectionMode.WORD;
562
+ }
563
+ }
564
+
565
+ /**
566
+ * Performs a triple click, selecting the current line and activating line
567
+ * select mode.
568
+ * @param event The mouse event.
569
+ */
570
+ private _onTripleClick(event: MouseEvent): void {
571
+ const coords = this._getMouseBufferCoords(event);
572
+ if (coords) {
573
+ this._activeSelectionMode = SelectionMode.LINE;
574
+ this._selectLineAt(coords[1]);
575
+ }
576
+ }
577
+
578
+ /**
579
+ * Returns whether the selection manager should operate in column select mode
580
+ * @param event the mouse or keyboard event
581
+ */
582
+ public shouldColumnSelect(event: KeyboardEvent | MouseEvent): boolean {
583
+ return event.altKey && !(Browser.isMac && this._optionsService.rawOptions.macOptionClickForcesSelection);
584
+ }
585
+
586
+ /**
587
+ * Handles the mousemove event when the mouse button is down, recording the
588
+ * end of the selection and refreshing the selection.
589
+ * @param event The mousemove event.
590
+ */
591
+ private _onMouseMove(event: MouseEvent): void {
592
+ // If the mousemove listener is active it means that a selection is
593
+ // currently being made, we should stop propagation to prevent mouse events
594
+ // to be sent to the pty.
595
+ event.stopImmediatePropagation();
596
+
597
+ // Do nothing if there is no selection start, this can happen if the first
598
+ // click in the terminal is an incremental click
599
+ if (!this._model.selectionStart) {
600
+ return;
601
+ }
602
+
603
+ // Record the previous position so we know whether to redraw the selection
604
+ // at the end.
605
+ const previousSelectionEnd = this._model.selectionEnd ? [this._model.selectionEnd[0], this._model.selectionEnd[1]] : null;
606
+
607
+ // Set the initial selection end based on the mouse coordinates
608
+ this._model.selectionEnd = this._getMouseBufferCoords(event);
609
+ if (!this._model.selectionEnd) {
610
+ this.refresh(true);
611
+ return;
612
+ }
613
+
614
+ // Select the entire line if line select mode is active.
615
+ if (this._activeSelectionMode === SelectionMode.LINE) {
616
+ if (this._model.selectionEnd[1] < this._model.selectionStart[1]) {
617
+ this._model.selectionEnd[0] = 0;
618
+ } else {
619
+ this._model.selectionEnd[0] = this._bufferService.cols;
620
+ }
621
+ } else if (this._activeSelectionMode === SelectionMode.WORD) {
622
+ this._selectToWordAt(this._model.selectionEnd);
623
+ }
624
+
625
+ // Determine the amount of scrolling that will happen.
626
+ this._dragScrollAmount = this._getMouseEventScrollAmount(event);
627
+
628
+ // If the cursor was above or below the viewport, make sure it's at the
629
+ // start or end of the viewport respectively. This should only happen when
630
+ // NOT in column select mode.
631
+ if (this._activeSelectionMode !== SelectionMode.COLUMN) {
632
+ if (this._dragScrollAmount > 0) {
633
+ this._model.selectionEnd[0] = this._bufferService.cols;
634
+ } else if (this._dragScrollAmount < 0) {
635
+ this._model.selectionEnd[0] = 0;
636
+ }
637
+ }
638
+
639
+ // If the character is a wide character include the cell to the right in the
640
+ // selection. Note that selections at the very end of the line will never
641
+ // have a character.
642
+ const buffer = this._bufferService.buffer;
643
+ if (this._model.selectionEnd[1] < buffer.lines.length) {
644
+ const line = buffer.lines.get(this._model.selectionEnd[1]);
645
+ if (line && line.hasWidth(this._model.selectionEnd[0]) === 0) {
646
+ this._model.selectionEnd[0]++;
647
+ }
648
+ }
649
+
650
+ // Only draw here if the selection changes.
651
+ if (!previousSelectionEnd ||
652
+ previousSelectionEnd[0] !== this._model.selectionEnd[0] ||
653
+ previousSelectionEnd[1] !== this._model.selectionEnd[1]) {
654
+ this.refresh(true);
655
+ }
656
+ }
657
+
658
+ /**
659
+ * The callback that occurs every DRAG_SCROLL_INTERVAL ms that does the
660
+ * scrolling of the viewport.
661
+ */
662
+ private _dragScroll(): void {
663
+ if (!this._model.selectionEnd || !this._model.selectionStart) {
664
+ return;
665
+ }
666
+ if (this._dragScrollAmount) {
667
+ this._onRequestScrollLines.fire({ amount: this._dragScrollAmount, suppressScrollEvent: false });
668
+ // Re-evaluate selection
669
+ // If the cursor was above or below the viewport, make sure it's at the
670
+ // start or end of the viewport respectively. This should only happen when
671
+ // NOT in column select mode.
672
+ const buffer = this._bufferService.buffer;
673
+ if (this._dragScrollAmount > 0) {
674
+ if (this._activeSelectionMode !== SelectionMode.COLUMN) {
675
+ this._model.selectionEnd[0] = this._bufferService.cols;
676
+ }
677
+ this._model.selectionEnd[1] = Math.min(buffer.ydisp + this._bufferService.rows, buffer.lines.length - 1);
678
+ } else {
679
+ if (this._activeSelectionMode !== SelectionMode.COLUMN) {
680
+ this._model.selectionEnd[0] = 0;
681
+ }
682
+ this._model.selectionEnd[1] = buffer.ydisp;
683
+ }
684
+ this.refresh();
685
+ }
686
+ }
687
+
688
+ /**
689
+ * Handles the mouseup event, removing the mousedown listeners.
690
+ * @param event The mouseup event.
691
+ */
692
+ private _onMouseUp(event: MouseEvent): void {
693
+ const timeElapsed = event.timeStamp - this._mouseDownTimeStamp;
694
+
695
+ this._removeMouseDownListeners();
696
+
697
+ if (this.selectionText.length <= 1 && timeElapsed < ALT_CLICK_MOVE_CURSOR_TIME && event.altKey && this._optionsService.getOption('altClickMovesCursor')) {
698
+ if (this._bufferService.buffer.ybase === this._bufferService.buffer.ydisp) {
699
+ const coordinates = this._mouseService.getCoords(
700
+ event,
701
+ this._element,
702
+ this._bufferService.cols,
703
+ this._bufferService.rows,
704
+ false
705
+ );
706
+ if (coordinates && coordinates[0] !== undefined && coordinates[1] !== undefined) {
707
+ const sequence = moveToCellSequence(coordinates[0] - 1, coordinates[1] - 1, this._bufferService, this._coreService.decPrivateModes.applicationCursorKeys);
708
+ this._coreService.triggerDataEvent(sequence, true);
709
+ }
710
+ }
711
+ } else {
712
+ this._fireEventIfSelectionChanged();
713
+ }
714
+ }
715
+
716
+ private _fireEventIfSelectionChanged(): void {
717
+ const start = this._model.finalSelectionStart;
718
+ const end = this._model.finalSelectionEnd;
719
+ const hasSelection = !!start && !!end && (start[0] !== end[0] || start[1] !== end[1]);
720
+
721
+ if (!hasSelection) {
722
+ if (this._oldHasSelection) {
723
+ this._fireOnSelectionChange(start, end, hasSelection);
724
+ }
725
+ return;
726
+ }
727
+
728
+ // Sanity check, these should not be undefined as there is a selection
729
+ if (!start || !end) {
730
+ return;
731
+ }
732
+
733
+ if (!this._oldSelectionStart || !this._oldSelectionEnd || (
734
+ start[0] !== this._oldSelectionStart[0] || start[1] !== this._oldSelectionStart[1] ||
735
+ end[0] !== this._oldSelectionEnd[0] || end[1] !== this._oldSelectionEnd[1])) {
736
+
737
+ this._fireOnSelectionChange(start, end, hasSelection);
738
+ }
739
+ }
740
+
741
+ private _fireOnSelectionChange(start: [number, number] | undefined, end: [number, number] | undefined, hasSelection: boolean): void {
742
+ this._oldSelectionStart = start;
743
+ this._oldSelectionEnd = end;
744
+ this._oldHasSelection = hasSelection;
745
+ this._onSelectionChange.fire();
746
+ }
747
+
748
+ private _onBufferActivate(e: {activeBuffer: IBuffer, inactiveBuffer: IBuffer}): void {
749
+ this.clearSelection();
750
+ // Only adjust the selection on trim, shiftElements is rarely used (only in
751
+ // reverseIndex) and delete in a splice is only ever used when the same
752
+ // number of elements was just added. Given this is could actually be
753
+ // beneficial to leave the selection as is for these cases.
754
+ this._trimListener.dispose();
755
+ this._trimListener = e.activeBuffer.lines.onTrim(amount => this._onTrim(amount));
756
+ }
757
+
758
+ /**
759
+ * Converts a viewport column to the character index on the buffer line, the
760
+ * latter takes into account wide characters.
761
+ * @param coords The coordinates to find the 2 index for.
762
+ */
763
+ private _convertViewportColToCharacterIndex(bufferLine: IBufferLine, coords: [number, number]): number {
764
+ let charIndex = coords[0];
765
+ for (let i = 0; coords[0] >= i; i++) {
766
+ const length = bufferLine.loadCell(i, this._workCell).getChars().length;
767
+ if (this._workCell.getWidth() === 0) {
768
+ // Wide characters aren't included in the line string so decrement the
769
+ // index so the index is back on the wide character.
770
+ charIndex--;
771
+ } else if (length > 1 && coords[0] !== i) {
772
+ // Emojis take up multiple characters, so adjust accordingly. For these
773
+ // we don't want ot include the character at the column as we're
774
+ // returning the start index in the string, not the end index.
775
+ charIndex += length - 1;
776
+ }
777
+ }
778
+ return charIndex;
779
+ }
780
+
781
+ public setSelection(col: number, row: number, length: number): void {
782
+ this._model.clearSelection();
783
+ this._removeMouseDownListeners();
784
+ this._model.selectionStart = [col, row];
785
+ this._model.selectionStartLength = length;
786
+ this.refresh();
787
+ this._fireEventIfSelectionChanged();
788
+ }
789
+
790
+ public rightClickSelect(ev: MouseEvent): void {
791
+ if (!this._isClickInSelection(ev)) {
792
+ if (this._selectWordAtCursor(ev, false)) {
793
+ this.refresh(true);
794
+ }
795
+ this._fireEventIfSelectionChanged();
796
+ }
797
+ }
798
+
799
+ /**
800
+ * Gets positional information for the word at the coordinated specified.
801
+ * @param coords The coordinates to get the word at.
802
+ */
803
+ private _getWordAt(coords: [number, number], allowWhitespaceOnlySelection: boolean, followWrappedLinesAbove: boolean = true, followWrappedLinesBelow: boolean = true): IWordPosition | undefined {
804
+ // Ensure coords are within viewport (eg. not within scroll bar)
805
+ if (coords[0] >= this._bufferService.cols) {
806
+ return undefined;
807
+ }
808
+
809
+ const buffer = this._bufferService.buffer;
810
+ const bufferLine = buffer.lines.get(coords[1]);
811
+ if (!bufferLine) {
812
+ return undefined;
813
+ }
814
+
815
+ const line = buffer.translateBufferLineToString(coords[1], false);
816
+
817
+ // Get actual index, taking into consideration wide characters
818
+ let startIndex = this._convertViewportColToCharacterIndex(bufferLine, coords);
819
+ let endIndex = startIndex;
820
+
821
+ // Record offset to be used later
822
+ const charOffset = coords[0] - startIndex;
823
+ let leftWideCharCount = 0;
824
+ let rightWideCharCount = 0;
825
+ let leftLongCharOffset = 0;
826
+ let rightLongCharOffset = 0;
827
+
828
+ if (line.charAt(startIndex) === ' ') {
829
+ // Expand until non-whitespace is hit
830
+ while (startIndex > 0 && line.charAt(startIndex - 1) === ' ') {
831
+ startIndex--;
832
+ }
833
+ while (endIndex < line.length && line.charAt(endIndex + 1) === ' ') {
834
+ endIndex++;
835
+ }
836
+ } else {
837
+ // Expand until whitespace is hit. This algorithm works by scanning left
838
+ // and right from the starting position, keeping both the index format
839
+ // (line) and the column format (bufferLine) in sync. When a wide
840
+ // character is hit, it is recorded and the column index is adjusted.
841
+ let startCol = coords[0];
842
+ let endCol = coords[0];
843
+
844
+ // Consider the initial position, skip it and increment the wide char
845
+ // variable
846
+ if (bufferLine.getWidth(startCol) === 0) {
847
+ leftWideCharCount++;
848
+ startCol--;
849
+ }
850
+ if (bufferLine.getWidth(endCol) === 2) {
851
+ rightWideCharCount++;
852
+ endCol++;
853
+ }
854
+
855
+ // Adjust the end index for characters whose length are > 1 (emojis)
856
+ const length = bufferLine.getString(endCol).length;
857
+ if (length > 1) {
858
+ rightLongCharOffset += length - 1;
859
+ endIndex += length - 1;
860
+ }
861
+
862
+ // Expand the string in both directions until a space is hit
863
+ while (startCol > 0 && startIndex > 0 && !this._isCharWordSeparator(bufferLine.loadCell(startCol - 1, this._workCell))) {
864
+ bufferLine.loadCell(startCol - 1, this._workCell);
865
+ const length = this._workCell.getChars().length;
866
+ if (this._workCell.getWidth() === 0) {
867
+ // If the next character is a wide char, record it and skip the column
868
+ leftWideCharCount++;
869
+ startCol--;
870
+ } else if (length > 1) {
871
+ // If the next character's string is longer than 1 char (eg. emoji),
872
+ // adjust the index
873
+ leftLongCharOffset += length - 1;
874
+ startIndex -= length - 1;
875
+ }
876
+ startIndex--;
877
+ startCol--;
878
+ }
879
+ while (endCol < bufferLine.length && endIndex + 1 < line.length && !this._isCharWordSeparator(bufferLine.loadCell(endCol + 1, this._workCell))) {
880
+ bufferLine.loadCell(endCol + 1, this._workCell);
881
+ const length = this._workCell.getChars().length;
882
+ if (this._workCell.getWidth() === 2) {
883
+ // If the next character is a wide char, record it and skip the column
884
+ rightWideCharCount++;
885
+ endCol++;
886
+ } else if (length > 1) {
887
+ // If the next character's string is longer than 1 char (eg. emoji),
888
+ // adjust the index
889
+ rightLongCharOffset += length - 1;
890
+ endIndex += length - 1;
891
+ }
892
+ endIndex++;
893
+ endCol++;
894
+ }
895
+ }
896
+
897
+ // Incremenet the end index so it is at the start of the next character
898
+ endIndex++;
899
+
900
+ // Calculate the start _column_, converting the the string indexes back to
901
+ // column coordinates.
902
+ let start =
903
+ startIndex // The index of the selection's start char in the line string
904
+ + charOffset // The difference between the initial char's column and index
905
+ - leftWideCharCount // The number of wide chars left of the initial char
906
+ + leftLongCharOffset; // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis)
907
+
908
+ // Calculate the length in _columns_, converting the the string indexes back
909
+ // to column coordinates.
910
+ let length = Math.min(this._bufferService.cols, // Disallow lengths larger than the terminal cols
911
+ endIndex // The index of the selection's end char in the line string
912
+ - startIndex // The index of the selection's start char in the line string
913
+ + leftWideCharCount // The number of wide chars left of the initial char
914
+ + rightWideCharCount // The number of wide chars right of the initial char (inclusive)
915
+ - leftLongCharOffset // The number of additional chars left of the initial char added by columns with strings longer than 1 (emojis)
916
+ - rightLongCharOffset); // The number of additional chars right of the initial char (inclusive) added by columns with strings longer than 1 (emojis)
917
+
918
+ if (!allowWhitespaceOnlySelection && line.slice(startIndex, endIndex).trim() === '') {
919
+ return undefined;
920
+ }
921
+
922
+ // Recurse upwards if the line is wrapped and the word wraps to the above line
923
+ if (followWrappedLinesAbove) {
924
+ if (start === 0 && bufferLine.getCodePoint(0) !== 32 /* ' ' */) {
925
+ const previousBufferLine = buffer.lines.get(coords[1] - 1);
926
+ if (previousBufferLine && bufferLine.isWrapped && previousBufferLine.getCodePoint(this._bufferService.cols - 1) !== 32 /* ' ' */) {
927
+ const previousLineWordPosition = this._getWordAt([this._bufferService.cols - 1, coords[1] - 1], false, true, false);
928
+ if (previousLineWordPosition) {
929
+ const offset = this._bufferService.cols - previousLineWordPosition.start;
930
+ start -= offset;
931
+ length += offset;
932
+ }
933
+ }
934
+ }
935
+ }
936
+
937
+ // Recurse downwards if the line is wrapped and the word wraps to the next line
938
+ if (followWrappedLinesBelow) {
939
+ if (start + length === this._bufferService.cols && bufferLine.getCodePoint(this._bufferService.cols - 1) !== 32 /* ' ' */) {
940
+ const nextBufferLine = buffer.lines.get(coords[1] + 1);
941
+ if (nextBufferLine?.isWrapped && nextBufferLine.getCodePoint(0) !== 32 /* ' ' */) {
942
+ const nextLineWordPosition = this._getWordAt([0, coords[1] + 1], false, false, true);
943
+ if (nextLineWordPosition) {
944
+ length += nextLineWordPosition.length;
945
+ }
946
+ }
947
+ }
948
+ }
949
+
950
+ return { start, length };
951
+ }
952
+
953
+ /**
954
+ * Selects the word at the coordinates specified.
955
+ * @param coords The coordinates to get the word at.
956
+ * @param allowWhitespaceOnlySelection If whitespace should be selected
957
+ */
958
+ protected _selectWordAt(coords: [number, number], allowWhitespaceOnlySelection: boolean): void {
959
+ const wordPosition = this._getWordAt(coords, allowWhitespaceOnlySelection);
960
+ if (wordPosition) {
961
+ // Adjust negative start value
962
+ while (wordPosition.start < 0) {
963
+ wordPosition.start += this._bufferService.cols;
964
+ coords[1]--;
965
+ }
966
+ this._model.selectionStart = [wordPosition.start, coords[1]];
967
+ this._model.selectionStartLength = wordPosition.length;
968
+ }
969
+ }
970
+
971
+ /**
972
+ * Sets the selection end to the word at the coordinated specified.
973
+ * @param coords The coordinates to get the word at.
974
+ */
975
+ private _selectToWordAt(coords: [number, number]): void {
976
+ const wordPosition = this._getWordAt(coords, true);
977
+ if (wordPosition) {
978
+ let endRow = coords[1];
979
+
980
+ // Adjust negative start value
981
+ while (wordPosition.start < 0) {
982
+ wordPosition.start += this._bufferService.cols;
983
+ endRow--;
984
+ }
985
+
986
+ // Adjust wrapped length value, this only needs to happen when values are reversed as in that
987
+ // case we're interested in the start of the word, not the end
988
+ if (!this._model.areSelectionValuesReversed()) {
989
+ while (wordPosition.start + wordPosition.length > this._bufferService.cols) {
990
+ wordPosition.length -= this._bufferService.cols;
991
+ endRow++;
992
+ }
993
+ }
994
+
995
+ this._model.selectionEnd = [this._model.areSelectionValuesReversed() ? wordPosition.start : wordPosition.start + wordPosition.length, endRow];
996
+ }
997
+ }
998
+
999
+ /**
1000
+ * Gets whether the character is considered a word separator by the select
1001
+ * word logic.
1002
+ * @param char The character to check.
1003
+ */
1004
+ private _isCharWordSeparator(cell: CellData): boolean {
1005
+ // Zero width characters are never separators as they are always to the
1006
+ // right of wide characters
1007
+ if (cell.getWidth() === 0) {
1008
+ return false;
1009
+ }
1010
+ return this._optionsService.rawOptions.wordSeparator.indexOf(cell.getChars()) >= 0;
1011
+ }
1012
+
1013
+ /**
1014
+ * Selects the line specified.
1015
+ * @param line The line index.
1016
+ */
1017
+ protected _selectLineAt(line: number): void {
1018
+ const wrappedRange = this._bufferService.buffer.getWrappedRangeForLine(line);
1019
+ const range: IBufferRange = {
1020
+ start: { x: 0, y: wrappedRange.first },
1021
+ end: { x: this._bufferService.cols - 1, y: wrappedRange.last }
1022
+ };
1023
+ this._model.selectionStart = [0, wrappedRange.first];
1024
+ this._model.selectionEnd = undefined;
1025
+ this._model.selectionStartLength = getRangeLength(range, this._bufferService.cols);
1026
+ }
1027
+ }