diff --git a/src/vs/editor/node/languageConfiguration.ts b/src/vs/editor/node/languageConfiguration.ts index eaf20caa26177..02e20dc20da98 100644 --- a/src/vs/editor/node/languageConfiguration.ts +++ b/src/vs/editor/node/languageConfiguration.ts @@ -14,6 +14,7 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo import { Extensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { Registry } from 'vs/platform/platform'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import { MainProcessTextMateSyntax } from 'vs/editor/node/textMate/TMSyntax'; type CharacterPair = [string, string]; @@ -32,31 +33,28 @@ interface ILanguageConfiguration { export class LanguageConfigurationFileHandler { private _modeService: IModeService; + private _done: { [modeId: string]: boolean; }; constructor( + tmSyntax: MainProcessTextMateSyntax, @IModeService modeService: IModeService ) { this._modeService = modeService; + this._done = Object.create(null); - this._handleModes(this._modeService.getRegisteredModes()); - this._modeService.onDidAddModes((modes) => this._handleModes(modes)); + // Listen for hints that a language configuration is needed/usefull and then load it once + this._modeService.onDidCreateMode((mode) => this._loadConfigurationsForMode(mode.getId())); + tmSyntax.onDidEncounterLanguage((language) => this._loadConfigurationsForMode(language)); } - private _handleModes(modes: string[]): void { - modes.forEach(modeId => this._handleMode(modeId)); - } - - private _handleMode(modeId: string): void { - let disposable = this._modeService.onDidCreateMode((mode) => { - if (mode.getId() !== modeId) { - return; - } - - let configurationFiles = this._modeService.getConfigurationFiles(modeId); - configurationFiles.forEach((configFilePath) => this._handleConfigFile(modeId, configFilePath)); + private _loadConfigurationsForMode(modeId: string): void { + if (this._done[modeId]) { + return; + } + this._done[modeId] = true; - disposable.dispose(); - }); + let configurationFiles = this._modeService.getConfigurationFiles(modeId); + configurationFiles.forEach((configFilePath) => this._handleConfigFile(modeId, configFilePath)); } private _handleConfigFile(modeId: string, configFilePath: string): void { diff --git a/src/vs/editor/node/textMate/TMSyntax.ts b/src/vs/editor/node/textMate/TMSyntax.ts index f02db32f4aad8..96f738fb8a6cb 100644 --- a/src/vs/editor/node/textMate/TMSyntax.ts +++ b/src/vs/editor/node/textMate/TMSyntax.ts @@ -8,6 +8,7 @@ import * as nls from 'vs/nls'; import { onUnexpectedError } from 'vs/base/common/errors'; import * as paths from 'vs/base/common/paths'; import * as strings from 'vs/base/common/strings'; +import Event, { Emitter } from 'vs/base/common/event'; import { IExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry'; import { ILineTokens, ITokenizationSupport, TokenizationRegistry } from 'vs/editor/common/modes'; import { TMState } from 'vs/editor/common/modes/TMState'; @@ -24,7 +25,7 @@ export interface ITMSyntaxExtensionPoint { injectTo: string[]; } -// @martin TS(2.0.2) - Type IJsonSchema has no defined property require. Keeping semantic using any cast +// TODO@Martin TS(2.0.2) - Type IJsonSchema has no defined property require. Keeping semantic using any cast let grammarsExtPoint = ExtensionsRegistry.registerExtensionPoint('grammars', { description: nls.localize('vscode.extension.contributes.grammars', 'Contributes textmate tokenizers.'), type: 'array', @@ -61,11 +62,16 @@ export class TMScopeRegistry { private _scopeNameToFilePath: { [scopeName: string]: string; }; private _scopeNameToLanguage: { [scopeName: string]: string; }; + private _encounteredLanguages: { [language: string]: boolean; }; private _cachedScopesRegex: RegExp; + private _onDidEncounterLanguage: Emitter = new Emitter(); + public onDidEncounterLanguage: Event = this._onDidEncounterLanguage.event; + constructor() { this._scopeNameToFilePath = Object.create(null); this._scopeNameToLanguage = Object.create(null); + this._encounteredLanguages = Object.create(null); this._cachedScopesRegex = null; } @@ -111,7 +117,13 @@ export class TMScopeRegistry { return null; } - return this._scopeNameToLanguage[m[1]] || null; + let language = this._scopeNameToLanguage[m[1]] || null; + if (language && !this._encounteredLanguages[language]) { + this._encounteredLanguages[language] = true; + this._onDidEncounterLanguage.fire(language); + } + + return language; } } @@ -121,11 +133,14 @@ export class MainProcessTextMateSyntax { private _scopeRegistry: TMScopeRegistry; private _injections: { [scopeName: string]: string[]; }; + public onDidEncounterLanguage: Event; + constructor( @IModeService modeService: IModeService ) { this._modeService = modeService; this._scopeRegistry = new TMScopeRegistry(); + this.onDidEncounterLanguage = this._scopeRegistry.onDidEncounterLanguage; this._injections = {}; this._grammarRegistry = new Registry({ diff --git a/src/vs/workbench/api/node/extHost.contribution.ts b/src/vs/workbench/api/node/extHost.contribution.ts index 0c08d17a79612..084e049ff3d8e 100644 --- a/src/vs/workbench/api/node/extHost.contribution.ts +++ b/src/vs/workbench/api/node/extHost.contribution.ts @@ -86,10 +86,10 @@ export class ExtHostContribution implements IWorkbenchContribution { col.finish(true, this.threadService); // Other interested parties - create(MainProcessTextMateSyntax); + let tmSyntax = create(MainProcessTextMateSyntax); create(MainProcessTextMateSnippet); create(JSONValidationExtensionPoint); - create(LanguageConfigurationFileHandler); + this.instantiationService.createInstance(LanguageConfigurationFileHandler, tmSyntax); create(MainThreadFileSystemEventService); create(SaveParticipant); }