var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
    function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
    var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
    var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
    var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
    var _, done = false;
    for (var i = decorators.length - 1; i >= 0; i--) {
        var context = {};
        for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
        for (var p in contextIn.access) context.access[p] = contextIn.access[p];
        context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
        var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
        if (kind === "accessor") {
            if (result === void 0) continue;
            if (result === null || typeof result !== "object") throw new TypeError("Object expected");
            if (_ = accept(result.get)) descriptor.get = _;
            if (_ = accept(result.set)) descriptor.set = _;
            if (_ = accept(result.init)) initializers.unshift(_);
        }
        else if (_ = accept(result)) {
            if (kind === "field") initializers.unshift(_);
            else descriptor[key] = _;
        }
    }
    if (target) Object.defineProperty(target, contextIn.name, descriptor);
    done = true;
};
var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
    var useValue = arguments.length > 2;
    for (var i = 0; i < initializers.length; i++) {
        value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
    }
    return useValue ? value : void 0;
};
import { Comment, CommentTag, DeclarationReflection, ParameterReflection, ReflectionFlag, ReflectionKind, ReflectionType, SignatureReflection, } from "../../models/index.js";
import { Option } from "../../utils/index.js";
import { filterMap, i18n, partition, removeIf, removeIfPresent, setIntersection, unique, } from "#utils";
import { ConverterComponent } from "../components.js";
import { ConverterEvents } from "../converter-events.js";
import { CategoryPlugin } from "./CategoryPlugin.js";
/**
 * These tags are not useful to display in the generated documentation.
 * They should be ignored when parsing comments. Any relevant type information
 * (for JS users) will be consumed by TypeScript and need not be preserved
 * in the comment.
 *
 * Note that param/arg/argument/return/returns are not present.
 * These tags will have their type information stripped when parsing, but still
 * provide useful information for documentation.
 */
const NEVER_RENDERED = [
    "@augments",
    "@callback",
    "@class",
    "@constructor",
    "@enum",
    "@extends",
    "@this",
    "@type",
    "@typedef",
    "@jsx",
];
// We might make this user configurable at some point, but for now,
// this set is configured here.
const MUTUALLY_EXCLUSIVE_MODIFIERS = [
    new Set([
        "@alpha",
        "@beta",
        "@experimental",
        "@internal",
        "@public",
    ]),
];
/**
 * Handles most behavior triggered by comments. `@group` and `@category` are handled by their respective plugins, but everything else is here.
 *
 * How it works today
 * ==================
 * During conversion:
 * - Handle visibility flags (`@private`, `@protected`. `@public`)
 * - Handle module renames (`@module`)
 * - Remove excluded tags & comment discovery tags (`@module`, `@packageDocumentation`)
 * - Copy comments for type parameters from the parent container (for classes/interfaces)
 *
 * Resolve begin:
 * - Remove hidden reflections
 *
 * Resolve:
 * - Apply `@label` tag
 * - Copy comments on signature containers to the signature if signatures don't already have a comment
 *   and then remove the comment on the container.
 * - Copy comments to parameters and type parameters (for signatures)
 * - Apply `@group` and `@category` tags
 *
 * Resolve end:
 * - Copy auto inherited comments from heritage clauses
 * - Handle `@inheritDoc`
 * - Resolve `@link` tags to point to target reflections
 *
 * How it should work
 * ==================
 * During conversion:
 * - Handle visibility flags (`@private`, `@protected`. `@public`)
 * - Handle module renames (`@module`)
 * - Remove excluded tags & comment discovery tags (`@module`, `@packageDocumentation`)
 *
 * Resolve begin (100):
 * - Copy auto inherited comments from heritage clauses
 * - Apply `@label` tag
 *
 * Resolve begin (75)
 * - Handle `@inheritDoc`
 *
 * Resolve begin (50)
 * - Copy comments on signature containers to the signature if signatures don't already have a comment
 *   and then remove the comment on the container.
 * - Copy comments for type parameters from the parent container (for classes/interfaces)
 *
 * Resolve begin (25)
 * - Remove hidden reflections
 *
 * Resolve:
 * - Copy comments to parameters and type parameters (for signatures)
 * - Apply `@group` and `@category` tags
 *
 * Resolve end:
 * - Resolve `@link` tags to point to target reflections
 */
