
import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
import { getCodeEditor } from '../../../../editor/browser/editorBrowser.js';
import { registerEditorAction, EditorAction } from '../../../../editor/browser/editorExtensions.js';
import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js';
import { localize } from '../../../../nls.js';
import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js';
import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.service.js';
import { CancellationTokenSource, CancellationToken } from '../../../../base/common/cancellation.js';
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
import { FormattingConflicts, FormattingKind, FormattingMode, getRealAndSyntheticDocumentFormattersOrdered, formatDocumentWithProvider, formatDocumentRangesWithProvider } from '../../../../editor/contrib/format/browser/format.js';
import { Range } from '../../../../editor/common/core/range.js';
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.service.js';
import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js';
import { Registry } from '../../../../platform/registry/common/platform.js';
import { Extensions as Extensions$1 } from '../../../../platform/configuration/common/configurationRegistry.js';
import { Extensions } from '../../../common/contributions.js';
import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js';
import { toExtension } from '../../../services/extensions/common/extensions.js';
import { IExtensionService } from '../../../services/extensions/common/extensions.service.js';
import { Disposable, DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js';
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.service.js';
import { NotificationPriority } from '../../../../platform/notification/common/notification.js';
import { INotificationService } from '../../../../platform/notification/common/notification.service.js';
import { ILanguageService } from '../../../../editor/common/languages/language.service.js';
import { IWorkbenchExtensionEnablementService } from '../../../services/extensionManagement/common/extensionManagement.service.js';
import { editorConfigurationBaseNode } from '../../../../editor/common/config/editorConfigurationSchema.js';
import { IDialogService } from '../../../../platform/dialogs/common/dialogs.service.js';
import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.service.js';
import { ILanguageStatusService } from '../../../services/languageStatus/common/languageStatusService.service.js';
import { IEditorService } from '../../../services/editor/common/editorService.service.js';
import { CommandsRegistry } from '../../../../platform/commands/common/commands.js';
import { generateUuid } from '../../../../base/common/uuid.js';
import Severity$1 from '../../../../base/common/severity.js';

var DefaultFormatter_1;
let DefaultFormatter = class DefaultFormatter extends Disposable {
    static { DefaultFormatter_1 = this; }
    static { this.configName = 'editor.defaultFormatter'; }
    static { this.extensionIds = []; }
    static { this.extensionItemLabels = []; }
    static { this.extensionDescriptions = []; }
    constructor(_extensionService, _extensionEnablementService, _configService, _notificationService, _dialogService, _quickInputService, _languageService, _languageFeaturesService, _languageStatusService, _editorService) {
        super();
        this._extensionService = _extensionService;
        this._extensionEnablementService = _extensionEnablementService;
        this._configService = _configService;
        this._notificationService = _notificationService;
        this._dialogService = _dialogService;
        this._quickInputService = _quickInputService;
        this._languageService = _languageService;
        this._languageFeaturesService = _languageFeaturesService;
        this._languageStatusService = _languageStatusService;
        this._editorService = _editorService;
        this._languageStatusStore = this._store.add(( new DisposableStore()));
        this._store.add(this._extensionService.onDidChangeExtensions(this._updateConfigValues, this));
        this._store.add(FormattingConflicts.setFormatterSelector((formatter, document, mode, kind) => this._selectFormatter(formatter, document, mode, kind)));
        this._store.add(_editorService.onDidActiveEditorChange(this._updateStatus, this));
        this._store.add(_languageFeaturesService.documentFormattingEditProvider.onDidChange(this._updateStatus, this));
        this._store.add(_languageFeaturesService.documentRangeFormattingEditProvider.onDidChange(this._updateStatus, this));
        this._store.add(_configService.onDidChangeConfiguration(e => e.affectsConfiguration(DefaultFormatter_1.configName) && this._updateStatus()));
        this._updateConfigValues();
    }
    async _updateConfigValues() {
        await this._extensionService.whenInstalledExtensionsRegistered();
        let extensions = [...this._extensionService.extensions];
        extensions = extensions.sort((a, b) => {
            const boostA = a.categories?.find(cat => cat === 'Formatters' || cat === 'Programming Languages');
            const boostB = b.categories?.find(cat => cat === 'Formatters' || cat === 'Programming Languages');
            if (boostA && !boostB) {
                return -1;
            }
            else if (!boostA && boostB) {
                return 1;
            }
            else {
                return a.name.localeCompare(b.name);
            }
        });
        DefaultFormatter_1.extensionIds.length = 0;
        DefaultFormatter_1.extensionItemLabels.length = 0;
        DefaultFormatter_1.extensionDescriptions.length = 0;
        DefaultFormatter_1.extensionIds.push(null);
        DefaultFormatter_1.extensionItemLabels.push(( localize(6963, 'None')));
        DefaultFormatter_1.extensionDescriptions.push(( localize(6964, "None")));
        for (const extension of extensions) {
            if (extension.main || extension.browser) {
                DefaultFormatter_1.extensionIds.push(extension.identifier.value);
                DefaultFormatter_1.extensionItemLabels.push(extension.displayName ?? '');
                DefaultFormatter_1.extensionDescriptions.push(extension.description ?? '');
            }
        }
    }
    static _maybeQuotes(s) {
        return s.match(/\s/) ? `'${s}'` : s;
    }
    async _analyzeFormatter(kind, formatter, document) {
        const defaultFormatterId = this._configService.getValue(DefaultFormatter_1.configName, {
            resource: document.uri,
            overrideIdentifier: document.getLanguageId()
        });
        if (defaultFormatterId) {
            const defaultFormatter = formatter.find(formatter => ExtensionIdentifier.equals(formatter.extensionId, defaultFormatterId));
            if (defaultFormatter) {
                return defaultFormatter;
            }
            const extension = await this._extensionService.getExtension(defaultFormatterId);
            if (extension && this._extensionEnablementService.isEnabled(toExtension(extension))) {
                const langName = this._languageService.getLanguageName(document.getLanguageId()) || document.getLanguageId();
                const detail = kind === FormattingKind.File
                    ? ( localize(
                    6965,
                    "Extension '{0}' is configured as formatter but it cannot format '{1}'-files",
                    extension.displayName || extension.name,
                    langName
                ))
                    : ( localize(
                    6966,
                    "Extension '{0}' is configured as formatter but it can only format '{1}'-files as a whole, not selections or parts of it.",
                    extension.displayName || extension.name,
                    langName
                ));
                return detail;
            }
        }
        else if (formatter.length === 1) {
            return formatter[0];
        }
        const langName = this._languageService.getLanguageName(document.getLanguageId()) || document.getLanguageId();
        const message = !defaultFormatterId
            ? ( localize(
            6967,
            "There are multiple formatters for '{0}' files. One of them should be configured as default formatter.",
            DefaultFormatter_1._maybeQuotes(langName)
        ))
            : ( localize(
            6968,
            "Extension '{0}' is configured as formatter but not available. Select a different default formatter to continue.",
            defaultFormatterId
        ));
        return message;
    }
    async _selectFormatter(formatter, document, mode, kind) {
        const formatterOrMessage = await this._analyzeFormatter(kind, formatter, document);
        if (typeof formatterOrMessage !== 'string') {
            return formatterOrMessage;
        }
        if (mode !== FormattingMode.Silent) {
            const { confirmed } = await this._dialogService.confirm({
                message: ( localize(6969, "Configure Default Formatter")),
                detail: formatterOrMessage,
                primaryButton: ( localize(6970, "&&Configure..."))
            });
            if (confirmed) {
                return this._pickAndPersistDefaultFormatter(formatter, document);
            }
        }
        else {
            this._notificationService.prompt(Severity$1.Info, formatterOrMessage, [{ label: ( localize(6971, "Configure...")), run: () => this._pickAndPersistDefaultFormatter(formatter, document) }], { priority: NotificationPriority.SILENT });
        }
        return undefined;
    }
    async _pickAndPersistDefaultFormatter(formatter, document) {
        const picks = ( formatter.map((formatter, index) => {
            return {
                index,
                label: formatter.displayName || (formatter.extensionId ? formatter.extensionId.value : '?'),
                description: formatter.extensionId && formatter.extensionId.value
            };
        }));
        const langName = this._languageService.getLanguageName(document.getLanguageId()) || document.getLanguageId();
        const pick = await this._quickInputService.pick(picks, { placeHolder: ( localize(
            6972,
            "Select a default formatter for '{0}' files",
            DefaultFormatter_1._maybeQuotes(langName)
        )) });
        if (!pick || !formatter[pick.index].extensionId) {
            return undefined;
        }
        this._configService.updateValue(DefaultFormatter_1.configName, formatter[pick.index].extensionId.value, {
            resource: document.uri,
            overrideIdentifier: document.getLanguageId()
        });
        return formatter[pick.index];
    }
    _updateStatus() {
        this._languageStatusStore.clear();
        const editor = getCodeEditor(this._editorService.activeTextEditorControl);
        if (!editor || !editor.hasModel()) {
            return;
        }
        const document = editor.getModel();
        const formatter = getRealAndSyntheticDocumentFormattersOrdered(this._languageFeaturesService.documentFormattingEditProvider, this._languageFeaturesService.documentRangeFormattingEditProvider, document);
        if (formatter.length === 0) {
            return;
        }
        const cts = ( new CancellationTokenSource());
        this._languageStatusStore.add(toDisposable(() => cts.dispose(true)));
        this._analyzeFormatter(FormattingKind.File, formatter, document).then(result => {
            if (cts.token.isCancellationRequested) {
                return;
            }
            if (typeof result !== 'string') {
                return;
            }
            const command = { id: `formatter/configure/dfl/${generateUuid()}`, title: ( localize(6973, "Configure...")) };
            this._languageStatusStore.add(CommandsRegistry.registerCommand(command.id, () => this._pickAndPersistDefaultFormatter(formatter, document)));
            this._languageStatusStore.add(this._languageStatusService.addStatus({
                id: 'formatter.conflict',
                name: ( localize(6974, "Formatter Conflicts")),
                selector: { language: document.getLanguageId(), pattern: document.uri.fsPath },
                severity: Severity$1.Error,
                label: ( localize(6975, "Formatting")),
                detail: result,
                busy: false,
                source: '',
                command,
                accessibilityInfo: undefined
            }));
        });
    }
};
DefaultFormatter = DefaultFormatter_1 = ( __decorate([
    ( __param(0, IExtensionService)),
    ( __param(1, IWorkbenchExtensionEnablementService)),
    ( __param(2, IConfigurationService)),
    ( __param(3, INotificationService)),
    ( __param(4, IDialogService)),
    ( __param(5, IQuickInputService)),
    ( __param(6, ILanguageService)),
    ( __param(7, ILanguageFeaturesService)),
    ( __param(8, ILanguageStatusService)),
    ( __param(9, IEditorService))
], DefaultFormatter));
( Registry.as(Extensions.Workbench)).registerWorkbenchContribution(DefaultFormatter, LifecyclePhase.Restored);
( Registry.as(Extensions$1.Configuration)).registerConfiguration({
    ...editorConfigurationBaseNode,
    properties: {
        [DefaultFormatter.configName]: {
            description: ( localize(
                6976,
                "Defines a default formatter which takes precedence over all other formatter settings. Must be the identifier of an extension contributing a formatter."
            )),
            type: ['string', 'null'],
            default: null,
            enum: DefaultFormatter.extensionIds,
            enumItemLabels: DefaultFormatter.extensionItemLabels,
            markdownEnumDescriptions: DefaultFormatter.extensionDescriptions
        }
    }
});
function logFormatterTelemetry(telemetryService, mode, options, pick) {
    function extKey(obj) {
        return obj.extensionId ? ExtensionIdentifier.toKey(obj.extensionId) : 'unknown';
    }
    telemetryService.publicLog2('formatterpick', {
        mode,
        extensions: ( options.map(extKey)),
        pick: pick ? extKey(pick) : 'none'
    });
}
async function showFormatterPick(accessor, model, formatters) {
    const quickPickService = accessor.get(IQuickInputService);
    const configService = accessor.get(IConfigurationService);
    const languageService = accessor.get(ILanguageService);
    const overrides = { resource: model.uri, overrideIdentifier: model.getLanguageId() };
    const defaultFormatter = configService.getValue(DefaultFormatter.configName, overrides);
    let defaultFormatterPick;
    const picks = ( formatters.map((provider, index) => {
        const isDefault = ExtensionIdentifier.equals(provider.extensionId, defaultFormatter);
        const pick = {
            index,
            label: provider.displayName || '',
            description: isDefault ? ( localize(6977, "(default)")) : undefined,
        };
        if (isDefault) {
            defaultFormatterPick = pick;
        }
        return pick;
    }));
    const configurePick = {
        label: ( localize(6978, "Configure Default Formatter..."))
    };
    const pick = await quickPickService.pick([...picks, { type: 'separator' }, configurePick], {
        placeHolder: ( localize(6979, "Select a formatter")),
        activeItem: defaultFormatterPick
    });
    if (!pick) {
        return undefined;
    }
    else if (pick === configurePick) {
        const langName = languageService.getLanguageName(model.getLanguageId()) || model.getLanguageId();
        const pick = await quickPickService.pick(picks, { placeHolder: ( localize(
            6972,
            "Select a default formatter for '{0}' files",
            DefaultFormatter._maybeQuotes(langName)
        )) });
        if (pick && formatters[pick.index].extensionId) {
            configService.updateValue(DefaultFormatter.configName, formatters[pick.index].extensionId.value, overrides);
        }
        return undefined;
    }
    else {
        return pick.index;
    }
}
registerEditorAction(class FormatDocumentMultipleAction extends EditorAction {
    constructor() {
        super({
            id: 'editor.action.formatDocument.multiple',
            label: ( localize(6980, "Format Document With...")),
            alias: 'Format Document...',
            precondition: ( ContextKeyExpr.and(
                EditorContextKeys.writable,
                EditorContextKeys.hasMultipleDocumentFormattingProvider
            )),
            contextMenuOpts: {
                group: '1_modification',
                order: 1.3
            }
        });
    }
    async run(accessor, editor, args) {
        if (!editor.hasModel()) {
            return;
        }
        const instaService = accessor.get(IInstantiationService);
        const telemetryService = accessor.get(ITelemetryService);
        const languageFeaturesService = accessor.get(ILanguageFeaturesService);
        const model = editor.getModel();
        const provider = getRealAndSyntheticDocumentFormattersOrdered(languageFeaturesService.documentFormattingEditProvider, languageFeaturesService.documentRangeFormattingEditProvider, model);
        const pick = await instaService.invokeFunction(showFormatterPick, model, provider);
        if (typeof pick === 'number') {
            await instaService.invokeFunction(formatDocumentWithProvider, provider[pick], editor, FormattingMode.Explicit, CancellationToken.None);
        }
        logFormatterTelemetry(telemetryService, 'document', provider, typeof pick === 'number' && provider[pick] || undefined);
    }
});
registerEditorAction(class FormatSelectionMultipleAction extends EditorAction {
    constructor() {
        super({
            id: 'editor.action.formatSelection.multiple',
            label: ( localize(6981, "Format Selection With...")),
            alias: 'Format Code...',
            precondition: ( ContextKeyExpr.and(( ContextKeyExpr.and(EditorContextKeys.writable)), EditorContextKeys.hasMultipleDocumentSelectionFormattingProvider)),
            contextMenuOpts: {
                when: ( ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection)),
                group: '1_modification',
                order: 1.31
            }
        });
    }
    async run(accessor, editor) {
        if (!editor.hasModel()) {
            return;
        }
        const instaService = accessor.get(IInstantiationService);
        const languageFeaturesService = accessor.get(ILanguageFeaturesService);
        const telemetryService = accessor.get(ITelemetryService);
        const model = editor.getModel();
        let range = editor.getSelection();
        if (range.isEmpty()) {
            range = ( new Range(
                range.startLineNumber,
                1,
                range.startLineNumber,
                model.getLineMaxColumn(range.startLineNumber)
            ));
        }
        const provider = languageFeaturesService.documentRangeFormattingEditProvider.ordered(model);
        const pick = await instaService.invokeFunction(showFormatterPick, model, provider);
        if (typeof pick === 'number') {
            await instaService.invokeFunction(formatDocumentRangesWithProvider, provider[pick], editor, range, CancellationToken.None, true);
        }
        logFormatterTelemetry(telemetryService, 'range', provider, typeof pick === 'number' && provider[pick] || undefined);
    }
});

export { DefaultFormatter };
