123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195 |
- /*---------------------------------------------------------------------------------------------
- * 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 { doHash } from '../../../base/common/hash.js';
- import { toDisposable } from '../../../base/common/lifecycle.js';
- import { LRUCache } from '../../../base/common/map.js';
- import { MovingAverage } from '../../../base/common/numbers.js';
- import { score } from './languageSelector.js';
- import { shouldSynchronizeModel } from '../services/modelService.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
- }
- }
- export class LanguageFeatureRegistry {
- constructor() {
- this._clock = 0;
- this._entries = [];
- this._onDidChange = new Emitter();
- }
- get onDidChange() {
- return 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) {
- let 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);
- const result = [];
- // from registry
- for (let entry of this._entries) {
- if (entry._score > 0) {
- result.push(entry.provider);
- }
- }
- return result;
- }
- ordered(model) {
- const result = [];
- this._orderedForEach(model, entry => result.push(entry.provider));
- return result;
- }
- orderedGroups(model) {
- const result = [];
- let lastBucket;
- let lastBucketScore;
- this._orderedForEach(model, entry => {
- if (lastBucket && lastBucketScore === entry._score) {
- lastBucket.push(entry.provider);
- }
- else {
- lastBucketScore = entry._score;
- lastBucket = [entry.provider];
- result.push(lastBucket);
- }
- });
- return result;
- }
- _orderedForEach(model, callback) {
- if (!model) {
- return;
- }
- this._updateScores(model);
- for (const entry of this._entries) {
- if (entry._score > 0) {
- callback(entry);
- }
- }
- }
- _updateScores(model) {
- let candidate = {
- uri: model.uri.toString(),
- language: model.getLanguageId()
- };
- if (this._lastCandidate
- && this._lastCandidate.language === candidate.language
- && this._lastCandidate.uri === candidate.uri) {
- // nothing has changed
- return;
- }
- this._lastCandidate = candidate;
- for (let entry of this._entries) {
- entry._score = score(entry.selector, model.uri, model.getLanguageId(), shouldSynchronizeModel(model));
- if (isExclusive(entry.selector) && entry._score > 0) {
- // support for one exclusive selector that overwrites
- // any other selector
- for (let 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;
- }
- else if (a._time < b._time) {
- return 1;
- }
- else if (a._time > b._time) {
- return -1;
- }
- else {
- return 0;
- }
- }
- }
- const _hashes = new WeakMap();
- let pool = 0;
- function weakHash(obj) {
- let value = _hashes.get(obj);
- if (value === undefined) {
- value = ++pool;
- _hashes.set(obj, value);
- }
- return value;
- }
- /**
- * Keeps moving average per model and set of providers so that requests
- * can be debounce according to the provider performance
- */
- export class LanguageFeatureRequestDelays {
- constructor(_registry, min, max = Number.MAX_SAFE_INTEGER) {
- this._registry = _registry;
- this.min = min;
- this.max = max;
- this._cache = new LRUCache(50, 0.7);
- }
- _key(model) {
- return model.id + this._registry.all(model).reduce((hashVal, obj) => doHash(weakHash(obj), hashVal), 0);
- }
- _clamp(value) {
- if (value === undefined) {
- return this.min;
- }
- else {
- return Math.min(this.max, Math.max(this.min, Math.floor(value * 1.3)));
- }
- }
- get(model) {
- const key = this._key(model);
- const avg = this._cache.get(key);
- return this._clamp(avg === null || avg === void 0 ? void 0 : avg.value);
- }
- update(model, value) {
- const key = this._key(model);
- let avg = this._cache.get(key);
- if (!avg) {
- avg = new MovingAverage();
- this._cache.set(key, avg);
- }
- avg.update(value);
- return this.get(model);
- }
- }
|