diff --git a/src/SqlFormatter.php b/src/SqlFormatter.php index aee3f23..d4e0875 100644 --- a/src/SqlFormatter.php +++ b/src/SqlFormatter.php @@ -66,6 +66,7 @@ public function format(string $string, string $indentString = ' '): string $inlineCount = 0; $inlineIndented = false; $clauseLimit = false; + $inBooleanExpression = false; $appendNewLineIfNotAddedFx = static function () use (&$addedNewline, &$return, $tab, &$indentLevel): void { // Add a newline if not already added @@ -301,8 +302,8 @@ public function format(string $string, string $indentString = ' '): string $newline = true; $increaseBlockIndent = true; } - } elseif (in_array($tokenValueUpper, ['WHEN', 'THEN', 'ELSE', 'END'], true)) { - if ($tokenValueUpper !== 'THEN') { + } elseif (in_array($tokenValueUpper, ['IF', 'WHEN', 'THEN', 'ELSE', 'ELSEIF', 'ELSIF', 'END'], true)) { + if ($tokenValueUpper !== 'THEN' && $tokenValueUpper !== 'IF') { $decreaseIndentationLevelFx(); if ($prevNotWhitespaceToken !== null && strtoupper($prevNotWhitespaceToken->value()) !== 'CASE') { @@ -314,6 +315,22 @@ public function format(string $string, string $indentString = ' '): string $newline = true; $increaseBlockIndent = true; } + + // Track boolean expression context for IF/ELSEIF/CASE WHEN conditions + // (not for "IF()" function calls) + if (in_array($tokenValueUpper, ['IF', 'ELSEIF', 'ELSIF'], true)) { + // Check if this IF is part of a conditional block by looking at the next token + $nextToken = $cursor->subCursor()->next(Token::TOKEN_TYPE_WHITESPACE); + if ( + $nextToken !== null && $nextToken->value() !== '(' + ) { + $inBooleanExpression = true; + } + } elseif ($tokenValueUpper === 'WHEN') { + $inBooleanExpression = true; + } elseif ($tokenValueUpper === 'THEN' || $tokenValueUpper === 'ELSE' || $tokenValueUpper === 'END') { + $inBooleanExpression = false; + } } elseif ( $clauseLimit && $token->value() !== ',' && @@ -332,9 +349,12 @@ public function format(string $string, string $indentString = ' '): string $newline = true; } } elseif ($token->isOfType(Token::TOKEN_TYPE_RESERVED_NEWLINE)) { - // Newline reserved words start a new line + // Newline reserved words start a new line, except for AND/OR within boolean expressions - $appendNewLineIfNotAddedFx(); + // Skip newlines for AND/OR when inside boolean expressions (IF conditions, CASE WHEN) + if (! $inBooleanExpression || ! in_array($tokenValueUpper, ['AND', 'OR'], true)) { + $appendNewLineIfNotAddedFx(); + } if ($token->hasExtraWhitespace()) { $highlighted = preg_replace('/\s+/', ' ', $highlighted); diff --git a/src/Tokenizer.php b/src/Tokenizer.php index f8f35a6..1ba23ff 100644 --- a/src/Tokenizer.php +++ b/src/Tokenizer.php @@ -104,6 +104,8 @@ final class Tokenizer 'DUPLICATE', 'DYNAMIC', 'ELSE', + 'ELSEIF', + 'ELSIF', 'ENCLOSED', 'END', 'ENGINE', diff --git a/tests/clihighlight.txt b/tests/clihighlight.txt index 01fcff0..0cf5d3b 100644 --- a/tests/clihighlight.txt +++ b/tests/clihighlight.txt @@ -1205,3 +1205,26 @@ MY_NON_TOP_LEVEL_KEYWORD_FX_3(); CREATE TABLE t ( c VARCHAR(20) ) DEFAULT CHARACTER SET utf8mb4 ENGINE = InnoDB +--- +BEGIN + IF first_name != '' AND last_name != '' THEN + RETURN CONCAT(first_name, ' ', last_name); + ELSEIF first_name != '' THEN + RETURN first_name; + ELSIF last_name != '' THEN + RETURN last_name; + ELSE + RETURN id; + END IF; +END; +--- +SELECT + CASE WHEN status = 'active' AND priority = 'high' THEN + 'urgent' + WHEN status = 'active' OR status = 'pending' THEN + 'normal' + ELSE + 'low' + END AS task_priority +FROM + tasks; diff --git a/tests/compress.txt b/tests/compress.txt index 58a0391..1eb4641 100644 --- a/tests/compress.txt +++ b/tests/compress.txt @@ -111,3 +111,7 @@ SELECT a FROM test STRAIGHT_JOIN test2 ON test.id = test2.id SELECT t.id, t.start, t.end, t.end AS e2, t.limit, t.begin, t.case, t.when, t.then, t.else FROM t WHERE t.start = t.end --- CREATE TABLE t (c VARCHAR(20)) DEFAULT CHARACTER SET utf8mb4 ENGINE = InnoDB +--- +BEGIN IF first_name != '' AND last_name != '' THEN RETURN CONCAT(first_name, ' ', last_name); ELSEIF first_name != '' THEN RETURN first_name; ELSIF last_name != '' THEN RETURN last_name; ELSE RETURN id; END IF; END; +--- +SELECT CASE WHEN status = 'active' AND priority = 'high' THEN 'urgent' WHEN status = 'active' OR status = 'pending' THEN 'normal' ELSE 'low' END AS task_priority FROM tasks; \ No newline at end of file diff --git a/tests/format-highlight.html b/tests/format-highlight.html index 5286c90..e1a3059 100644 --- a/tests/format-highlight.html +++ b/tests/format-highlight.html @@ -1205,3 +1205,26 @@
CREATE TABLE t (
   c VARCHAR(20)
 ) DEFAULT CHARACTER SET utf8mb4 ENGINE = InnoDB
+--- +
BEGIN
+  IF first_name != '' AND last_name != '' THEN
+    RETURN CONCAT(first_name, ' ', last_name);
+  ELSEIF first_name != '' THEN
+    RETURN first_name;
+  ELSIF last_name != '' THEN
+    RETURN last_name;
+  ELSE
+    RETURN id;
+  END IF;
+END;
+--- +
SELECT
+  CASE WHEN status = 'active' AND priority = 'high' THEN
+    'urgent'
+  WHEN status = 'active' OR status = 'pending' THEN
+    'normal'
+  ELSE
+    'low'
+  END AS task_priority
+FROM
+  tasks;
diff --git a/tests/format.txt b/tests/format.txt index 9216132..7495bb3 100644 --- a/tests/format.txt +++ b/tests/format.txt @@ -1203,3 +1203,26 @@ WHERE CREATE TABLE t ( c VARCHAR(20) ) DEFAULT CHARACTER SET utf8mb4 ENGINE = InnoDB +--- +BEGIN + IF first_name != '' AND last_name != '' THEN + RETURN CONCAT(first_name, ' ', last_name); + ELSEIF first_name != '' THEN + RETURN first_name; + ELSIF last_name != '' THEN + RETURN last_name; + ELSE + RETURN id; + END IF; +END; +--- +SELECT + CASE WHEN status = 'active' AND priority = 'high' THEN + 'urgent' + WHEN status = 'active' OR status = 'pending' THEN + 'normal' + ELSE + 'low' + END AS task_priority +FROM + tasks; diff --git a/tests/highlight.html b/tests/highlight.html index fa79610..249d5d9 100644 --- a/tests/highlight.html +++ b/tests/highlight.html @@ -425,3 +425,7 @@ WHERE t.start = t.end ---
CREATE TABLE t (c VARCHAR(20)) DEFAULT CHARACTER SET utf8mb4 ENGINE = InnoDB
+--- +
BEGIN IF first_name != '' AND last_name != '' THEN RETURN CONCAT(first_name, ' ', last_name); ELSEIF first_name != '' THEN RETURN first_name; ELSIF last_name != '' THEN RETURN last_name; ELSE RETURN id; END IF; END;
+--- +
SELECT CASE WHEN status = 'active' AND priority = 'high' THEN 'urgent' WHEN status = 'active' OR status = 'pending' THEN 'normal' ELSE 'low' END AS task_priority FROM tasks;
diff --git a/tests/sql.sql b/tests/sql.sql index d595f76..31672e2 100644 --- a/tests/sql.sql +++ b/tests/sql.sql @@ -425,3 +425,7 @@ FROM t WHERE t.start = t.end --- CREATE TABLE t (c VARCHAR(20)) DEFAULT CHARACTER SET utf8mb4 ENGINE = InnoDB +--- +BEGIN IF first_name != '' AND last_name != '' THEN RETURN CONCAT(first_name, ' ', last_name); ELSEIF first_name != '' THEN RETURN first_name; ELSIF last_name != '' THEN RETURN last_name; ELSE RETURN id; END IF; END; +--- +SELECT CASE WHEN status = 'active' AND priority = 'high' THEN 'urgent' WHEN status = 'active' OR status = 'pending' THEN 'normal' ELSE 'low' END AS task_priority FROM tasks;