UNPKG

monaco-editor-core

Version:

A browser based code editor

237 lines (236 loc) • 9.68 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 { Disposable } from '../../../base/common/lifecycle.js'; import { regExpLeadsToEndlessLoop } from '../../../base/common/strings.js'; import { clearPlatformLanguageAssociations, getLanguageIds, registerPlatformLanguageAssociation } from './languagesAssociations.js'; import { ModesRegistry, PLAINTEXT_LANGUAGE_ID } from '../languages/modesRegistry.js'; import { Extensions } from '../../../platform/configuration/common/configurationRegistry.js'; import { Registry } from '../../../platform/registry/common/platform.js'; const hasOwnProperty = Object.prototype.hasOwnProperty; const NULL_LANGUAGE_ID = 'vs.editor.nullLanguage'; export class LanguageIdCodec { constructor() { this._languageIdToLanguage = []; this._languageToLanguageId = new Map(); this._register(NULL_LANGUAGE_ID, 0 /* LanguageId.Null */); this._register(PLAINTEXT_LANGUAGE_ID, 1 /* LanguageId.PlainText */); this._nextLanguageId = 2; } _register(language, languageId) { this._languageIdToLanguage[languageId] = language; this._languageToLanguageId.set(language, languageId); } register(language) { if (this._languageToLanguageId.has(language)) { return; } const languageId = this._nextLanguageId++; this._register(language, languageId); } encodeLanguageId(languageId) { return this._languageToLanguageId.get(languageId) || 0 /* LanguageId.Null */; } decodeLanguageId(languageId) { return this._languageIdToLanguage[languageId] || NULL_LANGUAGE_ID; } } export class LanguagesRegistry extends Disposable { static { this.instanceCount = 0; } constructor(useModesRegistry = true, warnOnOverwrite = false) { super(); this._onDidChange = this._register(new Emitter()); this.onDidChange = this._onDidChange.event; LanguagesRegistry.instanceCount++; this._warnOnOverwrite = warnOnOverwrite; this.languageIdCodec = new LanguageIdCodec(); this._dynamicLanguages = []; this._languages = {}; this._mimeTypesMap = {}; this._nameMap = {}; this._lowercaseNameMap = {}; if (useModesRegistry) { this._initializeFromRegistry(); this._register(ModesRegistry.onDidChangeLanguages((m) => { this._initializeFromRegistry(); })); } } dispose() { LanguagesRegistry.instanceCount--; super.dispose(); } _initializeFromRegistry() { this._languages = {}; this._mimeTypesMap = {}; this._nameMap = {}; this._lowercaseNameMap = {}; clearPlatformLanguageAssociations(); const desc = [].concat(ModesRegistry.getLanguages()).concat(this._dynamicLanguages); this._registerLanguages(desc); } _registerLanguages(desc) { for (const d of desc) { this._registerLanguage(d); } // Rebuild fast path maps this._mimeTypesMap = {}; this._nameMap = {}; this._lowercaseNameMap = {}; Object.keys(this._languages).forEach((langId) => { const language = this._languages[langId]; if (language.name) { this._nameMap[language.name] = language.identifier; } language.aliases.forEach((alias) => { this._lowercaseNameMap[alias.toLowerCase()] = language.identifier; }); language.mimetypes.forEach((mimetype) => { this._mimeTypesMap[mimetype] = language.identifier; }); }); Registry.as(Extensions.Configuration).registerOverrideIdentifiers(this.getRegisteredLanguageIds()); this._onDidChange.fire(); } _registerLanguage(lang) { const langId = lang.id; let resolvedLanguage; if (hasOwnProperty.call(this._languages, langId)) { resolvedLanguage = this._languages[langId]; } else { this.languageIdCodec.register(langId); resolvedLanguage = { identifier: langId, name: null, mimetypes: [], aliases: [], extensions: [], filenames: [], configurationFiles: [], icons: [] }; this._languages[langId] = resolvedLanguage; } this._mergeLanguage(resolvedLanguage, lang); } _mergeLanguage(resolvedLanguage, lang) { const langId = lang.id; let primaryMime = null; if (Array.isArray(lang.mimetypes) && lang.mimetypes.length > 0) { resolvedLanguage.mimetypes.push(...lang.mimetypes); primaryMime = lang.mimetypes[0]; } if (!primaryMime) { primaryMime = `text/x-${langId}`; resolvedLanguage.mimetypes.push(primaryMime); } if (Array.isArray(lang.extensions)) { if (lang.configuration) { // insert first as this appears to be the 'primary' language definition resolvedLanguage.extensions = lang.extensions.concat(resolvedLanguage.extensions); } else { resolvedLanguage.extensions = resolvedLanguage.extensions.concat(lang.extensions); } for (const extension of lang.extensions) { registerPlatformLanguageAssociation({ id: langId, mime: primaryMime, extension: extension }, this._warnOnOverwrite); } } if (Array.isArray(lang.filenames)) { for (const filename of lang.filenames) { registerPlatformLanguageAssociation({ id: langId, mime: primaryMime, filename: filename }, this._warnOnOverwrite); resolvedLanguage.filenames.push(filename); } } if (Array.isArray(lang.filenamePatterns)) { for (const filenamePattern of lang.filenamePatterns) { registerPlatformLanguageAssociation({ id: langId, mime: primaryMime, filepattern: filenamePattern }, this._warnOnOverwrite); } } if (typeof lang.firstLine === 'string' && lang.firstLine.length > 0) { let firstLineRegexStr = lang.firstLine; if (firstLineRegexStr.charAt(0) !== '^') { firstLineRegexStr = '^' + firstLineRegexStr; } try { const firstLineRegex = new RegExp(firstLineRegexStr); if (!regExpLeadsToEndlessLoop(firstLineRegex)) { registerPlatformLanguageAssociation({ id: langId, mime: primaryMime, firstline: firstLineRegex }, this._warnOnOverwrite); } } catch (err) { // Most likely, the regex was bad console.warn(`[${lang.id}]: Invalid regular expression \`${firstLineRegexStr}\`: `, err); } } resolvedLanguage.aliases.push(langId); let langAliases = null; if (typeof lang.aliases !== 'undefined' && Array.isArray(lang.aliases)) { if (lang.aliases.length === 0) { // signal that this language should not get a name langAliases = [null]; } else { langAliases = lang.aliases; } } if (langAliases !== null) { for (const langAlias of langAliases) { if (!langAlias || langAlias.length === 0) { continue; } resolvedLanguage.aliases.push(langAlias); } } const containsAliases = (langAliases !== null && langAliases.length > 0); if (containsAliases && langAliases[0] === null) { // signal that this language should not get a name } else { const bestName = (containsAliases ? langAliases[0] : null) || langId; if (containsAliases || !resolvedLanguage.name) { resolvedLanguage.name = bestName; } } if (lang.configuration) { resolvedLanguage.configurationFiles.push(lang.configuration); } if (lang.icon) { resolvedLanguage.icons.push(lang.icon); } } isRegisteredLanguageId(languageId) { if (!languageId) { return false; } return hasOwnProperty.call(this._languages, languageId); } getRegisteredLanguageIds() { return Object.keys(this._languages); } getLanguageIdByLanguageName(languageName) { const languageNameLower = languageName.toLowerCase(); if (!hasOwnProperty.call(this._lowercaseNameMap, languageNameLower)) { return null; } return this._lowercaseNameMap[languageNameLower]; } getLanguageIdByMimeType(mimeType) { if (!mimeType) { return null; } if (hasOwnProperty.call(this._mimeTypesMap, mimeType)) { return this._mimeTypesMap[mimeType]; } return null; } guessLanguageIdByFilepathOrFirstLine(resource, firstLine) { if (!resource && !firstLine) { return []; } return getLanguageIds(resource, firstLine); } }