UNPKG

monaco-editor-core

Version:

A browser based code editor

178 lines (177 loc) 6.17 kB
/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Emitter } from '../../base/common/event.js'; import { toDisposable } from '../../base/common/lifecycle.js'; import { shouldSynchronizeModel } from './model.js'; import { score } from './languageSelector.js'; function isExclusive(selector) { if (typeof selector === 'string') { return false; } else if (Array.isArray(selector)) { return selector.every(isExclusive); } else { return !!selector.exclusive; // TODO: microsoft/TypeScript#42768 } } class MatchCandidate { constructor(uri, languageId, notebookUri, notebookType, recursive) { this.uri = uri; this.languageId = languageId; this.notebookUri = notebookUri; this.notebookType = notebookType; this.recursive = recursive; } equals(other) { return this.notebookType === other.notebookType && this.languageId === other.languageId && this.uri.toString() === other.uri.toString() && this.notebookUri?.toString() === other.notebookUri?.toString() && this.recursive === other.recursive; } } export class LanguageFeatureRegistry { constructor(_notebookInfoResolver) { this._notebookInfoResolver = _notebookInfoResolver; this._clock = 0; this._entries = []; this._onDidChange = new Emitter(); this.onDidChange = this._onDidChange.event; } register(selector, provider) { let entry = { selector, provider, _score: -1, _time: this._clock++ }; this._entries.push(entry); this._lastCandidate = undefined; this._onDidChange.fire(this._entries.length); return toDisposable(() => { if (entry) { const idx = this._entries.indexOf(entry); if (idx >= 0) { this._entries.splice(idx, 1); this._lastCandidate = undefined; this._onDidChange.fire(this._entries.length); entry = undefined; } } }); } has(model) { return this.all(model).length > 0; } all(model) { if (!model) { return []; } this._updateScores(model, false); const result = []; // from registry for (const entry of this._entries) { if (entry._score > 0) { result.push(entry.provider); } } return result; } ordered(model, recursive = false) { const result = []; this._orderedForEach(model, recursive, entry => result.push(entry.provider)); return result; } orderedGroups(model) { const result = []; let lastBucket; let lastBucketScore; this._orderedForEach(model, false, entry => { if (lastBucket && lastBucketScore === entry._score) { lastBucket.push(entry.provider); } else { lastBucketScore = entry._score; lastBucket = [entry.provider]; result.push(lastBucket); } }); return result; } _orderedForEach(model, recursive, callback) { this._updateScores(model, recursive); for (const entry of this._entries) { if (entry._score > 0) { callback(entry); } } } _updateScores(model, recursive) { const notebookInfo = this._notebookInfoResolver?.(model.uri); // use the uri (scheme, pattern) of the notebook info iff we have one // otherwise it's the model's/document's uri const candidate = notebookInfo ? new MatchCandidate(model.uri, model.getLanguageId(), notebookInfo.uri, notebookInfo.type, recursive) : new MatchCandidate(model.uri, model.getLanguageId(), undefined, undefined, recursive); if (this._lastCandidate?.equals(candidate)) { // nothing has changed return; } this._lastCandidate = candidate; for (const entry of this._entries) { entry._score = score(entry.selector, candidate.uri, candidate.languageId, shouldSynchronizeModel(model), candidate.notebookUri, candidate.notebookType); if (isExclusive(entry.selector) && entry._score > 0) { if (recursive) { entry._score = 0; } else { // support for one exclusive selector that overwrites // any other selector for (const entry of this._entries) { entry._score = 0; } entry._score = 1000; break; } } } // needs sorting this._entries.sort(LanguageFeatureRegistry._compareByScoreAndTime); } static _compareByScoreAndTime(a, b) { if (a._score < b._score) { return 1; } else if (a._score > b._score) { return -1; } // De-prioritize built-in providers if (isBuiltinSelector(a.selector) && !isBuiltinSelector(b.selector)) { return 1; } else if (!isBuiltinSelector(a.selector) && isBuiltinSelector(b.selector)) { return -1; } if (a._time < b._time) { return 1; } else if (a._time > b._time) { return -1; } else { return 0; } } } function isBuiltinSelector(selector) { if (typeof selector === 'string') { return false; } if (Array.isArray(selector)) { return selector.some(isBuiltinSelector); } return Boolean(selector.isBuiltin); }