Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/inlineProgress/browser/inlineProgress.ts
3296 views
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 * as dom from '../../../../base/browser/dom.js';
7
import { disposableTimeout } from '../../../../base/common/async.js';
8
import { Codicon } from '../../../../base/common/codicons.js';
9
import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js';
10
import { noBreakWhitespace } from '../../../../base/common/strings.js';
11
import { ThemeIcon } from '../../../../base/common/themables.js';
12
import './inlineProgressWidget.css';
13
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../browser/editorBrowser.js';
14
import { EditorOption } from '../../../common/config/editorOptions.js';
15
import { IPosition } from '../../../common/core/position.js';
16
import { Range } from '../../../common/core/range.js';
17
import { IEditorDecorationsCollection } from '../../../common/editorCommon.js';
18
import { TrackedRangeStickiness } from '../../../common/model.js';
19
import { ModelDecorationOptions } from '../../../common/model/textModel.js';
20
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
21
22
const inlineProgressDecoration = ModelDecorationOptions.register({
23
description: 'inline-progress-widget',
24
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
25
showIfCollapsed: true,
26
after: {
27
content: noBreakWhitespace,
28
inlineClassName: 'inline-editor-progress-decoration',
29
inlineClassNameAffectsLetterSpacing: true,
30
}
31
});
32
33
34
class InlineProgressWidget extends Disposable implements IContentWidget {
35
private static readonly baseId = 'editor.widget.inlineProgressWidget';
36
37
allowEditorOverflow = false;
38
suppressMouseDown = true;
39
40
private domNode!: HTMLElement;
41
42
constructor(
43
private readonly typeId: string,
44
private readonly editor: ICodeEditor,
45
private readonly range: Range,
46
title: string,
47
private readonly delegate: InlineProgressDelegate,
48
) {
49
super();
50
51
this.create(title);
52
53
this.editor.addContentWidget(this);
54
this.editor.layoutContentWidget(this);
55
}
56
57
private create(title: string): void {
58
this.domNode = dom.$('.inline-progress-widget');
59
this.domNode.role = 'button';
60
this.domNode.title = title;
61
62
const iconElement = dom.$('span.icon');
63
this.domNode.append(iconElement);
64
65
iconElement.classList.add(...ThemeIcon.asClassNameArray(Codicon.loading), 'codicon-modifier-spin');
66
67
const updateSize = () => {
68
const lineHeight = this.editor.getOption(EditorOption.lineHeight);
69
this.domNode.style.height = `${lineHeight}px`;
70
this.domNode.style.width = `${Math.ceil(0.8 * lineHeight)}px`;
71
};
72
updateSize();
73
74
this._register(this.editor.onDidChangeConfiguration(c => {
75
if (c.hasChanged(EditorOption.fontSize) || c.hasChanged(EditorOption.lineHeight)) {
76
updateSize();
77
}
78
}));
79
80
this._register(dom.addDisposableListener(this.domNode, dom.EventType.CLICK, e => {
81
this.delegate.cancel();
82
}));
83
}
84
85
getId(): string {
86
return InlineProgressWidget.baseId + '.' + this.typeId;
87
}
88
89
getDomNode(): HTMLElement {
90
return this.domNode;
91
}
92
93
getPosition(): IContentWidgetPosition | null {
94
return {
95
position: { lineNumber: this.range.startLineNumber, column: this.range.startColumn },
96
preference: [ContentWidgetPositionPreference.EXACT]
97
};
98
}
99
100
override dispose(): void {
101
super.dispose();
102
this.editor.removeContentWidget(this);
103
}
104
}
105
106
interface InlineProgressDelegate {
107
cancel(): void;
108
}
109
110
export class InlineProgressManager extends Disposable {
111
112
/** Delay before showing the progress widget */
113
private readonly _showDelay = 500; // ms
114
private readonly _showPromise = this._register(new MutableDisposable());
115
116
private readonly _currentDecorations: IEditorDecorationsCollection;
117
private readonly _currentWidget = this._register(new MutableDisposable<InlineProgressWidget>());
118
119
private _operationIdPool = 0;
120
private _currentOperation?: number;
121
122
constructor(
123
private readonly id: string,
124
private readonly _editor: ICodeEditor,
125
@IInstantiationService private readonly _instantiationService: IInstantiationService,
126
) {
127
super();
128
129
this._currentDecorations = _editor.createDecorationsCollection();
130
}
131
132
public override dispose(): void {
133
super.dispose();
134
this._currentDecorations.clear();
135
}
136
137
public async showWhile<R>(position: IPosition, title: string, promise: Promise<R>, delegate: InlineProgressDelegate, delayOverride?: number): Promise<R> {
138
const operationId = this._operationIdPool++;
139
this._currentOperation = operationId;
140
141
this.clear();
142
143
this._showPromise.value = disposableTimeout(() => {
144
const range = Range.fromPositions(position);
145
const decorationIds = this._currentDecorations.set([{
146
range: range,
147
options: inlineProgressDecoration,
148
}]);
149
150
if (decorationIds.length > 0) {
151
this._currentWidget.value = this._instantiationService.createInstance(InlineProgressWidget, this.id, this._editor, range, title, delegate);
152
}
153
}, delayOverride ?? this._showDelay);
154
155
try {
156
return await promise;
157
} finally {
158
if (this._currentOperation === operationId) {
159
this.clear();
160
this._currentOperation = undefined;
161
}
162
}
163
}
164
165
private clear() {
166
this._showPromise.clear();
167
this._currentDecorations.clear();
168
this._currentWidget.clear();
169
}
170
}
171
172