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 }