
import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import { localize } from '../../../../nls.js';
import { clearConfiguredLanguageAssociations, registerConfiguredLanguageAssociation } from '../../../../editor/common/services/languagesAssociations.js';
import { joinPath } from '../../../../base/common/resources.js';
import '../../../../platform/instantiation/common/instantiation.js';
import { LanguageService } from '../../../../editor/common/services/languageService.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.service.js';
import { IEnvironmentService } from '../../../../platform/environment/common/environment.service.js';
import { FILES_ASSOCIATIONS_CONFIG } from '../../../../platform/files/common/files.js';
import { IExtensionService } from '../../extensions/common/extensions.service.js';
import { ExtensionsRegistry } from '../../extensions/common/extensionsRegistry.js';
import '../../../../platform/instantiation/common/extensions.js';
import { ILogService } from '../../../../platform/log/common/log.service.js';
import { Disposable } from '../../../../base/common/lifecycle.js';
import { Extensions } from '../../extensionManagement/common/extensionFeatures.js';
import { Registry } from '../../../../platform/registry/common/platform.js';
import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js';
import { index } from '../../../../base/common/arrays.js';
import { MarkdownString } from '../../../../base/common/htmlContent.js';
import { isString } from '../../../../base/common/types.js';

const languagesExtPoint = ExtensionsRegistry.registerExtensionPoint({
    extensionPoint: "languages",
    jsonSchema: {
        description: ( localize(16157, "Contributes language declarations.")),
        type: "array",
        items: {
            type: "object",
            defaultSnippets: [{
                body: {
                    id: "${1:languageId}",
                    aliases: ["${2:label}"],
                    extensions: ["${3:extension}"],
                    configuration: "./language-configuration.json"
                }
            }],
            properties: {
                id: {
                    description: ( localize(16158, "ID of the language.")),
                    type: "string"
                },
                aliases: {
                    description: ( localize(16159, "Name aliases for the language.")),
                    type: "array",
                    items: {
                        type: "string"
                    }
                },
                extensions: {
                    description: ( localize(16160, "File extensions associated to the language.")),
                    default: [".foo"],
                    type: "array",
                    items: {
                        type: "string"
                    }
                },
                filenames: {
                    description: ( localize(16161, "File names associated to the language.")),
                    type: "array",
                    items: {
                        type: "string"
                    }
                },
                filenamePatterns: {
                    description: ( localize(16162, "File name glob patterns associated to the language.")),
                    type: "array",
                    items: {
                        type: "string"
                    }
                },
                mimetypes: {
                    description: ( localize(16163, "Mime types associated to the language.")),
                    type: "array",
                    items: {
                        type: "string"
                    }
                },
                firstLine: {
                    description: ( localize(
                        16164,
                        "A regular expression matching the first line of a file of the language."
                    )),
                    type: "string"
                },
                configuration: {
                    description: ( localize(
                        16165,
                        "A relative path to a file containing configuration options for the language."
                    )),
                    type: "string",
                    default: "./language-configuration.json"
                },
                icon: {
                    type: "object",
                    description: ( localize(
                        16166,
                        "A icon to use as file icon, if no icon theme provides one for the language."
                    )),
                    properties: {
                        light: {
                            description: ( localize(16167, "Icon path when a light theme is used")),
                            type: "string"
                        },
                        dark: {
                            description: ( localize(16168, "Icon path when a dark theme is used")),
                            type: "string"
                        }
                    }
                }
            }
        }
    },
    activationEventsGenerator: function*(languageContributions) {
        for (const languageContribution of languageContributions) {
            if (languageContribution.id && languageContribution.configuration) {
                yield `onLanguage:${languageContribution.id}`;
            }
        }
    }
});
class LanguageTableRenderer extends Disposable {
    constructor() {
        super(...arguments);
        this.type = "table";
    }
    shouldRender(manifest) {
        return !!manifest.contributes?.languages;
    }
    render(manifest) {
        const contributes = manifest.contributes;
        const rawLanguages = contributes?.languages || [];
        const languages = [];
        for (const l of rawLanguages) {
            if (isValidLanguageExtensionPoint(l)) {
                languages.push({
                    id: l.id,
                    name: (l.aliases || [])[0] || l.id,
                    extensions: l.extensions || [],
                    hasGrammar: false,
                    hasSnippets: false
                });
            }
        }
        const byId = index(languages, l => l.id);
        const grammars = contributes?.grammars || [];
        grammars.forEach(grammar => {
            if (!isString(grammar.language)) {
                return;
            }
            let language = byId[grammar.language];
            if (language) {
                language.hasGrammar = true;
            } else {
                language = {
                    id: grammar.language,
                    name: grammar.language,
                    extensions: [],
                    hasGrammar: true,
                    hasSnippets: false
                };
                byId[language.id] = language;
                languages.push(language);
            }
        });
        const snippets = contributes?.snippets || [];
        snippets.forEach(snippet => {
            if (!isString(snippet.language)) {
                return;
            }
            let language = byId[snippet.language];
            if (language) {
                language.hasSnippets = true;
            } else {
                language = {
                    id: snippet.language,
                    name: snippet.language,
                    extensions: [],
                    hasGrammar: false,
                    hasSnippets: true
                };
                byId[language.id] = language;
                languages.push(language);
            }
        });
        if (!languages.length) {
            return {
                data: {
                    headers: [],
                    rows: []
                },
                dispose: () => {}
            };
        }
        const headers = [( localize(16169, "ID")), ( localize(16170, "Name")), ( localize(16171, "File Extensions")), ( localize(16172, "Grammar")), ( localize(16173, "Snippets"))];
        const rows = ( languages.sort((a, b) => a.id.localeCompare(b.id)).map(l => {
            return [l.id, l.name, ( new MarkdownString()).appendMarkdown(`${( l.extensions.map(e => `\`${e}\``)).join("&nbsp;")}`), l.hasGrammar ? "✔︎" : "—", l.hasSnippets ? "✔︎" : "—"];
        }));
        return {
            data: {
                headers,
                rows
            },
            dispose: () => {}
        };
    }
}
( Registry.as(Extensions.ExtensionFeaturesRegistry)).registerExtensionFeature({
    id: "languages",
    label: ( localize(16174, "Programming Languages")),
    access: {
        canToggle: false
    },
    renderer: ( new SyncDescriptor(LanguageTableRenderer))
});
let WorkbenchLanguageService = class WorkbenchLanguageService extends LanguageService {
    constructor(extensionService, configurationService, environmentService, logService) {
        super(
            environmentService.verbose || environmentService.isExtensionDevelopment || !environmentService.isBuilt
        );
        this.logService = logService;
        this._configurationService = configurationService;
        this._extensionService = extensionService;
        languagesExtPoint.setHandler(extensions => {
            const allValidLanguages = [];
            for (let i = 0, len = extensions.length; i < len; i++) {
                const extension = extensions[i];
                if (!Array.isArray(extension.value)) {
                    extension.collector.error(( localize(
                        16175,
                        "Invalid `contributes.{0}`. Expected an array.",
                        languagesExtPoint.name
                    )));
                    continue;
                }
                for (let j = 0, lenJ = extension.value.length; j < lenJ; j++) {
                    const ext = extension.value[j];
                    if (isValidLanguageExtensionPoint(ext, extension.collector)) {
                        let configuration = undefined;
                        if (ext.configuration) {
                            configuration = joinPath(extension.description.extensionLocation, ext.configuration);
                        }
                        allValidLanguages.push({
                            id: ext.id,
                            extensions: ext.extensions,
                            filenames: ext.filenames,
                            filenamePatterns: ext.filenamePatterns,
                            firstLine: ext.firstLine,
                            aliases: ext.aliases,
                            mimetypes: ext.mimetypes,
                            configuration: configuration,
                            icon: ext.icon && {
                                light: joinPath(extension.description.extensionLocation, ext.icon.light),
                                dark: joinPath(extension.description.extensionLocation, ext.icon.dark)
                            }
                        });
                    }
                }
            }
            this._registry.setDynamicLanguages(allValidLanguages);
        });
        this.updateMime();
        this._register(this._configurationService.onDidChangeConfiguration(e => {
            if (e.affectsConfiguration(FILES_ASSOCIATIONS_CONFIG)) {
                this.updateMime();
            }
        }));
        this._extensionService.whenInstalledExtensionsRegistered().then(() => {
            this.updateMime();
        });
        this._register(this.onDidRequestRichLanguageFeatures(languageId => {
            this._extensionService.activateByEvent(`onLanguage:${languageId}`);
            this._extensionService.activateByEvent(`onLanguage`);
        }));
    }
    updateMime() {
        const configuration = this._configurationService.getValue();
        clearConfiguredLanguageAssociations();
        if (configuration.files?.associations) {
            ( Object.keys(configuration.files.associations)).forEach(pattern => {
                const langId = configuration.files.associations[pattern];
                if (typeof langId !== "string") {
                    this.logService.warn(
                        `Ignoring configured 'files.associations' for '${pattern}' because its type is not a string but '${typeof langId}'`
                    );
                    return;
                }
                const mimeType = this.getMimeType(langId) || `text/x-${langId}`;
                registerConfiguredLanguageAssociation({
                    id: langId,
                    mime: mimeType,
                    filepattern: pattern
                });
            });
        }
        this._onDidChange.fire();
    }
};
WorkbenchLanguageService = ( __decorate([( __param(0, IExtensionService)), ( __param(1, IConfigurationService)), ( __param(2, IEnvironmentService)), ( __param(3, ILogService))], WorkbenchLanguageService));
function isUndefinedOrStringArray(value) {
    if (typeof value === "undefined") {
        return true;
    }
    if (!Array.isArray(value)) {
        return false;
    }
    return value.every(item => typeof item === "string");
}
function isValidLanguageExtensionPoint(value, collector) {
    if (!value) {
        collector?.error(( localize(16176, "Empty value for `contributes.{0}`", languagesExtPoint.name)));
        return false;
    }
    if (typeof value.id !== "string") {
        collector?.error(( localize(16177, "property `{0}` is mandatory and must be of type `string`", "id")));
        return false;
    }
    if (!isUndefinedOrStringArray(value.extensions)) {
        collector?.error(( localize(
            16178,
            "property `{0}` can be omitted and must be of type `string[]`",
            "extensions"
        )));
        return false;
    }
    if (!isUndefinedOrStringArray(value.filenames)) {
        collector?.error(( localize(
            16179,
            "property `{0}` can be omitted and must be of type `string[]`",
            "filenames"
        )));
        return false;
    }
    if (typeof value.firstLine !== "undefined" && typeof value.firstLine !== "string") {
        collector?.error(( localize(
            16180,
            "property `{0}` can be omitted and must be of type `string`",
            "firstLine"
        )));
        return false;
    }
    if (typeof value.configuration !== "undefined" && typeof value.configuration !== "string") {
        collector?.error(( localize(
            16181,
            "property `{0}` can be omitted and must be of type `string`",
            "configuration"
        )));
        return false;
    }
    if (!isUndefinedOrStringArray(value.aliases)) {
        collector?.error(( localize(
            16182,
            "property `{0}` can be omitted and must be of type `string[]`",
            "aliases"
        )));
        return false;
    }
    if (!isUndefinedOrStringArray(value.mimetypes)) {
        collector?.error(( localize(
            16183,
            "property `{0}` can be omitted and must be of type `string[]`",
            "mimetypes"
        )));
        return false;
    }
    if (typeof value.icon !== "undefined") {
        if (typeof value.icon !== "object" || typeof value.icon.light !== "string" || typeof value.icon.dark !== "string") {
            collector?.error(( localize(
                16184,
                "property `{0}` can be omitted and must be of type `object` with properties `{1}` and `{2}` of type `string`",
                "icon",
                "light",
                "dark"
            )));
            return false;
        }
    }
    return true;
}

export { WorkbenchLanguageService, languagesExtPoint };
