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 = [ 137 cast(uint) dec.tokens[0].index, 138 cast(uint)(dec.tokens[$ - 1].index + dec.tokens[$ - 1].text.length + 1) 139 ]; 140 super.visit(dec); 141 if (writtenRegion && regions.length >= 2 && regions[$ - 2].sameBlockAs(regions[$ - 1])) 142 { 143 auto range = regions[$ - 1].region; 144 if (regions[$ - 1].minIndentation.scoreIndent < regions[$ - 2].minIndentation.scoreIndent) 145 regions[$ - 2].minIndentation = regions[$ - 1].minIndentation; 146 regions[$ - 2].region[1] = range[1]; 147 regions.length--; 148 } 149 } 150 151 override void visit(const AttributeDeclaration dec) 152 { 153 auto before = context.attributes[]; 154 dec.accept(this); 155 auto now = context.attributes; 156 if (now.length > before.length) 157 { 158 auto permaAdded = now[before.length .. $]; 159 160 } 161 } 162 163 void putRegion(CodeRegionType type, uint[2] range = typeof(uint.init)[2].init) 164 { 165 if (range == typeof(uint.init)[2].init) 166 range = currentRange; 167 168 CodeRegionProtection protection; 169 CodeRegionStatic staticness; 170 171 auto prot = context.protectionAttribute; 172 bool stickyProtection = false; 173 if (prot) 174 { 175 stickyProtection = prot.sticky; 176 if (prot.attributes[0].type == tok!"private") 177 protection = CodeRegionProtection.private_; 178 else if (prot.attributes[0].type == tok!"protected") 179 protection = CodeRegionProtection.protected_; 180 else if (prot.attributes[0].type == tok!"package") 181 { 182 if (prot.attributes.length > 1) 183 protection = CodeRegionProtection.packageIdentifier; 184 else 185 protection = CodeRegionProtection.package_; 186 } 187 else if (prot.attributes[0].type == tok!"public") 188 protection = CodeRegionProtection.public_; 189 } 190 191 staticness = context.isStatic ? CodeRegionStatic.static_ : CodeRegionStatic.instanced; 192 193 if (stickyProtection) 194 { 195 assert(prot); 196 //dfmt off 197 Region pr = { 198 type: cast(CodeRegionType)0, 199 protection: protection, 200 staticness: cast(CodeRegionStatic)0, 201 region: [cast(uint) prot.attributes[0].index, cast(uint) prot.attributes[0].index], 202 affectsFollowing: true 203 }; 204 //dfmt on 205 regions ~= pr; 206 } 207 208 //dfmt off 209 Region r = { 210 type: type, 211 protection: protection, 212 staticness: staticness, 213 minIndentation: determineIndentation(code[range[0] .. range[1]]), 214 region: range 215 }; 216 //dfmt on 217 regions ~= r; 218 writtenRegion = true; 219 } 220 221 alias visit = AttributesVisitor.visit; 222 223 bool writtenRegion; 224 const(char)[] code; 225 Region[] regions; 226 uint[2] currentRange; 227 } 228 229 private int scoreIndent(string indent) 230 { 231 auto len = indent.countUntil!(a => !a.isWhite); 232 if (len == -1) 233 return cast(int) indent.length; 234 return indent[0 .. len].map!(a => a == ' ' ? 1 : 4).sum; 235 }