@xterm/xterm 5.4.0-beta.1
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.
- package/LICENSE +21 -0
- package/README.md +235 -0
- package/css/xterm.css +209 -0
- package/lib/xterm.js +2 -0
- package/lib/xterm.js.map +1 -0
- package/package.json +101 -0
- package/src/browser/AccessibilityManager.ts +278 -0
- package/src/browser/Clipboard.ts +93 -0
- package/src/browser/ColorContrastCache.ts +34 -0
- package/src/browser/Lifecycle.ts +33 -0
- package/src/browser/Linkifier2.ts +416 -0
- package/src/browser/LocalizableStrings.ts +12 -0
- package/src/browser/OscLinkProvider.ts +128 -0
- package/src/browser/RenderDebouncer.ts +83 -0
- package/src/browser/Terminal.ts +1317 -0
- package/src/browser/TimeBasedDebouncer.ts +86 -0
- package/src/browser/Types.d.ts +181 -0
- package/src/browser/Viewport.ts +401 -0
- package/src/browser/decorations/BufferDecorationRenderer.ts +134 -0
- package/src/browser/decorations/ColorZoneStore.ts +117 -0
- package/src/browser/decorations/OverviewRulerRenderer.ts +218 -0
- package/src/browser/input/CompositionHelper.ts +246 -0
- package/src/browser/input/Mouse.ts +54 -0
- package/src/browser/input/MoveToCell.ts +249 -0
- package/src/browser/public/Terminal.ts +260 -0
- package/src/browser/renderer/dom/DomRenderer.ts +509 -0
- package/src/browser/renderer/dom/DomRendererRowFactory.ts +526 -0
- package/src/browser/renderer/dom/WidthCache.ts +160 -0
- package/src/browser/renderer/shared/CellColorResolver.ts +137 -0
- package/src/browser/renderer/shared/CharAtlasCache.ts +96 -0
- package/src/browser/renderer/shared/CharAtlasUtils.ts +75 -0
- package/src/browser/renderer/shared/Constants.ts +14 -0
- package/src/browser/renderer/shared/CursorBlinkStateManager.ts +146 -0
- package/src/browser/renderer/shared/CustomGlyphs.ts +687 -0
- package/src/browser/renderer/shared/DevicePixelObserver.ts +41 -0
- package/src/browser/renderer/shared/README.md +1 -0
- package/src/browser/renderer/shared/RendererUtils.ts +58 -0
- package/src/browser/renderer/shared/SelectionRenderModel.ts +91 -0
- package/src/browser/renderer/shared/TextureAtlas.ts +1082 -0
- package/src/browser/renderer/shared/Types.d.ts +173 -0
- package/src/browser/selection/SelectionModel.ts +144 -0
- package/src/browser/selection/Types.d.ts +15 -0
- package/src/browser/services/CharSizeService.ts +102 -0
- package/src/browser/services/CharacterJoinerService.ts +339 -0
- package/src/browser/services/CoreBrowserService.ts +137 -0
- package/src/browser/services/MouseService.ts +46 -0
- package/src/browser/services/RenderService.ts +279 -0
- package/src/browser/services/SelectionService.ts +1031 -0
- package/src/browser/services/Services.ts +147 -0
- package/src/browser/services/ThemeService.ts +237 -0
- package/src/common/CircularList.ts +241 -0
- package/src/common/Clone.ts +23 -0
- package/src/common/Color.ts +357 -0
- package/src/common/CoreTerminal.ts +284 -0
- package/src/common/EventEmitter.ts +78 -0
- package/src/common/InputHandler.ts +3461 -0
- package/src/common/Lifecycle.ts +108 -0
- package/src/common/MultiKeyMap.ts +42 -0
- package/src/common/Platform.ts +44 -0
- package/src/common/SortedList.ts +118 -0
- package/src/common/TaskQueue.ts +166 -0
- package/src/common/TypedArrayUtils.ts +17 -0
- package/src/common/Types.d.ts +553 -0
- package/src/common/WindowsMode.ts +27 -0
- package/src/common/buffer/AttributeData.ts +196 -0
- package/src/common/buffer/Buffer.ts +654 -0
- package/src/common/buffer/BufferLine.ts +524 -0
- package/src/common/buffer/BufferRange.ts +13 -0
- package/src/common/buffer/BufferReflow.ts +223 -0
- package/src/common/buffer/BufferSet.ts +134 -0
- package/src/common/buffer/CellData.ts +94 -0
- package/src/common/buffer/Constants.ts +149 -0
- package/src/common/buffer/Marker.ts +43 -0
- package/src/common/buffer/Types.d.ts +52 -0
- package/src/common/data/Charsets.ts +256 -0
- package/src/common/data/EscapeSequences.ts +153 -0
- package/src/common/input/Keyboard.ts +398 -0
- package/src/common/input/TextDecoder.ts +346 -0
- package/src/common/input/UnicodeV6.ts +145 -0
- package/src/common/input/WriteBuffer.ts +246 -0
- package/src/common/input/XParseColor.ts +80 -0
- package/src/common/parser/Constants.ts +58 -0
- package/src/common/parser/DcsParser.ts +192 -0
- package/src/common/parser/EscapeSequenceParser.ts +792 -0
- package/src/common/parser/OscParser.ts +238 -0
- package/src/common/parser/Params.ts +229 -0
- package/src/common/parser/Types.d.ts +275 -0
- package/src/common/public/AddonManager.ts +53 -0
- package/src/common/public/BufferApiView.ts +35 -0
- package/src/common/public/BufferLineApiView.ts +29 -0
- package/src/common/public/BufferNamespaceApi.ts +36 -0
- package/src/common/public/ParserApi.ts +37 -0
- package/src/common/public/UnicodeApi.ts +27 -0
- package/src/common/services/BufferService.ts +151 -0
- package/src/common/services/CharsetService.ts +34 -0
- package/src/common/services/CoreMouseService.ts +318 -0
- package/src/common/services/CoreService.ts +87 -0
- package/src/common/services/DecorationService.ts +140 -0
- package/src/common/services/InstantiationService.ts +85 -0
- package/src/common/services/LogService.ts +124 -0
- package/src/common/services/OptionsService.ts +202 -0
- package/src/common/services/OscLinkService.ts +115 -0
- package/src/common/services/ServiceRegistry.ts +49 -0
- package/src/common/services/Services.ts +373 -0
- package/src/common/services/UnicodeService.ts +111 -0
- package/src/headless/Terminal.ts +136 -0
- package/src/headless/public/Terminal.ts +195 -0
- package/typings/xterm.d.ts +1857 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
|
|
6
|
+
import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
|
|
7
|
+
import { Disposable, toDisposable } from 'common/Lifecycle';
|
|
8
|
+
import { IBufferService, IDecorationService, IInternalDecoration } from 'common/services/Services';
|
|
9
|
+
|
|
10
|
+
export class BufferDecorationRenderer extends Disposable {
|
|
11
|
+
private readonly _container: HTMLElement;
|
|
12
|
+
private readonly _decorationElements: Map<IInternalDecoration, HTMLElement> = new Map();
|
|
13
|
+
|
|
14
|
+
private _animationFrame: number | undefined;
|
|
15
|
+
private _altBufferIsActive: boolean = false;
|
|
16
|
+
private _dimensionsChanged: boolean = false;
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
private readonly _screenElement: HTMLElement,
|
|
20
|
+
@IBufferService private readonly _bufferService: IBufferService,
|
|
21
|
+
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService,
|
|
22
|
+
@IDecorationService private readonly _decorationService: IDecorationService,
|
|
23
|
+
@IRenderService private readonly _renderService: IRenderService
|
|
24
|
+
) {
|
|
25
|
+
super();
|
|
26
|
+
|
|
27
|
+
this._container = document.createElement('div');
|
|
28
|
+
this._container.classList.add('xterm-decoration-container');
|
|
29
|
+
this._screenElement.appendChild(this._container);
|
|
30
|
+
|
|
31
|
+
this.register(this._renderService.onRenderedViewportChange(() => this._doRefreshDecorations()));
|
|
32
|
+
this.register(this._renderService.onDimensionsChange(() => {
|
|
33
|
+
this._dimensionsChanged = true;
|
|
34
|
+
this._queueRefresh();
|
|
35
|
+
}));
|
|
36
|
+
this.register(this._coreBrowserService.onDprChange(() => this._queueRefresh()));
|
|
37
|
+
this.register(this._bufferService.buffers.onBufferActivate(() => {
|
|
38
|
+
this._altBufferIsActive = this._bufferService.buffer === this._bufferService.buffers.alt;
|
|
39
|
+
}));
|
|
40
|
+
this.register(this._decorationService.onDecorationRegistered(() => this._queueRefresh()));
|
|
41
|
+
this.register(this._decorationService.onDecorationRemoved(decoration => this._removeDecoration(decoration)));
|
|
42
|
+
this.register(toDisposable(() => {
|
|
43
|
+
this._container.remove();
|
|
44
|
+
this._decorationElements.clear();
|
|
45
|
+
}));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private _queueRefresh(): void {
|
|
49
|
+
if (this._animationFrame !== undefined) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
this._animationFrame = this._renderService.addRefreshCallback(() => {
|
|
53
|
+
this._doRefreshDecorations();
|
|
54
|
+
this._animationFrame = undefined;
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
private _doRefreshDecorations(): void {
|
|
59
|
+
for (const decoration of this._decorationService.decorations) {
|
|
60
|
+
this._renderDecoration(decoration);
|
|
61
|
+
}
|
|
62
|
+
this._dimensionsChanged = false;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private _renderDecoration(decoration: IInternalDecoration): void {
|
|
66
|
+
this._refreshStyle(decoration);
|
|
67
|
+
if (this._dimensionsChanged) {
|
|
68
|
+
this._refreshXPosition(decoration);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private _createElement(decoration: IInternalDecoration): HTMLElement {
|
|
73
|
+
const element = this._coreBrowserService.mainDocument.createElement('div');
|
|
74
|
+
element.classList.add('xterm-decoration');
|
|
75
|
+
element.classList.toggle('xterm-decoration-top-layer', decoration?.options?.layer === 'top');
|
|
76
|
+
element.style.width = `${Math.round((decoration.options.width || 1) * this._renderService.dimensions.css.cell.width)}px`;
|
|
77
|
+
element.style.height = `${(decoration.options.height || 1) * this._renderService.dimensions.css.cell.height}px`;
|
|
78
|
+
element.style.top = `${(decoration.marker.line - this._bufferService.buffers.active.ydisp) * this._renderService.dimensions.css.cell.height}px`;
|
|
79
|
+
element.style.lineHeight = `${this._renderService.dimensions.css.cell.height}px`;
|
|
80
|
+
|
|
81
|
+
const x = decoration.options.x ?? 0;
|
|
82
|
+
if (x && x > this._bufferService.cols) {
|
|
83
|
+
// exceeded the container width, so hide
|
|
84
|
+
element.style.display = 'none';
|
|
85
|
+
}
|
|
86
|
+
this._refreshXPosition(decoration, element);
|
|
87
|
+
|
|
88
|
+
return element;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private _refreshStyle(decoration: IInternalDecoration): void {
|
|
92
|
+
const line = decoration.marker.line - this._bufferService.buffers.active.ydisp;
|
|
93
|
+
if (line < 0 || line >= this._bufferService.rows) {
|
|
94
|
+
// outside of viewport
|
|
95
|
+
if (decoration.element) {
|
|
96
|
+
decoration.element.style.display = 'none';
|
|
97
|
+
decoration.onRenderEmitter.fire(decoration.element);
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
let element = this._decorationElements.get(decoration);
|
|
101
|
+
if (!element) {
|
|
102
|
+
element = this._createElement(decoration);
|
|
103
|
+
decoration.element = element;
|
|
104
|
+
this._decorationElements.set(decoration, element);
|
|
105
|
+
this._container.appendChild(element);
|
|
106
|
+
decoration.onDispose(() => {
|
|
107
|
+
this._decorationElements.delete(decoration);
|
|
108
|
+
element!.remove();
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
element.style.top = `${line * this._renderService.dimensions.css.cell.height}px`;
|
|
112
|
+
element.style.display = this._altBufferIsActive ? 'none' : 'block';
|
|
113
|
+
decoration.onRenderEmitter.fire(element);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private _refreshXPosition(decoration: IInternalDecoration, element: HTMLElement | undefined = decoration.element): void {
|
|
118
|
+
if (!element) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const x = decoration.options.x ?? 0;
|
|
122
|
+
if ((decoration.options.anchor || 'left') === 'right') {
|
|
123
|
+
element.style.right = x ? `${x * this._renderService.dimensions.css.cell.width}px` : '';
|
|
124
|
+
} else {
|
|
125
|
+
element.style.left = x ? `${x * this._renderService.dimensions.css.cell.width}px` : '';
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private _removeDecoration(decoration: IInternalDecoration): void {
|
|
130
|
+
this._decorationElements.get(decoration)?.remove();
|
|
131
|
+
this._decorationElements.delete(decoration);
|
|
132
|
+
decoration.dispose();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
|
|
6
|
+
import { IInternalDecoration } from 'common/services/Services';
|
|
7
|
+
|
|
8
|
+
export interface IColorZoneStore {
|
|
9
|
+
readonly zones: IColorZone[];
|
|
10
|
+
clear(): void;
|
|
11
|
+
addDecoration(decoration: IInternalDecoration): void;
|
|
12
|
+
/**
|
|
13
|
+
* Sets the amount of padding in lines that will be added between zones, if new lines intersect
|
|
14
|
+
* the padding they will be merged into the same zone.
|
|
15
|
+
*/
|
|
16
|
+
setPadding(padding: { [position: string]: number }): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface IColorZone {
|
|
20
|
+
/** Color in a format supported by canvas' fillStyle. */
|
|
21
|
+
color: string;
|
|
22
|
+
position: 'full' | 'left' | 'center' | 'right' | undefined;
|
|
23
|
+
startBufferLine: number;
|
|
24
|
+
endBufferLine: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface IMinimalDecorationForColorZone {
|
|
28
|
+
marker: Pick<IInternalDecoration['marker'], 'line'>;
|
|
29
|
+
options: Pick<IInternalDecoration['options'], 'overviewRulerOptions'>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class ColorZoneStore implements IColorZoneStore {
|
|
33
|
+
private _zones: IColorZone[] = [];
|
|
34
|
+
|
|
35
|
+
// The zone pool is used to keep zone objects from being freed between clearing the color zone
|
|
36
|
+
// store and fetching the zones. This helps reduce GC pressure since the color zones are
|
|
37
|
+
// accumulated on potentially every scroll event.
|
|
38
|
+
private _zonePool: IColorZone[] = [];
|
|
39
|
+
private _zonePoolIndex = 0;
|
|
40
|
+
|
|
41
|
+
private _linePadding: { [position: string]: number } = {
|
|
42
|
+
full: 0,
|
|
43
|
+
left: 0,
|
|
44
|
+
center: 0,
|
|
45
|
+
right: 0
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
public get zones(): IColorZone[] {
|
|
49
|
+
// Trim the zone pool to free unused memory
|
|
50
|
+
this._zonePool.length = Math.min(this._zonePool.length, this._zones.length);
|
|
51
|
+
return this._zones;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public clear(): void {
|
|
55
|
+
this._zones.length = 0;
|
|
56
|
+
this._zonePoolIndex = 0;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public addDecoration(decoration: IMinimalDecorationForColorZone): void {
|
|
60
|
+
if (!decoration.options.overviewRulerOptions) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
for (const z of this._zones) {
|
|
64
|
+
if (z.color === decoration.options.overviewRulerOptions.color &&
|
|
65
|
+
z.position === decoration.options.overviewRulerOptions.position) {
|
|
66
|
+
if (this._lineIntersectsZone(z, decoration.marker.line)) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (this._lineAdjacentToZone(z, decoration.marker.line, decoration.options.overviewRulerOptions.position)) {
|
|
70
|
+
this._addLineToZone(z, decoration.marker.line);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Create using zone pool if possible
|
|
76
|
+
if (this._zonePoolIndex < this._zonePool.length) {
|
|
77
|
+
this._zonePool[this._zonePoolIndex].color = decoration.options.overviewRulerOptions.color;
|
|
78
|
+
this._zonePool[this._zonePoolIndex].position = decoration.options.overviewRulerOptions.position;
|
|
79
|
+
this._zonePool[this._zonePoolIndex].startBufferLine = decoration.marker.line;
|
|
80
|
+
this._zonePool[this._zonePoolIndex].endBufferLine = decoration.marker.line;
|
|
81
|
+
this._zones.push(this._zonePool[this._zonePoolIndex++]);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// Create
|
|
85
|
+
this._zones.push({
|
|
86
|
+
color: decoration.options.overviewRulerOptions.color,
|
|
87
|
+
position: decoration.options.overviewRulerOptions.position,
|
|
88
|
+
startBufferLine: decoration.marker.line,
|
|
89
|
+
endBufferLine: decoration.marker.line
|
|
90
|
+
});
|
|
91
|
+
this._zonePool.push(this._zones[this._zones.length - 1]);
|
|
92
|
+
this._zonePoolIndex++;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public setPadding(padding: { [position: string]: number }): void {
|
|
96
|
+
this._linePadding = padding;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
private _lineIntersectsZone(zone: IColorZone, line: number): boolean {
|
|
100
|
+
return (
|
|
101
|
+
line >= zone.startBufferLine &&
|
|
102
|
+
line <= zone.endBufferLine
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private _lineAdjacentToZone(zone: IColorZone, line: number, position: IColorZone['position']): boolean {
|
|
107
|
+
return (
|
|
108
|
+
(line >= zone.startBufferLine - this._linePadding[position || 'full']) &&
|
|
109
|
+
(line <= zone.endBufferLine + this._linePadding[position || 'full'])
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private _addLineToZone(zone: IColorZone, line: number): void {
|
|
114
|
+
zone.startBufferLine = Math.min(zone.startBufferLine, line);
|
|
115
|
+
zone.endBufferLine = Math.max(zone.endBufferLine, line);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
|
|
6
|
+
import { ColorZoneStore, IColorZone, IColorZoneStore } from 'browser/decorations/ColorZoneStore';
|
|
7
|
+
import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
|
|
8
|
+
import { Disposable, toDisposable } from 'common/Lifecycle';
|
|
9
|
+
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
|
|
10
|
+
|
|
11
|
+
// Helper objects to avoid excessive calculation and garbage collection during rendering. These are
|
|
12
|
+
// static values for each render and can be accessed using the decoration position as the key.
|
|
13
|
+
const drawHeight = {
|
|
14
|
+
full: 0,
|
|
15
|
+
left: 0,
|
|
16
|
+
center: 0,
|
|
17
|
+
right: 0
|
|
18
|
+
};
|
|
19
|
+
const drawWidth = {
|
|
20
|
+
full: 0,
|
|
21
|
+
left: 0,
|
|
22
|
+
center: 0,
|
|
23
|
+
right: 0
|
|
24
|
+
};
|
|
25
|
+
const drawX = {
|
|
26
|
+
full: 0,
|
|
27
|
+
left: 0,
|
|
28
|
+
center: 0,
|
|
29
|
+
right: 0
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export class OverviewRulerRenderer extends Disposable {
|
|
33
|
+
private readonly _canvas: HTMLCanvasElement;
|
|
34
|
+
private readonly _ctx: CanvasRenderingContext2D;
|
|
35
|
+
private readonly _colorZoneStore: IColorZoneStore = new ColorZoneStore();
|
|
36
|
+
private get _width(): number {
|
|
37
|
+
return this._optionsService.options.overviewRulerWidth || 0;
|
|
38
|
+
}
|
|
39
|
+
private _animationFrame: number | undefined;
|
|
40
|
+
|
|
41
|
+
private _shouldUpdateDimensions: boolean | undefined = true;
|
|
42
|
+
private _shouldUpdateAnchor: boolean | undefined = true;
|
|
43
|
+
private _lastKnownBufferLength: number = 0;
|
|
44
|
+
|
|
45
|
+
private _containerHeight: number | undefined;
|
|
46
|
+
|
|
47
|
+
constructor(
|
|
48
|
+
private readonly _viewportElement: HTMLElement,
|
|
49
|
+
private readonly _screenElement: HTMLElement,
|
|
50
|
+
@IBufferService private readonly _bufferService: IBufferService,
|
|
51
|
+
@IDecorationService private readonly _decorationService: IDecorationService,
|
|
52
|
+
@IRenderService private readonly _renderService: IRenderService,
|
|
53
|
+
@IOptionsService private readonly _optionsService: IOptionsService,
|
|
54
|
+
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService
|
|
55
|
+
) {
|
|
56
|
+
super();
|
|
57
|
+
this._canvas = this._coreBrowserService.mainDocument.createElement('canvas');
|
|
58
|
+
this._canvas.classList.add('xterm-decoration-overview-ruler');
|
|
59
|
+
this._refreshCanvasDimensions();
|
|
60
|
+
this._viewportElement.parentElement?.insertBefore(this._canvas, this._viewportElement);
|
|
61
|
+
const ctx = this._canvas.getContext('2d');
|
|
62
|
+
if (!ctx) {
|
|
63
|
+
throw new Error('Ctx cannot be null');
|
|
64
|
+
} else {
|
|
65
|
+
this._ctx = ctx;
|
|
66
|
+
}
|
|
67
|
+
this._registerDecorationListeners();
|
|
68
|
+
this._registerBufferChangeListeners();
|
|
69
|
+
this._registerDimensionChangeListeners();
|
|
70
|
+
this.register(toDisposable(() => {
|
|
71
|
+
this._canvas?.remove();
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* On decoration add or remove, redraw
|
|
77
|
+
*/
|
|
78
|
+
private _registerDecorationListeners(): void {
|
|
79
|
+
this.register(this._decorationService.onDecorationRegistered(() => this._queueRefresh(undefined, true)));
|
|
80
|
+
this.register(this._decorationService.onDecorationRemoved(() => this._queueRefresh(undefined, true)));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* On buffer change, redraw
|
|
85
|
+
* and hide the canvas if the alt buffer is active
|
|
86
|
+
*/
|
|
87
|
+
private _registerBufferChangeListeners(): void {
|
|
88
|
+
this.register(this._renderService.onRenderedViewportChange(() => this._queueRefresh()));
|
|
89
|
+
this.register(this._bufferService.buffers.onBufferActivate(() => {
|
|
90
|
+
this._canvas!.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
|
|
91
|
+
}));
|
|
92
|
+
this.register(this._bufferService.onScroll(() => {
|
|
93
|
+
if (this._lastKnownBufferLength !== this._bufferService.buffers.normal.lines.length) {
|
|
94
|
+
this._refreshDrawHeightConstants();
|
|
95
|
+
this._refreshColorZonePadding();
|
|
96
|
+
}
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* On dimension change, update canvas dimensions
|
|
101
|
+
* and then redraw
|
|
102
|
+
*/
|
|
103
|
+
private _registerDimensionChangeListeners(): void {
|
|
104
|
+
// container height changed
|
|
105
|
+
this.register(this._renderService.onRender((): void => {
|
|
106
|
+
if (!this._containerHeight || this._containerHeight !== this._screenElement.clientHeight) {
|
|
107
|
+
this._queueRefresh(true);
|
|
108
|
+
this._containerHeight = this._screenElement.clientHeight;
|
|
109
|
+
}
|
|
110
|
+
}));
|
|
111
|
+
// overview ruler width changed
|
|
112
|
+
this.register(this._optionsService.onSpecificOptionChange('overviewRulerWidth', () => this._queueRefresh(true)));
|
|
113
|
+
// device pixel ratio changed
|
|
114
|
+
this.register(this._coreBrowserService.onDprChange(() => this._queueRefresh(true)));
|
|
115
|
+
// set the canvas dimensions
|
|
116
|
+
this._queueRefresh(true);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private _refreshDrawConstants(): void {
|
|
120
|
+
// width
|
|
121
|
+
const outerWidth = Math.floor(this._canvas.width / 3);
|
|
122
|
+
const innerWidth = Math.ceil(this._canvas.width / 3);
|
|
123
|
+
drawWidth.full = this._canvas.width;
|
|
124
|
+
drawWidth.left = outerWidth;
|
|
125
|
+
drawWidth.center = innerWidth;
|
|
126
|
+
drawWidth.right = outerWidth;
|
|
127
|
+
// height
|
|
128
|
+
this._refreshDrawHeightConstants();
|
|
129
|
+
// x
|
|
130
|
+
drawX.full = 0;
|
|
131
|
+
drawX.left = 0;
|
|
132
|
+
drawX.center = drawWidth.left;
|
|
133
|
+
drawX.right = drawWidth.left + drawWidth.center;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private _refreshDrawHeightConstants(): void {
|
|
137
|
+
drawHeight.full = Math.round(2 * this._coreBrowserService.dpr);
|
|
138
|
+
// Calculate actual pixels per line
|
|
139
|
+
const pixelsPerLine = this._canvas.height / this._bufferService.buffer.lines.length;
|
|
140
|
+
// Clamp actual pixels within a range
|
|
141
|
+
const nonFullHeight = Math.round(Math.max(Math.min(pixelsPerLine, 12), 6) * this._coreBrowserService.dpr);
|
|
142
|
+
drawHeight.left = nonFullHeight;
|
|
143
|
+
drawHeight.center = nonFullHeight;
|
|
144
|
+
drawHeight.right = nonFullHeight;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private _refreshColorZonePadding(): void {
|
|
148
|
+
this._colorZoneStore.setPadding({
|
|
149
|
+
full: Math.floor(this._bufferService.buffers.active.lines.length / (this._canvas.height - 1) * drawHeight.full),
|
|
150
|
+
left: Math.floor(this._bufferService.buffers.active.lines.length / (this._canvas.height - 1) * drawHeight.left),
|
|
151
|
+
center: Math.floor(this._bufferService.buffers.active.lines.length / (this._canvas.height - 1) * drawHeight.center),
|
|
152
|
+
right: Math.floor(this._bufferService.buffers.active.lines.length / (this._canvas.height - 1) * drawHeight.right)
|
|
153
|
+
});
|
|
154
|
+
this._lastKnownBufferLength = this._bufferService.buffers.normal.lines.length;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private _refreshCanvasDimensions(): void {
|
|
158
|
+
this._canvas.style.width = `${this._width}px`;
|
|
159
|
+
this._canvas.width = Math.round(this._width * this._coreBrowserService.dpr);
|
|
160
|
+
this._canvas.style.height = `${this._screenElement.clientHeight}px`;
|
|
161
|
+
this._canvas.height = Math.round(this._screenElement.clientHeight * this._coreBrowserService.dpr);
|
|
162
|
+
this._refreshDrawConstants();
|
|
163
|
+
this._refreshColorZonePadding();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private _refreshDecorations(): void {
|
|
167
|
+
if (this._shouldUpdateDimensions) {
|
|
168
|
+
this._refreshCanvasDimensions();
|
|
169
|
+
}
|
|
170
|
+
this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
|
|
171
|
+
this._colorZoneStore.clear();
|
|
172
|
+
for (const decoration of this._decorationService.decorations) {
|
|
173
|
+
this._colorZoneStore.addDecoration(decoration);
|
|
174
|
+
}
|
|
175
|
+
this._ctx.lineWidth = 1;
|
|
176
|
+
const zones = this._colorZoneStore.zones;
|
|
177
|
+
for (const zone of zones) {
|
|
178
|
+
if (zone.position !== 'full') {
|
|
179
|
+
this._renderColorZone(zone);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
for (const zone of zones) {
|
|
183
|
+
if (zone.position === 'full') {
|
|
184
|
+
this._renderColorZone(zone);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
this._shouldUpdateDimensions = false;
|
|
188
|
+
this._shouldUpdateAnchor = false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
private _renderColorZone(zone: IColorZone): void {
|
|
192
|
+
this._ctx.fillStyle = zone.color;
|
|
193
|
+
this._ctx.fillRect(
|
|
194
|
+
/* x */ drawX[zone.position || 'full'],
|
|
195
|
+
/* y */ Math.round(
|
|
196
|
+
(this._canvas.height - 1) * // -1 to ensure at least 2px are allowed for decoration on last line
|
|
197
|
+
(zone.startBufferLine / this._bufferService.buffers.active.lines.length) - drawHeight[zone.position || 'full'] / 2
|
|
198
|
+
),
|
|
199
|
+
/* w */ drawWidth[zone.position || 'full'],
|
|
200
|
+
/* h */ Math.round(
|
|
201
|
+
(this._canvas.height - 1) * // -1 to ensure at least 2px are allowed for decoration on last line
|
|
202
|
+
((zone.endBufferLine - zone.startBufferLine) / this._bufferService.buffers.active.lines.length) + drawHeight[zone.position || 'full']
|
|
203
|
+
)
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private _queueRefresh(updateCanvasDimensions?: boolean, updateAnchor?: boolean): void {
|
|
208
|
+
this._shouldUpdateDimensions = updateCanvasDimensions || this._shouldUpdateDimensions;
|
|
209
|
+
this._shouldUpdateAnchor = updateAnchor || this._shouldUpdateAnchor;
|
|
210
|
+
if (this._animationFrame !== undefined) {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
this._animationFrame = this._coreBrowserService.window.requestAnimationFrame(() => {
|
|
214
|
+
this._refreshDecorations();
|
|
215
|
+
this._animationFrame = undefined;
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|