1 // Copyright Brian Schott 2015. 2 // Distributed under the Boost Software License, Version 1.0. 3 // (See accompanying file LICENSE.txt or copy at 4 // http://www.boost.org/LICENSE_1_0.txt) 5 6 module dfmt.indentation; 7 8 import dparse.lexer; 9 10 /** 11 * Returns: true if the given token type is a wrap indent type 12 */ 13 bool isWrapIndent(IdType type) pure nothrow @nogc @safe 14 { 15 return type != tok!"{" && type != tok!"case" && type != tok!"@" 16 && type != tok!"]" && type != tok!"(" && type != tok!")" && isOperator(type); 17 } 18 19 /** 20 * Returns: true if the given token type is a temporary indent type 21 */ 22 bool isTempIndent(IdType type) pure nothrow @nogc @safe 23 { 24 return type != tok!")" && type != tok!"{" && type != tok!"case" && type != tok!"@"; 25 } 26 27 /** 28 * Stack for managing indent levels. 29 */ 30 struct IndentStack 31 { 32 /** 33 * Get the indent size at the most recent occurrence of the given indent type 34 */ 35 int indentToMostRecent(IdType item) const 36 { 37 if (index == 0) 38 return -1; 39 size_t i = index - 1; 40 while (true) 41 { 42 if (arr[i] == item) 43 return indentSize(i); 44 if (i > 0) 45 i--; 46 else 47 return -1; 48 } 49 } 50 51 int wrapIndents() const pure nothrow @property 52 { 53 if (index == 0) 54 return 0; 55 int tempIndentCount = 0; 56 for (size_t i = index; i > 0; i--) 57 { 58 if (!isWrapIndent(arr[i - 1]) && arr[i - 1] != tok!"]") 59 break; 60 tempIndentCount++; 61 } 62 return tempIndentCount; 63 } 64 65 /** 66 * Pushes the given indent type on to the stack. 67 */ 68 void push(IdType item) pure nothrow 69 { 70 arr[index] = item; 71 index = index + 1 == arr.length ? index : index + 1; 72 } 73 74 /** 75 * Pops the top indent from the stack. 76 */ 77 void pop() pure nothrow 78 { 79 index = index == 0 ? index : index - 1; 80 } 81 82 /** 83 * Pops all wrapping indents from the top of the stack. 84 */ 85 void popWrapIndents() pure nothrow @safe @nogc 86 { 87 while (index > 0 && isWrapIndent(arr[index - 1])) 88 index--; 89 } 90 91 /** 92 * Pops all temporary indents from the top of the stack. 93 */ 94 void popTempIndents() pure nothrow @safe @nogc 95 { 96 while (index > 0 && isTempIndent(arr[index - 1])) 97 index--; 98 } 99 100 bool topAre(IdType[] types...) 101 { 102 if (types.length > index) 103 return false; 104 return arr[index - types.length .. index] == types; 105 106 } 107 108 /** 109 * Returns: `true` if the top of the indent stack is the given indent type. 110 */ 111 bool topIs(IdType type) const pure nothrow @safe @nogc 112 { 113 return index > 0 && index <= arr.length && arr[index - 1] == type; 114 } 115 116 /** 117 * Returns: `true` if the top of the indent stack is a temporary indent 118 */ 119 bool topIsTemp() 120 { 121 return index > 0 && index <= arr.length && isTempIndent(arr[index - 1]); 122 } 123 124 /** 125 * Returns: `true` if the top of the indent stack is a wrapping indent 126 */ 127 bool topIsWrap() 128 { 129 return index > 0 && index <= arr.length && isWrapIndent(arr[index - 1]); 130 } 131 132 /** 133 * Returns: `true` if the top of the indent stack is one of the given token 134 * types. 135 */ 136 bool topIsOneOf(IdType[] types...) const pure nothrow @safe @nogc 137 { 138 if (index == 0) 139 return false; 140 immutable topType = arr[index - 1]; 141 foreach (t; types) 142 if (t == topType) 143 return true; 144 return false; 145 } 146 147 IdType top() const pure nothrow @property @safe @nogc 148 { 149 return arr[index - 1]; 150 } 151 152 int indentLevel() const pure nothrow @property @safe @nogc 153 { 154 return indentSize(); 155 } 156 157 int length() const pure nothrow @property @safe @nogc 158 { 159 return cast(int) index; 160 } 161 162 /+void dump() 163 { 164 import std.stdio : stderr; 165 import dparse.lexer : str; 166 import std.algorithm.iteration : map; 167 168 stderr.writefln("\033[31m%(%s %)\033[0m", arr[0 .. index].map!(a => str(a))); 169 }+/ 170 171 private: 172 173 size_t index; 174 175 IdType[256] arr; 176 177 int indentSize(const size_t k = size_t.max) const pure nothrow @safe @nogc 178 { 179 if (index == 0 || k == 0) 180 return 0; 181 immutable size_t j = k == size_t.max ? index : k; 182 int size = 0; 183 int parenCount; 184 foreach (i; 0 .. j) 185 { 186 immutable int pc = (arr[i] == tok!"!" || arr[i] == tok!"(" || arr[i] == tok!")") ? parenCount + 1 187 : parenCount; 188 if ((isWrapIndent(arr[i]) || arr[i] == tok!"(") && parenCount > 1) 189 { 190 parenCount = pc; 191 continue; 192 } 193 if (i + 1 < index) 194 { 195 if (arr[i] == tok!"]") 196 continue; 197 immutable currentIsNonWrapTemp = !isWrapIndent(arr[i]) 198 && isTempIndent(arr[i]) && arr[i] != tok!")" && arr[i] != tok!"!"; 199 if (arr[i] == tok!"static" && (arr[i + 1] == tok!"if" 200 || arr[i + 1] == tok!"else") && (i + 2 >= index || arr[i + 2] != tok!"{")) 201 { 202 parenCount = pc; 203 continue; 204 } 205 if (currentIsNonWrapTemp && (arr[i + 1] == tok!"switch" 206 || arr[i + 1] == tok!"{" || arr[i + 1] == tok!")")) 207 { 208 parenCount = pc; 209 continue; 210 } 211 } 212 else if (parenCount == 0 && arr[i] == tok!"(") 213 size++; 214 if (arr[i] == tok!"!") 215 size++; 216 parenCount = pc; 217 size++; 218 } 219 return size; 220 } 221 } 222 223 unittest 224 { 225 IndentStack stack; 226 stack.push(tok!"{"); 227 assert(stack.length == 1); 228 assert(stack.indentLevel == 1); 229 stack.pop(); 230 assert(stack.length == 0); 231 assert(stack.indentLevel == 0); 232 stack.push(tok!"if"); 233 assert(stack.topIsTemp()); 234 stack.popTempIndents(); 235 assert(stack.length == 0); 236 }