spark-sql-language-server 0.0.1-beta.6 → 0.2.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/sparksql-server-worker.js +1 -1
- package/out/sparksql-server-worker.js.map +1 -1
- package/out-tsc/assets/built-in-functions.d.ts +2 -0
- package/out-tsc/assets/built-in-functions.d.ts.map +1 -0
- package/out-tsc/assets/built-in-functions.js +4 -2
- package/out-tsc/assets/built-in-functions.js.map +1 -1
- package/out-tsc/completion-parser.d.ts +11 -0
- package/out-tsc/completion-parser.d.ts.map +1 -0
- package/out-tsc/completion-parser.js +148 -0
- package/out-tsc/completion-parser.js.map +1 -0
- package/out-tsc/constants.d.ts +9 -0
- package/out-tsc/constants.d.ts.map +1 -0
- package/out-tsc/constants.js +12 -0
- package/out-tsc/constants.js.map +1 -0
- package/out-tsc/cursor.d.ts +10 -0
- package/out-tsc/cursor.d.ts.map +1 -0
- package/out-tsc/cursor.js +3 -0
- package/out-tsc/cursor.js.map +1 -1
- package/out-tsc/execute-command.d.ts +7 -0
- package/out-tsc/execute-command.d.ts.map +1 -0
- package/out-tsc/execute-command.js +78 -0
- package/out-tsc/execute-command.js.map +1 -0
- package/out-tsc/execute-commands.d.ts +1 -0
- package/out-tsc/execute-commands.d.ts.map +1 -0
- package/out-tsc/execute-commands.js +3 -4
- package/out-tsc/execute-commands.js.map +1 -1
- package/out-tsc/formatter/boundary-writer.d.ts +30 -0
- package/out-tsc/formatter/boundary-writer.d.ts.map +1 -0
- package/out-tsc/formatter/boundary-writer.js +47 -0
- package/out-tsc/formatter/boundary-writer.js.map +1 -0
- package/out-tsc/formatter/core/comment-helper.d.ts +11 -0
- package/out-tsc/formatter/core/comment-helper.d.ts.map +1 -0
- package/out-tsc/formatter/core/comment-helper.js +139 -0
- package/out-tsc/formatter/core/comment-helper.js.map +1 -0
- package/out-tsc/formatter/core/edit-collector.d.ts +110 -0
- package/out-tsc/formatter/core/edit-collector.d.ts.map +1 -0
- package/out-tsc/formatter/core/edit-collector.js +159 -0
- package/out-tsc/formatter/core/edit-collector.js.map +1 -0
- package/out-tsc/formatter/core/formatting-context.d.ts +183 -0
- package/out-tsc/formatter/core/formatting-context.d.ts.map +1 -0
- package/out-tsc/formatter/core/formatting-context.js +294 -0
- package/out-tsc/formatter/core/formatting-context.js.map +1 -0
- package/out-tsc/formatter/core/index.d.ts +25 -0
- package/out-tsc/formatter/core/index.d.ts.map +1 -0
- package/out-tsc/formatter/core/index.js +57 -0
- package/out-tsc/formatter/core/index.js.map +1 -0
- package/out-tsc/formatter/core/pipeline/formatting-pipeline.d.ts +116 -0
- package/out-tsc/formatter/core/pipeline/formatting-pipeline.d.ts.map +1 -0
- package/out-tsc/formatter/core/pipeline/formatting-pipeline.js +237 -0
- package/out-tsc/formatter/core/pipeline/formatting-pipeline.js.map +1 -0
- package/out-tsc/formatter/core/pipeline/index.d.ts +12 -0
- package/out-tsc/formatter/core/pipeline/index.d.ts.map +1 -0
- package/out-tsc/formatter/core/pipeline/index.js +19 -0
- package/out-tsc/formatter/core/pipeline/index.js.map +1 -0
- package/out-tsc/formatter/core/pipeline/statement-separation.d.ts +5 -0
- package/out-tsc/formatter/core/pipeline/statement-separation.d.ts.map +1 -0
- package/out-tsc/formatter/core/pipeline/statement-separation.js +149 -0
- package/out-tsc/formatter/core/pipeline/statement-separation.js.map +1 -0
- package/out-tsc/formatter/core/rules/index.d.ts +11 -0
- package/out-tsc/formatter/core/rules/index.d.ts.map +1 -0
- package/out-tsc/formatter/core/rules/index.js +29 -0
- package/out-tsc/formatter/core/rules/index.js.map +1 -0
- package/out-tsc/formatter/core/rules/newline-rules.d.ts +169 -0
- package/out-tsc/formatter/core/rules/newline-rules.d.ts.map +1 -0
- package/out-tsc/formatter/core/rules/newline-rules.js +246 -0
- package/out-tsc/formatter/core/rules/newline-rules.js.map +1 -0
- package/out-tsc/formatter/core/rules/spacing-rules.d.ts +150 -0
- package/out-tsc/formatter/core/rules/spacing-rules.d.ts.map +1 -0
- package/out-tsc/formatter/core/rules/spacing-rules.js +219 -0
- package/out-tsc/formatter/core/rules/spacing-rules.js.map +1 -0
- package/out-tsc/formatter/core/strategy-bridge.d.ts +56 -0
- package/out-tsc/formatter/core/strategy-bridge.d.ts.map +1 -0
- package/out-tsc/formatter/core/strategy-bridge.js +159 -0
- package/out-tsc/formatter/core/strategy-bridge.js.map +1 -0
- package/out-tsc/formatter/core/strategy-interface.d.ts +123 -0
- package/out-tsc/formatter/core/strategy-interface.d.ts.map +1 -0
- package/out-tsc/formatter/core/strategy-interface.js +83 -0
- package/out-tsc/formatter/core/strategy-interface.js.map +1 -0
- package/out-tsc/formatter/core/token-helper.d.ts +12 -0
- package/out-tsc/formatter/core/token-helper.d.ts.map +1 -0
- package/out-tsc/formatter/core/token-helper.js +79 -0
- package/out-tsc/formatter/core/token-helper.js.map +1 -0
- package/out-tsc/formatter/core/unicode-utils.d.ts +107 -0
- package/out-tsc/formatter/core/unicode-utils.d.ts.map +1 -0
- package/out-tsc/formatter/core/unicode-utils.js +181 -0
- package/out-tsc/formatter/core/unicode-utils.js.map +1 -0
- package/out-tsc/formatter/core/whitespace-writer.d.ts +20 -0
- package/out-tsc/formatter/core/whitespace-writer.d.ts.map +1 -0
- package/out-tsc/formatter/core/whitespace-writer.js +86 -0
- package/out-tsc/formatter/core/whitespace-writer.js.map +1 -0
- package/out-tsc/formatter/formatter-adapter.d.ts +56 -0
- package/out-tsc/formatter/formatter-adapter.d.ts.map +1 -0
- package/out-tsc/formatter/formatter-adapter.js +719 -0
- package/out-tsc/formatter/formatter-adapter.js.map +1 -0
- package/out-tsc/formatter/index.d.ts +16 -0
- package/out-tsc/formatter/index.d.ts.map +1 -0
- package/out-tsc/formatter/index.js +43 -0
- package/out-tsc/formatter/index.js.map +1 -0
- package/out-tsc/formatter/pipe-step-classifier.d.ts +9 -0
- package/out-tsc/formatter/pipe-step-classifier.d.ts.map +1 -0
- package/out-tsc/formatter/pipe-step-classifier.js +49 -0
- package/out-tsc/formatter/pipe-step-classifier.js.map +1 -0
- package/out-tsc/formatter/state.d.ts +10 -0
- package/out-tsc/formatter/state.d.ts.map +1 -0
- package/out-tsc/formatter/state.js +12 -0
- package/out-tsc/formatter/state.js.map +1 -0
- package/out-tsc/formatter/strategies/block.strategy.d.ts +191 -0
- package/out-tsc/formatter/strategies/block.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/block.strategy.js +238 -0
- package/out-tsc/formatter/strategies/block.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/clause-head-body.strategy.d.ts +17 -0
- package/out-tsc/formatter/strategies/clause-head-body.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/clause-head-body.strategy.js +13 -0
- package/out-tsc/formatter/strategies/clause-head-body.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/ddl/alter-table-extended.strategy.d.ts +68 -0
- package/out-tsc/formatter/strategies/ddl/alter-table-extended.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/ddl/alter-table-extended.strategy.js +242 -0
- package/out-tsc/formatter/strategies/ddl/alter-table-extended.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/ddl/alter-table.strategy.d.ts +146 -0
- package/out-tsc/formatter/strategies/ddl/alter-table.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/ddl/alter-table.strategy.js +578 -0
- package/out-tsc/formatter/strategies/ddl/alter-table.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/ddl/create-table.strategy.d.ts +103 -0
- package/out-tsc/formatter/strategies/ddl/create-table.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/ddl/create-table.strategy.js +659 -0
- package/out-tsc/formatter/strategies/ddl/create-table.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/ddl/create-view-function.strategy.d.ts +64 -0
- package/out-tsc/formatter/strategies/ddl/create-view-function.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/ddl/create-view-function.strategy.js +402 -0
- package/out-tsc/formatter/strategies/ddl/create-view-function.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/ddl/drop-utility.strategy.d.ts +119 -0
- package/out-tsc/formatter/strategies/ddl/drop-utility.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/ddl/drop-utility.strategy.js +863 -0
- package/out-tsc/formatter/strategies/ddl/drop-utility.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/ddl/index.d.ts +17 -0
- package/out-tsc/formatter/strategies/ddl/index.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/ddl/index.js +57 -0
- package/out-tsc/formatter/strategies/ddl/index.js.map +1 -0
- package/out-tsc/formatter/strategies/ddl/namespace-catalog.strategy.d.ts +26 -0
- package/out-tsc/formatter/strategies/ddl/namespace-catalog.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/ddl/namespace-catalog.strategy.js +77 -0
- package/out-tsc/formatter/strategies/ddl/namespace-catalog.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/ddl/pipeline.strategy.d.ts +99 -0
- package/out-tsc/formatter/strategies/ddl/pipeline.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/ddl/pipeline.strategy.js +385 -0
- package/out-tsc/formatter/strategies/ddl/pipeline.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/ddl/set-reset-config.strategy.d.ts +48 -0
- package/out-tsc/formatter/strategies/ddl/set-reset-config.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/ddl/set-reset-config.strategy.js +145 -0
- package/out-tsc/formatter/strategies/ddl/set-reset-config.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/ddl/show-utility.strategy.d.ts +71 -0
- package/out-tsc/formatter/strategies/ddl/show-utility.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/ddl/show-utility.strategy.js +196 -0
- package/out-tsc/formatter/strategies/ddl/show-utility.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/dml/index.d.ts +10 -0
- package/out-tsc/formatter/strategies/dml/index.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/dml/index.js +35 -0
- package/out-tsc/formatter/strategies/dml/index.js.map +1 -0
- package/out-tsc/formatter/strategies/dml/insert-update-delete.strategy.d.ts +113 -0
- package/out-tsc/formatter/strategies/dml/insert-update-delete.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/dml/insert-update-delete.strategy.js +438 -0
- package/out-tsc/formatter/strategies/dml/insert-update-delete.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/dml/select-clause.strategy.d.ts +76 -0
- package/out-tsc/formatter/strategies/dml/select-clause.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/dml/select-clause.strategy.js +480 -0
- package/out-tsc/formatter/strategies/dml/select-clause.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/expression/function-call.strategy.d.ts +50 -0
- package/out-tsc/formatter/strategies/expression/function-call.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/expression/function-call.strategy.js +319 -0
- package/out-tsc/formatter/strategies/expression/function-call.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/expression/index.d.ts +18 -0
- package/out-tsc/formatter/strategies/expression/index.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/expression/index.js +51 -0
- package/out-tsc/formatter/strategies/expression/index.js.map +1 -0
- package/out-tsc/formatter/strategies/expression/logical-comparison.strategy.d.ts +55 -0
- package/out-tsc/formatter/strategies/expression/logical-comparison.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/expression/logical-comparison.strategy.js +266 -0
- package/out-tsc/formatter/strategies/expression/logical-comparison.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/expression/subquery-parenthesis.strategy.d.ts +38 -0
- package/out-tsc/formatter/strategies/expression/subquery-parenthesis.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/expression/subquery-parenthesis.strategy.js +397 -0
- package/out-tsc/formatter/strategies/expression/subquery-parenthesis.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/function-call.strategy.d.ts +80 -0
- package/out-tsc/formatter/strategies/function-call.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/function-call.strategy.js +136 -0
- package/out-tsc/formatter/strategies/function-call.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/index.d.ts +87 -0
- package/out-tsc/formatter/strategies/index.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/index.js +121 -0
- package/out-tsc/formatter/strategies/index.js.map +1 -0
- package/out-tsc/formatter/strategies/join.strategy.d.ts +31 -0
- package/out-tsc/formatter/strategies/join.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/join.strategy.js +29 -0
- package/out-tsc/formatter/strategies/join.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/keyword.strategy.d.ts +82 -0
- package/out-tsc/formatter/strategies/keyword.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/keyword.strategy.js +129 -0
- package/out-tsc/formatter/strategies/keyword.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/list.strategy.d.ts +159 -0
- package/out-tsc/formatter/strategies/list.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/list.strategy.js +193 -0
- package/out-tsc/formatter/strategies/list.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/pipe-aggregate.strategy.d.ts +24 -0
- package/out-tsc/formatter/strategies/pipe-aggregate.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/pipe-aggregate.strategy.js +47 -0
- package/out-tsc/formatter/strategies/pipe-aggregate.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/pipe-drop.strategy.d.ts +21 -0
- package/out-tsc/formatter/strategies/pipe-drop.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/pipe-drop.strategy.js +52 -0
- package/out-tsc/formatter/strategies/pipe-drop.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/pipe-extend.strategy.d.ts +21 -0
- package/out-tsc/formatter/strategies/pipe-extend.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/pipe-extend.strategy.js +45 -0
- package/out-tsc/formatter/strategies/pipe-extend.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/pipe-join.strategy.d.ts +22 -0
- package/out-tsc/formatter/strategies/pipe-join.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/pipe-join.strategy.js +42 -0
- package/out-tsc/formatter/strategies/pipe-join.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/pipe-pivot.strategy.d.ts +7 -0
- package/out-tsc/formatter/strategies/pipe-pivot.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/pipe-pivot.strategy.js +34 -0
- package/out-tsc/formatter/strategies/pipe-pivot.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/pipe-query-organization.strategy.d.ts +7 -0
- package/out-tsc/formatter/strategies/pipe-query-organization.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/pipe-query-organization.strategy.js +14 -0
- package/out-tsc/formatter/strategies/pipe-query-organization.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/pipe-sample.strategy.d.ts +7 -0
- package/out-tsc/formatter/strategies/pipe-sample.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/pipe-sample.strategy.js +18 -0
- package/out-tsc/formatter/strategies/pipe-sample.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/pipe-select.strategy.d.ts +22 -0
- package/out-tsc/formatter/strategies/pipe-select.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/pipe-select.strategy.js +51 -0
- package/out-tsc/formatter/strategies/pipe-select.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/pipe-set-operation.strategy.d.ts +7 -0
- package/out-tsc/formatter/strategies/pipe-set-operation.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/pipe-set-operation.strategy.js +30 -0
- package/out-tsc/formatter/strategies/pipe-set-operation.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/pipe-set.strategy.d.ts +21 -0
- package/out-tsc/formatter/strategies/pipe-set.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/pipe-set.strategy.js +52 -0
- package/out-tsc/formatter/strategies/pipe-set.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/pipe-step.strategy.d.ts +25 -0
- package/out-tsc/formatter/strategies/pipe-step.strategy.d.ts.map +1 -0
- package/out-tsc/{lib/SparkSqlParserVisitor.js → formatter/strategies/pipe-step.strategy.js} +1 -1
- package/out-tsc/formatter/strategies/pipe-step.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/pipe-unpivot.strategy.d.ts +7 -0
- package/out-tsc/formatter/strategies/pipe-unpivot.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/pipe-unpivot.strategy.js +33 -0
- package/out-tsc/formatter/strategies/pipe-unpivot.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/pipe-where.strategy.d.ts +21 -0
- package/out-tsc/formatter/strategies/pipe-where.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/pipe-where.strategy.js +44 -0
- package/out-tsc/formatter/strategies/pipe-where.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/procedure/case-statement.strategy.d.ts +20 -0
- package/out-tsc/formatter/strategies/procedure/case-statement.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/procedure/case-statement.strategy.js +132 -0
- package/out-tsc/formatter/strategies/procedure/case-statement.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/procedure/compound-statement.strategy.d.ts +43 -0
- package/out-tsc/formatter/strategies/procedure/compound-statement.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/procedure/compound-statement.strategy.js +174 -0
- package/out-tsc/formatter/strategies/procedure/compound-statement.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/procedure/handler.strategy.d.ts +23 -0
- package/out-tsc/formatter/strategies/procedure/handler.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/procedure/handler.strategy.js +81 -0
- package/out-tsc/formatter/strategies/procedure/handler.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/procedure/if-else.strategy.d.ts +19 -0
- package/out-tsc/formatter/strategies/procedure/if-else.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/procedure/if-else.strategy.js +80 -0
- package/out-tsc/formatter/strategies/procedure/if-else.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/procedure/index.d.ts +27 -0
- package/out-tsc/formatter/strategies/procedure/index.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/procedure/index.js +63 -0
- package/out-tsc/formatter/strategies/procedure/index.js.map +1 -0
- package/out-tsc/formatter/strategies/procedure/leave-iterate.strategy.d.ts +19 -0
- package/out-tsc/formatter/strategies/procedure/leave-iterate.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/procedure/leave-iterate.strategy.js +54 -0
- package/out-tsc/formatter/strategies/procedure/leave-iterate.strategy.js.map +1 -0
- package/out-tsc/formatter/strategies/procedure/loop.strategy.d.ts +71 -0
- package/out-tsc/formatter/strategies/procedure/loop.strategy.d.ts.map +1 -0
- package/out-tsc/formatter/strategies/procedure/loop.strategy.js +216 -0
- package/out-tsc/formatter/strategies/procedure/loop.strategy.js.map +1 -0
- package/out-tsc/formatter/strategy-based-formatter.d.ts +329 -0
- package/out-tsc/formatter/strategy-based-formatter.d.ts.map +1 -0
- package/out-tsc/formatter/strategy-based-formatter.js +3808 -0
- package/out-tsc/formatter/strategy-based-formatter.js.map +1 -0
- package/out-tsc/formatter/strategy-dispatcher.d.ts +24 -0
- package/out-tsc/formatter/strategy-dispatcher.d.ts.map +1 -0
- package/out-tsc/formatter/strategy-dispatcher.js +19 -0
- package/out-tsc/formatter/strategy-dispatcher.js.map +1 -0
- package/out-tsc/formatter/types.d.ts +2 -0
- package/out-tsc/formatter/types.d.ts.map +1 -0
- package/out-tsc/{lib/SparkSqlParserListener.js → formatter/types.js} +1 -1
- package/out-tsc/formatter/types.js.map +1 -0
- package/out-tsc/formatter/visitor/ddl/alter-table.d.ts +48 -0
- package/out-tsc/formatter/visitor/ddl/alter-table.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/ddl/alter-table.js +133 -0
- package/out-tsc/formatter/visitor/ddl/alter-table.js.map +1 -0
- package/out-tsc/formatter/visitor/ddl/create-table.d.ts +54 -0
- package/out-tsc/formatter/visitor/ddl/create-table.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/ddl/create-table.js +142 -0
- package/out-tsc/formatter/visitor/ddl/create-table.js.map +1 -0
- package/out-tsc/formatter/visitor/ddl/create-view.d.ts +27 -0
- package/out-tsc/formatter/visitor/ddl/create-view.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/ddl/create-view.js +61 -0
- package/out-tsc/formatter/visitor/ddl/create-view.js.map +1 -0
- package/out-tsc/formatter/visitor/ddl/drop.d.ts +30 -0
- package/out-tsc/formatter/visitor/ddl/drop.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/ddl/drop.js +69 -0
- package/out-tsc/formatter/visitor/ddl/drop.js.map +1 -0
- package/out-tsc/formatter/visitor/ddl/index.d.ts +10 -0
- package/out-tsc/formatter/visitor/ddl/index.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/ddl/index.js +26 -0
- package/out-tsc/formatter/visitor/ddl/index.js.map +1 -0
- package/out-tsc/formatter/visitor/dml/delete.d.ts +26 -0
- package/out-tsc/formatter/visitor/dml/delete.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/dml/delete.js +38 -0
- package/out-tsc/formatter/visitor/dml/delete.js.map +1 -0
- package/out-tsc/formatter/visitor/dml/index.d.ts +10 -0
- package/out-tsc/formatter/visitor/dml/index.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/dml/index.js +26 -0
- package/out-tsc/formatter/visitor/dml/index.js.map +1 -0
- package/out-tsc/formatter/visitor/dml/insert.d.ts +42 -0
- package/out-tsc/formatter/visitor/dml/insert.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/dml/insert.js +112 -0
- package/out-tsc/formatter/visitor/dml/insert.js.map +1 -0
- package/out-tsc/formatter/visitor/dml/merge.d.ts +40 -0
- package/out-tsc/formatter/visitor/dml/merge.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/dml/merge.js +67 -0
- package/out-tsc/formatter/visitor/dml/merge.js.map +1 -0
- package/out-tsc/formatter/visitor/dml/update.d.ts +33 -0
- package/out-tsc/formatter/visitor/dml/update.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/dml/update.js +49 -0
- package/out-tsc/formatter/visitor/dml/update.js.map +1 -0
- package/out-tsc/formatter/visitor/expression/arithmetic.d.ts +20 -0
- package/out-tsc/formatter/visitor/expression/arithmetic.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/expression/arithmetic.js +44 -0
- package/out-tsc/formatter/visitor/expression/arithmetic.js.map +1 -0
- package/out-tsc/formatter/visitor/expression/case-when.d.ts +41 -0
- package/out-tsc/formatter/visitor/expression/case-when.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/expression/case-when.js +94 -0
- package/out-tsc/formatter/visitor/expression/case-when.js.map +1 -0
- package/out-tsc/formatter/visitor/expression/comparison.d.ts +20 -0
- package/out-tsc/formatter/visitor/expression/comparison.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/expression/comparison.js +26 -0
- package/out-tsc/formatter/visitor/expression/comparison.js.map +1 -0
- package/out-tsc/formatter/visitor/expression/function-call.d.ts +20 -0
- package/out-tsc/formatter/visitor/expression/function-call.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/expression/function-call.js +33 -0
- package/out-tsc/formatter/visitor/expression/function-call.js.map +1 -0
- package/out-tsc/formatter/visitor/expression/index.d.ts +11 -0
- package/out-tsc/formatter/visitor/expression/index.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/expression/index.js +27 -0
- package/out-tsc/formatter/visitor/expression/index.js.map +1 -0
- package/out-tsc/formatter/visitor/expression/logical.d.ts +26 -0
- package/out-tsc/formatter/visitor/expression/logical.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/expression/logical.js +46 -0
- package/out-tsc/formatter/visitor/expression/logical.js.map +1 -0
- package/out-tsc/formatter/visitor/formatter-core.d.ts +79 -0
- package/out-tsc/formatter/visitor/formatter-core.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/formatter-core.js +211 -0
- package/out-tsc/formatter/visitor/formatter-core.js.map +1 -0
- package/out-tsc/formatter/visitor/index.d.ts +21 -0
- package/out-tsc/formatter/visitor/index.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/index.js +26 -0
- package/out-tsc/formatter/visitor/index.js.map +1 -0
- package/out-tsc/formatter/visitor/management/cache.d.ts +26 -0
- package/out-tsc/formatter/visitor/management/cache.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/management/cache.js +58 -0
- package/out-tsc/formatter/visitor/management/cache.js.map +1 -0
- package/out-tsc/formatter/visitor/management/index.d.ts +8 -0
- package/out-tsc/formatter/visitor/management/index.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/management/index.js +24 -0
- package/out-tsc/formatter/visitor/management/index.js.map +1 -0
- package/out-tsc/formatter/visitor/management/refresh.d.ts +26 -0
- package/out-tsc/formatter/visitor/management/refresh.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/management/refresh.js +55 -0
- package/out-tsc/formatter/visitor/management/refresh.js.map +1 -0
- package/out-tsc/formatter/visitor/pipeline/delta-live-tables.d.ts +31 -0
- package/out-tsc/formatter/visitor/pipeline/delta-live-tables.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/pipeline/delta-live-tables.js +46 -0
- package/out-tsc/formatter/visitor/pipeline/delta-live-tables.js.map +1 -0
- package/out-tsc/formatter/visitor/pipeline/index.d.ts +7 -0
- package/out-tsc/formatter/visitor/pipeline/index.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/pipeline/index.js +23 -0
- package/out-tsc/formatter/visitor/pipeline/index.js.map +1 -0
- package/out-tsc/formatter/visitor/query/cte.d.ts +34 -0
- package/out-tsc/formatter/visitor/query/cte.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/query/cte.js +54 -0
- package/out-tsc/formatter/visitor/query/cte.js.map +1 -0
- package/out-tsc/formatter/visitor/query/index.d.ts +11 -0
- package/out-tsc/formatter/visitor/query/index.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/query/index.js +27 -0
- package/out-tsc/formatter/visitor/query/index.js.map +1 -0
- package/out-tsc/formatter/visitor/query/query-organization.d.ts +25 -0
- package/out-tsc/formatter/visitor/query/query-organization.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/query/query-organization.js +75 -0
- package/out-tsc/formatter/visitor/query/query-organization.js.map +1 -0
- package/out-tsc/formatter/visitor/query/query.d.ts +22 -0
- package/out-tsc/formatter/visitor/query/query.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/query/query.js +30 -0
- package/out-tsc/formatter/visitor/query/query.js.map +1 -0
- package/out-tsc/formatter/visitor/query/select-clause.d.ts +17 -0
- package/out-tsc/formatter/visitor/query/select-clause.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/query/select-clause.js +35 -0
- package/out-tsc/formatter/visitor/query/select-clause.js.map +1 -0
- package/out-tsc/formatter/visitor/query/set-operation.d.ts +25 -0
- package/out-tsc/formatter/visitor/query/set-operation.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/query/set-operation.js +52 -0
- package/out-tsc/formatter/visitor/query/set-operation.js.map +1 -0
- package/out-tsc/formatter/visitor/statements/index.d.ts +7 -0
- package/out-tsc/formatter/visitor/statements/index.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/statements/index.js +23 -0
- package/out-tsc/formatter/visitor/statements/index.js.map +1 -0
- package/out-tsc/formatter/visitor/statements/program.d.ts +33 -0
- package/out-tsc/formatter/visitor/statements/program.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/statements/program.js +69 -0
- package/out-tsc/formatter/visitor/statements/program.js.map +1 -0
- package/out-tsc/formatter/visitor/types/complex-types.d.ts +24 -0
- package/out-tsc/formatter/visitor/types/complex-types.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/types/complex-types.js +47 -0
- package/out-tsc/formatter/visitor/types/complex-types.js.map +1 -0
- package/out-tsc/formatter/visitor/types/index.d.ts +8 -0
- package/out-tsc/formatter/visitor/types/index.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/types/index.js +24 -0
- package/out-tsc/formatter/visitor/types/index.js.map +1 -0
- package/out-tsc/formatter/visitor/types/primitive-types.d.ts +22 -0
- package/out-tsc/formatter/visitor/types/primitive-types.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/types/primitive-types.js +37 -0
- package/out-tsc/formatter/visitor/types/primitive-types.js.map +1 -0
- package/out-tsc/formatter/visitor/utils/context-helpers.d.ts +32 -0
- package/out-tsc/formatter/visitor/utils/context-helpers.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/utils/context-helpers.js +98 -0
- package/out-tsc/formatter/visitor/utils/context-helpers.js.map +1 -0
- package/out-tsc/formatter/visitor/utils/index.d.ts +11 -0
- package/out-tsc/formatter/visitor/utils/index.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/utils/index.js +45 -0
- package/out-tsc/formatter/visitor/utils/index.js.map +1 -0
- package/out-tsc/formatter/visitor/utils/keyword-formatting.d.ts +57 -0
- package/out-tsc/formatter/visitor/utils/keyword-formatting.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/utils/keyword-formatting.js +87 -0
- package/out-tsc/formatter/visitor/utils/keyword-formatting.js.map +1 -0
- package/out-tsc/formatter/visitor/utils/parenthesis-formatting.d.ts +53 -0
- package/out-tsc/formatter/visitor/utils/parenthesis-formatting.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/utils/parenthesis-formatting.js +88 -0
- package/out-tsc/formatter/visitor/utils/parenthesis-formatting.js.map +1 -0
- package/out-tsc/formatter/visitor/utils/space-compression.d.ts +55 -0
- package/out-tsc/formatter/visitor/utils/space-compression.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/utils/space-compression.js +71 -0
- package/out-tsc/formatter/visitor/utils/space-compression.js.map +1 -0
- package/out-tsc/formatter/visitor/utils/token-helpers.d.ts +64 -0
- package/out-tsc/formatter/visitor/utils/token-helpers.d.ts.map +1 -0
- package/out-tsc/formatter/visitor/utils/token-helpers.js +249 -0
- package/out-tsc/formatter/visitor/utils/token-helpers.js.map +1 -0
- package/out-tsc/index.d.ts +2 -1
- package/out-tsc/index.d.ts.map +1 -0
- package/out-tsc/index.js +3 -1
- package/out-tsc/index.js.map +1 -1
- package/out-tsc/lib/SparkSQLLexer.d.ts +557 -0
- package/out-tsc/lib/SparkSQLLexer.d.ts.map +1 -0
- package/out-tsc/lib/SparkSQLLexer.js +3676 -0
- package/out-tsc/lib/SparkSQLLexer.js.map +1 -0
- package/out-tsc/lib/SparkSQLParser.d.ts +7884 -0
- package/out-tsc/lib/SparkSQLParser.d.ts.map +1 -0
- package/out-tsc/lib/SparkSQLParser.js +45848 -0
- package/out-tsc/lib/SparkSQLParser.js.map +1 -0
- package/out-tsc/lib/SparkSQLParserListener.d.ts +5855 -0
- package/out-tsc/lib/SparkSQLParserListener.d.ts.map +1 -0
- package/out-tsc/lib/SparkSQLParserListener.js +4 -0
- package/out-tsc/lib/SparkSQLParserListener.js.map +1 -0
- package/out-tsc/lib/SparkSQLParserVisitor.d.ts +3674 -0
- package/out-tsc/lib/SparkSQLParserVisitor.d.ts.map +1 -0
- package/out-tsc/lib/SparkSQLParserVisitor.js +4 -0
- package/out-tsc/lib/SparkSQLParserVisitor.js.map +1 -0
- package/out-tsc/lineage.typing.d.ts +64 -0
- package/out-tsc/lineage.typing.d.ts.map +1 -0
- package/out-tsc/lineage.typing.js +3 -0
- package/out-tsc/lineage.typing.js.map +1 -0
- package/out-tsc/listeners/parse-error.listener.d.ts +3 -2
- package/out-tsc/listeners/parse-error.listener.d.ts.map +1 -0
- package/out-tsc/listeners/parse-error.listener.js +4 -4
- package/out-tsc/listeners/parse-error.listener.js.map +1 -1
- package/out-tsc/listeners/schema.listener.d.ts +32 -39
- package/out-tsc/listeners/schema.listener.d.ts.map +1 -0
- package/out-tsc/listeners/schema.listener.js +58 -100
- package/out-tsc/listeners/schema.listener.js.map +1 -1
- package/out-tsc/listeners/statement.listener.d.ts +263 -5
- package/out-tsc/listeners/statement.listener.d.ts.map +1 -0
- package/out-tsc/listeners/statement.listener.js +836 -11
- package/out-tsc/listeners/statement.listener.js.map +1 -1
- package/out-tsc/listeners/structure.listener.d.ts +70 -0
- package/out-tsc/listeners/structure.listener.d.ts.map +1 -0
- package/out-tsc/listeners/structure.listener.js +211 -0
- package/out-tsc/listeners/structure.listener.js.map +1 -0
- package/out-tsc/listeners/tokens-collector.listener.d.ts +27 -0
- package/out-tsc/listeners/tokens-collector.listener.d.ts.map +1 -0
- package/out-tsc/listeners/tokens-collector.listener.js +102 -0
- package/out-tsc/listeners/tokens-collector.listener.js.map +1 -0
- package/out-tsc/lsp-server.d.ts +79 -8
- package/out-tsc/lsp-server.d.ts.map +1 -0
- package/out-tsc/lsp-server.js +672 -237
- package/out-tsc/lsp-server.js.map +1 -1
- package/out-tsc/metadata.typing.d.ts +1 -0
- package/out-tsc/metadata.typing.d.ts.map +1 -0
- package/out-tsc/monaco-config.d.ts +219 -0
- package/out-tsc/monaco-config.d.ts.map +1 -0
- package/out-tsc/monaco-config.js +1032 -0
- package/out-tsc/monaco-config.js.map +1 -0
- package/out-tsc/parsing-warehouse.d.ts +8 -3
- package/out-tsc/parsing-warehouse.d.ts.map +1 -0
- package/out-tsc/parsing-warehouse.js +62 -6
- package/out-tsc/parsing-warehouse.js.map +1 -1
- package/out-tsc/protocol-translation.d.ts +12 -5
- package/out-tsc/protocol-translation.d.ts.map +1 -0
- package/out-tsc/protocol-translation.js +72 -19
- package/out-tsc/protocol-translation.js.map +1 -1
- package/out-tsc/public-apis.d.ts +1 -0
- package/out-tsc/public-apis.d.ts.map +1 -0
- package/out-tsc/schema-registry.d.ts +10 -7
- package/out-tsc/schema-registry.d.ts.map +1 -0
- package/out-tsc/schema-registry.js +87 -106
- package/out-tsc/schema-registry.js.map +1 -1
- package/out-tsc/server-worker.d.ts +1 -0
- package/out-tsc/server-worker.d.ts.map +1 -0
- package/out-tsc/server-worker.js +159 -44
- package/out-tsc/server-worker.js.map +1 -1
- package/out-tsc/tests/folding/block-comment-folding.test.d.ts +7 -0
- package/out-tsc/tests/folding/block-comment-folding.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/block-comment-folding.test.js +268 -0
- package/out-tsc/tests/folding/block-comment-folding.test.js.map +1 -0
- package/out-tsc/tests/folding/caching.test.d.ts +11 -0
- package/out-tsc/tests/folding/caching.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/caching.test.js +141 -0
- package/out-tsc/tests/folding/caching.test.js.map +1 -0
- package/out-tsc/tests/folding/cte-dependency-chain.test.d.ts +15 -0
- package/out-tsc/tests/folding/cte-dependency-chain.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/cte-dependency-chain.test.js +323 -0
- package/out-tsc/tests/folding/cte-dependency-chain.test.js.map +1 -0
- package/out-tsc/tests/folding/debug-format-flow.test.d.ts +2 -0
- package/out-tsc/tests/folding/debug-format-flow.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/debug-format-flow.test.js +114 -0
- package/out-tsc/tests/folding/debug-format-flow.test.js.map +1 -0
- package/out-tsc/tests/folding/debug-formatted.test.d.ts +2 -0
- package/out-tsc/tests/folding/debug-formatted.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/debug-formatted.test.js +175 -0
- package/out-tsc/tests/folding/debug-formatted.test.js.map +1 -0
- package/out-tsc/tests/folding/deduplication.test.d.ts +10 -0
- package/out-tsc/tests/folding/deduplication.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/deduplication.test.js +206 -0
- package/out-tsc/tests/folding/deduplication.test.js.map +1 -0
- package/out-tsc/tests/folding/edge-cases.test.d.ts +8 -0
- package/out-tsc/tests/folding/edge-cases.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/edge-cases.test.js +319 -0
- package/out-tsc/tests/folding/edge-cases.test.js.map +1 -0
- package/out-tsc/tests/folding/folding-level-config.test.d.ts +11 -0
- package/out-tsc/tests/folding/folding-level-config.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/folding-level-config.test.js +313 -0
- package/out-tsc/tests/folding/folding-level-config.test.js.map +1 -0
- package/out-tsc/tests/folding/folding-statistics.test.d.ts +13 -0
- package/out-tsc/tests/folding/folding-statistics.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/folding-statistics.test.js +144 -0
- package/out-tsc/tests/folding/folding-statistics.test.js.map +1 -0
- package/out-tsc/tests/folding/index.d.ts +8 -0
- package/out-tsc/tests/folding/index.d.ts.map +1 -0
- package/out-tsc/tests/folding/index.js +24 -0
- package/out-tsc/tests/folding/index.js.map +1 -0
- package/out-tsc/tests/folding/join-folding.test.d.ts +11 -0
- package/out-tsc/tests/folding/join-folding.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/join-folding.test.js +167 -0
- package/out-tsc/tests/folding/join-folding.test.js.map +1 -0
- package/out-tsc/tests/folding/kind-classification.test.d.ts +11 -0
- package/out-tsc/tests/folding/kind-classification.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/kind-classification.test.js +270 -0
- package/out-tsc/tests/folding/kind-classification.test.js.map +1 -0
- package/out-tsc/tests/folding/level1-statement.test.d.ts +8 -0
- package/out-tsc/tests/folding/level1-statement.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/level1-statement.test.js +278 -0
- package/out-tsc/tests/folding/level1-statement.test.js.map +1 -0
- package/out-tsc/tests/folding/level2-query-block.test.d.ts +8 -0
- package/out-tsc/tests/folding/level2-query-block.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/level2-query-block.test.js +374 -0
- package/out-tsc/tests/folding/level2-query-block.test.js.map +1 -0
- package/out-tsc/tests/folding/level3-expression.test.d.ts +8 -0
- package/out-tsc/tests/folding/level3-expression.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/level3-expression.test.js +361 -0
- package/out-tsc/tests/folding/level3-expression.test.js.map +1 -0
- package/out-tsc/tests/folding/level4-structure.test.d.ts +8 -0
- package/out-tsc/tests/folding/level4-structure.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/level4-structure.test.js +270 -0
- package/out-tsc/tests/folding/level4-structure.test.js.map +1 -0
- package/out-tsc/tests/folding/line-comment-folding.test.d.ts +13 -0
- package/out-tsc/tests/folding/line-comment-folding.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/line-comment-folding.test.js +215 -0
- package/out-tsc/tests/folding/line-comment-folding.test.js.map +1 -0
- package/out-tsc/tests/folding/nested-folding.test.d.ts +8 -0
- package/out-tsc/tests/folding/nested-folding.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/nested-folding.test.js +386 -0
- package/out-tsc/tests/folding/nested-folding.test.js.map +1 -0
- package/out-tsc/tests/folding/performance-benchmark.test.d.ts +11 -0
- package/out-tsc/tests/folding/performance-benchmark.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/performance-benchmark.test.js +256 -0
- package/out-tsc/tests/folding/performance-benchmark.test.js.map +1 -0
- package/out-tsc/tests/folding/region-marker-folding.test.d.ts +11 -0
- package/out-tsc/tests/folding/region-marker-folding.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/region-marker-folding.test.js +245 -0
- package/out-tsc/tests/folding/region-marker-folding.test.js.map +1 -0
- package/out-tsc/tests/folding/select-columns-folding.test.d.ts +10 -0
- package/out-tsc/tests/folding/select-columns-folding.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/select-columns-folding.test.js +139 -0
- package/out-tsc/tests/folding/select-columns-folding.test.js.map +1 -0
- package/out-tsc/tests/folding/test-utils.d.ts +73 -0
- package/out-tsc/tests/folding/test-utils.d.ts.map +1 -0
- package/out-tsc/tests/folding/test-utils.js +98 -0
- package/out-tsc/tests/folding/test-utils.js.map +1 -0
- package/out-tsc/tests/folding/values-folding.test.d.ts +10 -0
- package/out-tsc/tests/folding/values-folding.test.d.ts.map +1 -0
- package/out-tsc/tests/folding/values-folding.test.js +102 -0
- package/out-tsc/tests/folding/values-folding.test.js.map +1 -0
- package/out-tsc/tests/format/ddl/datatype-constraint.test.d.ts +5 -0
- package/out-tsc/tests/format/ddl/datatype-constraint.test.d.ts.map +1 -0
- package/out-tsc/tests/format/ddl/datatype-constraint.test.js +132 -0
- package/out-tsc/tests/format/ddl/datatype-constraint.test.js.map +1 -0
- package/out-tsc/tests/format/ddl/index.test.d.ts +5 -0
- package/out-tsc/tests/format/ddl/index.test.d.ts.map +1 -0
- package/out-tsc/tests/format/ddl/index.test.js +74 -0
- package/out-tsc/tests/format/ddl/index.test.js.map +1 -0
- package/out-tsc/tests/format/ddl/materialized-view.test.d.ts +5 -0
- package/out-tsc/tests/format/ddl/materialized-view.test.d.ts.map +1 -0
- package/out-tsc/tests/format/ddl/materialized-view.test.js +92 -0
- package/out-tsc/tests/format/ddl/materialized-view.test.js.map +1 -0
- package/out-tsc/tests/format/ddl/partition.test.d.ts +5 -0
- package/out-tsc/tests/format/ddl/partition.test.d.ts.map +1 -0
- package/out-tsc/tests/format/ddl/partition.test.js +109 -0
- package/out-tsc/tests/format/ddl/partition.test.js.map +1 -0
- package/out-tsc/tests/format/ddl/variable.test.d.ts +5 -0
- package/out-tsc/tests/format/ddl/variable.test.d.ts.map +1 -0
- package/out-tsc/tests/format/ddl/variable.test.js +58 -0
- package/out-tsc/tests/format/ddl/variable.test.js.map +1 -0
- package/out-tsc/tests/format/dml/select.test.d.ts +5 -0
- package/out-tsc/tests/format/dml/select.test.d.ts.map +1 -0
- package/out-tsc/tests/format/dml/select.test.js +114 -0
- package/out-tsc/tests/format/dml/select.test.js.map +1 -0
- package/out-tsc/tests/format/dml/subquery.test.d.ts +5 -0
- package/out-tsc/tests/format/dml/subquery.test.d.ts.map +1 -0
- package/out-tsc/tests/format/dml/subquery.test.js +221 -0
- package/out-tsc/tests/format/dml/subquery.test.js.map +1 -0
- package/out-tsc/tests/format/dql/comment.test.d.ts +7 -0
- package/out-tsc/tests/format/dql/comment.test.d.ts.map +1 -0
- package/out-tsc/tests/format/dql/comment.test.js +279 -0
- package/out-tsc/tests/format/dql/comment.test.js.map +1 -0
- package/out-tsc/tests/format/dql/cte.test.d.ts +7 -0
- package/out-tsc/tests/format/dql/cte.test.d.ts.map +1 -0
- package/out-tsc/tests/format/dql/cte.test.js +31 -0
- package/out-tsc/tests/format/dql/cte.test.js.map +1 -0
- package/out-tsc/tests/format/dql/dql-detail.test.d.ts +5 -0
- package/out-tsc/tests/format/dql/dql-detail.test.d.ts.map +1 -0
- package/out-tsc/tests/format/dql/dql-detail.test.js +191 -0
- package/out-tsc/tests/format/dql/dql-detail.test.js.map +1 -0
- package/out-tsc/tests/format/dql/lateral-view.test.d.ts +7 -0
- package/out-tsc/tests/format/dql/lateral-view.test.d.ts.map +1 -0
- package/out-tsc/tests/format/dql/lateral-view.test.js +34 -0
- package/out-tsc/tests/format/dql/lateral-view.test.js.map +1 -0
- package/out-tsc/tests/format/dql/pivot.test.d.ts +7 -0
- package/out-tsc/tests/format/dql/pivot.test.d.ts.map +1 -0
- package/out-tsc/tests/format/dql/pivot.test.js +56 -0
- package/out-tsc/tests/format/dql/pivot.test.js.map +1 -0
- package/out-tsc/tests/format/dql/window.test.d.ts +7 -0
- package/out-tsc/tests/format/dql/window.test.d.ts.map +1 -0
- package/out-tsc/tests/format/dql/window.test.js +89 -0
- package/out-tsc/tests/format/dql/window.test.js.map +1 -0
- package/out-tsc/tests/format/expression/expression.test.d.ts +5 -0
- package/out-tsc/tests/format/expression/expression.test.d.ts.map +1 -0
- package/out-tsc/tests/format/expression/expression.test.js +203 -0
- package/out-tsc/tests/format/expression/expression.test.js.map +1 -0
- package/out-tsc/tests/format/pipe/pipe-operator.test.d.ts +5 -0
- package/out-tsc/tests/format/pipe/pipe-operator.test.d.ts.map +1 -0
- package/out-tsc/tests/format/pipe/pipe-operator.test.js +290 -0
- package/out-tsc/tests/format/pipe/pipe-operator.test.js.map +1 -0
- package/out-tsc/tests/format/procedure/compound-statement.test.d.ts +16 -0
- package/out-tsc/tests/format/procedure/compound-statement.test.d.ts.map +1 -0
- package/out-tsc/tests/format/procedure/compound-statement.test.js +254 -0
- package/out-tsc/tests/format/procedure/compound-statement.test.js.map +1 -0
- package/out-tsc/tests/format/resource/resource.test.d.ts +5 -0
- package/out-tsc/tests/format/resource/resource.test.d.ts.map +1 -0
- package/out-tsc/tests/format/resource/resource.test.js +69 -0
- package/out-tsc/tests/format/resource/resource.test.js.map +1 -0
- package/out-tsc/tests/format/test-utils.d.ts +40 -0
- package/out-tsc/tests/format/test-utils.d.ts.map +1 -0
- package/out-tsc/tests/format/test-utils.js +53 -0
- package/out-tsc/tests/format/test-utils.js.map +1 -0
- package/out-tsc/tests/lsp-server.test.d.ts +2 -0
- package/out-tsc/tests/lsp-server.test.d.ts.map +1 -0
- package/out-tsc/tests/lsp-server.test.js +1525 -0
- package/out-tsc/tests/lsp-server.test.js.map +1 -0
- package/out-tsc/tests/spark-sql-inputs.test.d.ts +17 -0
- package/out-tsc/tests/spark-sql-inputs.test.d.ts.map +1 -0
- package/out-tsc/tests/spark-sql-inputs.test.js +570 -0
- package/out-tsc/tests/spark-sql-inputs.test.js.map +1 -0
- package/out-tsc/tests/validation.test.d.ts +2 -0
- package/out-tsc/tests/validation.test.d.ts.map +1 -0
- package/out-tsc/tests/validation.test.js +115 -0
- package/out-tsc/tests/validation.test.js.map +1 -0
- package/out-tsc/typings.d.ts +115 -1
- package/out-tsc/typings.d.ts.map +1 -0
- package/out-tsc/typings.js +28 -0
- package/out-tsc/typings.js.map +1 -1
- package/out-tsc/utils.d.ts +1 -0
- package/out-tsc/utils.d.ts.map +1 -0
- package/out-tsc/utils.js +4 -5
- package/out-tsc/utils.js.map +1 -1
- package/out-tsc/visitors/completion.visitor.d.ts +85 -0
- package/out-tsc/visitors/completion.visitor.d.ts.map +1 -0
- package/out-tsc/visitors/completion.visitor.js +379 -0
- package/out-tsc/visitors/completion.visitor.js.map +1 -0
- package/out-tsc/visitors/lineage.visitor.d.ts +34 -0
- package/out-tsc/visitors/lineage.visitor.d.ts.map +1 -0
- package/out-tsc/visitors/lineage.visitor.js +181 -0
- package/out-tsc/visitors/lineage.visitor.js.map +1 -0
- package/out-tsc/visitors/space-replacer-format.visitor.d.ts +1641 -0
- package/out-tsc/visitors/space-replacer-format.visitor.d.ts.map +1 -0
- package/out-tsc/visitors/space-replacer-format.visitor.js +7529 -0
- package/out-tsc/visitors/space-replacer-format.visitor.js.map +1 -0
- package/out-tsc/visitors/sparksql-relation.visitor.d.ts +154 -0
- package/out-tsc/visitors/sparksql-relation.visitor.d.ts.map +1 -0
- package/out-tsc/visitors/sparksql-relation.visitor.js +749 -0
- package/out-tsc/visitors/sparksql-relation.visitor.js.map +1 -0
- package/package.json +36 -9
- package/out-tsc/constant.d.ts +0 -3
- package/out-tsc/constant.js +0 -7
- package/out-tsc/constant.js.map +0 -1
- package/out-tsc/lib/SparkSqlLexer.d.ts +0 -421
- package/out-tsc/lib/SparkSqlLexer.js +0 -2750
- package/out-tsc/lib/SparkSqlLexer.js.map +0 -1
- package/out-tsc/lib/SparkSqlParser.d.ts +0 -6203
- package/out-tsc/lib/SparkSqlParser.js +0 -34733
- package/out-tsc/lib/SparkSqlParser.js.map +0 -1
- package/out-tsc/lib/SparkSqlParserListener.d.ts +0 -1158
- package/out-tsc/lib/SparkSqlParserListener.js.map +0 -1
- package/out-tsc/lib/SparkSqlParserVisitor.d.ts +0 -773
- package/out-tsc/lib/SparkSqlParserVisitor.js.map +0 -1
|
@@ -0,0 +1,3808 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StrategyBasedFormatter = void 0;
|
|
4
|
+
const ParserRuleContext_1 = require("antlr4ts/ParserRuleContext");
|
|
5
|
+
const tree_1 = require("antlr4ts/tree");
|
|
6
|
+
const core_1 = require("./core");
|
|
7
|
+
const strategies_1 = require("./strategies");
|
|
8
|
+
const block_strategy_1 = require("./strategies/block.strategy");
|
|
9
|
+
const SparkSQLLexer_1 = require("../lib/SparkSQLLexer");
|
|
10
|
+
const SparkSQLParser_1 = require("../lib/SparkSQLParser");
|
|
11
|
+
const typings_1 = require("../typings");
|
|
12
|
+
/**
|
|
13
|
+
* Strategy-based SQL Formatter
|
|
14
|
+
*
|
|
15
|
+
* Uses a registry of strategies to format different SQL statement types.
|
|
16
|
+
*/
|
|
17
|
+
class StrategyBasedFormatter extends tree_1.AbstractParseTreeVisitor {
|
|
18
|
+
constructor(tokens, config) {
|
|
19
|
+
super();
|
|
20
|
+
this.tokens = tokens;
|
|
21
|
+
this.config = config;
|
|
22
|
+
this.textEdits = [];
|
|
23
|
+
this.formattingErrors = [];
|
|
24
|
+
this.editCollector = new core_1.EditCollector();
|
|
25
|
+
this.blockStrategy = new block_strategy_1.BlockStrategy();
|
|
26
|
+
// Legacy flags for compatibility during transition
|
|
27
|
+
this.inPipeOperatorRightSide = false;
|
|
28
|
+
this.inPipeSubquery = false;
|
|
29
|
+
this.inWindowClause = false;
|
|
30
|
+
this.inUnpivotLongContent = false;
|
|
31
|
+
this.newline = '\n';
|
|
32
|
+
this.context = new core_1.FormattingContext(config.tabSize);
|
|
33
|
+
this.maxParenthesisLength = config.maxParenthesisLength ?? typings_1.DEFAULT_MAX_PARENTHESIS_LENGTH;
|
|
34
|
+
this.strategies = [...strategies_1.allStrategies];
|
|
35
|
+
// Create bridge with visitor reference
|
|
36
|
+
this.bridge = new core_1.StrategyFormattingBridge({
|
|
37
|
+
tokens: this.tokens,
|
|
38
|
+
context: this.context,
|
|
39
|
+
collector: this.editCollector,
|
|
40
|
+
visitor: {
|
|
41
|
+
visit: ctx => this.visit(ctx),
|
|
42
|
+
visitChildren: ctx => this.visitChildren(ctx)
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get formatted text edits
|
|
48
|
+
*/
|
|
49
|
+
getTextEdits() {
|
|
50
|
+
return this.textEdits;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Process the parse tree and collect edits
|
|
54
|
+
*/
|
|
55
|
+
format(ctx) {
|
|
56
|
+
this.textEdits = [];
|
|
57
|
+
this.formattingErrors = [];
|
|
58
|
+
this.editCollector.clear();
|
|
59
|
+
// Visit the tree
|
|
60
|
+
this.visit(ctx);
|
|
61
|
+
// Post-processing: normalize keyword spacing
|
|
62
|
+
this.normalizeAllKeywordSpacing();
|
|
63
|
+
// Post-processing: compress consecutive spaces
|
|
64
|
+
this.compressConsecutiveSpaces();
|
|
65
|
+
// Post-processing: clean parentheses spaces and comma spaces
|
|
66
|
+
this.cleanParenthesesSpaces();
|
|
67
|
+
// Collect all edits
|
|
68
|
+
this.textEdits = this.editCollector.getEdits();
|
|
69
|
+
return this.textEdits;
|
|
70
|
+
}
|
|
71
|
+
defaultResult() {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// ==================== Strategy Dispatch ====================
|
|
75
|
+
/**
|
|
76
|
+
* Try to format using registered strategies, fallback to default behavior
|
|
77
|
+
*/
|
|
78
|
+
tryFormatWithStrategy(ctx) {
|
|
79
|
+
for (const strategy of this.strategies) {
|
|
80
|
+
if (strategy.canHandle(ctx)) {
|
|
81
|
+
try {
|
|
82
|
+
strategy.format(ctx, this.bridge);
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
// Log error but continue
|
|
87
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
88
|
+
this.formattingErrors.push({
|
|
89
|
+
message: `Strategy ${strategy.id} failed: ${errorMessage}`,
|
|
90
|
+
line: ctx.start.line - 1
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
// ==================== Program & Statements ====================
|
|
98
|
+
visitProgram(ctx) {
|
|
99
|
+
const statements = ctx.statements();
|
|
100
|
+
if (statements) {
|
|
101
|
+
this.visit(statements);
|
|
102
|
+
}
|
|
103
|
+
this.compressConsecutiveSpaces();
|
|
104
|
+
}
|
|
105
|
+
visitStatements(ctx) {
|
|
106
|
+
const stmtForFoldings = ctx.statementForFolding();
|
|
107
|
+
stmtForFoldings.forEach((sff, index) => {
|
|
108
|
+
if (index > 0) {
|
|
109
|
+
// Handle statement separation
|
|
110
|
+
const prevStmt = stmtForFoldings[index - 1];
|
|
111
|
+
const prevStmtEnd = prevStmt._stop?.stopIndex ?? 0;
|
|
112
|
+
const currStmtStart = sff._start?.startIndex ?? 0;
|
|
113
|
+
const commentsBetween = this.findCommentTokensBetween(prevStmtEnd + 1, currStmtStart);
|
|
114
|
+
if (commentsBetween.length > 0) {
|
|
115
|
+
const firstComment = commentsBetween[0];
|
|
116
|
+
const prevToken = this.getPreviousNonHiddenTokenByStartIndex(firstComment.startIndex);
|
|
117
|
+
if (prevToken) {
|
|
118
|
+
const prevEndPos = this.getTokenEndPosition(prevToken);
|
|
119
|
+
this.addTextEdit({
|
|
120
|
+
range: {
|
|
121
|
+
start: prevEndPos,
|
|
122
|
+
end: { line: firstComment.line - 1, character: firstComment.charPositionInLine }
|
|
123
|
+
},
|
|
124
|
+
newText: this.newline
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
const prevStmt = stmtForFoldings[index - 1];
|
|
130
|
+
if (prevStmt._stop) {
|
|
131
|
+
const prevStmtEndIndex = prevStmt._stop.stopIndex;
|
|
132
|
+
const currStmtStartIndex = sff._start?.startIndex ?? 0;
|
|
133
|
+
const allTokens = this.tokens.getTokens();
|
|
134
|
+
let semicolonToken;
|
|
135
|
+
for (const token of allTokens) {
|
|
136
|
+
if (token.startIndex > prevStmtEndIndex && token.startIndex < currStmtStartIndex) {
|
|
137
|
+
if (token.channel === 0 && token.text === ';') {
|
|
138
|
+
semicolonToken = token;
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (semicolonToken) {
|
|
144
|
+
const semicolonEndPos = this.getTokenEndPosition(semicolonToken);
|
|
145
|
+
const currStmtRange = { start: { line: sff.start.line - 1, character: sff.start.charPositionInLine } };
|
|
146
|
+
this.addTextEdit({
|
|
147
|
+
range: { start: semicolonEndPos, end: currStmtRange.start },
|
|
148
|
+
newText: this.newline.repeat(2)
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
this.visit(sff);
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
159
|
+
this.formattingErrors.push({
|
|
160
|
+
message: `Statement formatting failed: ${errorMessage}`,
|
|
161
|
+
line: sff.start.line - 1
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
ctx.statementEnd().forEach(stmtEnd => this.visit(stmtEnd));
|
|
166
|
+
}
|
|
167
|
+
visitStatementEnd(ctx) {
|
|
168
|
+
const semicolon = ctx.SEMICOLON();
|
|
169
|
+
if (semicolon) {
|
|
170
|
+
// Clean whitespace before semicolon
|
|
171
|
+
const prevToken = this.getPreviousNonHiddenTokenByStartIndex(semicolon.symbol.startIndex);
|
|
172
|
+
if (prevToken) {
|
|
173
|
+
const prevEndPos = this.getTokenEndPosition(prevToken);
|
|
174
|
+
this.addTextEdit({
|
|
175
|
+
range: {
|
|
176
|
+
start: prevEndPos,
|
|
177
|
+
end: { line: semicolon.symbol.line - 1, character: semicolon.symbol.charPositionInLine }
|
|
178
|
+
},
|
|
179
|
+
newText: ''
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
// Clean whitespace after semicolon (before comment on same line)
|
|
183
|
+
const allTokens = this.tokens.getTokens();
|
|
184
|
+
const semicolonIdx = allTokens.indexOf(semicolon.symbol);
|
|
185
|
+
const semicolonEndPos = this.getTokenEndPosition(semicolon.symbol);
|
|
186
|
+
for (let i = semicolonIdx + 1; i < allTokens.length; i++) {
|
|
187
|
+
const token = allTokens[i];
|
|
188
|
+
// Skip EOF
|
|
189
|
+
if (token.type === 0)
|
|
190
|
+
break;
|
|
191
|
+
// Skip only whitespace tokens - comments (channel 1) should be preserved
|
|
192
|
+
if (token.channel === 1 && token.text?.trim() === '')
|
|
193
|
+
continue;
|
|
194
|
+
// Found next meaningful token on same line (comment or other)
|
|
195
|
+
if (token.line === semicolon.symbol.line) {
|
|
196
|
+
const nextTokenStartPos = {
|
|
197
|
+
line: token.line - 1,
|
|
198
|
+
character: token.charPositionInLine
|
|
199
|
+
};
|
|
200
|
+
// Only add edit if there's actual whitespace to clean
|
|
201
|
+
if (semicolonEndPos.character < nextTokenStartPos.character) {
|
|
202
|
+
this.addTextEdit({
|
|
203
|
+
range: {
|
|
204
|
+
start: semicolonEndPos,
|
|
205
|
+
end: nextTokenStartPos
|
|
206
|
+
},
|
|
207
|
+
newText: ' '
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
// Token on different line - stop looking
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
visitStatementForFolding(ctx) {
|
|
218
|
+
// Delegate to the inner statement, setResetStatement, or compoundStatement
|
|
219
|
+
const stmt = ctx.statement();
|
|
220
|
+
const setResetStmt = ctx.setResetStatement();
|
|
221
|
+
const compoundStmt = ctx.compoundStatement();
|
|
222
|
+
if (stmt) {
|
|
223
|
+
this.visit(stmt);
|
|
224
|
+
}
|
|
225
|
+
else if (setResetStmt) {
|
|
226
|
+
this.visit(setResetStmt);
|
|
227
|
+
}
|
|
228
|
+
else if (compoundStmt) {
|
|
229
|
+
this.visit(compoundStmt);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
// ==================== DDL Statements ====================
|
|
233
|
+
visitCreateTable(ctx) {
|
|
234
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
235
|
+
super.visitChildren(ctx);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
visitCreateTableLike(ctx) {
|
|
239
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
240
|
+
super.visitChildren(ctx);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
visitCreateTableHeader(ctx) {
|
|
244
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
245
|
+
super.visitChildren(ctx);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
visitCreateTableClauses(ctx) {
|
|
249
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
250
|
+
super.visitChildren(ctx);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
visitTableProvider(ctx) {
|
|
254
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
255
|
+
super.visitChildren(ctx);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
visitTableElementList(ctx) {
|
|
259
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
260
|
+
super.visitChildren(ctx);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
visitColDefinition(ctx) {
|
|
264
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
265
|
+
super.visitChildren(ctx);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
visitReplaceTable(ctx) {
|
|
269
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
270
|
+
super.visitChildren(ctx);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
visitReplaceTableHeader(ctx) {
|
|
274
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
275
|
+
super.visitChildren(ctx);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
visitCreatePipelineDataset(ctx) {
|
|
279
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
280
|
+
super.visitChildren(ctx);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
visitCreatePipelineDatasetHeader(ctx) {
|
|
284
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
285
|
+
super.visitChildren(ctx);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
visitCreatePipelineInsertIntoFlow(ctx) {
|
|
289
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
290
|
+
super.visitChildren(ctx);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
visitCreatePipelineFlowHeader(ctx) {
|
|
294
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
295
|
+
super.visitChildren(ctx);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
visitPartitionFieldList(ctx) {
|
|
299
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
300
|
+
super.visitChildren(ctx);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
visitPropertyList(ctx) {
|
|
304
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
305
|
+
super.visitChildren(ctx);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
visitExpressionPropertyList(ctx) {
|
|
309
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
310
|
+
super.visitChildren(ctx);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
visitPropertyWithKeyAndEquals(ctx) {
|
|
314
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
315
|
+
super.visitChildren(ctx);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
visitExpressionPropertyWithKeyAndEquals(ctx) {
|
|
319
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
320
|
+
super.visitChildren(ctx);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
visitCreateView(ctx) {
|
|
324
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
325
|
+
super.visitChildren(ctx);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
visitCreateMetricView(ctx) {
|
|
329
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
330
|
+
super.visitChildren(ctx);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
visitCreateTempViewUsing(ctx) {
|
|
334
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
335
|
+
super.visitChildren(ctx);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
visitCreateFunction(ctx) {
|
|
339
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
340
|
+
super.visitChildren(ctx);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
visitResource(ctx) {
|
|
344
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
345
|
+
super.visitChildren(ctx);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// ==================== ALTER TABLE ====================
|
|
349
|
+
visitAddTableColumns(ctx) {
|
|
350
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
351
|
+
super.visitChildren(ctx);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
visitRenameTableColumn(ctx) {
|
|
355
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
356
|
+
super.visitChildren(ctx);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
visitDropTableColumns(ctx) {
|
|
360
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
361
|
+
super.visitChildren(ctx);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
visitRenameTable(ctx) {
|
|
365
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
366
|
+
super.visitChildren(ctx);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
visitSetTableProperties(ctx) {
|
|
370
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
371
|
+
super.visitChildren(ctx);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
visitUnsetTableProperties(ctx) {
|
|
375
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
376
|
+
super.visitChildren(ctx);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
visitAlterTableAlterColumn(ctx) {
|
|
380
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
381
|
+
super.visitChildren(ctx);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
visitAlterColumnAction(ctx) {
|
|
385
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
386
|
+
super.visitChildren(ctx);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
visitHiveChangeColumn(ctx) {
|
|
390
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
391
|
+
super.visitChildren(ctx);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
visitAlterViewQuery(ctx) {
|
|
395
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
396
|
+
super.visitChildren(ctx);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
visitSetTableLocation(ctx) {
|
|
400
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
401
|
+
super.visitChildren(ctx);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
// ==================== PARTITION Statements ====================
|
|
405
|
+
visitAddTablePartition(ctx) {
|
|
406
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
407
|
+
super.visitChildren(ctx);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
visitDropTablePartitions(ctx) {
|
|
411
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
412
|
+
super.visitChildren(ctx);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
visitRenameTablePartition(ctx) {
|
|
416
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
417
|
+
super.visitChildren(ctx);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
// ==================== DROP Statements ====================
|
|
421
|
+
visitDropTable(ctx) {
|
|
422
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
423
|
+
super.visitChildren(ctx);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
visitDropView(ctx) {
|
|
427
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
428
|
+
super.visitChildren(ctx);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
visitDropFunction(ctx) {
|
|
432
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
433
|
+
super.visitChildren(ctx);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
visitDropNamespace(ctx) {
|
|
437
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
438
|
+
super.visitChildren(ctx);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
// ==================== RESOURCE Management Statements ====================
|
|
442
|
+
visitManageResource(ctx) {
|
|
443
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
444
|
+
super.visitChildren(ctx);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
// ==================== SET/RESET Statements ====================
|
|
448
|
+
visitSetVariable(ctx) {
|
|
449
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
450
|
+
super.visitChildren(ctx);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
// ==================== INDEX Statements ====================
|
|
454
|
+
visitCreateIndex(ctx) {
|
|
455
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
456
|
+
super.visitChildren(ctx);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
visitDropIndex(ctx) {
|
|
460
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
461
|
+
super.visitChildren(ctx);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
// ==================== DML Statements ====================
|
|
465
|
+
visitQuery(ctx) {
|
|
466
|
+
if (ctx.ctes()) {
|
|
467
|
+
this.updateBeforeNextTokenByContext(ctx.ctes(), this.newlineIndent);
|
|
468
|
+
}
|
|
469
|
+
super.visitChildren(ctx);
|
|
470
|
+
}
|
|
471
|
+
// ==================== CTE (Common Table Expression) ====================
|
|
472
|
+
visitCtes(ctx) {
|
|
473
|
+
// Ensure single space after WITH keyword
|
|
474
|
+
const withNode = ctx.WITH();
|
|
475
|
+
if (withNode) {
|
|
476
|
+
this.updateBeforeNextTokenByNode(withNode, ' ');
|
|
477
|
+
}
|
|
478
|
+
// Handle RECURSIVE keyword if present
|
|
479
|
+
const recursiveNode = ctx.RECURSIVE();
|
|
480
|
+
if (recursiveNode) {
|
|
481
|
+
this.updateAfterPreviousTokenByNode(recursiveNode, ' ');
|
|
482
|
+
this.updateBeforeNextTokenByNode(recursiveNode, ' ');
|
|
483
|
+
}
|
|
484
|
+
// Format commas between CTEs - each namedQuery on new line after comma
|
|
485
|
+
const namedQueries = ctx.namedQuery();
|
|
486
|
+
for (let i = 1; i < namedQueries.length; i++) {
|
|
487
|
+
this.updateAfterPreviousTokenByContext(namedQueries[i], this.newlineIndent);
|
|
488
|
+
}
|
|
489
|
+
super.visitChildren(ctx);
|
|
490
|
+
}
|
|
491
|
+
visitNamedQuery(ctx) {
|
|
492
|
+
// Format AS keyword
|
|
493
|
+
this.normalizeKeywordSpacing(ctx.AS());
|
|
494
|
+
// Format the parentheses and query content
|
|
495
|
+
const leftParen = ctx.LEFT_PAREN();
|
|
496
|
+
const rightParen = ctx.RIGHT_PAREN();
|
|
497
|
+
if (leftParen && rightParen) {
|
|
498
|
+
// Content on new line with indent after left parenthesis
|
|
499
|
+
this.addIndent();
|
|
500
|
+
this.updateBeforeNextTokenByNode(leftParen, this.newlineIndent);
|
|
501
|
+
// Visit children with added indent
|
|
502
|
+
super.visitChildren(ctx);
|
|
503
|
+
// Check if this CTE is part of a DML statement with INSERT
|
|
504
|
+
let parent = ctx.parent;
|
|
505
|
+
let isInDmlStatement = false;
|
|
506
|
+
while (parent) {
|
|
507
|
+
if (parent.constructor.name === 'DmlStatementContext') {
|
|
508
|
+
isInDmlStatement = true;
|
|
509
|
+
break;
|
|
510
|
+
}
|
|
511
|
+
parent = parent.parent;
|
|
512
|
+
}
|
|
513
|
+
// Reduce indent before right parenthesis (so it aligns with WITH)
|
|
514
|
+
this.minusIndent();
|
|
515
|
+
// Right parenthesis on new line without extra indent (aligned with WITH)
|
|
516
|
+
this.updateAfterPreviousTokenByNode(rightParen, this.newlineIndent);
|
|
517
|
+
if (isInDmlStatement) {
|
|
518
|
+
// For CTE followed by INSERT - keep INSERT on same line
|
|
519
|
+
this.updateBeforeNextTokenByNode(rightParen, ' ');
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
super.visitChildren(ctx);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
visitRegularQuerySpecification(ctx) {
|
|
527
|
+
// Legacy visitor doesn't add indent here - each clause handles its own indentation
|
|
528
|
+
super.visitChildren(ctx);
|
|
529
|
+
}
|
|
530
|
+
visitSelectClause(ctx) {
|
|
531
|
+
// Ensure setQuantifier (DISTINCT/ALL) stays on same line as SELECT
|
|
532
|
+
const setQuantifier = ctx.setQuantifier();
|
|
533
|
+
if (setQuantifier) {
|
|
534
|
+
const selectToken = ctx.SELECT();
|
|
535
|
+
if (selectToken) {
|
|
536
|
+
this.updateBeforeNextTokenByNode(selectToken, ' ');
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
540
|
+
super.visitChildren(ctx);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
visitFromClause(ctx) {
|
|
544
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
545
|
+
const fromToken = ctx.FROM();
|
|
546
|
+
const relations = ctx.relation();
|
|
547
|
+
const lateralViews = ctx.lateralView();
|
|
548
|
+
// Add newline before FROM
|
|
549
|
+
if (fromToken) {
|
|
550
|
+
this.updateAfterPreviousTokenByNode(fromToken, this.newlineIndent);
|
|
551
|
+
}
|
|
552
|
+
// Add indent for FROM body (but not for VALUES - that handles its own indent)
|
|
553
|
+
let addedIndent = false;
|
|
554
|
+
if (relations.length > 0) {
|
|
555
|
+
// Check if first relation is an InlineTable (VALUES clause)
|
|
556
|
+
const firstRelationPrimary = relations[0].relationPrimary();
|
|
557
|
+
const isFromValues = firstRelationPrimary?.constructor.name === 'InlineTableDefault2Context';
|
|
558
|
+
if (!isFromValues) {
|
|
559
|
+
this.addIndent();
|
|
560
|
+
addedIndent = true;
|
|
561
|
+
// First relation on new line with indent
|
|
562
|
+
this.updateAfterPreviousTokenByContext(relations[0], this.newlineIndent);
|
|
563
|
+
}
|
|
564
|
+
// For FROM VALUES, visitInlineTable handles all formatting including indent
|
|
565
|
+
}
|
|
566
|
+
// Format commas in FROM clause
|
|
567
|
+
const commas = ctx.COMMA();
|
|
568
|
+
for (const comma of commas) {
|
|
569
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
570
|
+
this.updateBeforeNextTokenByNode(comma, this.newlineIndent);
|
|
571
|
+
}
|
|
572
|
+
// Visit relations
|
|
573
|
+
for (const relation of relations) {
|
|
574
|
+
this.visit(relation);
|
|
575
|
+
}
|
|
576
|
+
// Visit LATERAL VIEW with proper indent (one level more than FROM body)
|
|
577
|
+
if (lateralViews.length > 0 && addedIndent) {
|
|
578
|
+
// LATERAL VIEW should have one more level of indent
|
|
579
|
+
// But we already have FROM body indent, so LATERAL VIEW just needs newline
|
|
580
|
+
for (const lateralView of lateralViews) {
|
|
581
|
+
this.visit(lateralView);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
else {
|
|
585
|
+
for (const lateralView of lateralViews) {
|
|
586
|
+
this.visit(lateralView);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
// Visit PIVOT and UNPIVOT
|
|
590
|
+
const pivotClause = ctx.pivotClause();
|
|
591
|
+
if (pivotClause) {
|
|
592
|
+
this.visit(pivotClause);
|
|
593
|
+
}
|
|
594
|
+
const unpivotClause = ctx.unpivotClause();
|
|
595
|
+
if (unpivotClause) {
|
|
596
|
+
this.visit(unpivotClause);
|
|
597
|
+
}
|
|
598
|
+
if (addedIndent) {
|
|
599
|
+
this.minusIndent();
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
visitWhereClause(ctx) {
|
|
604
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
605
|
+
const whereToken = ctx.WHERE();
|
|
606
|
+
const booleanExpr = ctx.booleanExpression();
|
|
607
|
+
// Add newline before WHERE (except in pipe context where `|> WHERE` is on same line)
|
|
608
|
+
if (whereToken && !this.inPipeOperatorRightSide) {
|
|
609
|
+
this.updateAfterPreviousTokenByNode(whereToken, this.newlineIndent);
|
|
610
|
+
}
|
|
611
|
+
// Add indent for WHERE body and newline before expression
|
|
612
|
+
// In pipe context, this is handled by PipeWhereStrategy
|
|
613
|
+
if (booleanExpr && !this.inPipeOperatorRightSide) {
|
|
614
|
+
this.addIndent();
|
|
615
|
+
this.updateAfterPreviousTokenByContext(booleanExpr, this.newlineIndent);
|
|
616
|
+
}
|
|
617
|
+
super.visitChildren(ctx);
|
|
618
|
+
if (booleanExpr && !this.inPipeOperatorRightSide) {
|
|
619
|
+
this.minusIndent();
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
visitAggregationClause(ctx) {
|
|
624
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
625
|
+
const groupToken = ctx.GROUP();
|
|
626
|
+
const byToken = ctx.BY();
|
|
627
|
+
const expressions = ctx.namedExpression();
|
|
628
|
+
const groupByClauses = ctx.groupByClause();
|
|
629
|
+
// In pipe context, GROUP BY formatting is handled by PipeAggregateStrategy
|
|
630
|
+
if (!this.inPipeOperatorRightSide) {
|
|
631
|
+
// Add newline before GROUP BY
|
|
632
|
+
if (groupToken) {
|
|
633
|
+
this.updateAfterPreviousTokenByNode(groupToken, this.newlineIndent);
|
|
634
|
+
}
|
|
635
|
+
if (byToken) {
|
|
636
|
+
this.updateBeforeNextTokenByNode(byToken, ' ');
|
|
637
|
+
this.updateAfterPreviousTokenByNode(byToken, ' ');
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
// Add indent for GROUP BY body (except in pipe context)
|
|
641
|
+
if (!this.inPipeOperatorRightSide) {
|
|
642
|
+
this.addIndent();
|
|
643
|
+
}
|
|
644
|
+
// Get first expression and put it on new line with indent
|
|
645
|
+
// In pipe context, use pipe-specific indent
|
|
646
|
+
const groupByBodyIndent = this.inPipeOperatorRightSide ? this.context.getPipeContentIndent() : this.newlineIndent;
|
|
647
|
+
if (groupByClauses.length > 0) {
|
|
648
|
+
this.updateAfterPreviousTokenByContext(groupByClauses[0], groupByBodyIndent);
|
|
649
|
+
}
|
|
650
|
+
else if (expressions.length > 0) {
|
|
651
|
+
this.updateAfterPreviousTokenByContext(expressions[0], groupByBodyIndent);
|
|
652
|
+
}
|
|
653
|
+
// Format commas
|
|
654
|
+
const commas = ctx.COMMA();
|
|
655
|
+
for (const comma of commas) {
|
|
656
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
657
|
+
this.updateBeforeNextTokenByNode(comma, groupByBodyIndent);
|
|
658
|
+
}
|
|
659
|
+
// Handle WITH ROLLUP / WITH CUBE - on separate line
|
|
660
|
+
const withToken = ctx.WITH();
|
|
661
|
+
if (withToken) {
|
|
662
|
+
this.updateAfterPreviousTokenByNode(withToken, this.newlineIndent);
|
|
663
|
+
this.updateBeforeNextTokenByNode(withToken, ' ');
|
|
664
|
+
}
|
|
665
|
+
const rollupToken = ctx.ROLLUP();
|
|
666
|
+
if (rollupToken) {
|
|
667
|
+
this.updateAfterPreviousTokenByNode(rollupToken, ' ');
|
|
668
|
+
}
|
|
669
|
+
const cubeToken = ctx.CUBE();
|
|
670
|
+
if (cubeToken) {
|
|
671
|
+
this.updateAfterPreviousTokenByNode(cubeToken, ' ');
|
|
672
|
+
}
|
|
673
|
+
// Note: GROUPING SETS is handled in visitGroupingAnalytics
|
|
674
|
+
super.visitChildren(ctx);
|
|
675
|
+
if (!this.inPipeOperatorRightSide) {
|
|
676
|
+
this.minusIndent();
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
visitHavingClause(ctx) {
|
|
681
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
682
|
+
const havingToken = ctx.HAVING();
|
|
683
|
+
const booleanExpr = ctx.booleanExpression();
|
|
684
|
+
// Add newline before HAVING
|
|
685
|
+
if (havingToken) {
|
|
686
|
+
this.updateAfterPreviousTokenByNode(havingToken, this.newlineIndent);
|
|
687
|
+
}
|
|
688
|
+
// Add indent for HAVING body and newline before expression
|
|
689
|
+
if (booleanExpr) {
|
|
690
|
+
this.addIndent();
|
|
691
|
+
this.updateAfterPreviousTokenByContext(booleanExpr, this.newlineIndent);
|
|
692
|
+
}
|
|
693
|
+
super.visitChildren(ctx);
|
|
694
|
+
if (booleanExpr) {
|
|
695
|
+
this.minusIndent();
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
visitQueryOrganization(ctx) {
|
|
700
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
701
|
+
// In pipe context, ORDER BY / LIMIT should stay on same line as |>\n // Pipe content indent: 5 spaces from `|>` position
|
|
702
|
+
const pipeContentIndent = this.inPipeOperatorRightSide ? this.context.getPipeContentIndent() : this.newlineIndent;
|
|
703
|
+
// For keywords like ORDER BY, use 3 spaces indent
|
|
704
|
+
const _pipeClauseIndent = this.inPipeOperatorRightSide ? `\n${this.indent} ` : this.newlineIndent;
|
|
705
|
+
// Handle ORDER BY
|
|
706
|
+
if (ctx.ORDER()) {
|
|
707
|
+
// In pipe context: `|> ORDER BY` on same line, content indent 5 spaces
|
|
708
|
+
if (!this.inPipeOperatorRightSide) {
|
|
709
|
+
this.updateAfterPreviousTokenByNode(ctx.ORDER(), this.newlineIndent);
|
|
710
|
+
}
|
|
711
|
+
const byTokens = ctx.BY();
|
|
712
|
+
for (const by of byTokens) {
|
|
713
|
+
this.updateBeforeNextTokenByNode(by, ' ');
|
|
714
|
+
this.updateAfterPreviousTokenByNode(by, ' ');
|
|
715
|
+
}
|
|
716
|
+
// Add indent and newline before first expression
|
|
717
|
+
if (!this.inPipeOperatorRightSide) {
|
|
718
|
+
this.addIndent();
|
|
719
|
+
}
|
|
720
|
+
const expressions = ctx.sortItem();
|
|
721
|
+
if (expressions.length > 0) {
|
|
722
|
+
this.updateAfterPreviousTokenByContext(expressions[0], pipeContentIndent);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
// Handle CLUSTER BY
|
|
726
|
+
if (ctx.CLUSTER()) {
|
|
727
|
+
if (!this.inPipeOperatorRightSide) {
|
|
728
|
+
this.updateAfterPreviousTokenByNode(ctx.CLUSTER(), this.newlineIndent);
|
|
729
|
+
}
|
|
730
|
+
const byTokens = ctx.BY();
|
|
731
|
+
for (const by of byTokens) {
|
|
732
|
+
this.updateBeforeNextTokenByNode(by, ' ');
|
|
733
|
+
this.updateAfterPreviousTokenByNode(by, ' ');
|
|
734
|
+
}
|
|
735
|
+
if (!this.inPipeOperatorRightSide) {
|
|
736
|
+
this.addIndent();
|
|
737
|
+
}
|
|
738
|
+
const expressions = ctx.expression();
|
|
739
|
+
if (expressions.length > 0) {
|
|
740
|
+
this.updateAfterPreviousTokenByContext(expressions[0], pipeContentIndent);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
// Handle DISTRIBUTE BY
|
|
744
|
+
if (ctx.DISTRIBUTE()) {
|
|
745
|
+
if (!this.inPipeOperatorRightSide) {
|
|
746
|
+
this.updateAfterPreviousTokenByNode(ctx.DISTRIBUTE(), this.newlineIndent);
|
|
747
|
+
}
|
|
748
|
+
const byTokens = ctx.BY();
|
|
749
|
+
for (const by of byTokens) {
|
|
750
|
+
this.updateBeforeNextTokenByNode(by, ' ');
|
|
751
|
+
this.updateAfterPreviousTokenByNode(by, ' ');
|
|
752
|
+
}
|
|
753
|
+
if (!this.inPipeOperatorRightSide) {
|
|
754
|
+
this.addIndent();
|
|
755
|
+
}
|
|
756
|
+
const expressions = ctx.expression();
|
|
757
|
+
if (expressions.length > 0) {
|
|
758
|
+
this.updateAfterPreviousTokenByContext(expressions[0], pipeContentIndent);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
// Handle SORT BY
|
|
762
|
+
if (ctx.SORT()) {
|
|
763
|
+
if (!this.inPipeOperatorRightSide) {
|
|
764
|
+
this.updateAfterPreviousTokenByNode(ctx.SORT(), this.newlineIndent);
|
|
765
|
+
}
|
|
766
|
+
const byTokens = ctx.BY();
|
|
767
|
+
for (const by of byTokens) {
|
|
768
|
+
this.updateBeforeNextTokenByNode(by, ' ');
|
|
769
|
+
this.updateAfterPreviousTokenByNode(by, ' ');
|
|
770
|
+
}
|
|
771
|
+
if (!this.inPipeOperatorRightSide) {
|
|
772
|
+
this.addIndent();
|
|
773
|
+
}
|
|
774
|
+
const expressions = ctx.sortItem();
|
|
775
|
+
if (expressions.length > 0) {
|
|
776
|
+
this.updateAfterPreviousTokenByContext(expressions[0], pipeContentIndent);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
// Handle LIMIT - in pipe context, stays on same line as |>
|
|
780
|
+
if (ctx.LIMIT() && !this.inPipeOperatorRightSide) {
|
|
781
|
+
this.updateAfterPreviousTokenByNode(ctx.LIMIT(), this.newlineIndent);
|
|
782
|
+
}
|
|
783
|
+
// Handle OFFSET
|
|
784
|
+
if (ctx.OFFSET() && !this.inPipeOperatorRightSide) {
|
|
785
|
+
this.updateAfterPreviousTokenByNode(ctx.OFFSET(), this.newlineIndent);
|
|
786
|
+
}
|
|
787
|
+
super.visitChildren(ctx);
|
|
788
|
+
// Minus indent after processing (except in pipe context)
|
|
789
|
+
if (!this.inPipeOperatorRightSide && (ctx.ORDER() || ctx.CLUSTER() || ctx.DISTRIBUTE() || ctx.SORT())) {
|
|
790
|
+
this.minusIndent();
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
visitSortItem(ctx) {
|
|
795
|
+
// Format ASC/DESC - space before
|
|
796
|
+
if (ctx.ASC()) {
|
|
797
|
+
this.updateAfterPreviousTokenByNode(ctx.ASC(), ' ');
|
|
798
|
+
}
|
|
799
|
+
if (ctx.DESC()) {
|
|
800
|
+
this.updateAfterPreviousTokenByNode(ctx.DESC(), ' ');
|
|
801
|
+
}
|
|
802
|
+
// Format NULLS FIRST/LAST - space before NULLS
|
|
803
|
+
if (ctx.NULLS()) {
|
|
804
|
+
this.updateAfterPreviousTokenByNode(ctx.NULLS(), ' ');
|
|
805
|
+
}
|
|
806
|
+
super.visitChildren(ctx);
|
|
807
|
+
}
|
|
808
|
+
// ==================== Window Clause ====================
|
|
809
|
+
visitWindowClause(ctx) {
|
|
810
|
+
// In pipe context, WINDOW formatting is handled by PipeSelectStrategy
|
|
811
|
+
if (!this.inPipeOperatorRightSide) {
|
|
812
|
+
// WINDOW on new line with indent
|
|
813
|
+
this.updateAfterPreviousTokenByContext(ctx, this.newlineIndent);
|
|
814
|
+
}
|
|
815
|
+
// 判断是否为多窗口
|
|
816
|
+
const namedWindows = ctx.namedWindow();
|
|
817
|
+
const _isMultipleWindows = namedWindows.length > 1;
|
|
818
|
+
// 检查每个窗口是否为简单窗口(无窗口帧、无多参数)
|
|
819
|
+
const windowIsSimple = [];
|
|
820
|
+
for (const nw of namedWindows) {
|
|
821
|
+
const windowSpec = nw.windowSpec();
|
|
822
|
+
if (windowSpec instanceof SparkSQLParser_1.WindowDefContext) {
|
|
823
|
+
const hasWindowFrame = windowSpec.windowFrame();
|
|
824
|
+
const partitionExprs = windowSpec._partition || [];
|
|
825
|
+
const sortItems = windowSpec.sortItem();
|
|
826
|
+
const hasMultiplePartitionExprs = windowSpec.PARTITION() && partitionExprs.length > 1;
|
|
827
|
+
const hasMultipleSortItems = windowSpec.ORDER() && sortItems.length > 1;
|
|
828
|
+
// 简单窗口:无窗口帧、无多参数
|
|
829
|
+
windowIsSimple.push(!hasWindowFrame && !hasMultiplePartitionExprs && !hasMultipleSortItems);
|
|
830
|
+
}
|
|
831
|
+
else {
|
|
832
|
+
windowIsSimple.push(true); // WindowRef 视为简单
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
// 短句判断:单窗口且简单窗口
|
|
836
|
+
const isShortForm = namedWindows.length === 1 && windowIsSimple[0];
|
|
837
|
+
if (isShortForm) {
|
|
838
|
+
// 短句格式:WINDOW w AS (PARTITION BY dept ORDER BY date)
|
|
839
|
+
// 不添加缩进,不换行
|
|
840
|
+
this.inWindowClause = false; // 标记不在需要换行的 WINDOW 子句中
|
|
841
|
+
// 标记每个窗口定义是在 WINDOW 子句中
|
|
842
|
+
for (const nw of namedWindows) {
|
|
843
|
+
const windowSpec = nw.windowSpec();
|
|
844
|
+
if (windowSpec) {
|
|
845
|
+
windowSpec._isInWindowClause = true;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
super.visitChildren(ctx);
|
|
849
|
+
}
|
|
850
|
+
else {
|
|
851
|
+
// 长句格式
|
|
852
|
+
// WINDOW 后换行
|
|
853
|
+
const windowToken = ctx.WINDOW();
|
|
854
|
+
if (windowToken) {
|
|
855
|
+
this.updateBeforeNextTokenByNode(windowToken, this.newlineIndent);
|
|
856
|
+
}
|
|
857
|
+
this.addIndent();
|
|
858
|
+
// Format commas between window definitions - each window on separate line
|
|
859
|
+
const commas = ctx.COMMA();
|
|
860
|
+
for (let i = 0; i < commas.length; i++) {
|
|
861
|
+
const comma = commas[i];
|
|
862
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
863
|
+
this.updateBeforeNextTokenByNode(comma, this.newlineIndent);
|
|
864
|
+
}
|
|
865
|
+
// 存储每个窗口是否简单的信息,供 visitWindowDef 使用
|
|
866
|
+
ctx._windowIsSimple = windowIsSimple;
|
|
867
|
+
ctx._currentWindowIndex = 0;
|
|
868
|
+
this.inWindowClause = true; // 标记在需要换行的 WINDOW 子句中
|
|
869
|
+
super.visitChildren(ctx);
|
|
870
|
+
this.minusIndent();
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
visitNamedWindow(ctx) {
|
|
874
|
+
// Format AS keyword - space before and after
|
|
875
|
+
this.normalizeKeywordSpacing(ctx.AS());
|
|
876
|
+
// 长句格式时,窗口名需要缩进 2 空格
|
|
877
|
+
if (this.inWindowClause) {
|
|
878
|
+
// 窗口名(identifier)前换行缩进
|
|
879
|
+
const identifier = ctx.errorCapturingIdentifier();
|
|
880
|
+
if (identifier) {
|
|
881
|
+
this.updateAfterPreviousTokenByContext(identifier, this.newlineIndent);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
// 标记这是 WINDOW 子句中的窗口定义(而不是 OVER 子句)
|
|
885
|
+
const windowSpec = ctx.windowSpec();
|
|
886
|
+
if (windowSpec) {
|
|
887
|
+
windowSpec._isInWindowClause = true;
|
|
888
|
+
}
|
|
889
|
+
// 检查父级 WindowClauseContext 中是否有窗口简单性信息
|
|
890
|
+
const parentCtx = ctx.parent;
|
|
891
|
+
if (parentCtx && parentCtx._windowIsSimple && parentCtx._currentWindowIndex !== undefined) {
|
|
892
|
+
const idx = parentCtx._currentWindowIndex;
|
|
893
|
+
// 将当前窗口的简单性信息传递给 windowDef
|
|
894
|
+
if (windowSpec) {
|
|
895
|
+
windowSpec._isSimpleInMultiWindow = parentCtx._windowIsSimple[idx];
|
|
896
|
+
}
|
|
897
|
+
parentCtx._currentWindowIndex++;
|
|
898
|
+
}
|
|
899
|
+
super.visitChildren(ctx);
|
|
900
|
+
}
|
|
901
|
+
visitWindowDef(ctx) {
|
|
902
|
+
const leftParen = ctx.LEFT_PAREN();
|
|
903
|
+
const rightParen = ctx.RIGHT_PAREN();
|
|
904
|
+
// Check if this is an empty window spec or has content
|
|
905
|
+
const hasPartition = ctx.PARTITION();
|
|
906
|
+
const hasDistribute = ctx.DISTRIBUTE();
|
|
907
|
+
const hasOrder = ctx.ORDER();
|
|
908
|
+
const hasSort = ctx.SORT();
|
|
909
|
+
const hasWindowFrame = ctx.windowFrame();
|
|
910
|
+
const hasContent = hasPartition || hasDistribute || hasOrder || hasSort || hasWindowFrame;
|
|
911
|
+
// In pipe context or for WINDOW clause definitions, use compact single-line format
|
|
912
|
+
// Example: WINDOW w as (partition by cate order by val)
|
|
913
|
+
const useCompactFormat = this.inPipeOperatorRightSide;
|
|
914
|
+
// 检查是否为简单窗口(用于 WINDOW 子句短句格式)
|
|
915
|
+
const partitionExprs = ctx._partition || [];
|
|
916
|
+
const sortItems = ctx.sortItem();
|
|
917
|
+
const hasMultiplePartitionExprs = ctx.PARTITION() && partitionExprs.length > 1;
|
|
918
|
+
const hasMultipleSortItems = ctx.ORDER() && sortItems.length > 1;
|
|
919
|
+
const isSimpleWindow = !hasWindowFrame && !hasMultiplePartitionExprs && !hasMultipleSortItems;
|
|
920
|
+
// 检查是否在多窗口场景下是简单窗口
|
|
921
|
+
const isSimpleInMultiWindow = ctx._isSimpleInMultiWindow;
|
|
922
|
+
// 短句格式判断:
|
|
923
|
+
// 只有在 WINDOW 子句中才能使用短句格式
|
|
924
|
+
// OVER 子句不使用短句格式
|
|
925
|
+
// 1. 在 WINDOW 子句中且不在长句格式(inWindowClause=false 表示单窗口简单场景)
|
|
926
|
+
// 2. 或者在多窗口场景下当前窗口是简单窗口
|
|
927
|
+
const isInWindowClause = this.inWindowClause || ctx._isInWindowClause;
|
|
928
|
+
const useShortFormat = isInWindowClause && !useCompactFormat && isSimpleWindow && hasContent && isSimpleInMultiWindow !== false;
|
|
929
|
+
if (useShortFormat && leftParen && rightParen) {
|
|
930
|
+
// 短句格式:WINDOW w AS (PARTITION BY dept ORDER BY date)
|
|
931
|
+
// 括号内容保持单行,紧凑格式
|
|
932
|
+
this.updateBeforeNextTokenByNode(leftParen, '');
|
|
933
|
+
this.updateAfterPreviousTokenByNode(rightParen, '');
|
|
934
|
+
// Handle keywords with proper spacing
|
|
935
|
+
if (ctx.PARTITION()) {
|
|
936
|
+
const prevToken = this.getPreviousNonHiddenTokenByStartIndex(ctx.PARTITION().symbol.startIndex);
|
|
937
|
+
if (prevToken && prevToken.text !== '(') {
|
|
938
|
+
this.updateAfterPreviousTokenByNode(ctx.PARTITION(), ' ');
|
|
939
|
+
}
|
|
940
|
+
const byTokens = ctx.BY();
|
|
941
|
+
for (const by of byTokens) {
|
|
942
|
+
this.updateAfterPreviousTokenByNode(by, ' ');
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
if (ctx.DISTRIBUTE()) {
|
|
946
|
+
const prevToken = this.getPreviousNonHiddenTokenByStartIndex(ctx.DISTRIBUTE().symbol.startIndex);
|
|
947
|
+
if (prevToken && prevToken.text !== '(') {
|
|
948
|
+
this.updateAfterPreviousTokenByNode(ctx.DISTRIBUTE(), ' ');
|
|
949
|
+
}
|
|
950
|
+
const byTokens = ctx.BY();
|
|
951
|
+
for (const by of byTokens) {
|
|
952
|
+
this.updateAfterPreviousTokenByNode(by, ' ');
|
|
953
|
+
}
|
|
954
|
+
}
|
|
955
|
+
if (ctx.ORDER()) {
|
|
956
|
+
const prevToken = this.getPreviousNonHiddenTokenByStartIndex(ctx.ORDER().symbol.startIndex);
|
|
957
|
+
if (prevToken && prevToken.text !== '(') {
|
|
958
|
+
this.updateAfterPreviousTokenByNode(ctx.ORDER(), ' ');
|
|
959
|
+
}
|
|
960
|
+
const byTokens = ctx.BY();
|
|
961
|
+
for (const by of byTokens) {
|
|
962
|
+
this.updateAfterPreviousTokenByNode(by, ' ');
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
if (ctx.SORT()) {
|
|
966
|
+
const prevToken = this.getPreviousNonHiddenTokenByStartIndex(ctx.SORT().symbol.startIndex);
|
|
967
|
+
if (prevToken && prevToken.text !== '(') {
|
|
968
|
+
this.updateAfterPreviousTokenByNode(ctx.SORT(), ' ');
|
|
969
|
+
}
|
|
970
|
+
const byTokens = ctx.BY();
|
|
971
|
+
for (const by of byTokens) {
|
|
972
|
+
this.updateAfterPreviousTokenByNode(by, ' ');
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
super.visitChildren(ctx);
|
|
976
|
+
}
|
|
977
|
+
else if (useCompactFormat && leftParen && rightParen) {
|
|
978
|
+
// Compact format for pipe context - single line
|
|
979
|
+
this.updateBeforeNextTokenByNode(leftParen, '');
|
|
980
|
+
this.updateAfterPreviousTokenByNode(rightParen, '');
|
|
981
|
+
// Handle keywords with proper spacing
|
|
982
|
+
// For PARTITION/ORDER etc, check if they are right after left paren
|
|
983
|
+
if (ctx.PARTITION()) {
|
|
984
|
+
// Check if PARTITION is right after left paren
|
|
985
|
+
const prevToken = this.getPreviousNonHiddenTokenByStartIndex(ctx.PARTITION().symbol.startIndex);
|
|
986
|
+
if (prevToken && prevToken.text === '(') {
|
|
987
|
+
// No space needed - it's right after left paren
|
|
988
|
+
}
|
|
989
|
+
else {
|
|
990
|
+
this.updateAfterPreviousTokenByNode(ctx.PARTITION(), ' ');
|
|
991
|
+
}
|
|
992
|
+
const byTokens = ctx.BY();
|
|
993
|
+
for (const by of byTokens) {
|
|
994
|
+
this.updateBeforeNextTokenByNode(by, ' ');
|
|
995
|
+
this.updateAfterPreviousTokenByNode(by, ' ');
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
if (ctx.DISTRIBUTE()) {
|
|
999
|
+
const prevToken = this.getPreviousNonHiddenTokenByStartIndex(ctx.DISTRIBUTE().symbol.startIndex);
|
|
1000
|
+
if (prevToken && prevToken.text !== '(') {
|
|
1001
|
+
this.updateAfterPreviousTokenByNode(ctx.DISTRIBUTE(), ' ');
|
|
1002
|
+
}
|
|
1003
|
+
const byTokens = ctx.BY();
|
|
1004
|
+
for (const by of byTokens) {
|
|
1005
|
+
this.updateBeforeNextTokenByNode(by, ' ');
|
|
1006
|
+
this.updateAfterPreviousTokenByNode(by, ' ');
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
if (ctx.ORDER()) {
|
|
1010
|
+
const prevToken = this.getPreviousNonHiddenTokenByStartIndex(ctx.ORDER().symbol.startIndex);
|
|
1011
|
+
if (prevToken && prevToken.text !== '(') {
|
|
1012
|
+
this.updateAfterPreviousTokenByNode(ctx.ORDER(), ' ');
|
|
1013
|
+
}
|
|
1014
|
+
const byTokens = ctx.BY();
|
|
1015
|
+
for (const by of byTokens) {
|
|
1016
|
+
this.updateBeforeNextTokenByNode(by, ' ');
|
|
1017
|
+
this.updateAfterPreviousTokenByNode(by, ' ');
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
if (ctx.SORT()) {
|
|
1021
|
+
const prevToken = this.getPreviousNonHiddenTokenByStartIndex(ctx.SORT().symbol.startIndex);
|
|
1022
|
+
if (prevToken && prevToken.text !== '(') {
|
|
1023
|
+
this.updateAfterPreviousTokenByNode(ctx.SORT(), ' ');
|
|
1024
|
+
}
|
|
1025
|
+
const byTokens = ctx.BY();
|
|
1026
|
+
for (const by of byTokens) {
|
|
1027
|
+
this.updateBeforeNextTokenByNode(by, ' ');
|
|
1028
|
+
this.updateAfterPreviousTokenByNode(by, ' ');
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
super.visitChildren(ctx);
|
|
1032
|
+
}
|
|
1033
|
+
else if (hasOrder && !hasPartition && !hasDistribute && !hasWindowFrame && leftParen && rightParen) {
|
|
1034
|
+
// Simple OVER clause - use hanging indent format with ORDER BY on new line
|
|
1035
|
+
// Format: OVER (
|
|
1036
|
+
// ORDER BY
|
|
1037
|
+
// col
|
|
1038
|
+
// )
|
|
1039
|
+
this.updateBeforeNextTokenByNode(leftParen, '');
|
|
1040
|
+
// Add indent for content (total 4 spaces for PARTITION BY / ORDER BY)
|
|
1041
|
+
this.addIndent();
|
|
1042
|
+
// ORDER BY on new line with indent
|
|
1043
|
+
const orderToken = ctx.ORDER();
|
|
1044
|
+
if (orderToken) {
|
|
1045
|
+
this.updateAfterPreviousTokenByNode(orderToken, this.newlineIndent);
|
|
1046
|
+
}
|
|
1047
|
+
// Space around BY keywords
|
|
1048
|
+
const byTokens = ctx.BY();
|
|
1049
|
+
for (const by of byTokens) {
|
|
1050
|
+
this.updateAfterPreviousTokenByNode(by, ' ');
|
|
1051
|
+
}
|
|
1052
|
+
// ORDER BY 后的 sortItem 需要换行,缩进 6 空格 (4 + 2)
|
|
1053
|
+
const sortItems = ctx.sortItem();
|
|
1054
|
+
const hasMultipleSortItems = sortItems.length > 1;
|
|
1055
|
+
if (sortItems.length > 0) {
|
|
1056
|
+
// 添加额外缩进,使 sortItem 缩进 6 空格
|
|
1057
|
+
this.addIndent();
|
|
1058
|
+
// 在第一个 BY 后换行
|
|
1059
|
+
if (byTokens.length > 0) {
|
|
1060
|
+
this.updateBeforeNextTokenByNode(byTokens[0], this.newlineIndent);
|
|
1061
|
+
}
|
|
1062
|
+
// 多参数时处理逗号换行
|
|
1063
|
+
if (hasMultipleSortItems) {
|
|
1064
|
+
const commas = ctx.COMMA();
|
|
1065
|
+
for (const comma of commas) {
|
|
1066
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
1067
|
+
this.updateBeforeNextTokenByNode(comma, this.newlineIndent);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
super.visitChildren(ctx);
|
|
1072
|
+
// 减去 sortItem 的额外缩进
|
|
1073
|
+
if (sortItems.length > 0) {
|
|
1074
|
+
this.minusIndent();
|
|
1075
|
+
}
|
|
1076
|
+
// Minus indent before right paren
|
|
1077
|
+
this.minusIndent();
|
|
1078
|
+
// Right paren on new line with 2-space indent
|
|
1079
|
+
this.updateAfterPreviousTokenByNode(rightParen, this.newlineIndent);
|
|
1080
|
+
}
|
|
1081
|
+
else if (hasContent && leftParen && rightParen) {
|
|
1082
|
+
// Complex window spec - format with hanging indent
|
|
1083
|
+
// Left paren stays on same line
|
|
1084
|
+
this.updateBeforeNextTokenByNode(leftParen, '');
|
|
1085
|
+
// Add indent for window spec content (content at 4 spaces)
|
|
1086
|
+
this.addIndent();
|
|
1087
|
+
// Handle PARTITION BY / DISTRIBUTE BY
|
|
1088
|
+
// PARTITION BY 多参数时,表达式换行缩进 6 空格
|
|
1089
|
+
// 注意:使用 _partition 数组获取 PARTITION BY 后的表达式数量
|
|
1090
|
+
const partitionExprs = ctx._partition || [];
|
|
1091
|
+
const hasMultiplePartitionExprs = ctx.PARTITION() && partitionExprs.length > 1;
|
|
1092
|
+
// 收集所有 BY tokens
|
|
1093
|
+
const allByTokens = ctx.BY();
|
|
1094
|
+
let byIndex = 0;
|
|
1095
|
+
if (ctx.PARTITION()) {
|
|
1096
|
+
this.updateAfterPreviousTokenByNode(ctx.PARTITION(), this.newlineIndent);
|
|
1097
|
+
// PARTITION BY 的 BY token
|
|
1098
|
+
if (allByTokens.length > byIndex) {
|
|
1099
|
+
const partitionBy = allByTokens[byIndex];
|
|
1100
|
+
byIndex++;
|
|
1101
|
+
// BY 前面加空格(保持 PARTITION BY 在同一行)
|
|
1102
|
+
this.updateAfterPreviousTokenByNode(partitionBy, ' ');
|
|
1103
|
+
if (hasMultiplePartitionExprs) {
|
|
1104
|
+
// 先添加额外缩进使表达式缩进 6 空格
|
|
1105
|
+
this.addIndent();
|
|
1106
|
+
// 多参数时,BY 后换行(此时 newlineIndent 已经是 6 空格)
|
|
1107
|
+
this.updateBeforeNextTokenByNode(partitionBy, this.newlineIndent);
|
|
1108
|
+
// 处理逗号换行
|
|
1109
|
+
const commas = ctx.COMMA();
|
|
1110
|
+
for (const comma of commas) {
|
|
1111
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
1112
|
+
this.updateBeforeNextTokenByNode(comma, this.newlineIndent);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
else {
|
|
1116
|
+
// 单参数时,BY 后空格
|
|
1117
|
+
this.updateBeforeNextTokenByNode(partitionBy, ' ');
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
if (ctx.DISTRIBUTE()) {
|
|
1122
|
+
this.updateAfterPreviousTokenByNode(ctx.DISTRIBUTE(), this.newlineIndent);
|
|
1123
|
+
if (allByTokens.length > byIndex) {
|
|
1124
|
+
const distributeBy = allByTokens[byIndex];
|
|
1125
|
+
byIndex++;
|
|
1126
|
+
this.updateAfterPreviousTokenByNode(distributeBy, ' ');
|
|
1127
|
+
this.updateBeforeNextTokenByNode(distributeBy, ' ');
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
// Handle ORDER BY / SORT BY
|
|
1131
|
+
// ORDER BY 多参数时,sortItem 换行缩进 6 空格
|
|
1132
|
+
const sortItems = ctx.sortItem();
|
|
1133
|
+
const hasMultipleSortItems = ctx.ORDER() && sortItems.length > 1;
|
|
1134
|
+
let hasOrderBy = false;
|
|
1135
|
+
if (ctx.ORDER()) {
|
|
1136
|
+
hasOrderBy = true;
|
|
1137
|
+
// 先减去 PARTITION BY 多参数的额外缩进(如果有)
|
|
1138
|
+
if (hasMultiplePartitionExprs) {
|
|
1139
|
+
this.minusIndent();
|
|
1140
|
+
}
|
|
1141
|
+
this.updateAfterPreviousTokenByNode(ctx.ORDER(), this.newlineIndent);
|
|
1142
|
+
// ORDER BY 的 BY token
|
|
1143
|
+
if (allByTokens.length > byIndex) {
|
|
1144
|
+
const orderBy = allByTokens[byIndex];
|
|
1145
|
+
byIndex++;
|
|
1146
|
+
// BY 前面加空格(保持 ORDER BY 在同一行)
|
|
1147
|
+
this.updateAfterPreviousTokenByNode(orderBy, ' ');
|
|
1148
|
+
// sortItem 换行,缩进 6 空格
|
|
1149
|
+
if (sortItems.length > 0) {
|
|
1150
|
+
// 先添加额外缩进使后续 sortItem 缩进 6 空格
|
|
1151
|
+
this.addIndent();
|
|
1152
|
+
// 在 BY 后换行
|
|
1153
|
+
this.updateBeforeNextTokenByNode(orderBy, this.newlineIndent);
|
|
1154
|
+
// 多参数时处理逗号换行
|
|
1155
|
+
if (hasMultipleSortItems) {
|
|
1156
|
+
const commas = ctx.COMMA();
|
|
1157
|
+
// 只处理 ORDER BY 相关的逗号(排除 PARTITION BY 的逗号)
|
|
1158
|
+
const partitionCommaCount = hasMultiplePartitionExprs ? partitionExprs.length - 1 : 0;
|
|
1159
|
+
for (let i = partitionCommaCount; i < commas.length; i++) {
|
|
1160
|
+
const comma = commas[i];
|
|
1161
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
1162
|
+
this.updateBeforeNextTokenByNode(comma, this.newlineIndent);
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
else {
|
|
1167
|
+
// 没有 sortItem 时,BY 后空格
|
|
1168
|
+
this.updateBeforeNextTokenByNode(orderBy, ' ');
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
if (ctx.SORT()) {
|
|
1173
|
+
// 先减去 PARTITION BY 多参数的额外缩进(如果有)
|
|
1174
|
+
if (hasMultiplePartitionExprs && !hasOrderBy) {
|
|
1175
|
+
this.minusIndent();
|
|
1176
|
+
}
|
|
1177
|
+
this.updateAfterPreviousTokenByNode(ctx.SORT(), this.newlineIndent);
|
|
1178
|
+
// SORT BY 的 BY token
|
|
1179
|
+
if (allByTokens.length > byIndex) {
|
|
1180
|
+
const sortBy = allByTokens[byIndex];
|
|
1181
|
+
byIndex++;
|
|
1182
|
+
this.updateAfterPreviousTokenByNode(sortBy, ' ');
|
|
1183
|
+
this.updateBeforeNextTokenByNode(sortBy, ' ');
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
// 窗口帧 ROWS/RANGE BETWEEN 需要独立成行,缩进 4 空格
|
|
1187
|
+
// 注意:如果前面有 ORDER BY sortItem,需要先减去 sortItem 的额外缩进
|
|
1188
|
+
if (hasOrderBy && sortItems.length > 0) {
|
|
1189
|
+
this.minusIndent();
|
|
1190
|
+
}
|
|
1191
|
+
super.visitChildren(ctx);
|
|
1192
|
+
// 减去 PARTITION BY 多参数的额外缩进(如果没有 ORDER BY)
|
|
1193
|
+
if (hasMultiplePartitionExprs && !ctx.ORDER() && !ctx.SORT()) {
|
|
1194
|
+
this.minusIndent();
|
|
1195
|
+
}
|
|
1196
|
+
// Minus indent before right paren
|
|
1197
|
+
this.minusIndent();
|
|
1198
|
+
// Right paren on new line with 2-space indent
|
|
1199
|
+
this.updateAfterPreviousTokenByNode(rightParen, this.newlineIndent);
|
|
1200
|
+
}
|
|
1201
|
+
else {
|
|
1202
|
+
// Empty or simple window ref - compact format
|
|
1203
|
+
if (leftParen) {
|
|
1204
|
+
this.updateBeforeNextTokenByNode(leftParen, '');
|
|
1205
|
+
}
|
|
1206
|
+
if (rightParen) {
|
|
1207
|
+
this.updateAfterPreviousTokenByNode(rightParen, '');
|
|
1208
|
+
}
|
|
1209
|
+
super.visitChildren(ctx);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
visitWindowFrame(ctx) {
|
|
1213
|
+
// ROWS or RANGE - 独立成行,缩进 4 空格(与 PARTITION BY 同级)
|
|
1214
|
+
if (ctx.ROWS()) {
|
|
1215
|
+
this.updateAfterPreviousTokenByNode(ctx.ROWS(), this.newlineIndent);
|
|
1216
|
+
this.updateBeforeNextTokenByNode(ctx.ROWS(), '');
|
|
1217
|
+
}
|
|
1218
|
+
if (ctx.RANGE()) {
|
|
1219
|
+
this.updateAfterPreviousTokenByNode(ctx.RANGE(), this.newlineIndent);
|
|
1220
|
+
this.updateBeforeNextTokenByNode(ctx.RANGE(), '');
|
|
1221
|
+
}
|
|
1222
|
+
// BETWEEN keyword - space after
|
|
1223
|
+
if (ctx.BETWEEN()) {
|
|
1224
|
+
this.updateAfterPreviousTokenByNode(ctx.BETWEEN(), ' ');
|
|
1225
|
+
}
|
|
1226
|
+
// AND keyword - space before and after
|
|
1227
|
+
if (ctx.AND()) {
|
|
1228
|
+
this.updateAfterPreviousTokenByNode(ctx.AND(), ' ');
|
|
1229
|
+
this.updateBeforeNextTokenByNode(ctx.AND(), ' ');
|
|
1230
|
+
}
|
|
1231
|
+
super.visitChildren(ctx);
|
|
1232
|
+
}
|
|
1233
|
+
visitFrameBound(ctx) {
|
|
1234
|
+
// UNBOUNDED - space after
|
|
1235
|
+
if (ctx.UNBOUNDED()) {
|
|
1236
|
+
this.updateAfterPreviousTokenByNode(ctx.UNBOUNDED(), ' ');
|
|
1237
|
+
this.updateBeforeNextTokenByNode(ctx.UNBOUNDED(), ' ');
|
|
1238
|
+
}
|
|
1239
|
+
// CURRENT - space after
|
|
1240
|
+
if (ctx.CURRENT()) {
|
|
1241
|
+
this.updateAfterPreviousTokenByNode(ctx.CURRENT(), ' ');
|
|
1242
|
+
this.updateBeforeNextTokenByNode(ctx.CURRENT(), ' ');
|
|
1243
|
+
}
|
|
1244
|
+
// PRECEDING / FOLLOWING - space before
|
|
1245
|
+
if (ctx.PRECEDING()) {
|
|
1246
|
+
this.updateAfterPreviousTokenByNode(ctx.PRECEDING(), ' ');
|
|
1247
|
+
}
|
|
1248
|
+
if (ctx.FOLLOWING()) {
|
|
1249
|
+
this.updateAfterPreviousTokenByNode(ctx.FOLLOWING(), ' ');
|
|
1250
|
+
}
|
|
1251
|
+
super.visitChildren(ctx);
|
|
1252
|
+
}
|
|
1253
|
+
// ==================== Grouping Analytics ====================
|
|
1254
|
+
visitGroupingAnalytics(ctx) {
|
|
1255
|
+
// ROLLUP, CUBE - put on new line after GROUP BY
|
|
1256
|
+
if (ctx.ROLLUP()) {
|
|
1257
|
+
this.updateAfterPreviousTokenByNode(ctx.ROLLUP(), this.newlineIndent);
|
|
1258
|
+
this.updateBeforeNextTokenByNode(ctx.ROLLUP(), ' ');
|
|
1259
|
+
}
|
|
1260
|
+
if (ctx.CUBE()) {
|
|
1261
|
+
this.updateAfterPreviousTokenByNode(ctx.CUBE(), this.newlineIndent);
|
|
1262
|
+
this.updateBeforeNextTokenByNode(ctx.CUBE(), ' ');
|
|
1263
|
+
}
|
|
1264
|
+
// GROUPING SETS - put GROUPING on new line after GROUP BY
|
|
1265
|
+
if (ctx.GROUPING()) {
|
|
1266
|
+
this.updateAfterPreviousTokenByNode(ctx.GROUPING(), this.newlineIndent);
|
|
1267
|
+
this.updateBeforeNextTokenByNode(ctx.GROUPING(), ' ');
|
|
1268
|
+
}
|
|
1269
|
+
if (ctx.SETS()) {
|
|
1270
|
+
this.updateAfterPreviousTokenByNode(ctx.SETS(), ' ');
|
|
1271
|
+
this.updateBeforeNextTokenByNode(ctx.SETS(), ' ');
|
|
1272
|
+
}
|
|
1273
|
+
const leftParen = ctx.LEFT_PAREN();
|
|
1274
|
+
const rightParen = ctx.RIGHT_PAREN();
|
|
1275
|
+
// Get grouping sets or grouping elements
|
|
1276
|
+
const _groupingSets = ctx.groupingSet();
|
|
1277
|
+
const _groupingElements = ctx.groupingElement();
|
|
1278
|
+
if (leftParen && rightParen) {
|
|
1279
|
+
// Check content length to determine format
|
|
1280
|
+
const contentLength = ctx.stop.stopIndex - ctx.start.startIndex;
|
|
1281
|
+
const isLongContent = contentLength > this.maxParenthesisLength;
|
|
1282
|
+
if (isLongContent) {
|
|
1283
|
+
// Long content: each item on separate line
|
|
1284
|
+
this.updateBeforeNextTokenByNode(leftParen, '');
|
|
1285
|
+
this.addIndent();
|
|
1286
|
+
// Format commas
|
|
1287
|
+
const commas = ctx.COMMA();
|
|
1288
|
+
for (const comma of commas) {
|
|
1289
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
1290
|
+
this.updateBeforeNextTokenByNode(comma, this.newlineIndent);
|
|
1291
|
+
}
|
|
1292
|
+
this.updateBeforeNextTokenByNode(rightParen, this.newlineIndent);
|
|
1293
|
+
super.visitChildren(ctx);
|
|
1294
|
+
this.minusIndent();
|
|
1295
|
+
}
|
|
1296
|
+
else {
|
|
1297
|
+
// Short content: compact format on same line - no space inside parens
|
|
1298
|
+
this.updateBeforeNextTokenByNode(leftParen, '');
|
|
1299
|
+
this.updateAfterPreviousTokenByNode(rightParen, '');
|
|
1300
|
+
super.visitChildren(ctx);
|
|
1301
|
+
}
|
|
1302
|
+
}
|
|
1303
|
+
else {
|
|
1304
|
+
super.visitChildren(ctx);
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
visitGroupingSet(ctx) {
|
|
1308
|
+
const leftParen = ctx.LEFT_PAREN();
|
|
1309
|
+
const rightParen = ctx.RIGHT_PAREN();
|
|
1310
|
+
if (leftParen && rightParen) {
|
|
1311
|
+
// Compact parentheses format - no extra spaces inside
|
|
1312
|
+
this.updateBeforeNextTokenByNode(leftParen, '');
|
|
1313
|
+
this.updateAfterPreviousTokenByNode(rightParen, '');
|
|
1314
|
+
// Format commas - space after, no space before
|
|
1315
|
+
const commas = ctx.COMMA();
|
|
1316
|
+
for (const comma of commas) {
|
|
1317
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
1318
|
+
this.updateBeforeNextTokenByNode(comma, ' ');
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
super.visitChildren(ctx);
|
|
1322
|
+
}
|
|
1323
|
+
// ==================== PIVOT/UNPIVOT ====================
|
|
1324
|
+
visitPivotClause(ctx) {
|
|
1325
|
+
const leftParen = ctx.LEFT_PAREN();
|
|
1326
|
+
const rightParen = ctx.RIGHT_PAREN();
|
|
1327
|
+
const namedExprSeq = ctx.namedExpressionSeq();
|
|
1328
|
+
// Count aggregation expressions
|
|
1329
|
+
const namedExpressions = namedExprSeq?.namedExpression() || [];
|
|
1330
|
+
const hasMultipleAggregations = namedExpressions.length > 1;
|
|
1331
|
+
// Check content length to determine format
|
|
1332
|
+
const PIVOT_MAX_LENGTH = 80;
|
|
1333
|
+
const contentLength = ctx.stop.stopIndex - ctx.start.startIndex;
|
|
1334
|
+
const isLongContent = contentLength > PIVOT_MAX_LENGTH;
|
|
1335
|
+
if (hasMultipleAggregations || isLongContent) {
|
|
1336
|
+
// Multiple aggregations or long content: PIVOT on separate line with extra indent
|
|
1337
|
+
// Format:
|
|
1338
|
+
// FROM
|
|
1339
|
+
// t
|
|
1340
|
+
// PIVOT (
|
|
1341
|
+
// SUM(amount) FOR year IN (
|
|
1342
|
+
// 2020,
|
|
1343
|
+
// 2021
|
|
1344
|
+
// )
|
|
1345
|
+
// );
|
|
1346
|
+
// Or with multiple aggregations:
|
|
1347
|
+
// PIVOT (
|
|
1348
|
+
// SUM(a),
|
|
1349
|
+
// SUM(b) FOR year IN (
|
|
1350
|
+
// 2020,
|
|
1351
|
+
// 2021
|
|
1352
|
+
// )
|
|
1353
|
+
// );
|
|
1354
|
+
this.addIndent();
|
|
1355
|
+
this.updateAfterPreviousTokenByNode(ctx.PIVOT(), this.newlineIndent);
|
|
1356
|
+
// Left paren after PIVOT - space before, newline + indent after
|
|
1357
|
+
if (leftParen && leftParen.length > 0) {
|
|
1358
|
+
this.updateAfterPreviousTokenByNode(leftParen[0], ' ');
|
|
1359
|
+
this.addIndent();
|
|
1360
|
+
this.updateBeforeNextTokenByNode(leftParen[0], this.newlineIndent);
|
|
1361
|
+
}
|
|
1362
|
+
// Format multiple aggregations - each on new line
|
|
1363
|
+
if (hasMultipleAggregations && namedExprSeq) {
|
|
1364
|
+
const commas = namedExprSeq.COMMA();
|
|
1365
|
+
for (const comma of commas) {
|
|
1366
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
1367
|
+
this.updateBeforeNextTokenByNode(comma, this.newlineIndent);
|
|
1368
|
+
}
|
|
1369
|
+
}
|
|
1370
|
+
// Format FOR keyword - space before and after
|
|
1371
|
+
if (ctx.FOR()) {
|
|
1372
|
+
this.updateAfterPreviousTokenByNode(ctx.FOR(), ' ');
|
|
1373
|
+
this.updateBeforeNextTokenByNode(ctx.FOR(), ' ');
|
|
1374
|
+
}
|
|
1375
|
+
// Format IN keyword - space before and after
|
|
1376
|
+
if (ctx.IN()) {
|
|
1377
|
+
this.updateAfterPreviousTokenByNode(ctx.IN(), ' ');
|
|
1378
|
+
this.updateBeforeNextTokenByNode(ctx.IN(), ' ');
|
|
1379
|
+
}
|
|
1380
|
+
// IN clause left paren - space before, newline + indent after
|
|
1381
|
+
if (leftParen && leftParen.length > 1) {
|
|
1382
|
+
this.updateAfterPreviousTokenByNode(leftParen[1], ' ');
|
|
1383
|
+
this.addIndent();
|
|
1384
|
+
this.updateBeforeNextTokenByNode(leftParen[1], this.newlineIndent);
|
|
1385
|
+
}
|
|
1386
|
+
// Format pivot values - each on new line
|
|
1387
|
+
const pivotCommas = ctx.COMMA();
|
|
1388
|
+
for (const comma of pivotCommas) {
|
|
1389
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
1390
|
+
this.updateBeforeNextTokenByNode(comma, this.newlineIndent);
|
|
1391
|
+
}
|
|
1392
|
+
// Right paren for IN clause - need to use direct edit for proper indent
|
|
1393
|
+
if (rightParen && rightParen.length > 0) {
|
|
1394
|
+
// IN right paren should align with 'SUM(amount) FOR year IN ('
|
|
1395
|
+
// First minusIndent to go back to that level
|
|
1396
|
+
this.minusIndent();
|
|
1397
|
+
const inClosingIndent = this.newlineIndent;
|
|
1398
|
+
this.bridge.collector.addEdit({
|
|
1399
|
+
range: {
|
|
1400
|
+
start: {
|
|
1401
|
+
line: rightParen[0].symbol.line - 1,
|
|
1402
|
+
character: rightParen[0].symbol.charPositionInLine
|
|
1403
|
+
},
|
|
1404
|
+
end: {
|
|
1405
|
+
line: rightParen[0].symbol.line - 1,
|
|
1406
|
+
character: rightParen[0].symbol.charPositionInLine
|
|
1407
|
+
}
|
|
1408
|
+
},
|
|
1409
|
+
newText: inClosingIndent
|
|
1410
|
+
}, { level: 10, source: 'pivot-in-right-paren' });
|
|
1411
|
+
}
|
|
1412
|
+
// Right paren for PIVOT main - need to use direct edit for proper indent
|
|
1413
|
+
if (rightParen && rightParen.length > 1) {
|
|
1414
|
+
// PIVOT right paren should align with 'PIVOT ('
|
|
1415
|
+
this.minusIndent();
|
|
1416
|
+
const pivotClosingIndent = this.newlineIndent;
|
|
1417
|
+
this.bridge.collector.addEdit({
|
|
1418
|
+
range: {
|
|
1419
|
+
start: {
|
|
1420
|
+
line: rightParen[1].symbol.line - 1,
|
|
1421
|
+
character: rightParen[1].symbol.charPositionInLine
|
|
1422
|
+
},
|
|
1423
|
+
end: {
|
|
1424
|
+
line: rightParen[1].symbol.line - 1,
|
|
1425
|
+
character: rightParen[1].symbol.charPositionInLine
|
|
1426
|
+
}
|
|
1427
|
+
},
|
|
1428
|
+
newText: pivotClosingIndent
|
|
1429
|
+
}, { level: 10, source: 'pivot-right-paren' });
|
|
1430
|
+
}
|
|
1431
|
+
else if (rightParen && rightParen.length === 1) {
|
|
1432
|
+
// Only one right paren (shouldn't happen but handle gracefully)
|
|
1433
|
+
this.minusIndent();
|
|
1434
|
+
const pivotClosingIndent = this.newlineIndent;
|
|
1435
|
+
this.bridge.collector.addEdit({
|
|
1436
|
+
range: {
|
|
1437
|
+
start: {
|
|
1438
|
+
line: rightParen[0].symbol.line - 1,
|
|
1439
|
+
character: rightParen[0].symbol.charPositionInLine
|
|
1440
|
+
},
|
|
1441
|
+
end: {
|
|
1442
|
+
line: rightParen[0].symbol.line - 1,
|
|
1443
|
+
character: rightParen[0].symbol.charPositionInLine
|
|
1444
|
+
}
|
|
1445
|
+
},
|
|
1446
|
+
newText: pivotClosingIndent
|
|
1447
|
+
}, { level: 10, source: 'pivot-right-paren' });
|
|
1448
|
+
}
|
|
1449
|
+
super.visitChildren(ctx);
|
|
1450
|
+
this.minusIndent(); // PIVOT indent
|
|
1451
|
+
}
|
|
1452
|
+
else {
|
|
1453
|
+
// Single aggregation, short content: PIVOT on same line as table
|
|
1454
|
+
// Format: FROM t PIVOT (SUM(amount) FOR year IN (2020, 2021, 2022))
|
|
1455
|
+
this.updateAfterPreviousTokenByNode(ctx.PIVOT(), ' ');
|
|
1456
|
+
// Left paren after PIVOT - ensure space before, no space after
|
|
1457
|
+
// Note: leftParen[0] is the main PIVOT parenthesis
|
|
1458
|
+
// We need to handle the space between PIVOT and (
|
|
1459
|
+
// updateAfterPreviousTokenByNode handles the space before leftParen[0]
|
|
1460
|
+
// We don't need to call updateBeforeNextTokenByNode(leftParen[0]) because
|
|
1461
|
+
// it would affect content after the paren
|
|
1462
|
+
// Format FOR keyword
|
|
1463
|
+
if (ctx.FOR()) {
|
|
1464
|
+
this.updateAfterPreviousTokenByNode(ctx.FOR(), ' ');
|
|
1465
|
+
this.updateBeforeNextTokenByNode(ctx.FOR(), ' ');
|
|
1466
|
+
}
|
|
1467
|
+
// Format IN keyword
|
|
1468
|
+
if (ctx.IN()) {
|
|
1469
|
+
this.updateAfterPreviousTokenByNode(ctx.IN(), ' ');
|
|
1470
|
+
this.updateBeforeNextTokenByNode(ctx.IN(), ' ');
|
|
1471
|
+
}
|
|
1472
|
+
// IN clause parentheses - leftParen[1] is the IN parenthesis
|
|
1473
|
+
// Ensure space before IN (
|
|
1474
|
+
if (leftParen && leftParen.length > 1) {
|
|
1475
|
+
this.updateAfterPreviousTokenByNode(leftParen[1], ' ');
|
|
1476
|
+
}
|
|
1477
|
+
// Right parens - no extra space
|
|
1478
|
+
if (rightParen && rightParen.length > 0) {
|
|
1479
|
+
this.updateAfterPreviousTokenByNode(rightParen[0], '');
|
|
1480
|
+
}
|
|
1481
|
+
if (rightParen && rightParen.length > 1) {
|
|
1482
|
+
this.updateAfterPreviousTokenByNode(rightParen[1], '');
|
|
1483
|
+
}
|
|
1484
|
+
// Format pivot values - compact (comma space)
|
|
1485
|
+
const pivotCommas = ctx.COMMA();
|
|
1486
|
+
for (const comma of pivotCommas) {
|
|
1487
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
1488
|
+
this.updateBeforeNextTokenByNode(comma, ' ');
|
|
1489
|
+
}
|
|
1490
|
+
// Skip strategy formatting for namedExpressionSeq in PIVOT - visit children directly
|
|
1491
|
+
// This prevents unwanted newlines inside short PIVOT clauses
|
|
1492
|
+
const children = ctx.children;
|
|
1493
|
+
if (children) {
|
|
1494
|
+
for (const child of children) {
|
|
1495
|
+
if (child instanceof ParserRuleContext_1.ParserRuleContext) {
|
|
1496
|
+
// Skip namedExpressionSeq - it's already formatted above
|
|
1497
|
+
if (child.constructor.name !== 'NamedExpressionSeqContext') {
|
|
1498
|
+
this.visit(child);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
visitUnpivotClause(ctx) {
|
|
1506
|
+
const leftParen = ctx.LEFT_PAREN();
|
|
1507
|
+
const rightParen = ctx.RIGHT_PAREN();
|
|
1508
|
+
const nullClause = ctx.unpivotNullClause();
|
|
1509
|
+
// Check content length to determine format
|
|
1510
|
+
const UNPIVOT_MAX_LENGTH = 80;
|
|
1511
|
+
const contentLength = ctx.stop.stopIndex - ctx.start.startIndex;
|
|
1512
|
+
const isLongContent = contentLength > UNPIVOT_MAX_LENGTH;
|
|
1513
|
+
if (isLongContent) {
|
|
1514
|
+
// Long content: UNPIVOT on separate line with extra indent
|
|
1515
|
+
// Format:
|
|
1516
|
+
// FROM t
|
|
1517
|
+
// UNPIVOT INCLUDE NULLS (
|
|
1518
|
+
// val FOR col IN (
|
|
1519
|
+
// a,
|
|
1520
|
+
// b,
|
|
1521
|
+
// c
|
|
1522
|
+
// )
|
|
1523
|
+
// )
|
|
1524
|
+
this.addIndent();
|
|
1525
|
+
this.updateAfterPreviousTokenByNode(ctx.UNPIVOT(), this.newlineIndent);
|
|
1526
|
+
// INCLUDE/EXCLUDE NULLS from nullClause
|
|
1527
|
+
if (nullClause) {
|
|
1528
|
+
if (nullClause.INCLUDE()) {
|
|
1529
|
+
this.updateAfterPreviousTokenByNode(nullClause.INCLUDE(), ' ');
|
|
1530
|
+
this.updateBeforeNextTokenByNode(nullClause.INCLUDE(), ' ');
|
|
1531
|
+
}
|
|
1532
|
+
if (nullClause.EXCLUDE()) {
|
|
1533
|
+
this.updateAfterPreviousTokenByNode(nullClause.EXCLUDE(), ' ');
|
|
1534
|
+
this.updateBeforeNextTokenByNode(nullClause.EXCLUDE(), ' ');
|
|
1535
|
+
}
|
|
1536
|
+
if (nullClause.NULLS()) {
|
|
1537
|
+
this.updateAfterPreviousTokenByNode(nullClause.NULLS(), ' ');
|
|
1538
|
+
this.updateBeforeNextTokenByNode(nullClause.NULLS(), ' ');
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
// Left paren - space before, newline + indent after
|
|
1542
|
+
if (leftParen) {
|
|
1543
|
+
this.updateAfterPreviousTokenByNode(leftParen, ' ');
|
|
1544
|
+
// Add indent BEFORE newline so content inside has proper indent
|
|
1545
|
+
this.addIndent();
|
|
1546
|
+
this.updateBeforeNextTokenByNode(leftParen, this.newlineIndent);
|
|
1547
|
+
}
|
|
1548
|
+
else {
|
|
1549
|
+
// No left paren, but still need indent for content
|
|
1550
|
+
this.addIndent();
|
|
1551
|
+
}
|
|
1552
|
+
// Set flag for IN clause formatting
|
|
1553
|
+
this.inUnpivotLongContent = true;
|
|
1554
|
+
// Visit children to handle IN clause
|
|
1555
|
+
const operator = ctx.unpivotOperator();
|
|
1556
|
+
if (operator) {
|
|
1557
|
+
this.visit(operator);
|
|
1558
|
+
}
|
|
1559
|
+
// Reset flag
|
|
1560
|
+
this.inUnpivotLongContent = false;
|
|
1561
|
+
// Right paren on new line - should have same indent as UNPIVOT line
|
|
1562
|
+
if (rightParen) {
|
|
1563
|
+
// UNPIVOT right paren should be at indent level 2 (4 spaces)
|
|
1564
|
+
// Current level is 3 (after IN clause minusIndent), need to go to level 2
|
|
1565
|
+
this.minusIndent();
|
|
1566
|
+
const closingIndent = this.newlineIndent;
|
|
1567
|
+
// Use direct edit since previous edit might conflict
|
|
1568
|
+
this.bridge.collector.addEdit({
|
|
1569
|
+
range: {
|
|
1570
|
+
start: {
|
|
1571
|
+
line: rightParen.symbol.line - 1,
|
|
1572
|
+
character: rightParen.symbol.charPositionInLine
|
|
1573
|
+
},
|
|
1574
|
+
end: {
|
|
1575
|
+
line: rightParen.symbol.line - 1,
|
|
1576
|
+
character: rightParen.symbol.charPositionInLine
|
|
1577
|
+
}
|
|
1578
|
+
},
|
|
1579
|
+
newText: closingIndent
|
|
1580
|
+
}, { level: 10, source: 'unpivot-right-paren' });
|
|
1581
|
+
}
|
|
1582
|
+
else {
|
|
1583
|
+
this.minusIndent();
|
|
1584
|
+
}
|
|
1585
|
+
this.minusIndent(); // UNPIVOT indent
|
|
1586
|
+
}
|
|
1587
|
+
else {
|
|
1588
|
+
// Short content: UNPIVOT on same line as table
|
|
1589
|
+
this.updateAfterPreviousTokenByNode(ctx.UNPIVOT(), ' ');
|
|
1590
|
+
// INCLUDE/EXCLUDE NULLS from nullClause
|
|
1591
|
+
if (nullClause) {
|
|
1592
|
+
if (nullClause.INCLUDE()) {
|
|
1593
|
+
this.updateAfterPreviousTokenByNode(nullClause.INCLUDE(), ' ');
|
|
1594
|
+
this.updateBeforeNextTokenByNode(nullClause.INCLUDE(), ' ');
|
|
1595
|
+
}
|
|
1596
|
+
if (nullClause.EXCLUDE()) {
|
|
1597
|
+
this.updateAfterPreviousTokenByNode(nullClause.EXCLUDE(), ' ');
|
|
1598
|
+
this.updateBeforeNextTokenByNode(nullClause.EXCLUDE(), ' ');
|
|
1599
|
+
}
|
|
1600
|
+
if (nullClause.NULLS()) {
|
|
1601
|
+
this.updateAfterPreviousTokenByNode(nullClause.NULLS(), ' ');
|
|
1602
|
+
this.updateBeforeNextTokenByNode(nullClause.NULLS(), ' ');
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
// Left paren - space before, no space after
|
|
1606
|
+
if (leftParen) {
|
|
1607
|
+
this.updateAfterPreviousTokenByNode(leftParen, ' ');
|
|
1608
|
+
}
|
|
1609
|
+
// Right paren - no extra space
|
|
1610
|
+
if (rightParen) {
|
|
1611
|
+
this.updateAfterPreviousTokenByNode(rightParen, '');
|
|
1612
|
+
}
|
|
1613
|
+
super.visitChildren(ctx);
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
visitUnpivotSingleValueColumnClause(ctx) {
|
|
1617
|
+
// Format: val FOR col IN (a, b, c)
|
|
1618
|
+
const inToken = ctx.IN();
|
|
1619
|
+
const leftParen = ctx.LEFT_PAREN();
|
|
1620
|
+
const rightParen = ctx.RIGHT_PAREN();
|
|
1621
|
+
const commas = ctx.COMMA();
|
|
1622
|
+
if (this.inUnpivotLongContent) {
|
|
1623
|
+
// Format IN clause with newlines
|
|
1624
|
+
// val FOR col IN (
|
|
1625
|
+
// a,
|
|
1626
|
+
// b,
|
|
1627
|
+
// c
|
|
1628
|
+
// )
|
|
1629
|
+
if (inToken) {
|
|
1630
|
+
this.updateAfterPreviousTokenByNode(inToken, ' ');
|
|
1631
|
+
this.updateBeforeNextTokenByNode(inToken, ' ');
|
|
1632
|
+
}
|
|
1633
|
+
if (leftParen) {
|
|
1634
|
+
this.updateAfterPreviousTokenByNode(leftParen, ' ');
|
|
1635
|
+
// Add indent before newline+indent for elements
|
|
1636
|
+
this.addIndent();
|
|
1637
|
+
this.updateBeforeNextTokenByNode(leftParen, this.newlineIndent);
|
|
1638
|
+
}
|
|
1639
|
+
// Commas - each element on new line with current indent
|
|
1640
|
+
for (const comma of commas) {
|
|
1641
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
1642
|
+
this.updateBeforeNextTokenByNode(comma, this.newlineIndent);
|
|
1643
|
+
}
|
|
1644
|
+
if (rightParen) {
|
|
1645
|
+
// IN clause right paren should align with 'val FOR col IN ('
|
|
1646
|
+
// First minusIndent to go back to that level
|
|
1647
|
+
this.minusIndent();
|
|
1648
|
+
// Now save the indent for IN closing paren
|
|
1649
|
+
const inClosingIndent = this.newlineIndent;
|
|
1650
|
+
// Use direct edit to insert before the right paren
|
|
1651
|
+
this.bridge.collector.addEdit({
|
|
1652
|
+
range: {
|
|
1653
|
+
start: {
|
|
1654
|
+
line: rightParen.symbol.line - 1,
|
|
1655
|
+
character: rightParen.symbol.charPositionInLine
|
|
1656
|
+
},
|
|
1657
|
+
end: {
|
|
1658
|
+
line: rightParen.symbol.line - 1,
|
|
1659
|
+
character: rightParen.symbol.charPositionInLine
|
|
1660
|
+
}
|
|
1661
|
+
},
|
|
1662
|
+
newText: inClosingIndent
|
|
1663
|
+
}, { level: 10, source: 'unpivot-in-right-paren' });
|
|
1664
|
+
}
|
|
1665
|
+
// Visit children to format column names
|
|
1666
|
+
super.visitChildren(ctx);
|
|
1667
|
+
}
|
|
1668
|
+
else {
|
|
1669
|
+
// Compact format
|
|
1670
|
+
if (inToken) {
|
|
1671
|
+
this.updateAfterPreviousTokenByNode(inToken, ' ');
|
|
1672
|
+
this.updateBeforeNextTokenByNode(inToken, ' ');
|
|
1673
|
+
}
|
|
1674
|
+
if (leftParen) {
|
|
1675
|
+
this.updateAfterPreviousTokenByNode(leftParen, ' ');
|
|
1676
|
+
}
|
|
1677
|
+
if (rightParen) {
|
|
1678
|
+
this.updateAfterPreviousTokenByNode(rightParen, '');
|
|
1679
|
+
}
|
|
1680
|
+
for (const comma of commas) {
|
|
1681
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
1682
|
+
this.updateBeforeNextTokenByNode(comma, ' ');
|
|
1683
|
+
}
|
|
1684
|
+
super.visitChildren(ctx);
|
|
1685
|
+
}
|
|
1686
|
+
}
|
|
1687
|
+
visitUnpivotMultiValueColumnClause(ctx) {
|
|
1688
|
+
// Similar handling for multi value column clause
|
|
1689
|
+
super.visitChildren(ctx);
|
|
1690
|
+
}
|
|
1691
|
+
// ==================== Table Definition ====================
|
|
1692
|
+
visitCreateFileFormat(ctx) {
|
|
1693
|
+
// STORED AS or STORED BY
|
|
1694
|
+
const storedNode = ctx.STORED();
|
|
1695
|
+
const asNode = ctx.AS();
|
|
1696
|
+
const byNode = ctx.BY();
|
|
1697
|
+
if (storedNode) {
|
|
1698
|
+
this.updateAfterPreviousTokenByNode(storedNode, this.newlineIndent);
|
|
1699
|
+
this.updateBeforeNextTokenByNode(storedNode, ' ');
|
|
1700
|
+
}
|
|
1701
|
+
if (asNode) {
|
|
1702
|
+
this.updateAfterPreviousTokenByNode(asNode, ' ');
|
|
1703
|
+
this.updateBeforeNextTokenByNode(asNode, ' ');
|
|
1704
|
+
}
|
|
1705
|
+
if (byNode) {
|
|
1706
|
+
this.updateAfterPreviousTokenByNode(byNode, ' ');
|
|
1707
|
+
this.updateBeforeNextTokenByNode(byNode, ' ');
|
|
1708
|
+
}
|
|
1709
|
+
// Check for INPUTFORMAT/OUTPUTFORMAT (tableFileFormat)
|
|
1710
|
+
const fileFormat = ctx.fileFormat();
|
|
1711
|
+
if (fileFormat && fileFormat instanceof SparkSQLParser_1.TableFileFormatContext) {
|
|
1712
|
+
const inputFormat = fileFormat.INPUTFORMAT();
|
|
1713
|
+
const outputFormat = fileFormat.OUTPUTFORMAT();
|
|
1714
|
+
const stringLits = fileFormat.stringLit();
|
|
1715
|
+
// Check if line is too long - use stop/start position for accurate length
|
|
1716
|
+
const contentLength = ctx.stop.stopIndex - ctx.start.startIndex;
|
|
1717
|
+
const isLongContent = contentLength > this.maxParenthesisLength;
|
|
1718
|
+
if (isLongContent) {
|
|
1719
|
+
// Long content format:
|
|
1720
|
+
// STORED AS INPUTFORMAT
|
|
1721
|
+
// 'string'
|
|
1722
|
+
// OUTPUTFORMAT
|
|
1723
|
+
// 'string'
|
|
1724
|
+
// INPUTFORMAT stays on same line as STORED AS
|
|
1725
|
+
if (inputFormat) {
|
|
1726
|
+
this.updateAfterPreviousTokenByNode(inputFormat, ' ');
|
|
1727
|
+
this.updateBeforeNextTokenByNode(inputFormat, ' ');
|
|
1728
|
+
}
|
|
1729
|
+
// String value on new line with indent
|
|
1730
|
+
if (stringLits.length > 0) {
|
|
1731
|
+
this.addIndent();
|
|
1732
|
+
this.updateAfterPreviousTokenByContext(stringLits[0], this.newlineIndent);
|
|
1733
|
+
this.minusIndent();
|
|
1734
|
+
}
|
|
1735
|
+
// OUTPUTFORMAT on new line (no extra indent)
|
|
1736
|
+
if (outputFormat) {
|
|
1737
|
+
this.updateAfterPreviousTokenByNode(outputFormat, this.newlineIndent);
|
|
1738
|
+
this.updateBeforeNextTokenByNode(outputFormat, ' ');
|
|
1739
|
+
}
|
|
1740
|
+
// String value on new line with indent
|
|
1741
|
+
if (stringLits.length > 1) {
|
|
1742
|
+
this.addIndent();
|
|
1743
|
+
this.updateAfterPreviousTokenByContext(stringLits[1], this.newlineIndent);
|
|
1744
|
+
this.minusIndent();
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
else {
|
|
1748
|
+
// Short content: all on same line with spaces
|
|
1749
|
+
if (inputFormat) {
|
|
1750
|
+
this.updateAfterPreviousTokenByNode(inputFormat, ' ');
|
|
1751
|
+
this.updateBeforeNextTokenByNode(inputFormat, ' ');
|
|
1752
|
+
}
|
|
1753
|
+
if (outputFormat) {
|
|
1754
|
+
this.updateAfterPreviousTokenByNode(outputFormat, ' ');
|
|
1755
|
+
this.updateBeforeNextTokenByNode(outputFormat, ' ');
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
super.visitChildren(ctx);
|
|
1760
|
+
}
|
|
1761
|
+
visitBucketSpec(ctx) {
|
|
1762
|
+
// CLUSTERED BY
|
|
1763
|
+
const clusteredNode = ctx.CLUSTERED();
|
|
1764
|
+
const byTokens = ctx.BY();
|
|
1765
|
+
if (clusteredNode) {
|
|
1766
|
+
this.updateAfterPreviousTokenByNode(clusteredNode, ' ');
|
|
1767
|
+
this.updateBeforeNextTokenByNode(clusteredNode, ' ');
|
|
1768
|
+
}
|
|
1769
|
+
if (byTokens.length > 0) {
|
|
1770
|
+
this.updateAfterPreviousTokenByNode(byTokens[0], ' ');
|
|
1771
|
+
this.updateBeforeNextTokenByNode(byTokens[0], ' ');
|
|
1772
|
+
}
|
|
1773
|
+
// SORTED BY (optional)
|
|
1774
|
+
const sortedNode = ctx.SORTED();
|
|
1775
|
+
if (sortedNode && byTokens.length > 1) {
|
|
1776
|
+
this.updateAfterPreviousTokenByNode(sortedNode, ' ');
|
|
1777
|
+
this.updateBeforeNextTokenByNode(sortedNode, ' ');
|
|
1778
|
+
this.updateAfterPreviousTokenByNode(byTokens[1], ' ');
|
|
1779
|
+
this.updateBeforeNextTokenByNode(byTokens[1], ' ');
|
|
1780
|
+
}
|
|
1781
|
+
// INTO n BUCKETS
|
|
1782
|
+
const intoNode = ctx.INTO();
|
|
1783
|
+
const bucketsNode = ctx.BUCKETS();
|
|
1784
|
+
if (intoNode) {
|
|
1785
|
+
this.updateAfterPreviousTokenByNode(intoNode, ' ');
|
|
1786
|
+
this.updateBeforeNextTokenByNode(intoNode, ' ');
|
|
1787
|
+
}
|
|
1788
|
+
if (bucketsNode) {
|
|
1789
|
+
this.updateAfterPreviousTokenByNode(bucketsNode, ' ');
|
|
1790
|
+
}
|
|
1791
|
+
super.visitChildren(ctx);
|
|
1792
|
+
}
|
|
1793
|
+
visitSample(ctx) {
|
|
1794
|
+
const tablesample = ctx.TABLESAMPLE();
|
|
1795
|
+
const repeatable = ctx.REPEATABLE();
|
|
1796
|
+
const leftParens = ctx.LEFT_PAREN();
|
|
1797
|
+
const rightParens = ctx.RIGHT_PAREN();
|
|
1798
|
+
// TABLESAMPLE - space after
|
|
1799
|
+
if (tablesample) {
|
|
1800
|
+
this.updateAfterPreviousTokenByNode(tablesample, ' ');
|
|
1801
|
+
this.updateBeforeNextTokenByNode(tablesample, ' ');
|
|
1802
|
+
}
|
|
1803
|
+
// Parentheses - compact format
|
|
1804
|
+
for (const leftParen of leftParens) {
|
|
1805
|
+
this.updateBeforeNextTokenByNode(leftParen, '');
|
|
1806
|
+
this.updateAfterPreviousTokenByNode(leftParen, '');
|
|
1807
|
+
}
|
|
1808
|
+
for (const rightParen of rightParens) {
|
|
1809
|
+
this.updateAfterPreviousTokenByNode(rightParen, '');
|
|
1810
|
+
}
|
|
1811
|
+
// REPEATABLE - space before and after
|
|
1812
|
+
if (repeatable) {
|
|
1813
|
+
this.updateAfterPreviousTokenByNode(repeatable, ' ');
|
|
1814
|
+
this.updateBeforeNextTokenByNode(repeatable, ' ');
|
|
1815
|
+
}
|
|
1816
|
+
super.visitChildren(ctx);
|
|
1817
|
+
}
|
|
1818
|
+
// ==================== CASE WHEN Expression ====================
|
|
1819
|
+
visitSearchedCase(ctx) {
|
|
1820
|
+
const _caseNode = ctx.CASE();
|
|
1821
|
+
const endNode = ctx.END();
|
|
1822
|
+
const elseNode = ctx.ELSE();
|
|
1823
|
+
const whenClauses = ctx.whenClause();
|
|
1824
|
+
const elseExpr = ctx.expression();
|
|
1825
|
+
// 增加 CASE 内部缩进(WHEN/ELSE 使用)
|
|
1826
|
+
this.addIndent();
|
|
1827
|
+
// 手动处理每个 WHEN 子句
|
|
1828
|
+
for (let i = 0; i < whenClauses.length; i++) {
|
|
1829
|
+
const whenClause = whenClauses[i];
|
|
1830
|
+
const thenNode = whenClause.THEN();
|
|
1831
|
+
const expressions = whenClause.expression();
|
|
1832
|
+
// 每个 WHEN 前换行(包括第一个),这样 CASE 后自动换行
|
|
1833
|
+
this.updateAfterPreviousTokenByContext(whenClause, this.newlineIndent);
|
|
1834
|
+
// 检查 THEN 后是否是嵌套 CASE WHEN
|
|
1835
|
+
const resultExpr = expressions && expressions.length > 1 ? expressions[1] : null;
|
|
1836
|
+
const isNestedCase = resultExpr && this.isCaseWhenExpression(resultExpr);
|
|
1837
|
+
if (isNestedCase) {
|
|
1838
|
+
// 嵌套 CASE 特殊格式:WHEN 条件换行
|
|
1839
|
+
// 先增加缩进用于条件表达式
|
|
1840
|
+
this.addIndent();
|
|
1841
|
+
const whenNode = whenClause.WHEN();
|
|
1842
|
+
if (whenNode) {
|
|
1843
|
+
// WHEN 后换行 + 缩进(此时缩进已增加)
|
|
1844
|
+
this.updateBeforeNextTokenByNode(whenNode, this.newlineIndent);
|
|
1845
|
+
}
|
|
1846
|
+
// 访问条件表达式
|
|
1847
|
+
if (expressions && expressions.length > 0) {
|
|
1848
|
+
this.visit(expressions[0]);
|
|
1849
|
+
}
|
|
1850
|
+
// 减少缩进(回到 CASE 内部缩进)
|
|
1851
|
+
this.minusIndent();
|
|
1852
|
+
// THEN 独占一行
|
|
1853
|
+
if (thenNode) {
|
|
1854
|
+
// THEN 前换行(使用当前 CASE 内部缩进)
|
|
1855
|
+
this.updateAfterPreviousTokenByNode(thenNode, this.newlineIndent);
|
|
1856
|
+
// THEN 后换行:先增加缩进,再设置
|
|
1857
|
+
this.addIndent();
|
|
1858
|
+
this.updateBeforeNextTokenByNode(thenNode, this.newlineIndent);
|
|
1859
|
+
}
|
|
1860
|
+
// 访问嵌套 CASE 结果表达式
|
|
1861
|
+
if (expressions && expressions.length > 1) {
|
|
1862
|
+
this.visit(expressions[1]);
|
|
1863
|
+
}
|
|
1864
|
+
// 减少缩进(THEN 后增加的)
|
|
1865
|
+
if (thenNode) {
|
|
1866
|
+
this.minusIndent();
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
else {
|
|
1870
|
+
// 普通格式:WHEN 条件不换行
|
|
1871
|
+
const whenNode = whenClause.WHEN();
|
|
1872
|
+
if (whenNode) {
|
|
1873
|
+
// WHEN 后空格
|
|
1874
|
+
this.updateBeforeNextTokenByNode(whenNode, ' ');
|
|
1875
|
+
}
|
|
1876
|
+
// 访问条件表达式
|
|
1877
|
+
if (expressions && expressions.length > 0) {
|
|
1878
|
+
this.visit(expressions[0]);
|
|
1879
|
+
}
|
|
1880
|
+
// THEN 前后空格
|
|
1881
|
+
if (thenNode) {
|
|
1882
|
+
this.updateBeforeNextTokenByNode(thenNode, ' ');
|
|
1883
|
+
// THEN 后空格
|
|
1884
|
+
this.updateAfterPreviousTokenByNode(thenNode, ' ');
|
|
1885
|
+
}
|
|
1886
|
+
// 访问结果表达式
|
|
1887
|
+
if (expressions && expressions.length > 1) {
|
|
1888
|
+
this.visit(expressions[1]);
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
// ELSE 前换行
|
|
1893
|
+
if (elseNode) {
|
|
1894
|
+
this.updateAfterPreviousTokenByNode(elseNode, this.newlineIndent);
|
|
1895
|
+
}
|
|
1896
|
+
// 访问 ELSE 后的表达式
|
|
1897
|
+
if (elseExpr) {
|
|
1898
|
+
this.visit(elseExpr);
|
|
1899
|
+
}
|
|
1900
|
+
// 减少 CASE 内部缩进
|
|
1901
|
+
this.minusIndent();
|
|
1902
|
+
// END 前换行(恢复到 CASE 同级缩进)
|
|
1903
|
+
if (endNode) {
|
|
1904
|
+
this.updateAfterPreviousTokenByNode(endNode, this.newlineIndent);
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
/**
|
|
1908
|
+
* 检查表达式是否是 CASE WHEN 表达式
|
|
1909
|
+
*/
|
|
1910
|
+
isCaseWhenExpression(ctx) {
|
|
1911
|
+
// 检查当前节点类型
|
|
1912
|
+
const className = ctx.constructor.name;
|
|
1913
|
+
if (className === 'SearchedCaseContext' || className === 'SimpleCaseContext') {
|
|
1914
|
+
return true;
|
|
1915
|
+
}
|
|
1916
|
+
// 递归检查子节点(因为 CASE WHEN 可能在括号内或其他表达式中)
|
|
1917
|
+
if (ctx.childCount > 0) {
|
|
1918
|
+
for (let i = 0; i < ctx.childCount; i++) {
|
|
1919
|
+
const child = ctx.getChild(i);
|
|
1920
|
+
if (child instanceof ParserRuleContext_1.ParserRuleContext) {
|
|
1921
|
+
if (this.isCaseWhenExpression(child)) {
|
|
1922
|
+
return true;
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
return false;
|
|
1928
|
+
}
|
|
1929
|
+
visitSimpleCase(ctx) {
|
|
1930
|
+
const caseNode = ctx.CASE();
|
|
1931
|
+
const endNode = ctx.END();
|
|
1932
|
+
const elseNode = ctx.ELSE();
|
|
1933
|
+
const whenClauses = ctx.whenClause();
|
|
1934
|
+
const expressions = ctx.expression();
|
|
1935
|
+
// CASE 后空格(保留 case 值表达式在同一行)
|
|
1936
|
+
if (caseNode) {
|
|
1937
|
+
this.updateBeforeNextTokenByNode(caseNode, ' ');
|
|
1938
|
+
}
|
|
1939
|
+
// 访问 case 值表达式(第一个表达式)
|
|
1940
|
+
if (expressions && expressions.length > 0) {
|
|
1941
|
+
this.visit(expressions[0]);
|
|
1942
|
+
}
|
|
1943
|
+
// 增加 CASE 内部缩进(WHEN/ELSE 使用)
|
|
1944
|
+
this.addIndent();
|
|
1945
|
+
// 手动处理每个 WHEN 子句
|
|
1946
|
+
for (const whenClause of whenClauses) {
|
|
1947
|
+
const thenNode = whenClause.THEN();
|
|
1948
|
+
const whenExprs = whenClause.expression();
|
|
1949
|
+
// 每个 WHEN 前换行
|
|
1950
|
+
this.updateAfterPreviousTokenByContext(whenClause, this.newlineIndent);
|
|
1951
|
+
// 检查 THEN 后是否是嵌套 CASE WHEN
|
|
1952
|
+
const resultExpr = whenExprs && whenExprs.length > 1 ? whenExprs[1] : null;
|
|
1953
|
+
const isNestedCase = resultExpr && this.isCaseWhenExpression(resultExpr);
|
|
1954
|
+
if (isNestedCase) {
|
|
1955
|
+
// 嵌套 CASE 特殊格式:WHEN 值换行
|
|
1956
|
+
// 先增加缩进用于 WHEN 值表达式
|
|
1957
|
+
this.addIndent();
|
|
1958
|
+
const whenNode = whenClause.WHEN();
|
|
1959
|
+
if (whenNode) {
|
|
1960
|
+
// WHEN 后换行 + 缩进(此时缩进已增加)
|
|
1961
|
+
this.updateBeforeNextTokenByNode(whenNode, this.newlineIndent);
|
|
1962
|
+
}
|
|
1963
|
+
// 访问 WHEN 值表达式
|
|
1964
|
+
if (whenExprs && whenExprs.length > 0) {
|
|
1965
|
+
this.visit(whenExprs[0]);
|
|
1966
|
+
}
|
|
1967
|
+
// 减少缩进(回到 CASE 内部缩进)
|
|
1968
|
+
this.minusIndent();
|
|
1969
|
+
// THEN 独占一行
|
|
1970
|
+
if (thenNode) {
|
|
1971
|
+
// THEN 前换行(使用当前 CASE 内部缩进)
|
|
1972
|
+
this.updateAfterPreviousTokenByNode(thenNode, this.newlineIndent);
|
|
1973
|
+
// THEN 后换行:先增加缩进,再设置
|
|
1974
|
+
this.addIndent();
|
|
1975
|
+
this.updateBeforeNextTokenByNode(thenNode, this.newlineIndent);
|
|
1976
|
+
}
|
|
1977
|
+
// 访问嵌套 CASE 结果表达式
|
|
1978
|
+
if (whenExprs && whenExprs.length > 1) {
|
|
1979
|
+
this.visit(whenExprs[1]);
|
|
1980
|
+
}
|
|
1981
|
+
// 减少缩进(THEN 后增加的)
|
|
1982
|
+
if (thenNode) {
|
|
1983
|
+
this.minusIndent();
|
|
1984
|
+
}
|
|
1985
|
+
}
|
|
1986
|
+
else {
|
|
1987
|
+
// 普通格式:WHEN 值不换行
|
|
1988
|
+
const whenNode = whenClause.WHEN();
|
|
1989
|
+
if (whenNode) {
|
|
1990
|
+
// WHEN 后空格
|
|
1991
|
+
this.updateBeforeNextTokenByNode(whenNode, ' ');
|
|
1992
|
+
}
|
|
1993
|
+
// 访问 WHEN 值表达式
|
|
1994
|
+
if (whenExprs && whenExprs.length > 0) {
|
|
1995
|
+
this.visit(whenExprs[0]);
|
|
1996
|
+
}
|
|
1997
|
+
// THEN 前后空格
|
|
1998
|
+
if (thenNode) {
|
|
1999
|
+
this.updateBeforeNextTokenByNode(thenNode, ' ');
|
|
2000
|
+
// THEN 后空格
|
|
2001
|
+
this.updateAfterPreviousTokenByNode(thenNode, ' ');
|
|
2002
|
+
}
|
|
2003
|
+
// 访问结果表达式
|
|
2004
|
+
if (whenExprs && whenExprs.length > 1) {
|
|
2005
|
+
this.visit(whenExprs[1]);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
2009
|
+
// ELSE 前换行
|
|
2010
|
+
if (elseNode) {
|
|
2011
|
+
this.updateAfterPreviousTokenByNode(elseNode, this.newlineIndent);
|
|
2012
|
+
}
|
|
2013
|
+
// 访问 ELSE 后的表达式
|
|
2014
|
+
if (expressions && expressions.length > 1) {
|
|
2015
|
+
this.visit(expressions[1]);
|
|
2016
|
+
}
|
|
2017
|
+
// 减少 CASE 内部缩进
|
|
2018
|
+
this.minusIndent();
|
|
2019
|
+
// END 前换行(恢复到 CASE 同级缩进)
|
|
2020
|
+
if (endNode) {
|
|
2021
|
+
this.updateAfterPreviousTokenByNode(endNode, this.newlineIndent);
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
visitWhenClause(_ctx) {
|
|
2025
|
+
// WHEN/THEN 的空格在 visitSearchedCase/visitSimpleCase 中处理
|
|
2026
|
+
// 这里不调用 super.visitChildren(ctx),因为子节点已经在上层处理过了
|
|
2027
|
+
}
|
|
2028
|
+
visitSetOperation(ctx) {
|
|
2029
|
+
// UNION, INTERSECT, EXCEPT, SETMINUS should be on their own line
|
|
2030
|
+
const unionNode = ctx.UNION();
|
|
2031
|
+
const intersectNode = ctx.INTERSECT();
|
|
2032
|
+
const exceptNode = ctx.EXCEPT();
|
|
2033
|
+
const setminusNode = ctx.SETMINUS();
|
|
2034
|
+
const operatorNode = unionNode || intersectNode || exceptNode || setminusNode;
|
|
2035
|
+
if (operatorNode) {
|
|
2036
|
+
// Operator keyword on new line
|
|
2037
|
+
this.updateAfterPreviousTokenByNode(operatorNode, this.newlineIndent);
|
|
2038
|
+
}
|
|
2039
|
+
// Handle setQuantifier (ALL/DISTINCT) - it should stay on same line as operator
|
|
2040
|
+
const setQuantifier = ctx.setQuantifier();
|
|
2041
|
+
if (setQuantifier) {
|
|
2042
|
+
// Space before ALL/DISTINCT
|
|
2043
|
+
this.updateAfterPreviousTokenByContext(setQuantifier, ' ');
|
|
2044
|
+
// Newline after ALL/DISTINCT, before right query
|
|
2045
|
+
this.updateBeforeNextTokenByContext(setQuantifier, this.newlineIndent);
|
|
2046
|
+
}
|
|
2047
|
+
// Right query should start on new line after UNION ALL / UNION / etc.
|
|
2048
|
+
// Only add newline if there's no setQuantifier (otherwise it's handled above)
|
|
2049
|
+
const rightQuery = ctx._right;
|
|
2050
|
+
if (rightQuery && !setQuantifier) {
|
|
2051
|
+
this.updateAfterPreviousTokenByContext(rightQuery, this.newlineIndent);
|
|
2052
|
+
}
|
|
2053
|
+
super.visitChildren(ctx);
|
|
2054
|
+
}
|
|
2055
|
+
visitSetQuantifier(ctx) {
|
|
2056
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2057
|
+
super.visitChildren(ctx);
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
visitNamedExpressionSeq(ctx) {
|
|
2061
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2062
|
+
super.visitChildren(ctx);
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
visitNamedExpression(ctx) {
|
|
2066
|
+
// Format AS keyword - ensure space before and after
|
|
2067
|
+
this.normalizeKeywordSpacing(ctx.AS());
|
|
2068
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2069
|
+
super.visitChildren(ctx);
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
// ==================== Column Type ====================
|
|
2073
|
+
visitColType(ctx) {
|
|
2074
|
+
// Single space between column name (identifier) and data type
|
|
2075
|
+
const dataType = ctx.dataType();
|
|
2076
|
+
if (dataType) {
|
|
2077
|
+
this.updateAfterPreviousTokenByContext(dataType, ' ');
|
|
2078
|
+
}
|
|
2079
|
+
// Handle COMMENT when there's no dataType - add space after column identifier
|
|
2080
|
+
const commentSpec = ctx.commentSpec();
|
|
2081
|
+
const identifier = ctx.errorCapturingIdentifier();
|
|
2082
|
+
if (!dataType && commentSpec && identifier) {
|
|
2083
|
+
// Add space after column identifier before COMMENT
|
|
2084
|
+
this.updateAfterPreviousTokenByContext(identifier, ' ');
|
|
2085
|
+
}
|
|
2086
|
+
// Handle NOT NULL
|
|
2087
|
+
const errorCapturingNot = ctx.errorCapturingNot();
|
|
2088
|
+
const nullNode = ctx.NULL();
|
|
2089
|
+
if (errorCapturingNot && nullNode) {
|
|
2090
|
+
this.updateAfterPreviousTokenByContext(errorCapturingNot, ' ');
|
|
2091
|
+
this.updateBeforeNextTokenByContext(errorCapturingNot, ' ');
|
|
2092
|
+
}
|
|
2093
|
+
super.visitChildren(ctx);
|
|
2094
|
+
}
|
|
2095
|
+
// ==================== JOIN ====================
|
|
2096
|
+
visitJoinRelation(ctx) {
|
|
2097
|
+
const bridge = this.getJoinFormattingBridge();
|
|
2098
|
+
const target = {
|
|
2099
|
+
joinContext: ctx,
|
|
2100
|
+
naturalKeyword: ctx.NATURAL() || undefined,
|
|
2101
|
+
joinKeyword: ctx.JOIN(),
|
|
2102
|
+
lateralKeyword: ctx.LATERAL() || undefined
|
|
2103
|
+
};
|
|
2104
|
+
if (this.inPipeOperatorRightSide) {
|
|
2105
|
+
strategies_1.coreStrategies.join.formatJoinEntry(target, bridge, { separatorBeforeJoin: ' ' });
|
|
2106
|
+
super.visitChildren(ctx);
|
|
2107
|
+
return;
|
|
2108
|
+
}
|
|
2109
|
+
// Check if there is ON clause (not USING - USING should stay on same line)
|
|
2110
|
+
const joinCriteria = ctx.joinCriteria();
|
|
2111
|
+
const hasOnClause = joinCriteria && joinCriteria.ON();
|
|
2112
|
+
// Check if NATURAL JOIN (no ON clause needed)
|
|
2113
|
+
const isNaturalJoin = ctx.NATURAL() !== null && ctx.NATURAL() !== undefined;
|
|
2114
|
+
// Check if CROSS JOIN (no ON clause needed)
|
|
2115
|
+
const joinType = ctx.joinType();
|
|
2116
|
+
const isCrossJoin = joinType && joinType.CROSS() !== null && joinType.CROSS() !== undefined;
|
|
2117
|
+
// Check if this JOIN is right after a closing parenthesis (inside parentheses)
|
|
2118
|
+
let isAfterClosingParen = false;
|
|
2119
|
+
if (isCrossJoin || isNaturalJoin) {
|
|
2120
|
+
const joinKeyword = ctx.JOIN();
|
|
2121
|
+
if (joinKeyword) {
|
|
2122
|
+
const _prevToken = this.getPreviousNonHiddenTokenByStartIndex(joinKeyword.symbol.startIndex);
|
|
2123
|
+
// Check if previous token is ) or the token before JOIN keyword is )
|
|
2124
|
+
// For CROSS JOIN, need to check before CROSS keyword
|
|
2125
|
+
const crossKeyword = joinType?.CROSS();
|
|
2126
|
+
const naturalKeyword = ctx.NATURAL();
|
|
2127
|
+
const checkToken = crossKeyword || naturalKeyword || joinKeyword;
|
|
2128
|
+
if (checkToken) {
|
|
2129
|
+
const tokenBeforeJoin = this.getPreviousNonHiddenTokenByStartIndex(checkToken.symbol.startIndex);
|
|
2130
|
+
isAfterClosingParen = tokenBeforeJoin?.text === ')';
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
}
|
|
2134
|
+
if (hasOnClause) {
|
|
2135
|
+
// Has ON: JOIN should be on new line with extra indent
|
|
2136
|
+
// Rule: JOIN relative to table name +1 indent level (total 2 levels, 4 spaces)
|
|
2137
|
+
this.addIndent();
|
|
2138
|
+
strategies_1.coreStrategies.join.formatJoinEntry(target, bridge, { separatorBeforeJoin: this.newlineIndent });
|
|
2139
|
+
super.visitChildren(ctx);
|
|
2140
|
+
this.minusIndent();
|
|
2141
|
+
}
|
|
2142
|
+
else if (isAfterClosingParen) {
|
|
2143
|
+
// JOIN after closing parenthesis inside parentheses: should be on new line with indent
|
|
2144
|
+
this.addIndent();
|
|
2145
|
+
strategies_1.coreStrategies.join.formatJoinEntry(target, bridge, { separatorBeforeJoin: this.newlineIndent });
|
|
2146
|
+
super.visitChildren(ctx);
|
|
2147
|
+
this.minusIndent();
|
|
2148
|
+
}
|
|
2149
|
+
else if (isNaturalJoin || isCrossJoin) {
|
|
2150
|
+
// NATURAL or CROSS JOIN: stays on same line as previous table
|
|
2151
|
+
// Rule 6: No ON, JOIN does not break line
|
|
2152
|
+
strategies_1.coreStrategies.join.formatJoinEntry(target, bridge, { separatorBeforeJoin: ' ' });
|
|
2153
|
+
super.visitChildren(ctx);
|
|
2154
|
+
}
|
|
2155
|
+
else {
|
|
2156
|
+
// Other JOINs without ON (may have USING or no criteria)
|
|
2157
|
+
// Check for USING - stays on same line
|
|
2158
|
+
const hasUsingClause = joinCriteria && joinCriteria.USING();
|
|
2159
|
+
if (hasUsingClause) {
|
|
2160
|
+
strategies_1.coreStrategies.join.formatJoinEntry(target, bridge, { separatorBeforeJoin: ' ' });
|
|
2161
|
+
super.visitChildren(ctx);
|
|
2162
|
+
}
|
|
2163
|
+
else {
|
|
2164
|
+
// No criteria at all - check if this is expected or error
|
|
2165
|
+
// For now, treat as no line break
|
|
2166
|
+
strategies_1.coreStrategies.join.formatJoinEntry(target, bridge, { separatorBeforeJoin: ' ' });
|
|
2167
|
+
super.visitChildren(ctx);
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
visitJoinType(ctx) {
|
|
2172
|
+
if (ctx.LEFT())
|
|
2173
|
+
this.updateBeforeNextTokenByNode(ctx.LEFT(), ' ');
|
|
2174
|
+
if (ctx.RIGHT())
|
|
2175
|
+
this.updateBeforeNextTokenByNode(ctx.RIGHT(), ' ');
|
|
2176
|
+
if (ctx.FULL())
|
|
2177
|
+
this.updateBeforeNextTokenByNode(ctx.FULL(), ' ');
|
|
2178
|
+
if (ctx.OUTER()) {
|
|
2179
|
+
this.updateAfterPreviousTokenByNode(ctx.OUTER(), ' ');
|
|
2180
|
+
this.updateBeforeNextTokenByNode(ctx.OUTER(), ' ');
|
|
2181
|
+
}
|
|
2182
|
+
if (ctx.INNER())
|
|
2183
|
+
this.updateBeforeNextTokenByNode(ctx.INNER(), ' ');
|
|
2184
|
+
if (ctx.CROSS())
|
|
2185
|
+
this.updateBeforeNextTokenByNode(ctx.CROSS(), ' ');
|
|
2186
|
+
if (ctx.SEMI()) {
|
|
2187
|
+
this.updateAfterPreviousTokenByNode(ctx.SEMI(), ' ');
|
|
2188
|
+
this.updateBeforeNextTokenByNode(ctx.SEMI(), ' ');
|
|
2189
|
+
}
|
|
2190
|
+
if (ctx.ANTI()) {
|
|
2191
|
+
this.updateAfterPreviousTokenByNode(ctx.ANTI(), ' ');
|
|
2192
|
+
this.updateBeforeNextTokenByNode(ctx.ANTI(), ' ');
|
|
2193
|
+
}
|
|
2194
|
+
super.visitChildren(ctx);
|
|
2195
|
+
}
|
|
2196
|
+
visitJoinCriteria(ctx) {
|
|
2197
|
+
const bridge = this.getJoinFormattingBridge();
|
|
2198
|
+
const target = {
|
|
2199
|
+
criteriaContext: ctx,
|
|
2200
|
+
usingKeyword: ctx.USING() || undefined,
|
|
2201
|
+
onKeyword: ctx.ON() || undefined,
|
|
2202
|
+
bodyStart: (ctx.booleanExpression() || ctx.identifierList()) ?? undefined
|
|
2203
|
+
};
|
|
2204
|
+
if (this.inPipeOperatorRightSide) {
|
|
2205
|
+
// In pipe context: ON should be on new line with pipe content indent
|
|
2206
|
+
if (ctx.ON()) {
|
|
2207
|
+
strategies_1.coreStrategies.join.formatJoinCriteriaEntry(target, bridge, {
|
|
2208
|
+
separatorBeforeCriteriaKeyword: this.context.getPipeContentIndent(),
|
|
2209
|
+
separatorAfterCriteriaKeyword: ' '
|
|
2210
|
+
});
|
|
2211
|
+
}
|
|
2212
|
+
else {
|
|
2213
|
+
// USING clause: stays on same line
|
|
2214
|
+
strategies_1.coreStrategies.join.formatJoinCriteriaEntry(target, bridge, {
|
|
2215
|
+
separatorBeforeCriteriaKeyword: ' ',
|
|
2216
|
+
separatorAfterCriteriaKeyword: ''
|
|
2217
|
+
});
|
|
2218
|
+
}
|
|
2219
|
+
super.visitChildren(ctx);
|
|
2220
|
+
return;
|
|
2221
|
+
}
|
|
2222
|
+
// ON clause: should be on new line with extra indent (relative to JOIN)
|
|
2223
|
+
// Rule: ON relative to JOIN +1 indent level (total 3 levels, 6 spaces)
|
|
2224
|
+
if (ctx.ON()) {
|
|
2225
|
+
this.addIndent();
|
|
2226
|
+
// Check if the ON clause starts with a parenthesized expression
|
|
2227
|
+
const booleanExpr = ctx.booleanExpression();
|
|
2228
|
+
const startsWithParen = booleanExpr && this.expressionStartsWithParen(booleanExpr);
|
|
2229
|
+
if (startsWithParen) {
|
|
2230
|
+
// ON (condition): keep space after ON, let ParenthesisStrategy handle the paren
|
|
2231
|
+
strategies_1.coreStrategies.join.formatJoinCriteriaEntry(target, bridge, {
|
|
2232
|
+
separatorBeforeCriteriaKeyword: this.newlineIndent,
|
|
2233
|
+
separatorAfterCriteriaKeyword: ' '
|
|
2234
|
+
});
|
|
2235
|
+
}
|
|
2236
|
+
else {
|
|
2237
|
+
// Normal ON clause: space after ON
|
|
2238
|
+
strategies_1.coreStrategies.join.formatJoinCriteriaEntry(target, bridge, {
|
|
2239
|
+
separatorBeforeCriteriaKeyword: this.newlineIndent,
|
|
2240
|
+
separatorAfterCriteriaKeyword: ' '
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2243
|
+
super.visitChildren(ctx);
|
|
2244
|
+
this.minusIndent();
|
|
2245
|
+
}
|
|
2246
|
+
else {
|
|
2247
|
+
// USING clause: stays on same line as JOIN
|
|
2248
|
+
// Rule 6: No ON, does not break line
|
|
2249
|
+
strategies_1.coreStrategies.join.formatJoinCriteriaEntry(target, bridge, {
|
|
2250
|
+
separatorBeforeCriteriaKeyword: ' ',
|
|
2251
|
+
separatorAfterCriteriaKeyword: ''
|
|
2252
|
+
});
|
|
2253
|
+
super.visitChildren(ctx);
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
expressionStartsWithParen(expr) {
|
|
2257
|
+
// Check if expression starts with a parenthesis
|
|
2258
|
+
const text = expr.text || '';
|
|
2259
|
+
return text.trimStart().startsWith('(');
|
|
2260
|
+
}
|
|
2261
|
+
visitLateralView(ctx) {
|
|
2262
|
+
if (this.inPipeOperatorRightSide) {
|
|
2263
|
+
super.visitChildren(ctx);
|
|
2264
|
+
return;
|
|
2265
|
+
}
|
|
2266
|
+
// LATERAL VIEW on new line with one level indent (relative to FROM)
|
|
2267
|
+
// Get the previous token (should be the end of relation or previous lateral view)
|
|
2268
|
+
const lateralToken = ctx.LATERAL();
|
|
2269
|
+
if (lateralToken) {
|
|
2270
|
+
// Find the previous non-hidden token
|
|
2271
|
+
const prevToken = this.getPreviousNonHiddenToken(lateralToken.symbol.tokenIndex);
|
|
2272
|
+
if (prevToken) {
|
|
2273
|
+
// Insert newline + indent between previous token and LATERAL
|
|
2274
|
+
const prevEndPos = this.getTokenEndPosition(prevToken);
|
|
2275
|
+
// Use high priority to ensure this edit is not overridden
|
|
2276
|
+
this.bridge.collector.addEdit({
|
|
2277
|
+
range: {
|
|
2278
|
+
start: prevEndPos,
|
|
2279
|
+
end: { line: lateralToken.symbol.line - 1, character: lateralToken.symbol.charPositionInLine }
|
|
2280
|
+
},
|
|
2281
|
+
newText: this.newlineIndent
|
|
2282
|
+
}, { level: 10, source: 'lateralView' });
|
|
2283
|
+
}
|
|
2284
|
+
}
|
|
2285
|
+
const viewToken = ctx.VIEW();
|
|
2286
|
+
const outerToken = ctx.OUTER();
|
|
2287
|
+
if (lateralToken && viewToken) {
|
|
2288
|
+
// Skip updateAfterPreviousTokenByNode(lateralToken) - already handled by newline+indent above
|
|
2289
|
+
this.updateBeforeNextTokenByNode(viewToken, ' ');
|
|
2290
|
+
this.updateAfterPreviousTokenByNode(viewToken, ' ');
|
|
2291
|
+
}
|
|
2292
|
+
if (outerToken) {
|
|
2293
|
+
this.updateAfterPreviousTokenByNode(outerToken, ' ');
|
|
2294
|
+
this.updateBeforeNextTokenByNode(outerToken, ' ');
|
|
2295
|
+
}
|
|
2296
|
+
const leftParen = ctx.LEFT_PAREN();
|
|
2297
|
+
const rightParen = ctx.RIGHT_PAREN();
|
|
2298
|
+
if (leftParen && rightParen) {
|
|
2299
|
+
this.updateAfterPreviousTokenByNode(leftParen, '');
|
|
2300
|
+
this.updateBeforeNextTokenByNode(leftParen, '');
|
|
2301
|
+
this.updateAfterPreviousTokenByNode(rightParen, '');
|
|
2302
|
+
const leftParenIndex = leftParen.symbol.tokenIndex;
|
|
2303
|
+
const rightParenIndex = rightParen.symbol.tokenIndex;
|
|
2304
|
+
const commas = ctx.COMMA();
|
|
2305
|
+
for (const comma of commas) {
|
|
2306
|
+
const commaIndex = comma.symbol.tokenIndex;
|
|
2307
|
+
if (commaIndex > leftParenIndex && commaIndex < rightParenIndex) {
|
|
2308
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
2309
|
+
this.updateBeforeNextTokenByNode(comma, ' ');
|
|
2310
|
+
}
|
|
2311
|
+
}
|
|
2312
|
+
}
|
|
2313
|
+
super.visitChildren(ctx);
|
|
2314
|
+
}
|
|
2315
|
+
// ==================== INSERT/UPDATE/DELETE/MERGE ====================
|
|
2316
|
+
visitSingleInsertQuery(ctx) {
|
|
2317
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2318
|
+
super.visitChildren(ctx);
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
visitInsertIntoTable(ctx) {
|
|
2322
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2323
|
+
super.visitChildren(ctx);
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
visitInsertOverwriteTable(ctx) {
|
|
2327
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2328
|
+
super.visitChildren(ctx);
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
visitInsertOverwriteHiveDir(ctx) {
|
|
2332
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2333
|
+
super.visitChildren(ctx);
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
visitInsertOverwriteDir(ctx) {
|
|
2337
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2338
|
+
super.visitChildren(ctx);
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
visitDeleteFromTable(ctx) {
|
|
2342
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2343
|
+
super.visitChildren(ctx);
|
|
2344
|
+
}
|
|
2345
|
+
}
|
|
2346
|
+
visitUpdateTable(ctx) {
|
|
2347
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2348
|
+
super.visitChildren(ctx);
|
|
2349
|
+
}
|
|
2350
|
+
}
|
|
2351
|
+
visitSetClause(ctx) {
|
|
2352
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2353
|
+
super.visitChildren(ctx);
|
|
2354
|
+
}
|
|
2355
|
+
}
|
|
2356
|
+
visitMergeIntoTable(ctx) {
|
|
2357
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2358
|
+
super.visitChildren(ctx);
|
|
2359
|
+
}
|
|
2360
|
+
}
|
|
2361
|
+
visitMatchedClause(ctx) {
|
|
2362
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2363
|
+
super.visitChildren(ctx);
|
|
2364
|
+
}
|
|
2365
|
+
}
|
|
2366
|
+
visitNotMatchedClause(ctx) {
|
|
2367
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2368
|
+
super.visitChildren(ctx);
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
visitNotMatchedBySourceClause(ctx) {
|
|
2372
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2373
|
+
super.visitChildren(ctx);
|
|
2374
|
+
}
|
|
2375
|
+
}
|
|
2376
|
+
// ==================== Utility Statements ====================
|
|
2377
|
+
visitExplain(ctx) {
|
|
2378
|
+
const explainNode = ctx.EXPLAIN();
|
|
2379
|
+
const modeNode = ctx.LOGICAL() || ctx.FORMATTED() || ctx.EXTENDED() || ctx.CODEGEN() || ctx.COST();
|
|
2380
|
+
if (modeNode) {
|
|
2381
|
+
this.updateBeforeNextTokenByNode(explainNode, ' ');
|
|
2382
|
+
this.updateBeforeNextTokenByNode(modeNode, this.newline);
|
|
2383
|
+
}
|
|
2384
|
+
else {
|
|
2385
|
+
this.updateBeforeNextTokenByNode(explainNode, this.newline);
|
|
2386
|
+
}
|
|
2387
|
+
super.visitChildren(ctx);
|
|
2388
|
+
}
|
|
2389
|
+
visitCall(ctx) {
|
|
2390
|
+
const callKeyword = ctx.CALL();
|
|
2391
|
+
this.updateBeforeNextTokenByNode(callKeyword, ' ');
|
|
2392
|
+
const leftParen = ctx.LEFT_PAREN();
|
|
2393
|
+
const rightParen = ctx.RIGHT_PAREN();
|
|
2394
|
+
if (leftParen && rightParen) {
|
|
2395
|
+
this.updateAfterPreviousTokenByNode(leftParen, '');
|
|
2396
|
+
const args = ctx.functionArgument();
|
|
2397
|
+
const argsLength = args.map(a => a.text).join(', ').length;
|
|
2398
|
+
if (argsLength > this.maxParenthesisLength) {
|
|
2399
|
+
this.addIndent();
|
|
2400
|
+
if (args.length > 0) {
|
|
2401
|
+
this.updateAfterPreviousTokenByContext(args[0], this.newlineIndent);
|
|
2402
|
+
}
|
|
2403
|
+
const commas = ctx.COMMA();
|
|
2404
|
+
for (const comma of commas) {
|
|
2405
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
2406
|
+
this.updateBeforeNextTokenByNode(comma, this.newlineIndent);
|
|
2407
|
+
}
|
|
2408
|
+
this.minusIndent();
|
|
2409
|
+
this.updateAfterPreviousTokenByNode(rightParen, this.newline);
|
|
2410
|
+
}
|
|
2411
|
+
else {
|
|
2412
|
+
const commas = ctx.COMMA();
|
|
2413
|
+
for (const comma of commas) {
|
|
2414
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
2415
|
+
this.updateBeforeNextTokenByNode(comma, ' ');
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
super.visitChildren(ctx);
|
|
2420
|
+
}
|
|
2421
|
+
visitExecuteImmediate(ctx) {
|
|
2422
|
+
const executeNode = ctx.EXECUTE();
|
|
2423
|
+
const immediateNode = ctx.IMMEDIATE();
|
|
2424
|
+
this.updateBeforeNextTokenByNode(executeNode, ' ');
|
|
2425
|
+
this.updateBeforeNextTokenByNode(immediateNode, ' ');
|
|
2426
|
+
const intoNode = ctx.INTO();
|
|
2427
|
+
if (intoNode) {
|
|
2428
|
+
this.updateAfterPreviousTokenByNode(intoNode, this.newline);
|
|
2429
|
+
this.updateBeforeNextTokenByNode(intoNode, ' ');
|
|
2430
|
+
}
|
|
2431
|
+
const targetVariable = ctx.multipartIdentifierList();
|
|
2432
|
+
if (targetVariable) {
|
|
2433
|
+
const commas = targetVariable.COMMA();
|
|
2434
|
+
for (const comma of commas) {
|
|
2435
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
2436
|
+
this.updateBeforeNextTokenByNode(comma, ' ');
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
const usingClause = ctx.executeImmediateUsing();
|
|
2440
|
+
if (usingClause) {
|
|
2441
|
+
this.visitExecuteImmediateUsing(usingClause);
|
|
2442
|
+
}
|
|
2443
|
+
super.visitChildren(ctx);
|
|
2444
|
+
}
|
|
2445
|
+
visitExecuteImmediateUsing(ctx) {
|
|
2446
|
+
const usingNode = ctx.USING();
|
|
2447
|
+
this.updateAfterPreviousTokenByNode(usingNode, this.newline);
|
|
2448
|
+
const leftParen = ctx.LEFT_PAREN();
|
|
2449
|
+
const rightParen = ctx.RIGHT_PAREN();
|
|
2450
|
+
if (leftParen && rightParen) {
|
|
2451
|
+
const paramsSeq = ctx.namedExpressionSeq();
|
|
2452
|
+
const params = paramsSeq.namedExpression();
|
|
2453
|
+
const paramsLength = params.map(p => p.text).join(', ').length;
|
|
2454
|
+
if (paramsLength > this.maxParenthesisLength) {
|
|
2455
|
+
this.addIndent();
|
|
2456
|
+
this.updateBeforeNextTokenByNode(leftParen, this.newlineIndent);
|
|
2457
|
+
this.updateAfterPreviousTokenByNode(rightParen, this.newlineIndent);
|
|
2458
|
+
this.minusIndent();
|
|
2459
|
+
}
|
|
2460
|
+
else {
|
|
2461
|
+
this.updateAfterPreviousTokenByNode(leftParen, ' ');
|
|
2462
|
+
this.updateBeforeNextTokenByNode(leftParen, '');
|
|
2463
|
+
this.updateAfterPreviousTokenByNode(rightParen, '');
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
else {
|
|
2467
|
+
this.updateBeforeNextTokenByNode(usingNode, ' ');
|
|
2468
|
+
}
|
|
2469
|
+
super.visitChildren(ctx);
|
|
2470
|
+
}
|
|
2471
|
+
visitCommentNamespace(ctx) {
|
|
2472
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2473
|
+
super.visitChildren(ctx);
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2476
|
+
visitCommentTable(ctx) {
|
|
2477
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2478
|
+
super.visitChildren(ctx);
|
|
2479
|
+
}
|
|
2480
|
+
}
|
|
2481
|
+
visitDescribeFunction(ctx) {
|
|
2482
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2483
|
+
super.visitChildren(ctx);
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
visitDescribeProcedure(ctx) {
|
|
2487
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2488
|
+
super.visitChildren(ctx);
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
visitDescribeNamespace(ctx) {
|
|
2492
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2493
|
+
super.visitChildren(ctx);
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
visitDescribeRelation(ctx) {
|
|
2497
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2498
|
+
super.visitChildren(ctx);
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
visitDescribeQuery(ctx) {
|
|
2502
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2503
|
+
super.visitChildren(ctx);
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
visitAnalyze(ctx) {
|
|
2507
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2508
|
+
super.visitChildren(ctx);
|
|
2509
|
+
}
|
|
2510
|
+
}
|
|
2511
|
+
visitAnalyzeTables(ctx) {
|
|
2512
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2513
|
+
super.visitChildren(ctx);
|
|
2514
|
+
}
|
|
2515
|
+
}
|
|
2516
|
+
visitRepairTable(ctx) {
|
|
2517
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2518
|
+
super.visitChildren(ctx);
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
visitTruncateTable(ctx) {
|
|
2522
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2523
|
+
super.visitChildren(ctx);
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
visitCacheTable(ctx) {
|
|
2527
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2528
|
+
super.visitChildren(ctx);
|
|
2529
|
+
}
|
|
2530
|
+
}
|
|
2531
|
+
visitUncacheTable(ctx) {
|
|
2532
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2533
|
+
super.visitChildren(ctx);
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
visitClearCache(ctx) {
|
|
2537
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2538
|
+
super.visitChildren(ctx);
|
|
2539
|
+
}
|
|
2540
|
+
}
|
|
2541
|
+
visitRefreshTable(ctx) {
|
|
2542
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2543
|
+
super.visitChildren(ctx);
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
visitRefreshFunction(ctx) {
|
|
2547
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2548
|
+
super.visitChildren(ctx);
|
|
2549
|
+
}
|
|
2550
|
+
}
|
|
2551
|
+
visitLoadData(ctx) {
|
|
2552
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2553
|
+
super.visitChildren(ctx);
|
|
2554
|
+
}
|
|
2555
|
+
}
|
|
2556
|
+
// ==================== Expression ====================
|
|
2557
|
+
visitFunctionCall(ctx) {
|
|
2558
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2559
|
+
super.visitChildren(ctx);
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2562
|
+
visitCast(ctx) {
|
|
2563
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2564
|
+
super.visitChildren(ctx);
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
visitTrim(ctx) {
|
|
2568
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2569
|
+
super.visitChildren(ctx);
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
visitAny_value(ctx) {
|
|
2573
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2574
|
+
super.visitChildren(ctx);
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
visitFirst(ctx) {
|
|
2578
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2579
|
+
super.visitChildren(ctx);
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2582
|
+
visitLast(ctx) {
|
|
2583
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2584
|
+
super.visitChildren(ctx);
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
visitTimestampadd(ctx) {
|
|
2588
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2589
|
+
super.visitChildren(ctx);
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
visitTimestampdiff(ctx) {
|
|
2593
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2594
|
+
super.visitChildren(ctx);
|
|
2595
|
+
}
|
|
2596
|
+
}
|
|
2597
|
+
visitPosition(ctx) {
|
|
2598
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2599
|
+
super.visitChildren(ctx);
|
|
2600
|
+
}
|
|
2601
|
+
}
|
|
2602
|
+
visitStruct(ctx) {
|
|
2603
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2604
|
+
super.visitChildren(ctx);
|
|
2605
|
+
}
|
|
2606
|
+
}
|
|
2607
|
+
visitLogicalBinary(ctx) {
|
|
2608
|
+
// In pipe context, use pipe-specific indent for AND/OR
|
|
2609
|
+
const indentForAndOr = this.inPipeOperatorRightSide ? this.context.getPipeContentIndent() : this.newlineIndent;
|
|
2610
|
+
// Format AND/OR on new line with indent
|
|
2611
|
+
if (ctx.AND()) {
|
|
2612
|
+
this.updateAfterPreviousTokenByNode(ctx.AND(), indentForAndOr);
|
|
2613
|
+
this.updateBeforeNextTokenByNode(ctx.AND(), ' ');
|
|
2614
|
+
}
|
|
2615
|
+
if (ctx.OR()) {
|
|
2616
|
+
this.updateAfterPreviousTokenByNode(ctx.OR(), indentForAndOr);
|
|
2617
|
+
this.updateBeforeNextTokenByNode(ctx.OR(), ' ');
|
|
2618
|
+
}
|
|
2619
|
+
super.visitChildren(ctx);
|
|
2620
|
+
}
|
|
2621
|
+
visitLogicalNot(ctx) {
|
|
2622
|
+
// Space after NOT (but NOT before - that's handled by parent context like WHERE)
|
|
2623
|
+
if (ctx.NOT()) {
|
|
2624
|
+
this.updateBeforeNextTokenByNode(ctx.NOT(), ' ');
|
|
2625
|
+
}
|
|
2626
|
+
if (ctx.BANG()) {
|
|
2627
|
+
this.updateBeforeNextTokenByNode(ctx.BANG(), ' ');
|
|
2628
|
+
}
|
|
2629
|
+
super.visitChildren(ctx);
|
|
2630
|
+
}
|
|
2631
|
+
visitComparison(ctx) {
|
|
2632
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2633
|
+
super.visitChildren(ctx);
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
visitPredicate(ctx) {
|
|
2637
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2638
|
+
super.visitChildren(ctx);
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
visitCollateClause(ctx) {
|
|
2642
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2643
|
+
super.visitChildren(ctx);
|
|
2644
|
+
}
|
|
2645
|
+
}
|
|
2646
|
+
visitCollate(ctx) {
|
|
2647
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2648
|
+
super.visitChildren(ctx);
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
visitArithmeticBinary(ctx) {
|
|
2652
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2653
|
+
super.visitChildren(ctx);
|
|
2654
|
+
}
|
|
2655
|
+
}
|
|
2656
|
+
visitIntervalValue(ctx) {
|
|
2657
|
+
// Add space before interval value if preceded by INTERVAL keyword
|
|
2658
|
+
const prevToken = this.getPreviousNonHiddenToken(ctx.start.tokenIndex);
|
|
2659
|
+
if (prevToken?.type === SparkSQLLexer_1.SparkSQLLexer.INTERVAL) {
|
|
2660
|
+
this.updateAfterPreviousTokenByContext(ctx, ' ');
|
|
2661
|
+
}
|
|
2662
|
+
// Handle MINUS sign with spaces
|
|
2663
|
+
const minus = ctx.MINUS();
|
|
2664
|
+
if (minus) {
|
|
2665
|
+
this.updateAfterPreviousTokenByNode(minus, ' ');
|
|
2666
|
+
this.updateBeforeNextTokenByNode(minus, ' ');
|
|
2667
|
+
}
|
|
2668
|
+
// Add space after the value before the unit
|
|
2669
|
+
const integerValue = ctx.INTEGER_VALUE();
|
|
2670
|
+
const decimalValue = ctx.DECIMAL_VALUE();
|
|
2671
|
+
const stringLit = ctx.stringLit();
|
|
2672
|
+
if (integerValue) {
|
|
2673
|
+
this.updateBeforeNextTokenByNode(integerValue, ' ');
|
|
2674
|
+
}
|
|
2675
|
+
else if (decimalValue) {
|
|
2676
|
+
this.updateBeforeNextTokenByNode(decimalValue, ' ');
|
|
2677
|
+
}
|
|
2678
|
+
else if (stringLit && !minus) {
|
|
2679
|
+
this.updateBeforeNextTokenByContext(stringLit, ' ');
|
|
2680
|
+
}
|
|
2681
|
+
super.visitChildren(ctx);
|
|
2682
|
+
}
|
|
2683
|
+
visitSubqueryExpression(ctx) {
|
|
2684
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2685
|
+
super.visitChildren(ctx);
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
visitSubquery(ctx) {
|
|
2689
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2690
|
+
super.visitChildren(ctx);
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
visitParenthesizedExpression(ctx) {
|
|
2694
|
+
// Check if this parenthesized expression is inside an InlineTable (VALUES clause)
|
|
2695
|
+
// For InlineTable, preserve space between VALUES keyword and left paren
|
|
2696
|
+
// The space was already added by visitInlineTable, so skip strategy formatting
|
|
2697
|
+
let isInInlineTable = false;
|
|
2698
|
+
let parent = ctx.parent;
|
|
2699
|
+
while (parent) {
|
|
2700
|
+
if (parent.constructor.name === 'InlineTableContext') {
|
|
2701
|
+
isInInlineTable = true;
|
|
2702
|
+
break;
|
|
2703
|
+
}
|
|
2704
|
+
parent = parent.parent;
|
|
2705
|
+
}
|
|
2706
|
+
if (isInInlineTable) {
|
|
2707
|
+
// Don't use strategy - it would override the space after VALUES
|
|
2708
|
+
// Just format the right paren, but NOT the left paren
|
|
2709
|
+
const rightParen = ctx.RIGHT_PAREN();
|
|
2710
|
+
if (rightParen) {
|
|
2711
|
+
this.updateAfterPreviousTokenByNode(rightParen, '');
|
|
2712
|
+
}
|
|
2713
|
+
super.visitChildren(ctx);
|
|
2714
|
+
}
|
|
2715
|
+
else if (!this.tryFormatWithStrategy(ctx)) {
|
|
2716
|
+
super.visitChildren(ctx);
|
|
2717
|
+
}
|
|
2718
|
+
}
|
|
2719
|
+
visitExists(ctx) {
|
|
2720
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
2721
|
+
super.visitChildren(ctx);
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
// ==================== Inline Table ====================
|
|
2725
|
+
visitInlineTableDefault2(ctx) {
|
|
2726
|
+
// InlineTable in FROM clause - visit inlineTable child directly
|
|
2727
|
+
// This ensures InlineTable sees InlineTableDefault2Context as its parent
|
|
2728
|
+
const inlineTable = ctx.inlineTable();
|
|
2729
|
+
if (inlineTable) {
|
|
2730
|
+
// Mark that this is FROM VALUES context
|
|
2731
|
+
inlineTable.__isFromValues = true;
|
|
2732
|
+
}
|
|
2733
|
+
super.visitChildren(ctx);
|
|
2734
|
+
}
|
|
2735
|
+
visitInlineTable(ctx) {
|
|
2736
|
+
// VALUES clause formatting
|
|
2737
|
+
const valuesToken = ctx.VALUES();
|
|
2738
|
+
const _expressions = ctx.expression();
|
|
2739
|
+
const tableAlias = ctx.tableAlias();
|
|
2740
|
+
if (!valuesToken) {
|
|
2741
|
+
super.visitChildren(ctx);
|
|
2742
|
+
return;
|
|
2743
|
+
}
|
|
2744
|
+
// Check if this is inside an INSERT statement or CREATE VIEW AS VALUES
|
|
2745
|
+
let parent = ctx.parent;
|
|
2746
|
+
let isInsideInsert = false;
|
|
2747
|
+
let isAfterCreateViewAs = false;
|
|
2748
|
+
let isInsideFromClause = false;
|
|
2749
|
+
// First check for FROM clause context
|
|
2750
|
+
let p = ctx.parent;
|
|
2751
|
+
while (p) {
|
|
2752
|
+
if (p.constructor.name === 'FromClauseContext') {
|
|
2753
|
+
isInsideFromClause = true;
|
|
2754
|
+
break;
|
|
2755
|
+
}
|
|
2756
|
+
if (p.constructor.name === 'InlineTableDefault2Context') {
|
|
2757
|
+
isInsideFromClause = true;
|
|
2758
|
+
break;
|
|
2759
|
+
}
|
|
2760
|
+
p = p.parent;
|
|
2761
|
+
}
|
|
2762
|
+
// Then check for INSERT or CREATE VIEW AS VALUES
|
|
2763
|
+
parent = ctx.parent;
|
|
2764
|
+
while (parent) {
|
|
2765
|
+
if (parent.constructor.name === 'SingleInsertQueryContext') {
|
|
2766
|
+
isInsideInsert = true;
|
|
2767
|
+
break;
|
|
2768
|
+
}
|
|
2769
|
+
// Only set isAfterCreateViewAs if VALUES is directly after AS (not in FROM clause)
|
|
2770
|
+
if (parent.constructor.name === 'CreateViewContext' && !isInsideFromClause) {
|
|
2771
|
+
isAfterCreateViewAs = true;
|
|
2772
|
+
break;
|
|
2773
|
+
}
|
|
2774
|
+
parent = parent.parent;
|
|
2775
|
+
}
|
|
2776
|
+
if (isInsideInsert) {
|
|
2777
|
+
// Inside INSERT - VALUES on new line with 2-space indent
|
|
2778
|
+
this.updateAfterPreviousTokenByNode(valuesToken, '\n ');
|
|
2779
|
+
const expressions = ctx.expression();
|
|
2780
|
+
const _commas = ctx.COMMA();
|
|
2781
|
+
if (expressions.length > 1) {
|
|
2782
|
+
// Multiple tuples - each on separate line with additional indent
|
|
2783
|
+
this.addIndent();
|
|
2784
|
+
// Mark inlineTable as long content for visitRowConstructor
|
|
2785
|
+
ctx.__isLongContent = true;
|
|
2786
|
+
// VALUES 后换行,所有元组在独立行
|
|
2787
|
+
this.updateBeforeNextTokenByNode(valuesToken, this.newlineIndent);
|
|
2788
|
+
// 在每个后续元组前添加换行+缩进
|
|
2789
|
+
for (let i = 1; i < expressions.length; i++) {
|
|
2790
|
+
const expr = expressions[i];
|
|
2791
|
+
// 使用 updateAfterPreviousTokenByContext 在元组前设置换行
|
|
2792
|
+
this.updateAfterPreviousTokenByContext(expr, this.newlineIndent);
|
|
2793
|
+
}
|
|
2794
|
+
super.visitChildren(ctx);
|
|
2795
|
+
this.minusIndent();
|
|
2796
|
+
}
|
|
2797
|
+
else {
|
|
2798
|
+
// Single tuple - on same line as VALUES
|
|
2799
|
+
this.updateBeforeNextTokenByNode(valuesToken, ' ');
|
|
2800
|
+
super.visitChildren(ctx);
|
|
2801
|
+
}
|
|
2802
|
+
}
|
|
2803
|
+
else if (isAfterCreateViewAs) {
|
|
2804
|
+
// Inside CREATE VIEW AS VALUES - VALUES on new line
|
|
2805
|
+
// The newline after AS is already handled by CreateViewStrategy
|
|
2806
|
+
// Only need to add space after VALUES keyword
|
|
2807
|
+
this.updateBeforeNextTokenByNode(valuesToken, ' ');
|
|
2808
|
+
const commas = ctx.COMMA();
|
|
2809
|
+
for (const comma of commas) {
|
|
2810
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
2811
|
+
this.updateBeforeNextTokenByNode(comma, ' ');
|
|
2812
|
+
}
|
|
2813
|
+
super.visitChildren(ctx);
|
|
2814
|
+
}
|
|
2815
|
+
else {
|
|
2816
|
+
// Not inside INSERT or CREATE VIEW AS VALUES
|
|
2817
|
+
// Check if this is a VALUES statement as pipe start (TABLE statement)
|
|
2818
|
+
// This is the case when VALUES appears at the start of a pipe expression
|
|
2819
|
+
let isPipeStart = false;
|
|
2820
|
+
let p = ctx.parent;
|
|
2821
|
+
while (p) {
|
|
2822
|
+
if (p.constructor.name === 'TableStatementContext' || p.constructor.name === 'OperatorPipeStatementContext') {
|
|
2823
|
+
isPipeStart = true;
|
|
2824
|
+
break;
|
|
2825
|
+
}
|
|
2826
|
+
// VALUES in InlineTableDefault1Context can be pipe start
|
|
2827
|
+
if (p.constructor.name === 'InlineTableDefault1Context') {
|
|
2828
|
+
isPipeStart = true;
|
|
2829
|
+
break;
|
|
2830
|
+
}
|
|
2831
|
+
p = p.parent;
|
|
2832
|
+
}
|
|
2833
|
+
// Check if content is long enough to require multiline formatting
|
|
2834
|
+
// For InlineTable, use ctx.text.length to match Legacy behavior (compressed text length)
|
|
2835
|
+
const contentLength = ctx.text.length;
|
|
2836
|
+
const isLongContent = contentLength > this.maxParenthesisLength || isPipeStart;
|
|
2837
|
+
const expressions = ctx.expression();
|
|
2838
|
+
if (isLongContent) {
|
|
2839
|
+
// Long content - use multiline format with each tuple on separate line
|
|
2840
|
+
// Add indent first so newlineIndent has proper indent for VALUES
|
|
2841
|
+
this.addIndent();
|
|
2842
|
+
// For pipe start, VALUES is the first token, set newline after it
|
|
2843
|
+
// For FROM VALUES, set newline before VALUES
|
|
2844
|
+
if (isPipeStart) {
|
|
2845
|
+
// VALUES is first token - set newline after VALUES
|
|
2846
|
+
this.updateBeforeNextTokenByNode(valuesToken, this.newlineIndent);
|
|
2847
|
+
}
|
|
2848
|
+
else {
|
|
2849
|
+
// VALUES is after FROM - set newline before VALUES
|
|
2850
|
+
this.updateAfterPreviousTokenByNode(valuesToken, this.newlineIndent);
|
|
2851
|
+
}
|
|
2852
|
+
// First tuple on new line after VALUES (same indent as VALUES content)
|
|
2853
|
+
// Format tuple-level commas: comma stays at end of line, next tuple on new line
|
|
2854
|
+
const commas = ctx.COMMA();
|
|
2855
|
+
for (let i = 0; i < commas.length; i++) {
|
|
2856
|
+
const comma = commas[i];
|
|
2857
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
2858
|
+
// Next tuple on new line
|
|
2859
|
+
if (expressions.length > i + 1) {
|
|
2860
|
+
this.updateAfterPreviousTokenByContext(expressions[i + 1], this.newlineIndent);
|
|
2861
|
+
}
|
|
2862
|
+
}
|
|
2863
|
+
// Mark inlineTable as long content for visitRowConstructor
|
|
2864
|
+
ctx.__isLongContent = true;
|
|
2865
|
+
super.visitChildren(ctx);
|
|
2866
|
+
this.minusIndent(); // Remove VALUES indent
|
|
2867
|
+
}
|
|
2868
|
+
else {
|
|
2869
|
+
// Short content - compact format
|
|
2870
|
+
// VALUES on new line with indent after FROM
|
|
2871
|
+
this.addIndent();
|
|
2872
|
+
this.updateAfterPreviousTokenByNode(valuesToken, this.newlineIndent);
|
|
2873
|
+
this.updateBeforeNextTokenByNode(valuesToken, ' ');
|
|
2874
|
+
// Legacy uses formatInlineCommaNodes for tuple-level commas
|
|
2875
|
+
const commas = ctx.COMMA();
|
|
2876
|
+
for (const comma of commas) {
|
|
2877
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
2878
|
+
this.updateBeforeNextTokenByNode(comma, ' ');
|
|
2879
|
+
}
|
|
2880
|
+
super.visitChildren(ctx);
|
|
2881
|
+
this.minusIndent();
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
// Handle table alias with AS keyword - AS should never have newline before it
|
|
2885
|
+
if (tableAlias) {
|
|
2886
|
+
this.normalizeKeywordSpacing(tableAlias.AS());
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
visitRowConstructor(ctx) {
|
|
2890
|
+
const leftParen = ctx.LEFT_PAREN();
|
|
2891
|
+
const rightParen = ctx.RIGHT_PAREN();
|
|
2892
|
+
if (leftParen && rightParen) {
|
|
2893
|
+
// Check if this row constructor is inside an inlineTable (VALUES clause)
|
|
2894
|
+
let inlineTableCtx = null;
|
|
2895
|
+
let p = ctx.parent;
|
|
2896
|
+
while (p) {
|
|
2897
|
+
if (p.constructor.name === 'InlineTableContext') {
|
|
2898
|
+
inlineTableCtx = p;
|
|
2899
|
+
break;
|
|
2900
|
+
}
|
|
2901
|
+
p = p.parent;
|
|
2902
|
+
}
|
|
2903
|
+
const isInInlineTable = inlineTableCtx !== null;
|
|
2904
|
+
const isLongContent = inlineTableCtx?.__isLongContent;
|
|
2905
|
+
const contentLength = ctx.text.length;
|
|
2906
|
+
const commas = ctx.COMMA();
|
|
2907
|
+
// For RowConstructor inside InlineTable with long content:
|
|
2908
|
+
// Keep tuple compact, don't modify space before left paren
|
|
2909
|
+
// (space/newline was already set by visitInlineTable)
|
|
2910
|
+
if (isInInlineTable && isLongContent) {
|
|
2911
|
+
// Compact format: only handle inside parens
|
|
2912
|
+
this.updateAfterPreviousTokenByNode(rightParen, '');
|
|
2913
|
+
// Internal commas: compact
|
|
2914
|
+
for (const comma of commas) {
|
|
2915
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
2916
|
+
this.updateBeforeNextTokenByNode(comma, ' ');
|
|
2917
|
+
}
|
|
2918
|
+
super.visitChildren(ctx);
|
|
2919
|
+
}
|
|
2920
|
+
else {
|
|
2921
|
+
this.formatParenthesizedContent(leftParen, rightParen, commas, contentLength, {
|
|
2922
|
+
forceCompact: true,
|
|
2923
|
+
spaceBeforeLeftParen: isInInlineTable,
|
|
2924
|
+
visitChildren: () => super.visitChildren(ctx)
|
|
2925
|
+
});
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
else {
|
|
2929
|
+
super.visitChildren(ctx);
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
// ==================== Aliased Query/Relation (FROM 后子查询) ====================
|
|
2933
|
+
/**
|
|
2934
|
+
* Format aliased query (FROM 后的括号子查询)
|
|
2935
|
+
* 规则:左括号换行,内容缩进 2 空格,右括号与 FROM 对齐
|
|
2936
|
+
*
|
|
2937
|
+
* 例如:
|
|
2938
|
+
* FROM
|
|
2939
|
+
* (
|
|
2940
|
+
* SELECT ...
|
|
2941
|
+
* ) AS alias
|
|
2942
|
+
*/
|
|
2943
|
+
visitAliasedQuery(ctx) {
|
|
2944
|
+
const leftParen = ctx.LEFT_PAREN();
|
|
2945
|
+
const rightParen = ctx.RIGHT_PAREN();
|
|
2946
|
+
const query = ctx.query();
|
|
2947
|
+
const tableAlias = ctx.tableAlias();
|
|
2948
|
+
if (!leftParen || !rightParen) {
|
|
2949
|
+
super.visitChildren(ctx);
|
|
2950
|
+
return;
|
|
2951
|
+
}
|
|
2952
|
+
// 左括号换行
|
|
2953
|
+
this.updateBeforeNextTokenByNode(leftParen, this.newlineIndent);
|
|
2954
|
+
// 增加缩进(子查询内容缩进 2 空格)
|
|
2955
|
+
this.addIndent();
|
|
2956
|
+
// 子查询内容换行
|
|
2957
|
+
if (query) {
|
|
2958
|
+
this.updateAfterPreviousTokenByContext(query, this.newlineIndent);
|
|
2959
|
+
}
|
|
2960
|
+
// 访问子查询
|
|
2961
|
+
if (query) {
|
|
2962
|
+
this.visit(query);
|
|
2963
|
+
}
|
|
2964
|
+
// 减少缩进
|
|
2965
|
+
this.minusIndent();
|
|
2966
|
+
// 右括号换行(与 FROM 对齐)
|
|
2967
|
+
this.updateAfterPreviousTokenByNode(rightParen, this.newlineIndent);
|
|
2968
|
+
// 处理别名 - 与右括号同行
|
|
2969
|
+
if (tableAlias) {
|
|
2970
|
+
const asNode = tableAlias.AS();
|
|
2971
|
+
if (asNode) {
|
|
2972
|
+
this.updateAfterPreviousTokenByNode(asNode, ' ');
|
|
2973
|
+
this.updateBeforeNextTokenByNode(asNode, ' ');
|
|
2974
|
+
}
|
|
2975
|
+
this.visit(tableAlias);
|
|
2976
|
+
}
|
|
2977
|
+
// 处理 sample 和 watermarkClause
|
|
2978
|
+
const sample = ctx.sample();
|
|
2979
|
+
if (sample) {
|
|
2980
|
+
this.visit(sample);
|
|
2981
|
+
}
|
|
2982
|
+
const watermarkClause = ctx.watermarkClause();
|
|
2983
|
+
if (watermarkClause) {
|
|
2984
|
+
this.visit(watermarkClause);
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
/**
|
|
2988
|
+
* Format aliased relation (FROM 后的括号关系)
|
|
2989
|
+
* 规则:左括号换行,内容缩进 2 空格,右括号与 FROM 对齐
|
|
2990
|
+
*/
|
|
2991
|
+
visitAliasedRelation(ctx) {
|
|
2992
|
+
const leftParen = ctx.LEFT_PAREN();
|
|
2993
|
+
const rightParen = ctx.RIGHT_PAREN();
|
|
2994
|
+
const relation = ctx.relation();
|
|
2995
|
+
const tableAlias = ctx.tableAlias();
|
|
2996
|
+
if (!leftParen || !rightParen) {
|
|
2997
|
+
super.visitChildren(ctx);
|
|
2998
|
+
return;
|
|
2999
|
+
}
|
|
3000
|
+
// 左括号换行
|
|
3001
|
+
this.updateBeforeNextTokenByNode(leftParen, this.newlineIndent);
|
|
3002
|
+
// 增加缩进(括号内容缩进 2 空格)
|
|
3003
|
+
this.addIndent();
|
|
3004
|
+
// 关系内容换行
|
|
3005
|
+
if (relation) {
|
|
3006
|
+
this.updateAfterPreviousTokenByContext(relation, this.newlineIndent);
|
|
3007
|
+
}
|
|
3008
|
+
// 访问关系
|
|
3009
|
+
if (relation) {
|
|
3010
|
+
this.visit(relation);
|
|
3011
|
+
}
|
|
3012
|
+
// 减少缩进
|
|
3013
|
+
this.minusIndent();
|
|
3014
|
+
// 右括号换行(与 FROM 对齐)
|
|
3015
|
+
this.updateAfterPreviousTokenByNode(rightParen, this.newlineIndent);
|
|
3016
|
+
// 处理别名 - 与右括号同行
|
|
3017
|
+
if (tableAlias) {
|
|
3018
|
+
const asNode = tableAlias.AS();
|
|
3019
|
+
if (asNode) {
|
|
3020
|
+
this.updateAfterPreviousTokenByNode(asNode, ' ');
|
|
3021
|
+
this.updateBeforeNextTokenByNode(asNode, ' ');
|
|
3022
|
+
}
|
|
3023
|
+
this.visit(tableAlias);
|
|
3024
|
+
}
|
|
3025
|
+
// 处理 sample 和 watermarkClause
|
|
3026
|
+
const sample = ctx.sample();
|
|
3027
|
+
if (sample) {
|
|
3028
|
+
this.visit(sample);
|
|
3029
|
+
}
|
|
3030
|
+
const watermarkClause = ctx.watermarkClause();
|
|
3031
|
+
if (watermarkClause) {
|
|
3032
|
+
this.visit(watermarkClause);
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
visitTableAlias(ctx) {
|
|
3036
|
+
const strictIdentifier = ctx.strictIdentifier();
|
|
3037
|
+
if (strictIdentifier) {
|
|
3038
|
+
this.updateAfterPreviousTokenByContext(strictIdentifier, ' ');
|
|
3039
|
+
}
|
|
3040
|
+
const identifierList = ctx.identifierList();
|
|
3041
|
+
if (identifierList) {
|
|
3042
|
+
const leftParen = identifierList.LEFT_PAREN();
|
|
3043
|
+
if (leftParen) {
|
|
3044
|
+
this.updateAfterPreviousTokenByNode(leftParen, '');
|
|
3045
|
+
}
|
|
3046
|
+
}
|
|
3047
|
+
super.visitChildren(ctx);
|
|
3048
|
+
}
|
|
3049
|
+
visitIdentifierList(ctx) {
|
|
3050
|
+
const leftParen = ctx.LEFT_PAREN();
|
|
3051
|
+
const rightParen = ctx.RIGHT_PAREN();
|
|
3052
|
+
if (leftParen && rightParen) {
|
|
3053
|
+
const isInsideTableAlias = ctx.parent?.constructor?.name === 'TableAliasContext';
|
|
3054
|
+
const identifierSeq = ctx.identifierSeq();
|
|
3055
|
+
const commas = identifierSeq?.COMMA() || [];
|
|
3056
|
+
const contentLength = ctx.stop.stopIndex - ctx.start.startIndex;
|
|
3057
|
+
if (contentLength > this.maxParenthesisLength) {
|
|
3058
|
+
this.addIndent();
|
|
3059
|
+
this.updateBeforeNextTokenByNode(leftParen, this.newlineIndent);
|
|
3060
|
+
this.updateAfterPreviousTokenByNode(rightParen, this.newlineIndent);
|
|
3061
|
+
this.minusIndent();
|
|
3062
|
+
for (const comma of commas) {
|
|
3063
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
3064
|
+
this.updateBeforeNextTokenByNode(comma, this.newlineIndent);
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
else {
|
|
3068
|
+
if (!isInsideTableAlias) {
|
|
3069
|
+
this.updateAfterPreviousTokenByNode(leftParen, ' ');
|
|
3070
|
+
}
|
|
3071
|
+
this.updateBeforeNextTokenByNode(leftParen, '');
|
|
3072
|
+
this.updateAfterPreviousTokenByNode(rightParen, '');
|
|
3073
|
+
for (const comma of commas) {
|
|
3074
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
3075
|
+
this.updateBeforeNextTokenByNode(comma, ' ');
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
3079
|
+
super.visitChildren(ctx);
|
|
3080
|
+
}
|
|
3081
|
+
visitIdentifierSeq(ctx) {
|
|
3082
|
+
if (ctx.parent?.constructor?.name === 'IdentifierListContext') {
|
|
3083
|
+
super.visitChildren(ctx);
|
|
3084
|
+
return;
|
|
3085
|
+
}
|
|
3086
|
+
const commas = ctx.COMMA();
|
|
3087
|
+
for (const comma of commas) {
|
|
3088
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
3089
|
+
this.updateBeforeNextTokenByNode(comma, ' ');
|
|
3090
|
+
}
|
|
3091
|
+
super.visitChildren(ctx);
|
|
3092
|
+
}
|
|
3093
|
+
// ==================== Partition ====================
|
|
3094
|
+
visitPartitionSpec(ctx) {
|
|
3095
|
+
const partitionNode = ctx.PARTITION();
|
|
3096
|
+
const leftParen = ctx.LEFT_PAREN();
|
|
3097
|
+
const rightParen = ctx.RIGHT_PAREN();
|
|
3098
|
+
if (partitionNode && leftParen && rightParen) {
|
|
3099
|
+
this.updateBeforeNextTokenByNode(partitionNode, ' ');
|
|
3100
|
+
this.updateBeforeNextTokenByNode(leftParen, '');
|
|
3101
|
+
this.updateAfterPreviousTokenByNode(rightParen, '');
|
|
3102
|
+
const commas = ctx.COMMA();
|
|
3103
|
+
for (const comma of commas) {
|
|
3104
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
3105
|
+
this.updateBeforeNextTokenByNode(comma, ' ');
|
|
3106
|
+
}
|
|
3107
|
+
}
|
|
3108
|
+
super.visitChildren(ctx);
|
|
3109
|
+
}
|
|
3110
|
+
visitPartitionVal(ctx) {
|
|
3111
|
+
const eqNode = ctx.EQ();
|
|
3112
|
+
if (eqNode) {
|
|
3113
|
+
this.updateAfterPreviousTokenByNode(eqNode, ' ');
|
|
3114
|
+
this.updateBeforeNextTokenByNode(eqNode, ' ');
|
|
3115
|
+
}
|
|
3116
|
+
super.visitChildren(ctx);
|
|
3117
|
+
}
|
|
3118
|
+
// ==================== Table ====================
|
|
3119
|
+
visitTable(ctx) {
|
|
3120
|
+
const tableToken = ctx.TABLE();
|
|
3121
|
+
if (tableToken) {
|
|
3122
|
+
this.updateBeforeNextTokenByNode(tableToken, ' ');
|
|
3123
|
+
}
|
|
3124
|
+
super.visitChildren(ctx);
|
|
3125
|
+
}
|
|
3126
|
+
visitTableName(ctx) {
|
|
3127
|
+
const tableAlias = ctx.tableAlias();
|
|
3128
|
+
if (tableAlias) {
|
|
3129
|
+
this.normalizeKeywordSpacing(tableAlias.AS());
|
|
3130
|
+
}
|
|
3131
|
+
super.visitChildren(ctx);
|
|
3132
|
+
}
|
|
3133
|
+
// ==================== Pipe Operator ====================
|
|
3134
|
+
visitOperatorPipeStatement(ctx) {
|
|
3135
|
+
const operatorPipeNode = ctx.OPERATOR_PIPE();
|
|
3136
|
+
const pipeNode = ctx.PIPE();
|
|
3137
|
+
const operatorNode = operatorPipeNode || pipeNode;
|
|
3138
|
+
if (operatorNode) {
|
|
3139
|
+
// `|> KEYWORD` should be on the same line (no newline before KEYWORD)
|
|
3140
|
+
// Just ensure space before and after `|>`
|
|
3141
|
+
this.updateAfterPreviousTokenByNode(operatorNode, '\n');
|
|
3142
|
+
this.updateBeforeNextTokenByNode(operatorNode, ' ');
|
|
3143
|
+
}
|
|
3144
|
+
super.visitChildren(ctx);
|
|
3145
|
+
}
|
|
3146
|
+
visitOperatorPipeRightSide(ctx) {
|
|
3147
|
+
// Save state
|
|
3148
|
+
const savedInPipeOperatorRightSide = this.inPipeOperatorRightSide;
|
|
3149
|
+
this.inPipeOperatorRightSide = true;
|
|
3150
|
+
// Sync state to FormattingContext so strategies can check via bridge.context.inPipeOperatorRightSide
|
|
3151
|
+
this.context.enterPipeContext();
|
|
3152
|
+
// Get pipe step kind and apply appropriate strategy
|
|
3153
|
+
const stepKind = this.getPipeStepKind(ctx);
|
|
3154
|
+
const formatting = {
|
|
3155
|
+
savedIndentNumber: this.context.currentIndentLevel,
|
|
3156
|
+
savedIndentString: ' ',
|
|
3157
|
+
previousInPipeOperatorRightSide: savedInPipeOperatorRightSide,
|
|
3158
|
+
newline: this.newline,
|
|
3159
|
+
newlineIndent: this.newlineIndent
|
|
3160
|
+
};
|
|
3161
|
+
switch (stepKind) {
|
|
3162
|
+
case 'pipe.select':
|
|
3163
|
+
strategies_1.coreStrategies.pipeSelect.apply(ctx, this.getPipeStepBridge(), formatting);
|
|
3164
|
+
break;
|
|
3165
|
+
case 'pipe.where':
|
|
3166
|
+
strategies_1.coreStrategies.pipeWhere.apply(ctx, this.getPipeStepBridge(), formatting);
|
|
3167
|
+
break;
|
|
3168
|
+
case 'pipe.set':
|
|
3169
|
+
strategies_1.coreStrategies.pipeSet.apply(ctx, this.getPipeStepBridge(), formatting);
|
|
3170
|
+
break;
|
|
3171
|
+
case 'pipe.drop':
|
|
3172
|
+
strategies_1.coreStrategies.pipeDrop.apply(ctx, this.getPipeStepBridge(), formatting);
|
|
3173
|
+
break;
|
|
3174
|
+
case 'pipe.extend':
|
|
3175
|
+
strategies_1.coreStrategies.pipeExtend.apply(ctx, this.getPipeStepBridge(), formatting);
|
|
3176
|
+
break;
|
|
3177
|
+
case 'pipe.join':
|
|
3178
|
+
strategies_1.coreStrategies.pipeJoin.apply(ctx, this.getPipeStepBridge(), formatting);
|
|
3179
|
+
break;
|
|
3180
|
+
case 'pipe.aggregate':
|
|
3181
|
+
strategies_1.coreStrategies.pipeAggregate.apply(ctx, this.getPipeStepBridge(), formatting);
|
|
3182
|
+
break;
|
|
3183
|
+
case 'pipe.setOperation':
|
|
3184
|
+
strategies_1.coreStrategies.pipeSetOperation.apply(ctx, this.getPipeStepBridge(), formatting);
|
|
3185
|
+
break;
|
|
3186
|
+
case 'pipe.queryOrganization':
|
|
3187
|
+
strategies_1.coreStrategies.pipeQueryOrganization.apply(ctx, this.getPipeStepBridge(), formatting);
|
|
3188
|
+
break;
|
|
3189
|
+
case 'pipe.sample':
|
|
3190
|
+
strategies_1.coreStrategies.pipeSample.apply(ctx, this.getPipeStepBridge(), formatting);
|
|
3191
|
+
break;
|
|
3192
|
+
case 'pipe.pivot':
|
|
3193
|
+
strategies_1.coreStrategies.pipePivot.apply(ctx, this.getPipeStepBridge(), formatting);
|
|
3194
|
+
break;
|
|
3195
|
+
case 'pipe.unpivot':
|
|
3196
|
+
strategies_1.coreStrategies.pipeUnpivot.apply(ctx, this.getPipeStepBridge(), formatting);
|
|
3197
|
+
break;
|
|
3198
|
+
default:
|
|
3199
|
+
super.visitChildren(ctx);
|
|
3200
|
+
}
|
|
3201
|
+
// Restore state
|
|
3202
|
+
this.inPipeOperatorRightSide = savedInPipeOperatorRightSide;
|
|
3203
|
+
this.context.exitPipeContext();
|
|
3204
|
+
}
|
|
3205
|
+
// ==================== Helper Methods ====================
|
|
3206
|
+
get indent() {
|
|
3207
|
+
return this.context.indent;
|
|
3208
|
+
}
|
|
3209
|
+
get newlineIndent() {
|
|
3210
|
+
return this.context.newlineIndent;
|
|
3211
|
+
}
|
|
3212
|
+
addIndent() {
|
|
3213
|
+
this.context.addIndent();
|
|
3214
|
+
}
|
|
3215
|
+
minusIndent() {
|
|
3216
|
+
this.context.minusIndent();
|
|
3217
|
+
}
|
|
3218
|
+
addTextEdit(edit) {
|
|
3219
|
+
this.editCollector.addEdit(edit);
|
|
3220
|
+
}
|
|
3221
|
+
updateAfterPreviousTokenByContext(ctx, newText) {
|
|
3222
|
+
this.bridge.updateAfterPreviousTokenByContext(ctx, newText);
|
|
3223
|
+
}
|
|
3224
|
+
updateAfterPreviousTokenByNode(node, newText) {
|
|
3225
|
+
this.bridge.updateAfterPreviousTokenByNode(node, newText);
|
|
3226
|
+
}
|
|
3227
|
+
updateBeforeNextTokenByNode(node, newText) {
|
|
3228
|
+
this.bridge.updateBeforeNextTokenByNode(node, newText);
|
|
3229
|
+
}
|
|
3230
|
+
updateBeforeNextTokenByContext(ctx, newText) {
|
|
3231
|
+
this.bridge.updateBeforeNextTokenByContext(ctx, newText);
|
|
3232
|
+
}
|
|
3233
|
+
getPreviousNonHiddenToken(tokenIndex) {
|
|
3234
|
+
return this.bridge.getPreviousNonHiddenToken(tokenIndex);
|
|
3235
|
+
}
|
|
3236
|
+
getPreviousNonHiddenTokenByStartIndex(startIndex) {
|
|
3237
|
+
return this.bridge.getPreviousNonHiddenTokenByStartIndex(startIndex);
|
|
3238
|
+
}
|
|
3239
|
+
getNextNonHiddenToken(tokenIndex) {
|
|
3240
|
+
return this.bridge.getNextNonHiddenToken(tokenIndex);
|
|
3241
|
+
}
|
|
3242
|
+
getTokenEndPosition(token) {
|
|
3243
|
+
return this.bridge.getTokenEndPosition(token);
|
|
3244
|
+
}
|
|
3245
|
+
findCommentTokensBetween(startIndex, endIndex) {
|
|
3246
|
+
return this.bridge.findCommentTokensBetween(startIndex, endIndex);
|
|
3247
|
+
}
|
|
3248
|
+
/**
|
|
3249
|
+
* Normalize keyword spacing - ensure single space before and after
|
|
3250
|
+
* Used for keywords like AS, VALUES, TABLE, etc.
|
|
3251
|
+
*/
|
|
3252
|
+
normalizeKeywordSpacing(node) {
|
|
3253
|
+
if (!node) {
|
|
3254
|
+
return;
|
|
3255
|
+
}
|
|
3256
|
+
this.updateAfterPreviousTokenByNode(node, ' ');
|
|
3257
|
+
this.updateBeforeNextTokenByNode(node, ' ');
|
|
3258
|
+
}
|
|
3259
|
+
/**
|
|
3260
|
+
* Normalize multiple keywords at once
|
|
3261
|
+
*/
|
|
3262
|
+
normalizeKeywordsSpacing(...nodes) {
|
|
3263
|
+
for (const node of nodes) {
|
|
3264
|
+
this.normalizeKeywordSpacing(node);
|
|
3265
|
+
}
|
|
3266
|
+
}
|
|
3267
|
+
// ==================== Legacy Helper Methods Migration ====================
|
|
3268
|
+
/**
|
|
3269
|
+
* Add space after a node (shorthand for common pattern)
|
|
3270
|
+
*/
|
|
3271
|
+
addSpaceAfterNode(node) {
|
|
3272
|
+
if (node) {
|
|
3273
|
+
this.updateBeforeNextTokenByNode(node, ' ');
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
/**
|
|
3277
|
+
* Add space after a context (shorthand for common pattern)
|
|
3278
|
+
*/
|
|
3279
|
+
addSpaceAfterContext(ctx) {
|
|
3280
|
+
if (ctx) {
|
|
3281
|
+
this.updateBeforeNextTokenByContext(ctx, ' ');
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
3284
|
+
/**
|
|
3285
|
+
* Add space after multiple nodes (batch operation)
|
|
3286
|
+
*/
|
|
3287
|
+
addSpaceAfterNodes(...nodes) {
|
|
3288
|
+
for (const node of nodes) {
|
|
3289
|
+
if (node) {
|
|
3290
|
+
this.updateBeforeNextTokenByNode(node, ' ');
|
|
3291
|
+
}
|
|
3292
|
+
}
|
|
3293
|
+
}
|
|
3294
|
+
/**
|
|
3295
|
+
* Add newline before a node
|
|
3296
|
+
*/
|
|
3297
|
+
addNewlineBeforeNode(node) {
|
|
3298
|
+
if (node) {
|
|
3299
|
+
this.updateAfterPreviousTokenByNode(node, this.newline);
|
|
3300
|
+
}
|
|
3301
|
+
}
|
|
3302
|
+
/**
|
|
3303
|
+
* Add newline after a node
|
|
3304
|
+
*/
|
|
3305
|
+
addNewlineAfterNode(node) {
|
|
3306
|
+
if (node) {
|
|
3307
|
+
this.updateBeforeNextTokenByNode(node, this.newline);
|
|
3308
|
+
}
|
|
3309
|
+
}
|
|
3310
|
+
/**
|
|
3311
|
+
* Add newline with indent before a node
|
|
3312
|
+
*/
|
|
3313
|
+
addNewlineIndentBeforeNode(node) {
|
|
3314
|
+
if (node) {
|
|
3315
|
+
this.updateAfterPreviousTokenByNode(node, this.newlineIndent);
|
|
3316
|
+
}
|
|
3317
|
+
}
|
|
3318
|
+
/**
|
|
3319
|
+
* Add newline before a context
|
|
3320
|
+
*/
|
|
3321
|
+
addNewlineBeforeContext(ctx) {
|
|
3322
|
+
if (ctx) {
|
|
3323
|
+
this.updateAfterPreviousTokenByContext(ctx, this.newline);
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
/**
|
|
3327
|
+
* Add newline with indent before a context
|
|
3328
|
+
*/
|
|
3329
|
+
addNewlineIndentBeforeContext(ctx) {
|
|
3330
|
+
if (ctx) {
|
|
3331
|
+
this.updateAfterPreviousTokenByContext(ctx, this.newlineIndent);
|
|
3332
|
+
}
|
|
3333
|
+
}
|
|
3334
|
+
/**
|
|
3335
|
+
* Add indent and put node on new line with indent
|
|
3336
|
+
*/
|
|
3337
|
+
indentAfterNode(node) {
|
|
3338
|
+
if (!node) {
|
|
3339
|
+
return;
|
|
3340
|
+
}
|
|
3341
|
+
this.addIndent();
|
|
3342
|
+
this.updateBeforeNextTokenByNode(node, this.newlineIndent);
|
|
3343
|
+
this.minusIndent();
|
|
3344
|
+
}
|
|
3345
|
+
/**
|
|
3346
|
+
* Format compact parentheses (no newlines inside)
|
|
3347
|
+
*/
|
|
3348
|
+
formatCompactParentheses(leftParen, rightParen, options) {
|
|
3349
|
+
if (!leftParen || !rightParen) {
|
|
3350
|
+
return;
|
|
3351
|
+
}
|
|
3352
|
+
const spaceBeforeLeftParen = options?.spaceBeforeLeftParen ?? false;
|
|
3353
|
+
if (spaceBeforeLeftParen) {
|
|
3354
|
+
this.updateAfterPreviousTokenByNode(leftParen, ' ');
|
|
3355
|
+
}
|
|
3356
|
+
this.updateBeforeNextTokenByNode(leftParen, '');
|
|
3357
|
+
this.updateAfterPreviousTokenByNode(rightParen, '');
|
|
3358
|
+
if (options?.commas) {
|
|
3359
|
+
this.formatInlineCommaNodes(options.commas);
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
/**
|
|
3363
|
+
* Format inline comma nodes - comma stays at end with space after
|
|
3364
|
+
*/
|
|
3365
|
+
formatInlineCommaNodes(commas) {
|
|
3366
|
+
for (const comma of commas) {
|
|
3367
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
3368
|
+
this.updateBeforeNextTokenByNode(comma, ' ');
|
|
3369
|
+
}
|
|
3370
|
+
}
|
|
3371
|
+
/**
|
|
3372
|
+
* Get bridge for BlockStrategy formatting
|
|
3373
|
+
*/
|
|
3374
|
+
getBlockFormattingBridge() {
|
|
3375
|
+
return {
|
|
3376
|
+
updateAfterPreviousTokenByNode: (node, newText) => this.updateAfterPreviousTokenByNode(node, newText),
|
|
3377
|
+
updateBeforeNextTokenByNode: (node, newText) => this.updateBeforeNextTokenByNode(node, newText),
|
|
3378
|
+
updateAfterPreviousTokenByContext: (ctx, newText) => this.updateAfterPreviousTokenByContext(ctx, newText)
|
|
3379
|
+
};
|
|
3380
|
+
}
|
|
3381
|
+
/**
|
|
3382
|
+
* Format parenthesized content with automatic line length detection
|
|
3383
|
+
*/
|
|
3384
|
+
formatParenthesizedContent(leftParen, rightParen, commas, contentLength, options) {
|
|
3385
|
+
if (!leftParen || !rightParen) {
|
|
3386
|
+
options?.visitChildren?.();
|
|
3387
|
+
return;
|
|
3388
|
+
}
|
|
3389
|
+
const opts = options || {};
|
|
3390
|
+
const useMultiline = opts.forceMultiline || (!opts.forceCompact && contentLength > this.maxParenthesisLength);
|
|
3391
|
+
const bridge = this.getBlockFormattingBridge();
|
|
3392
|
+
if (useMultiline) {
|
|
3393
|
+
if (opts.addIndentBeforeFormat) {
|
|
3394
|
+
this.addIndent();
|
|
3395
|
+
}
|
|
3396
|
+
this.addIndent();
|
|
3397
|
+
this.blockStrategy.formatMultilineBlock({ leftParen, rightParen }, bridge, {
|
|
3398
|
+
spaceBeforeLeftParen: opts.spaceBeforeLeftParen,
|
|
3399
|
+
separatorAfterLeftParen: this.newlineIndent,
|
|
3400
|
+
separatorBeforeRightParen: this.newlineIndent
|
|
3401
|
+
});
|
|
3402
|
+
for (const comma of commas) {
|
|
3403
|
+
this.updateAfterPreviousTokenByNode(comma, '');
|
|
3404
|
+
this.updateBeforeNextTokenByNode(comma, this.newlineIndent);
|
|
3405
|
+
}
|
|
3406
|
+
opts.visitChildren?.();
|
|
3407
|
+
this.minusIndent();
|
|
3408
|
+
this.addNewlineIndentBeforeNode(rightParen);
|
|
3409
|
+
if (opts.addIndentBeforeFormat) {
|
|
3410
|
+
this.minusIndent();
|
|
3411
|
+
}
|
|
3412
|
+
}
|
|
3413
|
+
else {
|
|
3414
|
+
this.blockStrategy.formatCompactBlock({ leftParen, rightParen }, bridge, {
|
|
3415
|
+
spaceBeforeLeftParen: opts.spaceBeforeLeftParen,
|
|
3416
|
+
separatorAfterLeftParen: '',
|
|
3417
|
+
separatorBeforeRightParen: ''
|
|
3418
|
+
});
|
|
3419
|
+
this.formatInlineCommaNodes(commas);
|
|
3420
|
+
opts.visitChildren?.();
|
|
3421
|
+
}
|
|
3422
|
+
}
|
|
3423
|
+
/**
|
|
3424
|
+
* Post-process: normalize keyword spacing in final output
|
|
3425
|
+
* This catches any keywords that weren't handled by visitor methods
|
|
3426
|
+
*/
|
|
3427
|
+
normalizeAllKeywordSpacing() {
|
|
3428
|
+
// Keywords that need space before and after (when not at line start)
|
|
3429
|
+
const keywordsNeedingSpace = [
|
|
3430
|
+
'AS',
|
|
3431
|
+
'VALUES',
|
|
3432
|
+
'TABLE',
|
|
3433
|
+
'VIEW',
|
|
3434
|
+
'INDEX',
|
|
3435
|
+
'FUNCTION',
|
|
3436
|
+
'INNER',
|
|
3437
|
+
'LEFT',
|
|
3438
|
+
'RIGHT',
|
|
3439
|
+
'FULL',
|
|
3440
|
+
'OUTER',
|
|
3441
|
+
'CROSS',
|
|
3442
|
+
'NATURAL',
|
|
3443
|
+
'UNION',
|
|
3444
|
+
'INTERSECT',
|
|
3445
|
+
'EXCEPT',
|
|
3446
|
+
'JOIN',
|
|
3447
|
+
'ON',
|
|
3448
|
+
'AND',
|
|
3449
|
+
'OR',
|
|
3450
|
+
'NOT',
|
|
3451
|
+
'ORDER',
|
|
3452
|
+
'GROUP',
|
|
3453
|
+
'BY',
|
|
3454
|
+
'HAVING',
|
|
3455
|
+
'LIMIT',
|
|
3456
|
+
'OFFSET',
|
|
3457
|
+
'WHERE',
|
|
3458
|
+
'FROM',
|
|
3459
|
+
'SELECT',
|
|
3460
|
+
'INSERT',
|
|
3461
|
+
'UPDATE',
|
|
3462
|
+
'DELETE',
|
|
3463
|
+
'CREATE',
|
|
3464
|
+
'ALTER',
|
|
3465
|
+
'DROP',
|
|
3466
|
+
'SET',
|
|
3467
|
+
'INTO',
|
|
3468
|
+
'OVERWRITE'
|
|
3469
|
+
];
|
|
3470
|
+
const allTokens = this.tokens.getTokens();
|
|
3471
|
+
for (let i = 0; i < allTokens.length; i++) {
|
|
3472
|
+
const token = allTokens[i];
|
|
3473
|
+
const tokenText = token.text?.toUpperCase();
|
|
3474
|
+
if (token.channel !== 0)
|
|
3475
|
+
continue;
|
|
3476
|
+
if (!tokenText || !keywordsNeedingSpace.includes(tokenText))
|
|
3477
|
+
continue;
|
|
3478
|
+
// Skip if no edits were made near this token (it was already handled by visitor)
|
|
3479
|
+
const tokenLine = token.line - 1;
|
|
3480
|
+
const tokenStart = token.charPositionInLine;
|
|
3481
|
+
const tokenEnd = tokenStart + (token.text?.length || 0);
|
|
3482
|
+
// Check if there's already an edit for this token's surrounding whitespace
|
|
3483
|
+
if (this.editCollector.hasOverlappingEdit(tokenLine, tokenStart - 1, tokenLine, tokenEnd + 1)) {
|
|
3484
|
+
continue;
|
|
3485
|
+
}
|
|
3486
|
+
// Ensure space before keyword (unless at line start or after newline)
|
|
3487
|
+
if (i > 0) {
|
|
3488
|
+
const prevToken = allTokens[i - 1];
|
|
3489
|
+
if (prevToken.channel === 1 && prevToken.text && !prevToken.text.includes('\n')) {
|
|
3490
|
+
// Previous is whitespace on same line - ensure single space
|
|
3491
|
+
if (!prevToken.text.endsWith(' ')) {
|
|
3492
|
+
const prevStartChar = prevToken.charPositionInLine;
|
|
3493
|
+
const prevEndChar = prevStartChar + prevToken.text.length;
|
|
3494
|
+
if (!this.editCollector.hasOverlappingEdit(prevToken.line - 1, prevStartChar, prevToken.line - 1, prevEndChar)) {
|
|
3495
|
+
this.editCollector.addEdit({
|
|
3496
|
+
range: {
|
|
3497
|
+
start: { line: prevToken.line - 1, character: prevStartChar },
|
|
3498
|
+
end: { line: prevToken.line - 1, character: prevEndChar }
|
|
3499
|
+
},
|
|
3500
|
+
newText: `${prevToken.text} `
|
|
3501
|
+
});
|
|
3502
|
+
}
|
|
3503
|
+
}
|
|
3504
|
+
}
|
|
3505
|
+
}
|
|
3506
|
+
// Ensure space after keyword
|
|
3507
|
+
if (i + 1 < allTokens.length) {
|
|
3508
|
+
const nextToken = allTokens[i + 1];
|
|
3509
|
+
if (nextToken.channel === 1 && nextToken.text && !nextToken.text.includes('\n')) {
|
|
3510
|
+
// Next is whitespace on same line - ensure single space
|
|
3511
|
+
if (!nextToken.text.startsWith(' ')) {
|
|
3512
|
+
const nextStartChar = nextToken.charPositionInLine;
|
|
3513
|
+
const nextEndChar = nextStartChar + nextToken.text.length;
|
|
3514
|
+
if (!this.editCollector.hasOverlappingEdit(nextToken.line - 1, nextStartChar, nextToken.line - 1, nextEndChar)) {
|
|
3515
|
+
this.editCollector.addEdit({
|
|
3516
|
+
range: {
|
|
3517
|
+
start: { line: nextToken.line - 1, character: nextStartChar },
|
|
3518
|
+
end: { line: nextToken.line - 1, character: nextEndChar }
|
|
3519
|
+
},
|
|
3520
|
+
newText: ` ${nextToken.text}`
|
|
3521
|
+
});
|
|
3522
|
+
}
|
|
3523
|
+
}
|
|
3524
|
+
}
|
|
3525
|
+
}
|
|
3526
|
+
}
|
|
3527
|
+
}
|
|
3528
|
+
compressConsecutiveSpaces() {
|
|
3529
|
+
const allTokens = this.tokens.getTokens();
|
|
3530
|
+
for (const token of allTokens) {
|
|
3531
|
+
// Skip non-whitespace tokens (channel 1 is HIDDEN/whitespace)
|
|
3532
|
+
if (token.channel !== 1)
|
|
3533
|
+
continue;
|
|
3534
|
+
// Skip comment tokens (they are also on HIDDEN channel but should not be modified)
|
|
3535
|
+
if (token.type === SparkSQLLexer_1.SparkSQLLexer.SIMPLE_COMMENT || token.type === SparkSQLLexer_1.SparkSQLLexer.BRACKETED_COMMENT) {
|
|
3536
|
+
continue;
|
|
3537
|
+
}
|
|
3538
|
+
const text = token.text;
|
|
3539
|
+
if (!text)
|
|
3540
|
+
continue;
|
|
3541
|
+
const startLine = token.line - 1;
|
|
3542
|
+
const startChar = token.charPositionInLine;
|
|
3543
|
+
const endChar = startChar + text.length;
|
|
3544
|
+
if (this.editCollector.hasOverlappingEdit(startLine, startChar, startLine, endChar))
|
|
3545
|
+
continue;
|
|
3546
|
+
// Rule 1: Compress multiple spaces to single space (no newlines)
|
|
3547
|
+
if (!text.includes('\n') && !text.includes('\r') && text.includes(' ')) {
|
|
3548
|
+
this.editCollector.addEdit({
|
|
3549
|
+
range: {
|
|
3550
|
+
start: { line: startLine, character: startChar },
|
|
3551
|
+
end: { line: startLine, character: endChar }
|
|
3552
|
+
},
|
|
3553
|
+
newText: ' '
|
|
3554
|
+
});
|
|
3555
|
+
continue;
|
|
3556
|
+
}
|
|
3557
|
+
// Rule 3: Replace multiple newlines with single space between tokens
|
|
3558
|
+
// (newlines between keywords should become space)
|
|
3559
|
+
// This is handled by checking for consecutive newlines
|
|
3560
|
+
if ((text.includes('\n') || text.includes('\r')) && !text.includes(' ')) {
|
|
3561
|
+
// Count newlines
|
|
3562
|
+
const newlineCount = (text.match(/\n/g) || []).length + (text.match(/\r/g) || []).length;
|
|
3563
|
+
if (newlineCount > 1) {
|
|
3564
|
+
// Multiple newlines - replace with single space
|
|
3565
|
+
this.editCollector.addEdit({
|
|
3566
|
+
range: {
|
|
3567
|
+
start: { line: startLine, character: startChar },
|
|
3568
|
+
end: { line: startLine, character: endChar }
|
|
3569
|
+
},
|
|
3570
|
+
newText: ' '
|
|
3571
|
+
});
|
|
3572
|
+
}
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
}
|
|
3576
|
+
/**
|
|
3577
|
+
* Clean up extra spaces inside parentheses: ( a, b ) -> (a, b)
|
|
3578
|
+
* Clean up comma spacing: a , b -> a, b
|
|
3579
|
+
* Called after main formatting is done
|
|
3580
|
+
*/
|
|
3581
|
+
cleanParenthesesSpaces() {
|
|
3582
|
+
const allTokens = this.tokens.getTokens();
|
|
3583
|
+
// Find all parentheses pairs and clean spaces inside
|
|
3584
|
+
for (let i = 0; i < allTokens.length; i++) {
|
|
3585
|
+
const token = allTokens[i];
|
|
3586
|
+
// Check for left paren - ensure no space after
|
|
3587
|
+
if (token.text === '(' && token.channel === 0) {
|
|
3588
|
+
// Check if next token is whitespace with leading space
|
|
3589
|
+
if (i + 1 < allTokens.length) {
|
|
3590
|
+
const nextToken = allTokens[i + 1];
|
|
3591
|
+
// Only process if next token is whitespace on same line
|
|
3592
|
+
if (nextToken.channel === 1 &&
|
|
3593
|
+
nextToken.text &&
|
|
3594
|
+
nextToken.text.includes(' ') &&
|
|
3595
|
+
nextToken.line === token.line) {
|
|
3596
|
+
// Check if this is IN ( or EXISTS ( - skip cleaning as it should be "IN (\n SELECT"
|
|
3597
|
+
// Check if previous token is IN or EXISTS
|
|
3598
|
+
let skipCleaning = false;
|
|
3599
|
+
if (i > 0) {
|
|
3600
|
+
const prevToken = allTokens[i - 1];
|
|
3601
|
+
if (prevToken.channel === 0 &&
|
|
3602
|
+
(prevToken.text?.toUpperCase() === 'IN' || prevToken.text?.toUpperCase() === 'EXISTS')) {
|
|
3603
|
+
skipCleaning = true;
|
|
3604
|
+
}
|
|
3605
|
+
}
|
|
3606
|
+
if (!skipCleaning) {
|
|
3607
|
+
// Remove the space part after (
|
|
3608
|
+
const nextStartChar = nextToken.charPositionInLine;
|
|
3609
|
+
const nextEndChar = nextStartChar + nextToken.text.length;
|
|
3610
|
+
if (!this.editCollector.hasOverlappingEdit(nextToken.line - 1, nextStartChar, nextToken.line - 1, nextEndChar)) {
|
|
3611
|
+
// Keep only the non-space parts (like newlines)
|
|
3612
|
+
const nonSpacePart = nextToken.text.replace(/^ +/, '');
|
|
3613
|
+
this.editCollector.addEdit({
|
|
3614
|
+
range: {
|
|
3615
|
+
start: { line: nextToken.line - 1, character: nextStartChar },
|
|
3616
|
+
end: { line: nextToken.line - 1, character: nextEndChar }
|
|
3617
|
+
},
|
|
3618
|
+
newText: nonSpacePart
|
|
3619
|
+
});
|
|
3620
|
+
}
|
|
3621
|
+
}
|
|
3622
|
+
}
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
// Check for right paren - ensure no space before
|
|
3626
|
+
if (token.text === ')' && token.channel === 0) {
|
|
3627
|
+
// Check if previous token is whitespace with trailing space
|
|
3628
|
+
if (i > 0) {
|
|
3629
|
+
const prevToken = allTokens[i - 1];
|
|
3630
|
+
// Only process if prev token is whitespace on same line
|
|
3631
|
+
if (prevToken.channel === 1 &&
|
|
3632
|
+
prevToken.text &&
|
|
3633
|
+
prevToken.text.includes(' ') &&
|
|
3634
|
+
prevToken.line === token.line) {
|
|
3635
|
+
// Remove the space part before )
|
|
3636
|
+
const prevStartChar = prevToken.charPositionInLine;
|
|
3637
|
+
const prevEndChar = prevStartChar + prevToken.text.length;
|
|
3638
|
+
if (!this.editCollector.hasOverlappingEdit(prevToken.line - 1, prevStartChar, prevToken.line - 1, prevEndChar)) {
|
|
3639
|
+
// Keep only the non-space parts (like newlines)
|
|
3640
|
+
const nonSpacePart = prevToken.text.replace(/ +$/, '');
|
|
3641
|
+
this.editCollector.addEdit({
|
|
3642
|
+
range: {
|
|
3643
|
+
start: { line: prevToken.line - 1, character: prevStartChar },
|
|
3644
|
+
end: { line: prevToken.line - 1, character: prevEndChar }
|
|
3645
|
+
},
|
|
3646
|
+
newText: nonSpacePart
|
|
3647
|
+
});
|
|
3648
|
+
}
|
|
3649
|
+
}
|
|
3650
|
+
}
|
|
3651
|
+
}
|
|
3652
|
+
// Clean space before comma
|
|
3653
|
+
if (token.text === ',' && token.channel === 0) {
|
|
3654
|
+
// Check if previous token is whitespace with space
|
|
3655
|
+
if (i > 0) {
|
|
3656
|
+
const prevToken = allTokens[i - 1];
|
|
3657
|
+
if (prevToken.channel === 1 &&
|
|
3658
|
+
prevToken.text &&
|
|
3659
|
+
prevToken.text.includes(' ') &&
|
|
3660
|
+
prevToken.line === token.line) {
|
|
3661
|
+
// Remove space before comma
|
|
3662
|
+
const prevStartChar = prevToken.charPositionInLine;
|
|
3663
|
+
const prevEndChar = prevStartChar + prevToken.text.length;
|
|
3664
|
+
if (!this.editCollector.hasOverlappingEdit(prevToken.line - 1, prevStartChar, prevToken.line - 1, prevEndChar)) {
|
|
3665
|
+
const nonSpacePart = prevToken.text.replace(/ +$/, '');
|
|
3666
|
+
this.editCollector.addEdit({
|
|
3667
|
+
range: {
|
|
3668
|
+
start: { line: prevToken.line - 1, character: prevStartChar },
|
|
3669
|
+
end: { line: prevToken.line - 1, character: prevEndChar }
|
|
3670
|
+
},
|
|
3671
|
+
newText: nonSpacePart
|
|
3672
|
+
});
|
|
3673
|
+
}
|
|
3674
|
+
}
|
|
3675
|
+
}
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
3678
|
+
}
|
|
3679
|
+
getJoinFormattingBridge() {
|
|
3680
|
+
return {
|
|
3681
|
+
updateAfterPreviousTokenByContext: (ctx, newText) => this.updateAfterPreviousTokenByContext(ctx, newText),
|
|
3682
|
+
updateAfterPreviousTokenByNode: (node, newText) => this.updateAfterPreviousTokenByNode(node, newText),
|
|
3683
|
+
updateBeforeNextTokenByNode: (node, newText) => this.updateBeforeNextTokenByNode(node, newText)
|
|
3684
|
+
};
|
|
3685
|
+
}
|
|
3686
|
+
getPipeStepBridge() {
|
|
3687
|
+
return {
|
|
3688
|
+
visit: (ctx) => this.visit(ctx),
|
|
3689
|
+
visitNode: (ctx) => this.visit(ctx),
|
|
3690
|
+
visitChildren: (ctx) => super.visitChildren(ctx),
|
|
3691
|
+
updateAfterPreviousTokenByContext: (ctx, newText) => this.updateAfterPreviousTokenByContext(ctx, newText),
|
|
3692
|
+
updateBeforeNextTokenByContext: (ctx, newText) => this.updateBeforeNextTokenByContext(ctx, newText),
|
|
3693
|
+
updateAfterPreviousTokenByNode: (node, newText) => this.updateAfterPreviousTokenByNode(node, newText),
|
|
3694
|
+
updateBeforeNextTokenByNode: (node, newText) => this.updateBeforeNextTokenByNode(node, newText),
|
|
3695
|
+
restorePipeFormattingState: (formatting) => {
|
|
3696
|
+
this.inPipeOperatorRightSide = formatting.previousInPipeOperatorRightSide;
|
|
3697
|
+
}
|
|
3698
|
+
};
|
|
3699
|
+
}
|
|
3700
|
+
getPipeStepKind(ctx) {
|
|
3701
|
+
if (ctx.selectClause())
|
|
3702
|
+
return 'pipe.select';
|
|
3703
|
+
if (ctx.whereClause())
|
|
3704
|
+
return 'pipe.where';
|
|
3705
|
+
if (ctx.SET())
|
|
3706
|
+
return 'pipe.set';
|
|
3707
|
+
if (ctx.DROP())
|
|
3708
|
+
return 'pipe.drop';
|
|
3709
|
+
if (ctx.EXTEND())
|
|
3710
|
+
return 'pipe.extend';
|
|
3711
|
+
if (ctx.joinRelation())
|
|
3712
|
+
return 'pipe.join';
|
|
3713
|
+
if (ctx.aggregationClause())
|
|
3714
|
+
return 'pipe.aggregate';
|
|
3715
|
+
if (ctx.queryOrganization())
|
|
3716
|
+
return 'pipe.queryOrganization';
|
|
3717
|
+
if (ctx.sample())
|
|
3718
|
+
return 'pipe.sample';
|
|
3719
|
+
if (ctx.pivotClause())
|
|
3720
|
+
return 'pipe.pivot';
|
|
3721
|
+
if (ctx.unpivotClause())
|
|
3722
|
+
return 'pipe.unpivot';
|
|
3723
|
+
return 'pipe.unknown';
|
|
3724
|
+
}
|
|
3725
|
+
// ==================== Compound Statement (Stored Procedures) ====================
|
|
3726
|
+
visitSingleCompoundStatement(ctx) {
|
|
3727
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
3728
|
+
super.visitChildren(ctx);
|
|
3729
|
+
}
|
|
3730
|
+
}
|
|
3731
|
+
visitBeginEndCompoundBlock(ctx) {
|
|
3732
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
3733
|
+
super.visitChildren(ctx);
|
|
3734
|
+
}
|
|
3735
|
+
}
|
|
3736
|
+
visitCompoundBody(ctx) {
|
|
3737
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
3738
|
+
super.visitChildren(ctx);
|
|
3739
|
+
}
|
|
3740
|
+
}
|
|
3741
|
+
visitCompoundStatement(ctx) {
|
|
3742
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
3743
|
+
super.visitChildren(ctx);
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3746
|
+
visitIfElseStatement(ctx) {
|
|
3747
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
3748
|
+
super.visitChildren(ctx);
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3751
|
+
visitWhileStatement(ctx) {
|
|
3752
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
3753
|
+
super.visitChildren(ctx);
|
|
3754
|
+
}
|
|
3755
|
+
}
|
|
3756
|
+
visitLoopStatement(ctx) {
|
|
3757
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
3758
|
+
super.visitChildren(ctx);
|
|
3759
|
+
}
|
|
3760
|
+
}
|
|
3761
|
+
visitRepeatStatement(ctx) {
|
|
3762
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
3763
|
+
super.visitChildren(ctx);
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3766
|
+
visitForStatement(ctx) {
|
|
3767
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
3768
|
+
super.visitChildren(ctx);
|
|
3769
|
+
}
|
|
3770
|
+
}
|
|
3771
|
+
visitCaseStatement(ctx) {
|
|
3772
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
3773
|
+
super.visitChildren(ctx);
|
|
3774
|
+
}
|
|
3775
|
+
}
|
|
3776
|
+
visitSearchedCaseStatement(ctx) {
|
|
3777
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
3778
|
+
super.visitChildren(ctx);
|
|
3779
|
+
}
|
|
3780
|
+
}
|
|
3781
|
+
visitSimpleCaseStatement(ctx) {
|
|
3782
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
3783
|
+
super.visitChildren(ctx);
|
|
3784
|
+
}
|
|
3785
|
+
}
|
|
3786
|
+
visitLeaveStatement(ctx) {
|
|
3787
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
3788
|
+
super.visitChildren(ctx);
|
|
3789
|
+
}
|
|
3790
|
+
}
|
|
3791
|
+
visitIterateStatement(ctx) {
|
|
3792
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
3793
|
+
super.visitChildren(ctx);
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3796
|
+
visitDeclareConditionStatement(ctx) {
|
|
3797
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
3798
|
+
super.visitChildren(ctx);
|
|
3799
|
+
}
|
|
3800
|
+
}
|
|
3801
|
+
visitDeclareHandlerStatement(ctx) {
|
|
3802
|
+
if (!this.tryFormatWithStrategy(ctx)) {
|
|
3803
|
+
super.visitChildren(ctx);
|
|
3804
|
+
}
|
|
3805
|
+
}
|
|
3806
|
+
}
|
|
3807
|
+
exports.StrategyBasedFormatter = StrategyBasedFormatter;
|
|
3808
|
+
//# sourceMappingURL=strategy-based-formatter.js.map
|