1 /// Finds methods in a specified interface or class location.
2 module workspaced.visitors.methodfinder;
3 
4 import workspaced.visitors.attributes;
5 
6 import workspaced.dparseext;
7 
8 import dparse.ast;
9 import dparse.formatter;
10 import dparse.lexer;
11 
12 import std.algorithm;
13 import std.array;
14 import std.range;
15 import std.string;
16 
17 struct ArgumentInfo
18 {
19 	string signature, type, name;
20 
21 	string toString() const
22 	{
23 		return name;
24 	}
25 }
26 
27 struct MethodDetails
28 {
29 	string name, signature, returnType;
30 	ArgumentInfo[] arguments;
31 	bool isNothrowOrNogc;
32 	bool hasBody;
33 	bool needsImplementation;
34 	bool optionalImplementation;
35 
36 	string identifier()
37 	{
38 		return format("%s %s(%(%s,%))", returnType, name, arguments.map!"a.type");
39 	}
40 }
41 
42 struct FieldDetails
43 {
44 	string name, type;
45 	bool isPrivate;
46 }
47 
48 struct InterfaceDetails
49 {
50 	/// Entire code of the file
51 	string code;
52 	bool needsOverride;
53 	string name;
54 	FieldDetails[] fields;
55 	MethodDetails[] methods;
56 	string[] parents;
57 	string[] normalizedParents;
58 	int[] parentPositions;
59 }
60 
61 class InterfaceMethodFinder : AttributesVisitor
62 {
63 	this(string code, int targetPosition)
64 	{
65 		this.code = code;
66 		details.code = code;
67 		this.targetPosition = targetPosition;
68 	}
69 
70 	override void visit(const ClassDeclaration dec)
71 	{
72 		auto c = context.save();
73 		context.pushContainer(ASTContext.ContainerAttribute.Type.class_, dec.name.text);
74 		visitInterface(dec.name, dec.baseClassList, dec.structBody, true);
75 		context.restore(c);
76 	}
77 
78 	override void visit(const InterfaceDeclaration dec)
79 	{
80 		auto c = context.save();
81 		context.pushContainer(ASTContext.ContainerAttribute.Type.interface_, dec.name.text);
82 		visitInterface(dec.name, dec.baseClassList, dec.structBody, false);
83 		context.restore(c);
84 	}
85 
86 	private void visitInterface(const Token name, const BaseClassList baseClassList,
87 			const StructBody structBody, bool needsOverride)
88 	{
89 		if (!structBody)
90 			return;
91 		if (targetPosition >= name.index && targetPosition < structBody.endLocation)
92 		{
93 			details.name = name.text;
94 			if (baseClassList)
95 				foreach (base; baseClassList.items)
96 				{
97 					if (!base.type2 || !base.type2.typeIdentifierPart
98 							|| !base.type2.typeIdentifierPart.identifierOrTemplateInstance)
99 						continue;
100 					// TODO: template support!
101 					details.parents ~= astToString(base.type2);
102 					details.normalizedParents ~= astToString(base.type2);
103 					details.parentPositions ~= cast(
104 							int) base.type2.typeIdentifierPart.identifierOrTemplateInstance.identifier.index + 1;
105 				}
106 			details.needsOverride = needsOverride;
107 			inTarget = true;
108 			super.visit(structBody);
109 			inTarget = false;
110 		}
111 	}
112 
113 	override void visit(const FunctionDeclaration dec)
114 	{
115 		if (!inTarget)
116 			return;
117 		auto origBody = (cast() dec).functionBody;
118 		auto origComment = (cast() dec).comment;
119 		const implLevel = context.requiredImplementationLevel;
120 		const optionalImplementation = implLevel == 1 && origBody is null;
121 		const needsImplementation = implLevel == 9 || optionalImplementation;
122 		(cast() dec).functionBody = null;
123 		(cast() dec).comment = null;
124 		scope (exit)
125 		{
126 			(cast() dec).functionBody = origBody;
127 			(cast() dec).comment = origComment;
128 		}
129 		auto t = appender!string;
130 		format(t, dec);
131 		string method = context.localFormattedAttributes.chain([t.data.strip])
132 			.filter!(a => a.length > 0 && !a.among!("abstract", "final")).join(" ");
133 		const hasBody = !!origBody;
134 		ArgumentInfo[] arguments;
135 		if (dec.parameters)
136 			foreach (arg; dec.parameters.parameters)
137 				arguments ~= ArgumentInfo(astToString(arg), astToString(arg.type), arg.name.text);
138 		string returnType = dec.returnType ? astToString(dec.returnType) : "void";
139 		details.methods ~= MethodDetails(dec.name.text, method, returnType, arguments,
140 				context.isNothrow || context.isNogc, hasBody, needsImplementation, optionalImplementation);
141 	}
142 
143 	override void visit(const FunctionBody)
144 	{
145 	}
146 
147 	override void visit(const VariableDeclaration variable)
148 	{
149 		if (!inTarget)
150 			return;
151 		if (!variable.type)
152 			return;
153 		string type = astToString(variable.type);
154 		auto isPrivate = context.protectionType == tok!"private";
155 
156 		foreach (decl; variable.declarators)
157 			details.fields ~= FieldDetails(decl.name.text, type, isPrivate);
158 	}
159 
160 	alias visit = AttributesVisitor.visit;
161 
162 	string code;
163 	bool inTarget;
164 	int targetPosition;
165 	InterfaceDetails details;
166 }