"use strict";
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.MdExtractLinkDefinitionCodeActionProvider = void 0;
const l10n = require("@vscode/l10n");
const lsp = require("vscode-languageserver-types");
const position_1 = require("../../types/position");
const range_1 = require("../../types/range");
const textDocument_1 = require("../../types/textDocument");
const editBuilder_1 = require("../../util/editBuilder");
const documentLinks_1 = require("../documentLinks");
const organizeLinkDefs_1 = require("../organizeLinkDefs");
const util_1 = require("./util");
class MdExtractLinkDefinitionCodeActionProvider {
    static genericTitle = l10n.t('Extract to link definition');
    static #kind = lsp.CodeActionKind.RefactorExtract + '.linkDefinition';
    static notOnLinkAction = {
        title: MdExtractLinkDefinitionCodeActionProvider.genericTitle,
        kind: MdExtractLinkDefinitionCodeActionProvider.#kind,
        disabled: {
            reason: l10n.t('Not on link'),
        }
    };
    static alreadyRefLinkAction = {
        title: MdExtractLinkDefinitionCodeActionProvider.genericTitle,
        kind: MdExtractLinkDefinitionCodeActionProvider.#kind,
        disabled: {
            reason: l10n.t('Link is already a reference'),
        }
    };
    #linkProvider;
    constructor(linkProvider) {
        this.#linkProvider = linkProvider;
    }
    async getActions(doc, range, context, token) {
        if (!this.#isEnabled(context)) {
            return [];
        }
        const linkInfo = await this.#linkProvider.getLinks(doc);
        if (token.isCancellationRequested) {
            return [];
        }
        const linksInRange = linkInfo.links.filter(link => link.kind !== documentLinks_1.MdLinkKind.Definition && (0, range_1.rangeIntersects)(range, link.source.range));
        if (!linksInRange.length) {
            return [MdExtractLinkDefinitionCodeActionProvider.notOnLinkAction];
        }
        // Sort by range start to get most specific link
        linksInRange.sort((a, b) => (0, position_1.comparePosition)(b.source.range.start, a.source.range.start));
        // Even though multiple links may be in the selection, we only generate an action for the first link we find.
        // Creating actions for every link is overwhelming when users select all in a file
        const targetLink = linksInRange.find(link => link.href.kind === documentLinks_1.HrefKind.External || link.href.kind === documentLinks_1.HrefKind.Internal);
        if (!targetLink) {
            return [MdExtractLinkDefinitionCodeActionProvider.alreadyRefLinkAction];
        }
        return [this.#getExtractLinkAction(doc, linkInfo, targetLink)];
    }
    #isEnabled(context) {
        if (typeof context.only === 'undefined') {
            return true;
        }
        return context.only.some(kind => (0, util_1.codeActionKindContains)(lsp.CodeActionKind.Refactor, kind));
    }
    #getExtractLinkAction(doc, linkInfo, targetLink) {
        const builder = new editBuilder_1.WorkspaceEditBuilder();
        const resource = (0, textDocument_1.getDocUri)(doc);
        const placeholder = this.#getPlaceholder(linkInfo.definitions);
        // Rewrite all inline occurrences of the link
        for (const link of linkInfo.links) {
            if (link.kind === documentLinks_1.MdLinkKind.Link && this.#matchesHref(targetLink.href, link)) {
                builder.replace(resource, link.source.targetRange, `[${placeholder}]`);
            }
        }
        // And append new definition to link definition block
        const definitionText = this.#getLinkTargetText(doc, targetLink).trim();
        const definitions = linkInfo.links.filter(link => link.kind === documentLinks_1.MdLinkKind.Definition);
        const defBlock = (0, organizeLinkDefs_1.getExistingDefinitionBlock)(doc, definitions);
        if (!defBlock) {
            builder.insert(resource, { line: doc.lineCount, character: 0 }, `\n\n[${placeholder}]: ${definitionText}`);
        }
        else {
            const line = (0, textDocument_1.getLine)(doc, defBlock.endLine);
            builder.insert(resource, { line: defBlock.endLine, character: line.length }, `\n[${placeholder}]: ${definitionText}`);
        }
        const renamePosition = (0, position_1.translatePosition)(targetLink.source.targetRange.start, { characterDelta: 1 });
        return {
            title: MdExtractLinkDefinitionCodeActionProvider.genericTitle,
            kind: MdExtractLinkDefinitionCodeActionProvider.#kind,
            edit: builder.getEdit(),
            command: {
                command: 'vscodeMarkdownLanguageservice.rename',
                title: 'Rename',
                arguments: [(0, textDocument_1.getDocUri)(doc), renamePosition],
            }
        };
    }
    #getLinkTargetText(doc, link) {
        const afterHrefRange = (0, range_1.makeRange)((0, position_1.translatePosition)(link.source.targetRange.start, { characterDelta: 1 }), (0, position_1.translatePosition)(link.source.targetRange.end, { characterDelta: -1 }));
        return doc.getText(afterHrefRange);
    }
    #getPlaceholder(definitions) {
        const base = 'def';
        for (let i = 1;; ++i) {
            const name = i === 1 ? base : `${base}${i}`;
            if (typeof definitions.lookup(name) === 'undefined') {
                return name;
            }
        }
    }
    #matchesHref(href, link) {
        if (link.href.kind === documentLinks_1.HrefKind.External && href.kind === documentLinks_1.HrefKind.External) {
            return link.href.uri.toString() === href.uri.toString();
        }
        if (link.href.kind === documentLinks_1.HrefKind.Internal && href.kind === documentLinks_1.HrefKind.Internal) {
            return link.href.path.toString() === href.path.toString() && link.href.fragment === href.fragment;
        }
        return false;
    }
}
exports.MdExtractLinkDefinitionCodeActionProvider = MdExtractLinkDefinitionCodeActionProvider;
//# sourceMappingURL=extractLinkDef.js.map