UNPKG

monaco-editor-core

Version:

A browser based code editor

329 lines (328 loc) • 15.6 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __param = (this && this.__param) || function (paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } }; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; import { OutlineElement, OutlineGroup, OutlineModel } from '../../documentSymbols/browser/outlineModel.js'; import { createCancelablePromise, Delayer } from '../../../../base/common/async.js'; import { FoldingController, RangesLimitReporter } from '../../folding/browser/folding.js'; import { SyntaxRangeProvider } from '../../folding/browser/syntaxRangeProvider.js'; import { IndentRangeProvider } from '../../folding/browser/indentRangeProvider.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; import { onUnexpectedError } from '../../../../base/common/errors.js'; import { StickyElement, StickyModel, StickyRange } from './stickyScrollElement.js'; import { Iterable } from '../../../../base/common/iterator.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; var ModelProvider; (function (ModelProvider) { ModelProvider["OUTLINE_MODEL"] = "outlineModel"; ModelProvider["FOLDING_PROVIDER_MODEL"] = "foldingProviderModel"; ModelProvider["INDENTATION_MODEL"] = "indentationModel"; })(ModelProvider || (ModelProvider = {})); var Status; (function (Status) { Status[Status["VALID"] = 0] = "VALID"; Status[Status["INVALID"] = 1] = "INVALID"; Status[Status["CANCELED"] = 2] = "CANCELED"; })(Status || (Status = {})); let StickyModelProvider = class StickyModelProvider extends Disposable { constructor(_editor, onProviderUpdate, _languageConfigurationService, _languageFeaturesService) { super(); this._editor = _editor; this._modelProviders = []; this._modelPromise = null; this._updateScheduler = this._register(new Delayer(300)); this._updateOperation = this._register(new DisposableStore()); switch (this._editor.getOption(116 /* EditorOption.stickyScroll */).defaultModel) { case ModelProvider.OUTLINE_MODEL: this._modelProviders.push(new StickyModelFromCandidateOutlineProvider(this._editor, _languageFeaturesService)); // fall through case ModelProvider.FOLDING_PROVIDER_MODEL: this._modelProviders.push(new StickyModelFromCandidateSyntaxFoldingProvider(this._editor, onProviderUpdate, _languageFeaturesService)); // fall through case ModelProvider.INDENTATION_MODEL: this._modelProviders.push(new StickyModelFromCandidateIndentationFoldingProvider(this._editor, _languageConfigurationService)); break; } } dispose() { this._modelProviders.forEach(provider => provider.dispose()); this._updateOperation.clear(); this._cancelModelPromise(); super.dispose(); } _cancelModelPromise() { if (this._modelPromise) { this._modelPromise.cancel(); this._modelPromise = null; } } async update(token) { this._updateOperation.clear(); this._updateOperation.add({ dispose: () => { this._cancelModelPromise(); this._updateScheduler.cancel(); } }); this._cancelModelPromise(); return await this._updateScheduler.trigger(async () => { for (const modelProvider of this._modelProviders) { const { statusPromise, modelPromise } = modelProvider.computeStickyModel(token); this._modelPromise = modelPromise; const status = await statusPromise; if (this._modelPromise !== modelPromise) { return null; } switch (status) { case Status.CANCELED: this._updateOperation.clear(); return null; case Status.VALID: return modelProvider.stickyModel; } } return null; }).catch((error) => { onUnexpectedError(error); return null; }); } }; StickyModelProvider = __decorate([ __param(2, IInstantiationService), __param(3, ILanguageFeaturesService) ], StickyModelProvider); export { StickyModelProvider }; class StickyModelCandidateProvider extends Disposable { constructor(_editor) { super(); this._editor = _editor; this._stickyModel = null; } get stickyModel() { return this._stickyModel; } _invalid() { this._stickyModel = null; return Status.INVALID; } computeStickyModel(token) { if (token.isCancellationRequested || !this.isProviderValid()) { return { statusPromise: this._invalid(), modelPromise: null }; } const providerModelPromise = createCancelablePromise(token => this.createModelFromProvider(token)); return { statusPromise: providerModelPromise.then(providerModel => { if (!this.isModelValid(providerModel)) { return this._invalid(); } if (token.isCancellationRequested) { return Status.CANCELED; } this._stickyModel = this.createStickyModel(token, providerModel); return Status.VALID; }).then(undefined, (err) => { onUnexpectedError(err); return Status.CANCELED; }), modelPromise: providerModelPromise }; } /** * Method which checks whether the model returned by the provider is valid and can be used to compute a sticky model. * This method by default returns true. * @param model model returned by the provider * @returns boolean indicating whether the model is valid */ isModelValid(model) { return true; } /** * Method which checks whether the provider is valid before applying it to find the provider model. * This method by default returns true. * @returns boolean indicating whether the provider is valid */ isProviderValid() { return true; } } let StickyModelFromCandidateOutlineProvider = class StickyModelFromCandidateOutlineProvider extends StickyModelCandidateProvider { constructor(_editor, _languageFeaturesService) { super(_editor); this._languageFeaturesService = _languageFeaturesService; } createModelFromProvider(token) { return OutlineModel.create(this._languageFeaturesService.documentSymbolProvider, this._editor.getModel(), token); } createStickyModel(token, model) { const { stickyOutlineElement, providerID } = this._stickyModelFromOutlineModel(model, this._stickyModel?.outlineProviderId); const textModel = this._editor.getModel(); return new StickyModel(textModel.uri, textModel.getVersionId(), stickyOutlineElement, providerID); } isModelValid(model) { return model && model.children.size > 0; } _stickyModelFromOutlineModel(outlineModel, preferredProvider) { let outlineElements; // When several possible outline providers if (Iterable.first(outlineModel.children.values()) instanceof OutlineGroup) { const provider = Iterable.find(outlineModel.children.values(), outlineGroupOfModel => outlineGroupOfModel.id === preferredProvider); if (provider) { outlineElements = provider.children; } else { let tempID = ''; let maxTotalSumOfRanges = -1; let optimalOutlineGroup = undefined; for (const [_key, outlineGroup] of outlineModel.children.entries()) { const totalSumRanges = this._findSumOfRangesOfGroup(outlineGroup); if (totalSumRanges > maxTotalSumOfRanges) { optimalOutlineGroup = outlineGroup; maxTotalSumOfRanges = totalSumRanges; tempID = outlineGroup.id; } } preferredProvider = tempID; outlineElements = optimalOutlineGroup.children; } } else { outlineElements = outlineModel.children; } const stickyChildren = []; const outlineElementsArray = Array.from(outlineElements.values()).sort((element1, element2) => { const range1 = new StickyRange(element1.symbol.range.startLineNumber, element1.symbol.range.endLineNumber); const range2 = new StickyRange(element2.symbol.range.startLineNumber, element2.symbol.range.endLineNumber); return this._comparator(range1, range2); }); for (const outlineElement of outlineElementsArray) { stickyChildren.push(this._stickyModelFromOutlineElement(outlineElement, outlineElement.symbol.selectionRange.startLineNumber)); } const stickyOutlineElement = new StickyElement(undefined, stickyChildren, undefined); return { stickyOutlineElement: stickyOutlineElement, providerID: preferredProvider }; } _stickyModelFromOutlineElement(outlineElement, previousStartLine) { const children = []; for (const child of outlineElement.children.values()) { if (child.symbol.selectionRange.startLineNumber !== child.symbol.range.endLineNumber) { if (child.symbol.selectionRange.startLineNumber !== previousStartLine) { children.push(this._stickyModelFromOutlineElement(child, child.symbol.selectionRange.startLineNumber)); } else { for (const subchild of child.children.values()) { children.push(this._stickyModelFromOutlineElement(subchild, child.symbol.selectionRange.startLineNumber)); } } } } children.sort((child1, child2) => this._comparator(child1.range, child2.range)); const range = new StickyRange(outlineElement.symbol.selectionRange.startLineNumber, outlineElement.symbol.range.endLineNumber); return new StickyElement(range, children, undefined); } _comparator(range1, range2) { if (range1.startLineNumber !== range2.startLineNumber) { return range1.startLineNumber - range2.startLineNumber; } else { return range2.endLineNumber - range1.endLineNumber; } } _findSumOfRangesOfGroup(outline) { let res = 0; for (const child of outline.children.values()) { res += this._findSumOfRangesOfGroup(child); } if (outline instanceof OutlineElement) { return res + outline.symbol.range.endLineNumber - outline.symbol.selectionRange.startLineNumber; } else { return res; } } }; StickyModelFromCandidateOutlineProvider = __decorate([ __param(1, ILanguageFeaturesService) ], StickyModelFromCandidateOutlineProvider); class StickyModelFromCandidateFoldingProvider extends StickyModelCandidateProvider { constructor(editor) { super(editor); this._foldingLimitReporter = new RangesLimitReporter(editor); } createStickyModel(token, model) { const foldingElement = this._fromFoldingRegions(model); const textModel = this._editor.getModel(); return new StickyModel(textModel.uri, textModel.getVersionId(), foldingElement, undefined); } isModelValid(model) { return model !== null; } _fromFoldingRegions(foldingRegions) { const length = foldingRegions.length; const orderedStickyElements = []; // The root sticky outline element const stickyOutlineElement = new StickyElement(undefined, [], undefined); for (let i = 0; i < length; i++) { // Finding the parent index of the current range const parentIndex = foldingRegions.getParentIndex(i); let parentNode; if (parentIndex !== -1) { // Access the reference of the parent node parentNode = orderedStickyElements[parentIndex]; } else { // In that case the parent node is the root node parentNode = stickyOutlineElement; } const child = new StickyElement(new StickyRange(foldingRegions.getStartLineNumber(i), foldingRegions.getEndLineNumber(i) + 1), [], parentNode); parentNode.children.push(child); orderedStickyElements.push(child); } return stickyOutlineElement; } } let StickyModelFromCandidateIndentationFoldingProvider = class StickyModelFromCandidateIndentationFoldingProvider extends StickyModelFromCandidateFoldingProvider { constructor(editor, _languageConfigurationService) { super(editor); this._languageConfigurationService = _languageConfigurationService; this.provider = this._register(new IndentRangeProvider(editor.getModel(), this._languageConfigurationService, this._foldingLimitReporter)); } async createModelFromProvider(token) { return this.provider.compute(token); } }; StickyModelFromCandidateIndentationFoldingProvider = __decorate([ __param(1, ILanguageConfigurationService) ], StickyModelFromCandidateIndentationFoldingProvider); let StickyModelFromCandidateSyntaxFoldingProvider = class StickyModelFromCandidateSyntaxFoldingProvider extends StickyModelFromCandidateFoldingProvider { constructor(editor, onProviderUpdate, _languageFeaturesService) { super(editor); this._languageFeaturesService = _languageFeaturesService; const selectedProviders = FoldingController.getFoldingRangeProviders(this._languageFeaturesService, editor.getModel()); if (selectedProviders.length > 0) { this.provider = this._register(new SyntaxRangeProvider(editor.getModel(), selectedProviders, onProviderUpdate, this._foldingLimitReporter, undefined)); } } isProviderValid() { return this.provider !== undefined; } async createModelFromProvider(token) { return this.provider?.compute(token) ?? null; } }; StickyModelFromCandidateSyntaxFoldingProvider = __decorate([ __param(2, ILanguageFeaturesService) ], StickyModelFromCandidateSyntaxFoldingProvider);