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[0m();[0m [37mCREATE[0m [37mTABLE[0m t[0m ( c[0m [37mVARCHAR[0m([32;1m20[0m) ) [37mDEFAULT[0m [37mCHARACTER[0m [37mSET[0m utf8mb4[0m [37mENGINE[0m =[0m InnoDB[0m +--- +[37mBEGIN[0m + [37mIF[0m first_name[0m ![0m=[0m [34;1m''[0m [37mAND[0m last_name[0m ![0m=[0m [34;1m''[0m [37mTHEN[0m + [37mRETURN[0m [37mCONCAT[0m(first_name[0m,[0m [34;1m' '[0m,[0m last_name[0m);[0m + [37mELSEIF[0m first_name[0m ![0m=[0m [34;1m''[0m [37mTHEN[0m + [37mRETURN[0m first_name[0m;[0m + [37mELSIF[0m last_name[0m ![0m=[0m [34;1m''[0m [37mTHEN[0m + [37mRETURN[0m last_name[0m;[0m + [37mELSE[0m + [37mRETURN[0m id[0m;[0m + [37mEND[0m [37mIF[0m;[0m +[37mEND[0m;[0m +--- +[37mSELECT[0m + [37mCASE[0m [37mWHEN[0m [37mstatus[0m =[0m [34;1m'active'[0m [37mAND[0m priority[0m =[0m [34;1m'high'[0m [37mTHEN[0m + [34;1m'urgent'[0m + [37mWHEN[0m [37mstatus[0m =[0m [34;1m'active'[0m [37mOR[0m [37mstatus[0m =[0m [34;1m'pending'[0m [37mTHEN[0m + [34;1m'normal'[0m + [37mELSE[0m + [34;1m'low'[0m + [37mEND[0m [37mAS[0m task_priority[0m +[37mFROM[0m + tasks[0m;[0m 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;