/
ImplementsPlugin.ts
124 lines (110 loc) · 5.22 KB
/
ImplementsPlugin.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import { Reflection, ReflectionKind, DeclarationReflection, SignatureReflection } from '../../models/reflections/index';
import { Type, ReferenceType } from '../../models/types/index';
import { Component, ConverterComponent } from '../components';
import { Converter } from '../converter';
import { Context } from '../context';
import { Comment } from '../../models/comments/comment';
/**
* A plugin that detects interface implementations of functions and
* properties on classes and links them.
*/
@Component({name: 'implements'})
export class ImplementsPlugin extends ConverterComponent {
/**
* Create a new ImplementsPlugin instance.
*/
initialize() {
this.listenTo(this.owner, Converter.EVENT_RESOLVE, this.onResolve, -10);
}
/**
* Mark all members of the given class to be the implementation of the matching interface member.
*
* @param context The context object describing the current state the converter is in.
* @param classReflection The reflection of the classReflection class.
* @param interfaceReflection The reflection of the interfaceReflection interface.
*/
private analyzeClass(context: Context, classReflection: DeclarationReflection, interfaceReflection: DeclarationReflection) {
if (!interfaceReflection.children) {
return;
}
interfaceReflection.children.forEach((interfaceMember: DeclarationReflection) => {
if (!(interfaceMember instanceof DeclarationReflection)) {
return;
}
let classMember: DeclarationReflection | undefined;
if (!classReflection.children) {
return;
}
for (let index = 0, count = classReflection.children.length; index < count; index++) {
const child = classReflection.children[index];
if (child.name !== interfaceMember.name) {
continue;
}
if (child.flags.isStatic !== interfaceMember.flags.isStatic) {
continue;
}
classMember = child;
break;
}
if (!classMember) {
return;
}
const interfaceMemberName = interfaceReflection.name + '.' + interfaceMember.name;
classMember.implementationOf = new ReferenceType(interfaceMemberName, ReferenceType.SYMBOL_FQN_RESOLVED, interfaceMember);
this.copyComment(classMember, interfaceMember);
if (interfaceMember.kindOf(ReflectionKind.FunctionOrMethod) && interfaceMember.signatures && classMember.signatures) {
interfaceMember.signatures.forEach((interfaceSignature: SignatureReflection) => {
const interfaceParameters = interfaceSignature.getParameterTypes();
(classMember!.signatures || []).forEach((classSignature: SignatureReflection) => {
if (Type.isTypeListEqual(interfaceParameters, classSignature.getParameterTypes())) {
classSignature.implementationOf = new ReferenceType(interfaceMemberName, ReferenceType.SYMBOL_FQN_RESOLVED, interfaceSignature);
this.copyComment(classSignature, interfaceSignature);
}
});
});
}
});
}
/**
* Copy the comment of the source reflection to the target reflection.
*
* @param target
* @param source
*/
private copyComment(target: Reflection, source: Reflection) {
if (target.comment && source.comment && target.comment.hasTag('inheritdoc')) {
target.comment.copyFrom(source.comment);
if (target instanceof SignatureReflection && target.parameters &&
source instanceof SignatureReflection && source.parameters) {
for (let index = 0, count = target.parameters.length; index < count; index++) {
const sourceParameter = source.parameters[index];
if (sourceParameter && sourceParameter.comment) {
const targetParameter = target.parameters[index];
if (!targetParameter.comment) {
targetParameter.comment = new Comment();
targetParameter.comment.copyFrom(sourceParameter.comment);
}
}
}
}
}
}
/**
* Triggered when the converter resolves a reflection.
*
* @param context The context object describing the current state the converter is in.
* @param reflection The reflection that is currently resolved.
*/
private onResolve(context: Context, reflection: DeclarationReflection) {
if (reflection.kindOf(ReflectionKind.Class) && reflection.implementedTypes) {
reflection.implementedTypes.forEach((type: Type) => {
if (!(type instanceof ReferenceType)) {
return;
}
if (type.reflection && type.reflection.kindOf(ReflectionKind.Interface)) {
this.analyzeClass(context, reflection, <DeclarationReflection> type.reflection);
}
});
}
}
}