diff --git a/src/dfmt/formatter.d b/src/dfmt/formatter.d index 5438dc6..d762537 100644 --- a/src/dfmt/formatter.d +++ b/src/dfmt/formatter.d @@ -318,7 +318,7 @@ private: void formatModuleOrImport() { - auto t = current.type; + immutable t = current.type; writeToken(); if (currentIs(tok!"(")) { @@ -464,12 +464,20 @@ private: void formatColon() { - if (astInformation.caseEndLocations.canFindIndex(current.index) - || astInformation.attributeDeclarationLines.canFindIndex(current.line)) + import dfmt.editorconfig : OptionalBoolean; + + immutable bool isCase = astInformation.caseEndLocations.canFindIndex(current.index); + immutable bool isAttribute = astInformation.attributeDeclarationLines.canFindIndex(current.line); + if (isCase || isAttribute) { writeToken(); if (!currentIs(tok!"{")) + { + if (isCase && !indents.topIs(tok!"case") && config.dfmt_align_switch_statements == OptionalBoolean.f) + indents.push(tok!"case"); newline(); + } + } else if (peekBackIs(tok!"identifier") && (peekBack2Is(tok!"{", true) || peekBack2Is(tok!"}", true) || peekBack2Is(tok!";", true) @@ -541,7 +549,7 @@ private: { if (currentIs(tok!"{")) indents.popTempIndents(); - indentLevel = indents.indentSize; + indentLevel = indents.indentLevel; newline(); } } @@ -588,9 +596,9 @@ private: { indents.popWrapIndents(); if (indents.length && isTempIndent(indents.top)) - indentLevel = indents.indentSize - 1; + indentLevel = indents.indentLevel - 1; else - indentLevel = indents.indentSize; + indentLevel = indents.indentLevel; if (!peekBackIsSlashSlash()) { @@ -604,7 +612,7 @@ private: { writeToken(); indents.popTempIndents(); - indentLevel = indents.indentSize - 1; + indentLevel = indents.indentLevel - 1; } indents.push(tok!"{"); if (!currentIs(tok!"{")) @@ -1064,7 +1072,6 @@ private: if (hasCurrent) { - bool switchLabel = false; if (currentIs(tok!"else")) { auto i = indents.indentToMostRecent(tok!"if"); @@ -1075,36 +1082,31 @@ private: } else if (currentIs(tok!"identifier") && peekIs(tok!":")) { - while ((peekBackIs(tok!"}", true) || peekBackIs(tok!";", true)) - && indents.length && isTempIndent(indents.top())) - { - indents.pop(); - } + if (peekBackIs(tok!"}", true) || peekBackIs(tok!";", true)) + indents.popTempIndents(); immutable l = indents.indentToMostRecent(tok!"switch"); - if (l != -1) - { + if (l != -1 && config.dfmt_align_switch_statements == OptionalBoolean.t) indentLevel = l; - switchLabel = true; - } else if (config.dfmt_compact_labeled_statements == OptionalBoolean.f - || !isBlockHeader(2) || peek2Is(tok!"if")) + || !isBlockHeader(2) || peek2Is(tok!"if")) { immutable l2 = indents.indentToMostRecent(tok!"{"); - indentLevel = l2 == -1 ? indentLevel : l2; + indentLevel = l2 != -1 ? l2 : indents.indentLevel - 1; } else - indentLevel = indents.indentSize; + indentLevel = indents.indentLevel; } else if (currentIs(tok!"case") || currentIs(tok!"default")) { - while (indents.length && (peekBackIs(tok!"}", true) - || peekBackIs(tok!";", true)) && isTempIndent(indents.top())) + if (peekBackIs(tok!"}", true) || peekBackIs(tok!";", true)) { - indents.pop(); + indents.popTempIndents(); + if (indents.topIs(tok!"case")) + indents.pop(); } immutable l = indents.indentToMostRecent(tok!"switch"); if (l != -1) - indentLevel = l; + indentLevel = config.dfmt_align_switch_statements == OptionalBoolean.t ? l : indents.indentLevel; } else if (currentIs(tok!"{")) { @@ -1112,13 +1114,15 @@ private: if (peekBackIsSlashSlash()) { indents.popTempIndents(); - indentLevel = indents.indentSize; + indentLevel = indents.indentLevel; } } else if (currentIs(tok!"}")) { indents.popTempIndents(); - if (indents.top == tok!"{") + if (indents.topIs(tok!"case")) + indents.pop(); + if (indents.topIs(tok!"{")) { indentLevel = indents.indentToMostRecent(tok!"{"); indents.pop(); @@ -1135,7 +1139,7 @@ private: if (indents.topIs(tok!"]")) { indents.pop(); - indentLevel = indents.indentSize; + indentLevel = indents.indentLevel; } } else if (astInformation.attributeDeclarationLines.canFindIndex(current.line)) @@ -1150,7 +1154,7 @@ private: { indents.pop(); } - indentLevel = indents.indentSize; + indentLevel = indents.indentLevel; } indent(); } diff --git a/src/dfmt/indentation.d b/src/dfmt/indentation.d index 2322a24..8369cac 100644 --- a/src/dfmt/indentation.d +++ b/src/dfmt/indentation.d @@ -7,19 +7,32 @@ module dfmt.indentation; import std.d.lexer; +/** + * Returns: true if the given token type is a wrap indent type + */ bool isWrapIndent(IdType type) pure nothrow @nogc @safe { - return type != tok!"{" && type != tok!":" && type != tok!"]" && isOperator(type); + return type != tok!"{" && type != tok!"case" && type != tok!"]" && isOperator(type); } +/** + * Returns: true if the given token type is a wrap indent type + */ bool isTempIndent(IdType type) pure nothrow @nogc @safe { - return type != tok!"{"; + return type != tok!"{" && type != tok!"case"; } +/** + * Stack for managing indent levels. + */ struct IndentStack { - int indentToMostRecent(IdType item) + /** + * Modifies the indent stack to match the state that it had at the most + * recent appearance of the given token type. + */ + int indentToMostRecent(IdType item) const { size_t i = index; while (true) @@ -47,44 +60,69 @@ struct IndentStack return tempIndentCount; } + /** + * Pushes the given indent type on to the stack. + */ void push(IdType item) pure nothrow { index = index == 255 ? index : index + 1; arr[index] = item; } + /** + * Pops the top indent from the stack. + */ void pop() pure nothrow { index = index == 0 ? index : index - 1; } + /** + * Pops all wrapping indents from the top of the stack. + */ void popWrapIndents() pure nothrow @safe @nogc { while (index > 0 && isWrapIndent(arr[index])) index--; } + /** + * Pops all temporary indents from the top of the stack. + */ void popTempIndents() pure nothrow @safe @nogc { while (index > 0 && isTempIndent(arr[index])) index--; } + /** + * Returns: `true` if the top of the indent stack is the given indent type. + */ bool topIs(IdType type) const pure nothrow @safe @nogc { return index > 0 && arr[index] == type; } + /** + * Returns: `true` if the top of the indent stack is a temporary indent + */ bool topIsTemp() { return index > 0 && index < arr.length && isTempIndent(arr[index]); } + /** + * Returns: `true` if the top of the indent stack is a wrapping indent + */ bool topIsWrap() { return index > 0 && index < arr.length && isWrapIndent(arr[index]); } + /** + * Returns: `true` if the top of the indent stack is one of the given token + * types. + */ bool topIsOneOf(IdType[] types...) const pure nothrow @safe @nogc { if (index <= 0) @@ -101,23 +139,9 @@ struct IndentStack return arr[index]; } - int indentSize(size_t k = size_t.max) const pure nothrow @safe @nogc + int indentLevel() const pure nothrow @safe @nogc @property { - if (index == 0) - return 0; - immutable size_t j = k == size_t.max ? index : k - 1; - int size = 0; - foreach (i; 1 .. j + 1) - { - if ((i + 1 <= index && arr[i] != tok!"]" && !isWrapIndent(arr[i]) - && isTempIndent(arr[i]) && (!isTempIndent(arr[i + 1]) - || arr[i + 1] == tok!"switch"))) - { - continue; - } - size++; - } - return size; + return indentSize(); } int length() const pure nothrow @property @@ -126,6 +150,31 @@ struct IndentStack } private: + size_t index; + IdType[256] arr; + + int indentSize(size_t k = size_t.max) const pure nothrow @safe @nogc + { + if (index == 0) + return 0; + immutable size_t j = k == size_t.max ? index : k - 1; + int size = 0; + foreach (i; 1 .. j + 1) + { + if (i + 1 <= index) + { + if (arr[i] == tok!"]") + continue; + immutable bool currentIsTemp = isTempIndent(arr[i]); + immutable bool nextIsTemp = isTempIndent(arr[i + 1]); + immutable bool nextIsSwitch = arr[i + 1] == tok!"switch"; + if (currentIsTemp && (!nextIsTemp || nextIsSwitch)) + continue; + } + size++; + } + return size; + } } diff --git a/tests/allman/issue0097.d.ref b/tests/allman/issue0097.d.ref new file mode 100644 index 0000000..632cf84 --- /dev/null +++ b/tests/allman/issue0097.d.ref @@ -0,0 +1,42 @@ +unittest +{ + switch (x) + { + case 0: + version (none) + { + // Comment + case '\n': + break; + } + } +} + +unittest +{ + switch (x) + { + case 0: + { + Label: while (1) + { + } + break; + } + Label2: + doStuff(); + } +} + +unittest +{ + switch (a) + { + case a: + doStuff(); + doOtherStuff(); + break; + default: + break; + } +} diff --git a/tests/issue0097.args b/tests/issue0097.args new file mode 100644 index 0000000..91e2439 --- /dev/null +++ b/tests/issue0097.args @@ -0,0 +1 @@ +--align_switch_statements=false diff --git a/tests/issue0097.d b/tests/issue0097.d new file mode 100644 index 0000000..bcb66db --- /dev/null +++ b/tests/issue0097.d @@ -0,0 +1,42 @@ +unittest +{ + switch (x) + { + case 0: + version (none) + { + // Comment + case '\n': + break; + } + } +} + +unittest +{ + switch (x) + { + case 0: + { + Label: while (1) + { + } + break; + } + Label2: + doStuff(); + } +} + +unittest +{ + switch (a) + { + case a: + doStuff(); + doOtherStuff(); + break; + default: + break; + } +} diff --git a/tests/otbs/issue0097.d.ref b/tests/otbs/issue0097.d.ref new file mode 100644 index 0000000..c43ea0d --- /dev/null +++ b/tests/otbs/issue0097.d.ref @@ -0,0 +1,33 @@ +unittest { + switch (x) { + case 0: + version (none) { + // Comment + case '\n': + break; + } + } +} + +unittest { + switch (x) { + case 0: { + Label: while (1) { + } + break; + } + Label2: + doStuff(); + } +} + +unittest { + switch (a) { + case a: + doStuff(); + doOtherStuff(); + break; + default: + break; + } +}