1 /// Visitor classifying types and groups of regions of root definitions. 2 module workspaced.visitors.classifier; 3 4 import workspaced.visitors.attributes; 5 6 import workspaced.helpers : determineIndentation; 7 8 import workspaced.com.dcdext; 9 10 import std.algorithm; 11 import std.ascii; 12 import std.meta; 13 import std.range; 14 import std.string; 15 16 import dparse.ast; 17 import dparse.lexer; 18 19 class CodeDefinitionClassifier : AttributesVisitor 20 { 21 struct Region 22 { 23 CodeRegionType type; 24 CodeRegionProtection protection; 25 CodeRegionStatic staticness; 26 string minIndentation; 27 uint[2] region; 28 bool affectsFollowing; 29 30 bool sameBlockAs(in Region other) 31 { 32 return type == other.type && protection == other.protection && staticness == other.staticness; 33 } 34 } 35 36 this(const(char)[] code) 37 { 38 this.code = code; 39 } 40 41 override void visit(const AliasDeclaration aliasDecl) 42 { 43 putRegion(CodeRegionType.aliases); 44 } 45 46 override void visit(const AliasThisDeclaration aliasDecl) 47 { 48 putRegion(CodeRegionType.aliases); 49 } 50 51 override void visit(const ClassDeclaration typeDecl) 52 { 53 putRegion(CodeRegionType.types); 54 } 55 56 override void visit(const InterfaceDeclaration typeDecl) 57 { 58 putRegion(CodeRegionType.types); 59 } 60 61 override void visit(const StructDeclaration typeDecl) 62 { 63 putRegion(CodeRegionType.types); 64 } 65 66 override void visit(const UnionDeclaration typeDecl) 67 { 68 putRegion(CodeRegionType.types); 69 } 70 71 override void visit(const EnumDeclaration typeDecl) 72 { 73 putRegion(CodeRegionType.types); 74 } 75 76 override void visit(const AnonymousEnumDeclaration typeDecl) 77 { 78 putRegion(CodeRegionType.types); 79 } 80 81 override void visit(const AutoDeclaration field) 82 { 83 putRegion(CodeRegionType.fields); 84 } 85 86 override void visit(const VariableDeclaration field) 87 { 88 putRegion(CodeRegionType.fields); 89 } 90 91 override void visit(const Constructor ctor) 92 { 93 putRegion(CodeRegionType.ctor); 94 } 95 96 override void visit(const StaticConstructor ctor) 97 { 98 putRegion(CodeRegionType.ctor); 99 } 100 101 override void visit(const SharedStaticConstructor ctor) 102 { 103 putRegion(CodeRegionType.ctor); 104 } 105 106 override void visit(const Postblit copyctor) 107 { 108 putRegion(CodeRegionType.copyctor); 109 } 110 111 override void visit(const Destructor dtor) 112 { 113 putRegion(CodeRegionType.dtor); 114 } 115 116 override void visit(const StaticDestructor dtor) 117 { 118 putRegion(CodeRegionType.dtor); 119 } 120 121 override void visit(const SharedStaticDestructor dtor) 122 { 123 putRegion(CodeRegionType.dtor); 124 } 125 126 override void visit(const FunctionDeclaration method) 127 { 128 putRegion((method.attributes && method.attributes.any!(a => a.atAttribute 129 && a.atAttribute.identifier.text == "property")) ? CodeRegionType.properties 130 : CodeRegionType.methods); 131 } 132 133 override void visit(const Declaration dec) 134 { 135 writtenRegion = false; 136 currentRange = [cast(uint) dec.tokens[0].index, 137 cast(uint)(dec.tokens[$ - 1].index + dec.tokens[$ - 1].text.length + 1)]; 138 super.visit(dec); 139 if (writtenRegion && regions.length >= 2 && regions[$ - 2].sameBlockAs(regions[$ - 1])) 140 { 141 auto range = regions[$ - 1].region; 142 if (regions[$ - 1].minIndentation.scoreIndent < regions[$ - 2].minIndentation.scoreIndent) 143 regions[$ - 2].minIndentation = regions[$ - 1].minIndentation; 144 regions[$ - 2].region[1] = range[1]; 145 regions.length--; 146 } 147 } 148 149 override void visit(const AttributeDeclaration dec) 150 { 151 auto before = context.attributes[]; 152 dec.accept(this); 153 auto now = context.attributes; 154 if (now.length > before.length) 155 { 156 auto permaAdded = now[before.length .. $]; 157 158 } 159 } 160 161 void putRegion(CodeRegionType type, uint[2] range = typeof(uint.init)[2].init) 162 { 163 if (range == typeof(uint.init)[2].init) 164 range = currentRange; 165 166 CodeRegionProtection protection; 167 CodeRegionStatic staticness; 168 169 auto prot = context.protectionAttribute; 170 bool stickyProtection = false; 171 if (prot) 172 { 173 stickyProtection = prot.sticky; 174 if (prot.attributes[0].type == tok!"private") 175 protection = CodeRegionProtection.private_; 176 else if (prot.attributes[0].type == tok!"protected") 177 protection = CodeRegionProtection.protected_; 178 else if (prot.attributes[0].type == tok!"package") 179 { 180 if (prot.attributes.length > 1) 181 protection = CodeRegionProtection.packageIdentifier; 182 else 183 protection = CodeRegionProtection.package_; 184 } 185 else if (prot.attributes[0].type == tok!"public") 186 protection = CodeRegionProtection.public_; 187 } 188 189 staticness = context.isStatic ? CodeRegionStatic.static_ : CodeRegionStatic.instanced; 190 191 if (stickyProtection) 192 { 193 assert(prot); 194 //dfmt off 195 Region pr = { 196 type: cast(CodeRegionType)0, 197 protection: protection, 198 staticness: cast(CodeRegionStatic)0, 199 region: [cast(uint) prot.attributes[0].index, cast(uint) prot.attributes[0].index], 200 affectsFollowing: true 201 }; 202 //dfmt on 203 regions ~= pr; 204 } 205 206 //dfmt off 207 Region r = { 208 type: type, 209 protection: protection, 210 staticness: staticness, 211 minIndentation: determineIndentation(code[range[0] .. range[1]]), 212 region: range 213 }; 214 //dfmt on 215 regions ~= r; 216 writtenRegion = true; 217 } 218 219 alias visit = AttributesVisitor.visit; 220 221 bool writtenRegion; 222 const(char)[] code; 223 Region[] regions; 224 uint[2] currentRange; 225 } 226 227 private int scoreIndent(string indent) 228 { 229 auto len = indent.countUntil!(a => !a.isWhite); 230 if (len == -1) 231 return cast(int) indent.length; 232 return indent[0 .. len].map!(a => a == ' ' ? 1 : 4).sum; 233 }