Path: blob/main/src/vs/editor/common/services/languageService.ts
3294 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { Emitter, Event } from '../../../base/common/event.js';6import { Disposable, IDisposable } from '../../../base/common/lifecycle.js';7import { URI } from '../../../base/common/uri.js';8import { LanguagesRegistry } from './languagesRegistry.js';9import { ILanguageNameIdPair, ILanguageSelection, ILanguageService, ILanguageIcon, ILanguageExtensionPoint } from '../languages/language.js';10import { ILanguageIdCodec, TokenizationRegistry } from '../languages.js';11import { PLAINTEXT_LANGUAGE_ID } from '../languages/modesRegistry.js';12import { IObservable, observableFromEvent } from '../../../base/common/observable.js';1314export class LanguageService extends Disposable implements ILanguageService {15public _serviceBrand: undefined;1617static instanceCount = 0;1819private readonly _onDidRequestBasicLanguageFeatures = this._register(new Emitter<string>());20public readonly onDidRequestBasicLanguageFeatures = this._onDidRequestBasicLanguageFeatures.event;2122private readonly _onDidRequestRichLanguageFeatures = this._register(new Emitter<string>());23public readonly onDidRequestRichLanguageFeatures = this._onDidRequestRichLanguageFeatures.event;2425protected readonly _onDidChange = this._register(new Emitter<void>({ leakWarningThreshold: 200 /* https://github.com/microsoft/vscode/issues/119968 */ }));26public readonly onDidChange: Event<void> = this._onDidChange.event;2728private readonly _requestedBasicLanguages = new Set<string>();29private readonly _requestedRichLanguages = new Set<string>();3031protected readonly _registry: LanguagesRegistry;32public readonly languageIdCodec: ILanguageIdCodec;3334constructor(warnOnOverwrite = false) {35super();36LanguageService.instanceCount++;37this._registry = this._register(new LanguagesRegistry(true, warnOnOverwrite));38this.languageIdCodec = this._registry.languageIdCodec;39this._register(this._registry.onDidChange(() => this._onDidChange.fire()));40}4142public override dispose(): void {43LanguageService.instanceCount--;44super.dispose();45}4647public registerLanguage(def: ILanguageExtensionPoint): IDisposable {48return this._registry.registerLanguage(def);49}5051public isRegisteredLanguageId(languageId: string | null | undefined): boolean {52return this._registry.isRegisteredLanguageId(languageId);53}5455public getRegisteredLanguageIds(): string[] {56return this._registry.getRegisteredLanguageIds();57}5859public getSortedRegisteredLanguageNames(): ILanguageNameIdPair[] {60return this._registry.getSortedRegisteredLanguageNames();61}6263public getLanguageName(languageId: string): string | null {64return this._registry.getLanguageName(languageId);65}6667public getMimeType(languageId: string): string | null {68return this._registry.getMimeType(languageId);69}7071public getIcon(languageId: string): ILanguageIcon | null {72return this._registry.getIcon(languageId);73}7475public getExtensions(languageId: string): ReadonlyArray<string> {76return this._registry.getExtensions(languageId);77}7879public getFilenames(languageId: string): ReadonlyArray<string> {80return this._registry.getFilenames(languageId);81}8283public getConfigurationFiles(languageId: string): ReadonlyArray<URI> {84return this._registry.getConfigurationFiles(languageId);85}8687public getLanguageIdByLanguageName(languageName: string): string | null {88return this._registry.getLanguageIdByLanguageName(languageName);89}9091public getLanguageIdByMimeType(mimeType: string | null | undefined): string | null {92return this._registry.getLanguageIdByMimeType(mimeType);93}9495public guessLanguageIdByFilepathOrFirstLine(resource: URI | null, firstLine?: string): string | null {96const languageIds = this._registry.guessLanguageIdByFilepathOrFirstLine(resource, firstLine);97return languageIds.at(0) ?? null;98}99100public createById(languageId: string | null | undefined): ILanguageSelection {101return new LanguageSelection(this.onDidChange, () => {102return this._createAndGetLanguageIdentifier(languageId);103});104}105106public createByMimeType(mimeType: string | null | undefined): ILanguageSelection {107return new LanguageSelection(this.onDidChange, () => {108const languageId = this.getLanguageIdByMimeType(mimeType);109return this._createAndGetLanguageIdentifier(languageId);110});111}112113public createByFilepathOrFirstLine(resource: URI | null, firstLine?: string): ILanguageSelection {114return new LanguageSelection(this.onDidChange, () => {115const languageId = this.guessLanguageIdByFilepathOrFirstLine(resource, firstLine);116return this._createAndGetLanguageIdentifier(languageId);117});118}119120private _createAndGetLanguageIdentifier(languageId: string | null | undefined): string {121if (!languageId || !this.isRegisteredLanguageId(languageId)) {122// Fall back to plain text if language is unknown123languageId = PLAINTEXT_LANGUAGE_ID;124}125126return languageId;127}128129public requestBasicLanguageFeatures(languageId: string): void {130if (!this._requestedBasicLanguages.has(languageId)) {131this._requestedBasicLanguages.add(languageId);132this._onDidRequestBasicLanguageFeatures.fire(languageId);133}134}135136public requestRichLanguageFeatures(languageId: string): void {137if (!this._requestedRichLanguages.has(languageId)) {138this._requestedRichLanguages.add(languageId);139140// Ensure basic features are requested141this.requestBasicLanguageFeatures(languageId);142143// Ensure tokenizers are created144TokenizationRegistry.getOrCreate(languageId);145146this._onDidRequestRichLanguageFeatures.fire(languageId);147}148}149}150151class LanguageSelection implements ILanguageSelection {152private readonly _value: IObservable<string>;153public readonly onDidChange: Event<string>;154155constructor(onDidChangeLanguages: Event<void>, selector: () => string) {156this._value = observableFromEvent(this, onDidChangeLanguages, () => selector());157this.onDidChange = Event.fromObservable(this._value);158}159160public get languageId(): string {161return this._value.get();162}163}164165166