let CommentPlugin = (() => {
    let _classSuper = ConverterComponent;
    let _excludeTags_decorators;
    let _excludeTags_initializers = [];
    let _excludeTags_extraInitializers = [];
    let _cascadedModifierTags_decorators;
    let _cascadedModifierTags_initializers = [];
    let _cascadedModifierTags_extraInitializers = [];
    let _excludeInternal_decorators;
    let _excludeInternal_initializers = [];
    let _excludeInternal_extraInitializers = [];
    let _excludePrivate_decorators;
    let _excludePrivate_initializers = [];
    let _excludePrivate_extraInitializers = [];
    let _excludeProtected_decorators;
    let _excludeProtected_initializers = [];
    let _excludeProtected_extraInitializers = [];
    let _excludeNotDocumented_decorators;
    let _excludeNotDocumented_initializers = [];
    let _excludeNotDocumented_extraInitializers = [];
    let _excludeCategories_decorators;
    let _excludeCategories_initializers = [];
    let _excludeCategories_extraInitializers = [];
    let _defaultCategory_decorators;
    let _defaultCategory_initializers = [];
    let _defaultCategory_extraInitializers = [];
    return class CommentPlugin extends _classSuper {
        static {
            const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
            _excludeTags_decorators = [Option("excludeTags")];
            _cascadedModifierTags_decorators = [Option("cascadedModifierTags")];
            _excludeInternal_decorators = [Option("excludeInternal")];
            _excludePrivate_decorators = [Option("excludePrivate")];
            _excludeProtected_decorators = [Option("excludeProtected")];
            _excludeNotDocumented_decorators = [Option("excludeNotDocumented")];
            _excludeCategories_decorators = [Option("excludeCategories")];
            _defaultCategory_decorators = [Option("defaultCategory")];
            __esDecorate(this, null, _excludeTags_decorators, { kind: "accessor", name: "excludeTags", static: false, private: false, access: { has: obj => "excludeTags" in obj, get: obj => obj.excludeTags, set: (obj, value) => { obj.excludeTags = value; } }, metadata: _metadata }, _excludeTags_initializers, _excludeTags_extraInitializers);
            __esDecorate(this, null, _cascadedModifierTags_decorators, { kind: "accessor", name: "cascadedModifierTags", static: false, private: false, access: { has: obj => "cascadedModifierTags" in obj, get: obj => obj.cascadedModifierTags, set: (obj, value) => { obj.cascadedModifierTags = value; } }, metadata: _metadata }, _cascadedModifierTags_initializers, _cascadedModifierTags_extraInitializers);
            __esDecorate(this, null, _excludeInternal_decorators, { kind: "accessor", name: "excludeInternal", static: false, private: false, access: { has: obj => "excludeInternal" in obj, get: obj => obj.excludeInternal, set: (obj, value) => { obj.excludeInternal = value; } }, metadata: _metadata }, _excludeInternal_initializers, _excludeInternal_extraInitializers);
            __esDecorate(this, null, _excludePrivate_decorators, { kind: "accessor", name: "excludePrivate", static: false, private: false, access: { has: obj => "excludePrivate" in obj, get: obj => obj.excludePrivate, set: (obj, value) => { obj.excludePrivate = value; } }, metadata: _metadata }, _excludePrivate_initializers, _excludePrivate_extraInitializers);
            __esDecorate(this, null, _excludeProtected_decorators, { kind: "accessor", name: "excludeProtected", static: false, private: false, access: { has: obj => "excludeProtected" in obj, get: obj => obj.excludeProtected, set: (obj, value) => { obj.excludeProtected = value; } }, metadata: _metadata }, _excludeProtected_initializers, _excludeProtected_extraInitializers);
            __esDecorate(this, null, _excludeNotDocumented_decorators, { kind: "accessor", name: "excludeNotDocumented", static: false, private: false, access: { has: obj => "excludeNotDocumented" in obj, get: obj => obj.excludeNotDocumented, set: (obj, value) => { obj.excludeNotDocumented = value; } }, metadata: _metadata }, _excludeNotDocumented_initializers, _excludeNotDocumented_extraInitializers);
            __esDecorate(this, null, _excludeCategories_decorators, { kind: "accessor", name: "excludeCategories", static: false, private: false, access: { has: obj => "excludeCategories" in obj, get: obj => obj.excludeCategories, set: (obj, value) => { obj.excludeCategories = value; } }, metadata: _metadata }, _excludeCategories_initializers, _excludeCategories_extraInitializers);
            __esDecorate(this, null, _defaultCategory_decorators, { kind: "accessor", name: "defaultCategory", static: false, private: false, access: { has: obj => "defaultCategory" in obj, get: obj => obj.defaultCategory, set: (obj, value) => { obj.defaultCategory = value; } }, metadata: _metadata }, _defaultCategory_initializers, _defaultCategory_extraInitializers);
            if (_metadata) Object.defineProperty(this, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
        }
        #excludeTags_accessor_storage = __runInitializers(this, _excludeTags_initializers, void 0);
        get excludeTags() { return this.#excludeTags_accessor_storage; }
        set excludeTags(value) { this.#excludeTags_accessor_storage = value; }
        #cascadedModifierTags_accessor_storage = (__runInitializers(this, _excludeTags_extraInitializers), __runInitializers(this, _cascadedModifierTags_initializers, void 0));
        get cascadedModifierTags() { return this.#cascadedModifierTags_accessor_storage; }
        set cascadedModifierTags(value) { this.#cascadedModifierTags_accessor_storage = value; }
        #excludeInternal_accessor_storage = (__runInitializers(this, _cascadedModifierTags_extraInitializers), __runInitializers(this, _excludeInternal_initializers, void 0));
        get excludeInternal() { return this.#excludeInternal_accessor_storage; }
        set excludeInternal(value) { this.#excludeInternal_accessor_storage = value; }
        #excludePrivate_accessor_storage = (__runInitializers(this, _excludeInternal_extraInitializers), __runInitializers(this, _excludePrivate_initializers, void 0));
        get excludePrivate() { return this.#excludePrivate_accessor_storage; }
        set excludePrivate(value) { this.#excludePrivate_accessor_storage = value; }
        #excludeProtected_accessor_storage = (__runInitializers(this, _excludePrivate_extraInitializers), __runInitializers(this, _excludeProtected_initializers, void 0));
        get excludeProtected() { return this.#excludeProtected_accessor_storage; }
        set excludeProtected(value) { this.#excludeProtected_accessor_storage = value; }
        #excludeNotDocumented_accessor_storage = (__runInitializers(this, _excludeProtected_extraInitializers), __runInitializers(this, _excludeNotDocumented_initializers, void 0));
        get excludeNotDocumented() { return this.#excludeNotDocumented_accessor_storage; }
        set excludeNotDocumented(value) { this.#excludeNotDocumented_accessor_storage = value; }
        #excludeCategories_accessor_storage = (__runInitializers(this, _excludeNotDocumented_extraInitializers), __runInitializers(this, _excludeCategories_initializers, void 0));
        get excludeCategories() { return this.#excludeCategories_accessor_storage; }
        set excludeCategories(value) { this.#excludeCategories_accessor_storage = value; }
        #defaultCategory_accessor_storage = (__runInitializers(this, _excludeCategories_extraInitializers), __runInitializers(this, _defaultCategory_initializers, void 0));
        get defaultCategory() { return this.#defaultCategory_accessor_storage; }
        set defaultCategory(value) { this.#defaultCategory_accessor_storage = value; }
        _excludeKinds = __runInitializers(this, _defaultCategory_extraInitializers);
        get excludeNotDocumentedKinds() {
            this._excludeKinds ??= this.application.options
                .getValue("excludeNotDocumentedKinds")
                .reduce((a, b) => a | ReflectionKind[b], 0);
            return this._excludeKinds;
        }
        constructor(owner) {
            super(owner);
            this.owner.on(ConverterEvents.CREATE_DECLARATION, this.onDeclaration.bind(this));
            this.owner.on(ConverterEvents.CREATE_SIGNATURE, this.onDeclaration.bind(this));
            this.owner.on(ConverterEvents.CREATE_TYPE_PARAMETER, this.onCreateTypeParameter.bind(this));
            this.owner.on(ConverterEvents.RESOLVE_BEGIN, this.onBeginResolve.bind(this));
            this.owner.on(ConverterEvents.RESOLVE, this.onResolve.bind(this));
            this.owner.on(ConverterEvents.END, () => {
                this._excludeKinds = undefined;
            });
        }
        /**
         * Apply all comment tag modifiers to the given reflection.
         *
         * @param reflection  The reflection the modifiers should be applied to.
         * @param comment  The comment that should be searched for modifiers.
         */
        applyModifiers(reflection, comment) {
            if (reflection.kindOf(ReflectionKind.SomeModule)) {
                comment.removeModifier("@namespace");
            }
            if (reflection.kindOf(ReflectionKind.Interface)) {
                comment.removeModifier("@interface");
            }
            if (comment.hasModifier("@abstract")) {
                if (reflection.kindOf(ReflectionKind.SomeSignature)) {
                    reflection.parent.setFlag(ReflectionFlag.Abstract);
                }
                else {
                    reflection.setFlag(ReflectionFlag.Abstract);
                }
                comment.removeModifier("@abstract");
            }
            if (comment.hasModifier("@private")) {
                reflection.setFlag(ReflectionFlag.Private);
                if (reflection.kindOf(ReflectionKind.CallSignature)) {
                    reflection.parent?.setFlag(ReflectionFlag.Private);
                }
                comment.removeModifier("@private");
            }
            if (comment.hasModifier("@protected")) {
                reflection.setFlag(ReflectionFlag.Protected);
                if (reflection.kindOf(ReflectionKind.CallSignature)) {
                    reflection.parent?.setFlag(ReflectionFlag.Protected);
                }
                comment.removeModifier("@protected");
            }
            if (comment.hasModifier("@public")) {
                reflection.setFlag(ReflectionFlag.Public);
                if (reflection.kindOf(ReflectionKind.CallSignature)) {
                    reflection.parent?.setFlag(ReflectionFlag.Public);
                }
                comment.removeModifier("@public");
            }
            if (comment.hasModifier("@readonly")) {
                const target = reflection.kindOf(ReflectionKind.GetSignature)
                    ? reflection.parent
                    : reflection;
                target.setFlag(ReflectionFlag.Readonly);
                comment.removeModifier("@readonly");
            }
            if (comment.hasModifier("@event") ||
                comment.hasModifier("@eventProperty")) {
                comment.blockTags.push(new CommentTag("@group", [{ kind: "text", text: "Events" }]));
                comment.removeModifier("@event");
                comment.removeModifier("@eventProperty");
            }
            if (reflection.kindOf(ReflectionKind.Project | ReflectionKind.SomeModule)) {
                comment.removeTags("@module");
                comment.removeModifier("@packageDocumentation");
            }
        }
        /**
         * Triggered when the converter has created a type parameter reflection.
         *
         * @param context  The context object describing the current state the converter is in.
         * @param reflection  The reflection that is currently processed.
         */
        onCreateTypeParameter(_context, reflection) {
            const comment = reflection.parent?.comment;
            if (comment) {
                let tag = comment.getIdentifiedTag(reflection.name, "@typeParam");
                if (!tag) {
                    tag = comment.getIdentifiedTag(reflection.name, "@template");
                }
                if (!tag) {
                    tag = comment.getIdentifiedTag(`<${reflection.name}>`, "@param");
                }
                if (!tag) {
                    tag = comment.getIdentifiedTag(reflection.name, "@param");
                }
                if (tag) {
                    reflection.comment = new Comment(tag.content);
                    reflection.comment.sourcePath = comment.sourcePath;
                    removeIfPresent(comment.blockTags, tag);
                }
            }
        }
        /**
         * Triggered when the converter has created a declaration or signature reflection.
         *
         * Invokes the comment parser.
         *
         * @param context  The context object describing the current state the converter is in.
         * @param reflection  The reflection that is currently processed.
         * @param node  The node that is currently processed if available.
         */
        onDeclaration(_context, reflection) {
            this.cascadeModifiers(reflection);
            const comment = reflection.comment;
            if (!comment)
                return;
            if (reflection.kindOf(ReflectionKind.SomeModule)) {
                const tag = comment.getTag("@module");
                if (tag) {
                    // If no name is specified, this is a flag to mark a comment as a module comment
                    // and should not result in a reflection rename.
                    const newName = Comment.combineDisplayParts(tag.content).trim();
                    if (newName.length && !newName.includes("\n")) {
                        reflection.name = newName;
                    }
                    removeIfPresent(comment.blockTags, tag);
                }
            }
            this.applyModifiers(reflection, comment);
            this.removeExcludedTags(comment);
        }
        /**
         * Triggered when the converter begins resolving a project.
         *
         * @param context  The context object describing the current state the converter is in.
         */
        onBeginResolve(context) {
            if (context.project.comment) {
                this.applyModifiers(context.project, context.project.comment);
                this.removeExcludedTags(context.project.comment);
            }
            const project = context.project;
            const reflections = Object.values(project.reflections);
            // Remove hidden reflections
            const hidden = new Set();
            for (const ref of reflections) {
                if (ref.kindOf(ReflectionKind.Accessor) && ref.flags.isReadonly) {
                    const decl = ref;
                    if (decl.setSignature) {
                        hidden.add(decl.setSignature);
                    }
                    // Clear flag set by @readonly since it shouldn't be rendered.
                    ref.setFlag(ReflectionFlag.Readonly, false);
                }
                if (this.isHidden(ref)) {
                    hidden.add(ref);
                }
            }
            hidden.forEach((reflection) => project.removeReflection(reflection));
            // remove functions with empty signatures after their signatures have been removed
            const [allRemoved, someRemoved] = partition(unique(filterMap(hidden, (reflection) => reflection.parent?.kindOf(ReflectionKind.SignatureContainer)
                ? reflection.parent
                : void 0)), (method) => method.getNonIndexSignatures().length === 0);
            allRemoved.forEach((reflection) => {
                project.removeReflection(reflection);
            });
            someRemoved.forEach((reflection) => {
                reflection.sources = reflection
                    .getNonIndexSignatures()
                    .flatMap((s) => s.sources ?? []);
            });
        }
        /**
         * Triggered when the converter resolves a reflection.
         *
         * Cleans up comment tags related to signatures like `@param` or `@returns`
         * and moves their data to the corresponding parameter reflections.
         *
         * This hook also copies over the comment of function implementations to their
         * signatures.
         *
         * @param context  The context object describing the current state the converter is in.
         * @param reflection  The reflection that is currently resolved.
         */
        onResolve(context, reflection) {
            if (reflection.comment) {
                if (reflection.comment.label &&
                    !/[A-Z_][A-Z0-9_]/.test(reflection.comment.label)) {
                    context.logger.warn(i18n.label_0_for_1_cannot_be_referenced(reflection.comment.label, reflection.getFriendlyFullName()));
                }
                for (const group of MUTUALLY_EXCLUSIVE_MODIFIERS) {
                    const intersect = setIntersection(group, reflection.comment.modifierTags);
                    if (intersect.size > 1) {
                        const [a, b] = intersect;
                        context.logger.warn(i18n.modifier_tag_0_is_mutually_exclusive_with_1_in_comment_for_2(a, b, reflection.getFriendlyFullName()));
                    }
                }
                mergeSeeTags(reflection.comment);
                movePropertyTags(reflection.comment, reflection);
                // Unlike other modifiers, this one has to wait until resolution to be removed
                // as it needs to remain present so that it can be checked when `@hidden` tags are
                // being processed.
                if (reflection.kindOf(ReflectionKind.Class)) {
                    reflection.comment.removeModifier("@hideconstructor");
                }
                // Similar story for this one, if a namespace is merged the tag should
                // still apply when merging the second declaration of the namespace
                reflection.comment.removeModifier("@primaryExport");
            }
            if ((reflection instanceof DeclarationReflection ||
                reflection instanceof ParameterReflection) &&
                reflection.comment) {
                let sigs = [];
                if (reflection.type instanceof ReflectionType) {
                    sigs = reflection.type.declaration.getNonIndexSignatures();
                }
                else if (reflection instanceof DeclarationReflection) {
                    sigs = reflection.getNonIndexSignatures();
                }
                // For variables and properties, the symbol might own the comment but we might also
                // have @param and @returns comments for an owned signature. Only do this if there is
                // exactly one signature as otherwise we have no hope of doing validation right.
                if (sigs.length === 1 && !sigs[0].comment) {
                    this.moveSignatureParamComments(sigs[0], reflection.comment);
                    const returnsTag = reflection.comment.getTag("@returns");
                    if (returnsTag) {
                        sigs[0].comment = new Comment();
                        sigs[0].comment.sourcePath = reflection.comment.sourcePath;
                        sigs[0].comment.blockTags.push(returnsTag);
                        reflection.comment.removeTags("@returns");
                    }
                }
                // Any cascaded tags will show up twice, once on this and once on our signatures
                // This is completely redundant, so remove them from the wrapping function.
                if (sigs.length && reflection.type?.type !== "reflection") {
                    for (const mod of this.cascadedModifierTags) {
                        reflection.comment.modifierTags.delete(mod);
                    }
                }
            }
            if (reflection instanceof SignatureReflection) {
                this.moveSignatureParamComments(reflection);
            }
        }
        moveSignatureParamComments(signature, comment = signature.comment) {
            if (!comment)
                return;
            signature.parameters?.forEach((parameter, index) => {
                if (parameter.name === "__namedParameters") {
                    const commentParams = comment.blockTags.filter((tag) => tag.tag === "@param" && !tag.name?.includes("."));
                    if (signature.parameters?.length === commentParams.length &&
                        commentParams[index].name) {
                        parameter.name = commentParams[index].name;
                    }
                }
                const tag = comment.getIdentifiedTag(parameter.name, "@param");
                if (tag) {
                    parameter.comment = new Comment(Comment.cloneDisplayParts(tag.content));
                    parameter.comment.sourcePath = comment.sourcePath;
                }
            });
            for (const parameter of signature.typeParameters || []) {
                const tag = comment.getIdentifiedTag(parameter.name, "@typeParam") ||
                    comment.getIdentifiedTag(parameter.name, "@template") ||
                    comment.getIdentifiedTag(`<${parameter.name}>`, "@param");
                if (tag) {
                    parameter.comment = new Comment(Comment.cloneDisplayParts(tag.content));
                    parameter.comment.sourcePath = comment.sourcePath;
                }
            }
            this.validateParamTags(signature, comment, signature.parameters || []);
            comment.removeTags("@param");
            comment.removeTags("@typeParam");
            comment.removeTags("@template");
        }
        removeExcludedTags(comment) {
            for (const tag of NEVER_RENDERED) {
                comment.removeTags(tag);
                comment.removeModifier(tag);
            }
            for (const tag of this.excludeTags) {
                comment.removeTags(tag);
                comment.removeModifier(tag);
            }
        }
        cascadeModifiers(reflection) {
            const parentComment = reflection.parent?.comment;
            if (!parentComment || reflection.kindOf(ReflectionKind.TypeLiteral)) {
                return;
            }
            const childMods = reflection.comment?.modifierTags ?? new Set();
            for (const mod of this.cascadedModifierTags) {
                if (parentComment.hasModifier(mod)) {
                    const exclusiveSet = MUTUALLY_EXCLUSIVE_MODIFIERS.find((tags) => tags.has(mod));
                    if (!exclusiveSet ||
                        Array.from(exclusiveSet).every((tag) => !childMods.has(tag))) {
                        reflection.comment ||= new Comment();
                        reflection.comment.modifierTags.add(mod);
                    }
                }
            }
        }
        /**
         * Determines whether or not a reflection has been hidden
         *
         * @param reflection Reflection to check if hidden
         */
        isHidden(reflection) {
            const comment = reflection.comment;
            if (reflection.flags.hasFlag(ReflectionFlag.Private) &&
                this.excludePrivate) {
                return true;
            }
            if (reflection.flags.hasFlag(ReflectionFlag.Protected) &&
                this.excludeProtected) {
                return true;
            }
            if (this.excludedByCategory(reflection)) {
                return true;
            }
            if (reflection.kindOf(ReflectionKind.ConstructorSignature |
                ReflectionKind.Constructor)) {
                if (comment?.hasModifier("@hideconstructor"))
                    return true;
                const cls = reflection.parent?.kindOf(ReflectionKind.Class)
                    ? reflection.parent
                    : reflection.parent?.parent?.kindOf(ReflectionKind.Class)
                        ? reflection.parent.parent
                        : undefined;
                if (cls?.comment?.hasModifier("@hideconstructor")) {
                    return true;
                }
            }
            if (!comment) {
                // We haven't moved comments from the parent for signatures without a direct
                // comment, so don't exclude those due to not being documented.
                if (reflection.kindOf(ReflectionKind.CallSignature |
                    ReflectionKind.ConstructorSignature) &&
                    reflection.parent?.comment) {
                    return false;
                }
                if (this.excludeNotDocumented) {
                    // Don't let excludeNotDocumented remove parameters.
                    if (!(reflection instanceof DeclarationReflection) &&
                        !(reflection instanceof SignatureReflection)) {
                        return false;
                    }
                    if (!reflection.kindOf(this.excludeNotDocumentedKinds)) {
                        return false;
                    }
                    // excludeNotDocumented should hide a module only if it has no visible children
                    if (reflection.kindOf(ReflectionKind.SomeModule)) {
                        if (!reflection.children) {
                            return true;
                        }
                        return reflection.children.every((child) => this.isHidden(child));
                    }
                    // signature containers should only be hidden if all their signatures are hidden
                    if (reflection.kindOf(ReflectionKind.SignatureContainer)) {
                        return reflection
                            .getAllSignatures()
                            .every((child) => this.isHidden(child));
                    }
                    // excludeNotDocumented should never hide parts of "type" reflections
                    return inTypeLiteral(reflection) === false;
                }
                return false;
            }
            const isHidden = comment.hasModifier("@hidden") ||
                comment.hasModifier("@ignore") ||
                (comment.hasModifier("@internal") && this.excludeInternal);
            if (!isHidden &&
                reflection.kindOf(ReflectionKind.ContainsCallSignatures)) {
                return reflection
                    .getNonIndexSignatures()
                    .every((sig) => this.isHidden(sig));
            }
            return isHidden;
        }
        excludedByCategory(reflection) {
            const excludeCategories = this.excludeCategories;
            let target;
            if (reflection instanceof DeclarationReflection) {
                target = reflection;
            }
            else if (reflection instanceof SignatureReflection) {
                target = reflection.parent;
            }
            if (!target || !excludeCategories.length)
                return false;
            const categories = CategoryPlugin.getCategories(target);
            if (categories.size === 0) {
                categories.add(this.defaultCategory);
            }
            return excludeCategories.some((cat) => categories.has(cat));
        }
        validateParamTags(signature, comment, params) {
            const paramTags = comment.blockTags.filter((tag) => tag.tag === "@param");
            removeIf(paramTags, (tag) => params.some((param) => param.name === tag.name));
            moveNestedParamTags(/* in-out */ paramTags, params, comment.sourcePath);
            if (!comment.inheritedFromParentDeclaration) {
                for (const tag of paramTags) {
                    this.application.logger.warn(i18n.signature_0_has_unused_param_with_name_1(signature.getFriendlyFullName(), tag.name ?? "(missing)"));
                }
            }
        }
    };
})();
export { CommentPlugin };
function inTypeLiteral(refl) {
    while (refl) {
        if (refl.kind === ReflectionKind.TypeLiteral) {
            return true;
        }
        refl = refl.parent;
    }
    return false;
}
function validHighlightedName(ref, name) {
    const refl = ref.reflection;
    // Assume external types are documented properly
    if (!refl)
        return true;
    // If it is a direct child, it is valid.
    if (refl.getChildByName([name]))
        return true;
    // Or if it is the child of the referenced reflection's type
    if (refl.isDeclaration() && refl.type?.type === "reflection") {
        if (refl.type.declaration.getChildByName([name])) {
            return true;
        }
    }
    return false;
}
// Moves tags like `@param foo.bar docs for bar` into the `bar` property of the `foo` parameter.
function moveNestedParamTags(
/* in-out */ paramTags, parameters, sourcePath) {
    const used = new Set();
    for (const param of parameters) {
        const visitor = {
            reflection(target) {
                const tags = paramTags.filter((t) => t.name?.startsWith(`${param.name}.`));
                for (const tag of tags) {
                    const path = tag.name.split(".");
                    path.shift();
                    const child = target.declaration.getChildOrTypePropertyByName(path);
                    if (child && !child.comment) {
                        child.comment = new Comment(Comment.cloneDisplayParts(tag.content));
                        child.comment.sourcePath = sourcePath;
                        used.add(paramTags.indexOf(tag));
                    }
                }
            },
            // #1876, also do this for unions/intersections.
            union(u) {
                u.types.forEach((t) => t.visit(visitor));
            },
            intersection(i) {
                i.types.forEach((t) => t.visit(visitor));
            },
            // #2147, support highlighting parts of a referenced type
            reference(ref) {
                for (let i = 0; i < paramTags.length; ++i) {
                    const tag = paramTags[i];
                    if (tag.name?.startsWith(`${param.name}.`)) {
                        const childName = tag.name.substring(param.name.length + 1);
                        if (!validHighlightedName(ref, childName)) {
                            continue;
                        }
                        ref.highlightedProperties ??= new Map();
                        ref.highlightedProperties.set(childName, paramTags[i].content);
                        used.add(i);
                    }
                }
            },
        };
        param.type?.visit(visitor);
    }
    const toRemove = Array.from(used)
        .sort((a, b) => a - b)
        .reverse();
    for (const index of toRemove) {
        paramTags.splice(index, 1);
    }
}
function movePropertyTags(comment, container) {
    const propTags = comment.blockTags.filter((tag) => tag.tag === "@prop" || tag.tag === "@property");
    comment.removeTags("@prop");
    comment.removeTags("@property");
    for (const prop of propTags) {
        if (!prop.name)
            continue;
        const child = container.getChildByName(prop.name);
        if (child) {
            child.comment = new Comment(Comment.cloneDisplayParts(prop.content));
            child.comment.sourcePath = comment.sourcePath;
            if (child instanceof DeclarationReflection && child.signatures) {
                for (const sig of child.signatures) {
                    sig.comment = new Comment(Comment.cloneDisplayParts(prop.content));
                    sig.comment.sourcePath = comment.sourcePath;
                }
            }
        }
    }
}
function mergeSeeTags(comment) {
    const see = comment.getTags("@see");
    if (see.length < 2)
        return;
    const index = comment.blockTags.indexOf(see[0]);
    comment.removeTags("@see");
    see[0].content = see.flatMap((part) => [
        { kind: "text", text: " - " },
        ...part.content,
        { kind: "text", text: "\n" },
    ]);
    comment.blockTags.splice(index, 0, see[0]);
}
