Path: blob/main/src/vs/editor/contrib/inlineProgress/browser/inlineProgress.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as dom from '../../../../base/browser/dom.js';6import { disposableTimeout } from '../../../../base/common/async.js';7import { Codicon } from '../../../../base/common/codicons.js';8import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js';9import { noBreakWhitespace } from '../../../../base/common/strings.js';10import { ThemeIcon } from '../../../../base/common/themables.js';11import './inlineProgressWidget.css';12import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../browser/editorBrowser.js';13import { EditorOption } from '../../../common/config/editorOptions.js';14import { IPosition } from '../../../common/core/position.js';15import { Range } from '../../../common/core/range.js';16import { IEditorDecorationsCollection } from '../../../common/editorCommon.js';17import { TrackedRangeStickiness } from '../../../common/model.js';18import { ModelDecorationOptions } from '../../../common/model/textModel.js';19import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';2021const inlineProgressDecoration = ModelDecorationOptions.register({22description: 'inline-progress-widget',23stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,24showIfCollapsed: true,25after: {26content: noBreakWhitespace,27inlineClassName: 'inline-editor-progress-decoration',28inlineClassNameAffectsLetterSpacing: true,29}30});313233class InlineProgressWidget extends Disposable implements IContentWidget {34private static readonly baseId = 'editor.widget.inlineProgressWidget';3536allowEditorOverflow = false;37suppressMouseDown = true;3839private domNode!: HTMLElement;4041constructor(42private readonly typeId: string,43private readonly editor: ICodeEditor,44private readonly range: Range,45title: string,46private readonly delegate: InlineProgressDelegate,47) {48super();4950this.create(title);5152this.editor.addContentWidget(this);53this.editor.layoutContentWidget(this);54}5556private create(title: string): void {57this.domNode = dom.$('.inline-progress-widget');58this.domNode.role = 'button';59this.domNode.title = title;6061const iconElement = dom.$('span.icon');62this.domNode.append(iconElement);6364iconElement.classList.add(...ThemeIcon.asClassNameArray(Codicon.loading), 'codicon-modifier-spin');6566const updateSize = () => {67const lineHeight = this.editor.getOption(EditorOption.lineHeight);68this.domNode.style.height = `${lineHeight}px`;69this.domNode.style.width = `${Math.ceil(0.8 * lineHeight)}px`;70};71updateSize();7273this._register(this.editor.onDidChangeConfiguration(c => {74if (c.hasChanged(EditorOption.fontSize) || c.hasChanged(EditorOption.lineHeight)) {75updateSize();76}77}));7879this._register(dom.addDisposableListener(this.domNode, dom.EventType.CLICK, e => {80this.delegate.cancel();81}));82}8384getId(): string {85return InlineProgressWidget.baseId + '.' + this.typeId;86}8788getDomNode(): HTMLElement {89return this.domNode;90}9192getPosition(): IContentWidgetPosition | null {93return {94position: { lineNumber: this.range.startLineNumber, column: this.range.startColumn },95preference: [ContentWidgetPositionPreference.EXACT]96};97}9899override dispose(): void {100super.dispose();101this.editor.removeContentWidget(this);102}103}104105interface InlineProgressDelegate {106cancel(): void;107}108109export class InlineProgressManager extends Disposable {110111/** Delay before showing the progress widget */112private readonly _showDelay = 500; // ms113private readonly _showPromise = this._register(new MutableDisposable());114115private readonly _currentDecorations: IEditorDecorationsCollection;116private readonly _currentWidget = this._register(new MutableDisposable<InlineProgressWidget>());117118private _operationIdPool = 0;119private _currentOperation?: number;120121constructor(122private readonly id: string,123private readonly _editor: ICodeEditor,124@IInstantiationService private readonly _instantiationService: IInstantiationService,125) {126super();127128this._currentDecorations = _editor.createDecorationsCollection();129}130131public override dispose(): void {132super.dispose();133this._currentDecorations.clear();134}135136public async showWhile<R>(position: IPosition, title: string, promise: Promise<R>, delegate: InlineProgressDelegate, delayOverride?: number): Promise<R> {137const operationId = this._operationIdPool++;138this._currentOperation = operationId;139140this.clear();141142this._showPromise.value = disposableTimeout(() => {143const range = Range.fromPositions(position);144const decorationIds = this._currentDecorations.set([{145range: range,146options: inlineProgressDecoration,147}]);148149if (decorationIds.length > 0) {150this._currentWidget.value = this._instantiationService.createInstance(InlineProgressWidget, this.id, this._editor, range, title, delegate);151}152}, delayOverride ?? this._showDelay);153154try {155return await promise;156} finally {157if (this._currentOperation === operationId) {158this.clear();159this._currentOperation = undefined;160}161}162}163164private clear() {165this._showPromise.clear();166this._currentDecorations.clear();167this._currentWidget.clear();168}169}170171172