• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* @internal */
2namespace ts.codefix {
3    const deleteUnmatchedParameter = "deleteUnmatchedParameter";
4    const renameUnmatchedParameter = "renameUnmatchedParameter";
5
6    const errorCodes = [
7        Diagnostics.JSDoc_param_tag_has_name_0_but_there_is_no_parameter_with_that_name.code,
8    ];
9
10    registerCodeFix({
11        fixIds: [deleteUnmatchedParameter, renameUnmatchedParameter],
12        errorCodes,
13        getCodeActions: function getCodeActionsToFixUnmatchedParameter(context) {
14            const { sourceFile, span } = context;
15            const actions: CodeFixAction[] = [];
16            const info = getInfo(sourceFile, span.start);
17            if (info) {
18                append(actions, getDeleteAction(context, info));
19                append(actions, getRenameAction(context, info));
20                return actions;
21            }
22            return undefined;
23        },
24        getAllCodeActions: function getAllCodeActionsToFixUnmatchedParameter(context) {
25            const tagsToSignature = new Map<SignatureDeclaration, JSDocTag[]>();
26            return createCombinedCodeActions(textChanges.ChangeTracker.with(context, changes => {
27                eachDiagnostic(context, errorCodes, ({ file, start }) => {
28                    const info = getInfo(file, start);
29                    if (info) {
30                        tagsToSignature.set(info.signature, append(tagsToSignature.get(info.signature), info.jsDocParameterTag));
31                    }
32                });
33
34                tagsToSignature.forEach((tags, signature) => {
35                    if (context.fixId === deleteUnmatchedParameter) {
36                        const tagsSet = new Set(tags);
37                        changes.filterJSDocTags(signature.getSourceFile(), signature, t => !tagsSet.has(t));
38                    }
39                });
40            }));
41        }
42    });
43
44    function getDeleteAction(context: CodeFixContext, { name, signature, jsDocParameterTag }: Info) {
45        const changes = textChanges.ChangeTracker.with(context, changeTracker =>
46            changeTracker.filterJSDocTags(context.sourceFile, signature, t => t !== jsDocParameterTag));
47        return createCodeFixAction(
48            deleteUnmatchedParameter,
49            changes,
50            [Diagnostics.Delete_unused_param_tag_0, name.getText(context.sourceFile)],
51            deleteUnmatchedParameter,
52            Diagnostics.Delete_all_unused_param_tags
53        );
54    }
55
56    function getRenameAction(context: CodeFixContext, { name, signature, jsDocParameterTag }: Info) {
57        if (!length(signature.parameters)) return undefined;
58
59        const sourceFile = context.sourceFile;
60        const tags = getJSDocTags(signature);
61        const names = new Set<__String>();
62        for (const tag of tags) {
63            if (isJSDocParameterTag(tag) && isIdentifier(tag.name)) {
64                names.add(tag.name.escapedText);
65            }
66        }
67        // @todo - match to all available names instead to the first parameter name
68        // @see /codeFixRenameUnmatchedParameter3.ts
69        const parameterName = firstDefined(signature.parameters, p =>
70            isIdentifier(p.name) && !names.has(p.name.escapedText) ? p.name.getText(sourceFile) : undefined);
71        if (parameterName === undefined) return undefined;
72
73        const newJSDocParameterTag = factory.updateJSDocParameterTag(
74            jsDocParameterTag,
75            jsDocParameterTag.tagName,
76            factory.createIdentifier(parameterName),
77            jsDocParameterTag.isBracketed,
78            jsDocParameterTag.typeExpression,
79            jsDocParameterTag.isNameFirst,
80            jsDocParameterTag.comment
81        );
82        const changes = textChanges.ChangeTracker.with(context, changeTracker =>
83            changeTracker.replaceJSDocComment(sourceFile, signature, map(tags, t => t === jsDocParameterTag ? newJSDocParameterTag : t)));
84        return createCodeFixActionWithoutFixAll(renameUnmatchedParameter, changes, [Diagnostics.Rename_param_tag_name_0_to_1, name.getText(sourceFile), parameterName]);
85    }
86
87    interface Info {
88        readonly signature: SignatureDeclaration;
89        readonly jsDocParameterTag: JSDocParameterTag;
90        readonly name: Identifier;
91    }
92
93    function getInfo(sourceFile: SourceFile, pos: number): Info | undefined {
94        const token = getTokenAtPosition(sourceFile, pos);
95        if (token.parent && isJSDocParameterTag(token.parent) && isIdentifier(token.parent.name)) {
96            const jsDocParameterTag = token.parent;
97            const signature = getHostSignatureFromJSDoc(jsDocParameterTag);
98            if (signature) {
99                return { signature, name: token.parent.name, jsDocParameterTag };
100            }
101        }
102        return undefined;
103    }
104}
105