"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var MarkedLinksPlugin_1;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MarkedLinksPlugin = void 0;
const Util = require("util");
const components_1 = require("../components");
const events_1 = require("../events");
const utils_1 = require("../../utils");
/**
 * A plugin that builds links in markdown texts.
 */
let MarkedLinksPlugin = MarkedLinksPlugin_1 = class MarkedLinksPlugin extends components_1.ContextAwareRendererComponent {
    constructor() {
        super(...arguments);
        /**
         * Regular expression for detecting bracket links.
         */
        this.brackets = /\[\[([^\]]+)\]\]/g;
        /**
         * Regular expression for detecting inline tags like {&amp;link ...}.
         */
        this.inlineTag = /(?:\[(.+?)\])?\{@(link|linkcode|linkplain)\s+((?:.|\n)+?)\}/gi;
        this.warnings = [];
    }
    /**
     * Create a new MarkedLinksPlugin instance.
     */
    initialize() {
        super.initialize();
        this.listenTo(this.owner, {
            [events_1.MarkdownEvent.PARSE]: this.onParseMarkdown,
            [events_1.RendererEvent.END]: this.onEndRenderer,
        }, undefined, 100);
    }
    /**
     * Find all references to symbols within the given text and transform them into a link.
     *
     * This function is aware of the current context and will try to find the symbol within the
     * current reflection. It will walk up the reflection chain till the symbol is found or the
     * root reflection is reached. As a last resort the function will search the entire project
     * for the given symbol.
     *
     * @param text  The text that should be parsed.
     * @returns The text with symbol references replaced by links.
     */
    replaceBrackets(text) {
        return text.replace(this.brackets, (match, content) => {
            const monospace = content[0] === "`" && content[content.length - 1] === "`";
            const split = MarkedLinksPlugin_1.splitLinkText(monospace ? content.slice(1, -1) : content);
            return this.buildLink(match, split.target, split.caption, monospace);
        });
    }
    /**
     * Find symbol {&amp;link ...} strings in text and turn into html links
     *
     * @param text  The string in which to replace the inline tags.
     * @return      The updated string.
     */
    replaceInlineTags(text) {
        return text.replace(this.inlineTag, (match, leading, tagName, content) => {
            const split = MarkedLinksPlugin_1.splitLinkText(content);
            const target = split.target;
            const caption = leading || split.caption;
            const monospace = tagName === "linkcode";
            return this.buildLink(match, target, caption, monospace);
        });
    }
    /**
     * Format a link with the given text and target.
     *
     * @param original   The original link string, will be returned if the target cannot be resolved..
     * @param target     The link target.
     * @param caption    The caption of the link.
     * @param monospace  Whether to use monospace formatting or not.
     * @returns A html link tag.
     */
    buildLink(original, target, caption, monospace) {
        let attributes = "";
        if (this.urlPrefix.test(target)) {
            attributes = ' class="external"';
        }
        else {
            let reflection;
            if (this.reflection) {
                reflection = this.reflection.findReflectionByName(target);
            }
            else if (this.project) {
                reflection = this.project.findReflectionByName(target);
            }
            if (reflection && reflection.url) {
                if (this.urlPrefix.test(reflection.url)) {
                    target = reflection.url;
                    attributes = ' class="external"';
                }
                else {
                    target = this.getRelativeUrl(reflection.url);
                }
            }
            else {
                const fullName = (this.reflection ||
                    this.project).getFriendlyFullName();
                this.warnings.push(`In ${fullName}: ${original}`);
                return original;
            }
        }
        if (monospace) {
            caption = "<code>" + caption + "</code>";
        }
        return Util.format('<a href="%s"%s>%s</a>', target, attributes, caption);
    }
    /**
     * Triggered when {@link MarkedPlugin} parses a markdown string.
     *
     * @param event
     */
    onParseMarkdown(event) {
        event.parsedText = this.replaceInlineTags(this.replaceBrackets(event.parsedText));
    }
    /**
     * Triggered when {@link Renderer} is finished
     */
    onEndRenderer(_event) {
        const enabled = this.listInvalidSymbolLinks || this.validation.invalidLink;
        if (this.listInvalidSymbolLinks) {
            this.application.logger.warn("listInvalidSymbolLinks is deprecated and will be removed in 0.23, set validation.invalidLink instead.");
        }
        if (enabled && this.warnings.length > 0) {
            this.application.logger.warn("\n[MarkedLinksPlugin]: Found invalid symbol reference(s) in JSDocs, " +
                "they will not render as links in the generated documentation." +
                "\n  " +
                this.warnings.join("\n  "));
        }
        this.warnings = [];
    }
    /**
     * Split the given link into text and target at first pipe or space.
     *
     * @param text  The source string that should be checked for a split character.
     * @returns An object containing the link text and target.
     */
    static splitLinkText(text) {
        let splitIndex = text.indexOf("|");
        if (splitIndex === -1) {
            splitIndex = text.search(/\s/);
        }
        if (splitIndex !== -1) {
            return {
                caption: text
                    .substr(splitIndex + 1)
                    .replace(/\n+/, " ")
                    .trim(),
                target: text.substr(0, splitIndex).trim(),
            };
        }
        else {
            return {
                caption: text,
                target: text,
            };
        }
    }
};
__decorate([
    (0, utils_1.BindOption)("listInvalidSymbolLinks")
], MarkedLinksPlugin.prototype, "listInvalidSymbolLinks", void 0);
__decorate([
    (0, utils_1.BindOption)("validation")
], MarkedLinksPlugin.prototype, "validation", void 0);
MarkedLinksPlugin = MarkedLinksPlugin_1 = __decorate([
    (0, components_1.Component)({ name: "marked-links" })
], MarkedLinksPlugin);
exports.MarkedLinksPlugin = MarkedLinksPlugin;
