"use strict";
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
    __setModuleDefault(result, mod);
    return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TypeScriptAutoFixProvider = void 0;
const lsp = __importStar(require("vscode-languageserver/node"));
const protocol_translation_1 = require("../protocol-translation");
const errorCodes = __importStar(require("../utils/errorCodes"));
const fixNames = __importStar(require("../utils/fixNames"));
const types_1 = require("../utils/types");
function buildIndividualFixes(fixes, client, file, documents, diagnostics) {
    var _a;
    return __awaiter(this, void 0, void 0, function* () {
        const edits = [];
        for (const diagnostic of diagnostics) {
            for (const { codes, fixName } of fixes) {
                if (!codes.has(diagnostic.code)) {
                    continue;
                }
                const args = Object.assign(Object.assign({}, (0, protocol_translation_1.toFileRangeRequestArgs)(file, diagnostic.range)), { errorCodes: [+diagnostic.code] });
                const response = yield client.request("getCodeFixes" /* GetCodeFixes */, args);
                if (response.type !== 'response') {
                    continue;
                }
                const fix = (_a = response.body) === null || _a === void 0 ? void 0 : _a.find(fix => fix.fixName === fixName);
                if (fix) {
                    edits.push(...fix.changes.map(change => (0, protocol_translation_1.toTextDocumentEdit)(change, documents)));
                    break;
                }
            }
        }
        return edits;
    });
}
function buildCombinedFix(fixes, client, file, documents, diagnostics) {
    var _a, _b;
    return __awaiter(this, void 0, void 0, function* () {
        const edits = [];
        for (const diagnostic of diagnostics) {
            for (const { codes, fixName } of fixes) {
                if (!codes.has(diagnostic.code)) {
                    continue;
                }
                const args = Object.assign(Object.assign({}, (0, protocol_translation_1.toFileRangeRequestArgs)(file, diagnostic.range)), { errorCodes: [+diagnostic.code] });
                const response = yield client.request("getCodeFixes" /* GetCodeFixes */, args);
                if (response.type !== 'response' || !((_a = response.body) === null || _a === void 0 ? void 0 : _a.length)) {
                    continue;
                }
                const fix = (_b = response.body) === null || _b === void 0 ? void 0 : _b.find(fix => fix.fixName === fixName);
                if (!fix) {
                    continue;
                }
                if (!fix.fixId) {
                    edits.push(...fix.changes.map(change => (0, protocol_translation_1.toTextDocumentEdit)(change, documents)));
                    return edits;
                }
                const combinedArgs = {
                    scope: {
                        type: 'file',
                        args: { file }
                    },
                    fixId: fix.fixId
                };
                const combinedResponse = yield client.request("getCombinedCodeFix" /* GetCombinedCodeFix */, combinedArgs);
                if (combinedResponse.type !== 'response' || !combinedResponse.body) {
                    return edits;
                }
                edits.push(...combinedResponse.body.changes.map(change => (0, protocol_translation_1.toTextDocumentEdit)(change, documents)));
                return edits;
            }
        }
        return edits;
    });
}
// #region Source Actions
class SourceAction {
}
class SourceFixAll extends SourceAction {
    constructor() {
        super(...arguments);
        this.title = 'Fix all';
    }
    build(client, file, documents, diagnostics) {
        return __awaiter(this, void 0, void 0, function* () {
            const edits = [];
            edits.push(...yield buildIndividualFixes([
                { codes: errorCodes.incorrectlyImplementsInterface, fixName: fixNames.classIncorrectlyImplementsInterface },
                { codes: errorCodes.asyncOnlyAllowedInAsyncFunctions, fixName: fixNames.awaitInSyncFunction }
            ], client, file, documents, diagnostics));
            edits.push(...yield buildCombinedFix([
                { codes: errorCodes.unreachableCode, fixName: fixNames.unreachableCode }
            ], client, file, documents, diagnostics));
            if (!edits.length) {
                return null;
            }
            return lsp.CodeAction.create(this.title, { documentChanges: edits }, SourceFixAll.kind.value);
        });
    }
}
SourceFixAll.kind = types_1.CodeActionKind.SourceFixAllTs;
class SourceRemoveUnused extends SourceAction {
    constructor() {
        super(...arguments);
        this.title = 'Remove all unused code';
    }
    build(client, file, documents, diagnostics) {
        return __awaiter(this, void 0, void 0, function* () {
            const edits = yield buildCombinedFix([
                { codes: errorCodes.variableDeclaredButNeverUsed, fixName: fixNames.unusedIdentifier }
            ], client, file, documents, diagnostics);
            if (!edits.length) {
                return null;
            }
            return lsp.CodeAction.create(this.title, { documentChanges: edits }, SourceRemoveUnused.kind.value);
        });
    }
}
SourceRemoveUnused.kind = types_1.CodeActionKind.SourceRemoveUnusedTs;
class SourceAddMissingImports extends SourceAction {
    constructor() {
        super(...arguments);
        this.title = 'Add all missing imports';
    }
    build(client, file, documents, diagnostics) {
        return __awaiter(this, void 0, void 0, function* () {
            const edits = yield buildCombinedFix([
                { codes: errorCodes.cannotFindName, fixName: fixNames.fixImport }
            ], client, file, documents, diagnostics);
            if (!edits.length) {
                return null;
            }
            return lsp.CodeAction.create(this.title, { documentChanges: edits }, SourceAddMissingImports.kind.value);
        });
    }
}
SourceAddMissingImports.kind = types_1.CodeActionKind.SourceAddMissingImportsTs;
//#endregion
class TypeScriptAutoFixProvider {
    constructor(client) {
        this.client = client;
    }
    static get kinds() {
        return TypeScriptAutoFixProvider.kindProviders.map(provider => provider.kind);
    }
    provideCodeActions(kinds, file, diagnostics, documents) {
        return __awaiter(this, void 0, void 0, function* () {
            const results = [];
            for (const provider of TypeScriptAutoFixProvider.kindProviders) {
                if (kinds.some(kind => kind.contains(provider.kind))) {
                    results.push((new provider).build(this.client, file, documents, diagnostics));
                }
            }
            return (yield Promise.all(results)).flatMap(result => result || []);
        });
    }
}
exports.TypeScriptAutoFixProvider = TypeScriptAutoFixProvider;
TypeScriptAutoFixProvider.kindProviders = [
    SourceFixAll,
    SourceRemoveUnused,
    SourceAddMissingImports
];
//# sourceMappingURL=fix-all.js.map