diff --git a/src/vs/editor/common/modes/languageFeatureRegistry.ts b/src/vs/editor/common/modes/languageFeatureRegistry.ts index 2ffe037a5ba..c5e4e7010d8 100644 --- a/src/vs/editor/common/modes/languageFeatureRegistry.ts +++ b/src/vs/editor/common/modes/languageFeatureRegistry.ts @@ -18,6 +18,16 @@ interface Entry { _time: number; } +function isExclusive(selector: LanguageSelector): boolean { + if (typeof selector === 'string') { + return false; + } else if (Array.isArray(selector)) { + return selector.every(isExclusive); + } else { + return selector.exclusive; + } +} + export default class LanguageFeatureRegistry { private _clock: number = 0; @@ -142,6 +152,16 @@ export default class LanguageFeatureRegistry { for (let entry of this._entries) { entry._score = score(entry.selector, model.uri, model.getLanguageIdentifier().language, 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 diff --git a/src/vs/editor/common/modes/languageSelector.ts b/src/vs/editor/common/modes/languageSelector.ts index a5bac2ab4fb..513ca2cc2d6 100644 --- a/src/vs/editor/common/modes/languageSelector.ts +++ b/src/vs/editor/common/modes/languageSelector.ts @@ -16,6 +16,7 @@ export interface LanguageFilter { * This provider is implemented in the UI thread. */ hasAccessToAllModels?: boolean; + exclusive?: boolean; } export type LanguageSelector = string | LanguageFilter | (string | LanguageFilter)[]; @@ -43,8 +44,8 @@ export function score(selector: LanguageSelector, candidateUri: URI, candidateLa } // short-hand notion, desugars to - // 'fooLang' -> [{ language: 'fooLang', scheme: 'file' }, { language: 'fooLang', scheme: 'untitled' }] - // '*' -> { language: '*', scheme: '*' } + // 'fooLang' -> { language: 'fooLang'} + // '*' -> { language: '*' } if (selector === '*') { return 5; } else if (selector === candidateLanguage) { diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 17f98c7bf01..10bc6631678 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -488,4 +488,12 @@ declare module 'vscode' { } //#endregion + + //#region Joh -> exclusive document filters + + export interface DocumentFilter { + exclusive?: boolean; + } + + //#endregion } diff --git a/src/vs/workbench/api/node/extHost.api.impl.ts b/src/vs/workbench/api/node/extHost.api.impl.ts index d35d3e69609..0c2c16c0ea9 100644 --- a/src/vs/workbench/api/node/extHost.api.impl.ts +++ b/src/vs/workbench/api/node/extHost.api.impl.ts @@ -147,18 +147,23 @@ export function createApiFactory( // we cannot say if the extension is doing it right or wrong... let checkSelector = (function () { let done = initData.environment.extensionDevelopmentPath !== extension.extensionFolderPath; - function inform(selector: vscode.DocumentSelector) { - console.info(`Extension '${extension.id}' uses a document selector without scheme. Learn more about this: https://go.microsoft.com/fwlink/?linkid=872305`); - done = true; + function informOnce(selector: vscode.DocumentSelector) { + if (!done) { + console.info(`Extension '${extension.id}' uses a document selector without scheme. Learn more about this: https://go.microsoft.com/fwlink/?linkid=872305`); + done = true; + } } return function perform(selector: vscode.DocumentSelector): vscode.DocumentSelector { - if (!done) { - if (Array.isArray(selector)) { - selector.forEach(perform); - } else if (typeof selector === 'string') { - inform(selector); - } else if (typeof selector.scheme === 'undefined') { - inform(selector); + if (Array.isArray(selector)) { + selector.forEach(perform); + } else if (typeof selector === 'string') { + informOnce(selector); + } else { + if (typeof selector.scheme === 'undefined') { + informOnce(selector); + } + if (!extension.enableProposedApi && typeof selector.exclusive === 'boolean') { + throwProposedApiError(extension); } } return selector; diff --git a/src/vs/workbench/api/node/extHost.protocol.ts b/src/vs/workbench/api/node/extHost.protocol.ts index 19423ae8218..ed29c4320ed 100644 --- a/src/vs/workbench/api/node/extHost.protocol.ts +++ b/src/vs/workbench/api/node/extHost.protocol.ts @@ -258,6 +258,7 @@ export interface ISerializedDocumentFilter { language?: string; scheme?: string; pattern?: vscode.GlobPattern; + exclusive?: boolean; } export interface MainThreadLanguageFeaturesShape extends IDisposable { diff --git a/src/vs/workbench/api/node/extHostLanguageFeatures.ts b/src/vs/workbench/api/node/extHostLanguageFeatures.ts index a5032c4686c..4c22a34d548 100644 --- a/src/vs/workbench/api/node/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/node/extHostLanguageFeatures.ts @@ -887,7 +887,8 @@ export class ExtHostLanguageFeatures implements ExtHostLanguageFeaturesShape { $serialized: true, language: selector.language, scheme: this._transformScheme(selector.scheme), - pattern: selector.pattern + pattern: selector.pattern, + exclusive: selector.exclusive }; } diff --git a/src/vs/workbench/api/node/extHostTypeConverters.ts b/src/vs/workbench/api/node/extHostTypeConverters.ts index 149733b823a..c8aec09c9de 100644 --- a/src/vs/workbench/api/node/extHostTypeConverters.ts +++ b/src/vs/workbench/api/node/extHostTypeConverters.ts @@ -696,7 +696,8 @@ function doToLanguageSelector(selector: string | vscode.DocumentFilter): string return { language: selector.language, scheme: selector.scheme, - pattern: toGlobPattern(selector.pattern) + pattern: toGlobPattern(selector.pattern), + exclusive: selector.exclusive }; }