sqltidy 0.2.2__tar.gz
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.
- sqltidy-0.2.2/LICENSE +21 -0
- sqltidy-0.2.2/PKG-INFO +26 -0
- sqltidy-0.2.2/README.md +15 -0
- sqltidy-0.2.2/pyproject.toml +21 -0
- sqltidy-0.2.2/setup.cfg +4 -0
- sqltidy-0.2.2/sqltidy/__init__.py +5 -0
- sqltidy-0.2.2/sqltidy/api.py +41 -0
- sqltidy-0.2.2/sqltidy/cli.py +185 -0
- sqltidy-0.2.2/sqltidy/config.py +19 -0
- sqltidy-0.2.2/sqltidy/core.py +47 -0
- sqltidy-0.2.2/sqltidy/generator.py +210 -0
- sqltidy-0.2.2/sqltidy/rules/__init__.py +3 -0
- sqltidy-0.2.2/sqltidy/rules/base.py +14 -0
- sqltidy-0.2.2/sqltidy/rules/loader.py +88 -0
- sqltidy-0.2.2/sqltidy/rules/rewrite/__init__.py +1 -0
- sqltidy-0.2.2/sqltidy/rules/rewrite/alias_style_abc.py +124 -0
- sqltidy-0.2.2/sqltidy/rules/rewrite/alias_style_t_numeric.py +118 -0
- sqltidy-0.2.2/sqltidy/rules/rewrite/subquery_to_cte.py +111 -0
- sqltidy-0.2.2/sqltidy/rules/rules.py +253 -0
- sqltidy-0.2.2/sqltidy/rules/tidy/__init__.py +1 -0
- sqltidy-0.2.2/sqltidy/rules/tidy/compact_whitespace.py +16 -0
- sqltidy-0.2.2/sqltidy/rules/tidy/indent_select_columns.py +52 -0
- sqltidy-0.2.2/sqltidy/rules/tidy/leading_commas.py +57 -0
- sqltidy-0.2.2/sqltidy/rules/tidy/newline_after_select.py +28 -0
- sqltidy-0.2.2/sqltidy/rules/tidy/uppercase_keywords.py +17 -0
- sqltidy-0.2.2/sqltidy/tokenizer.py +44 -0
- sqltidy-0.2.2/sqltidy.egg-info/PKG-INFO +26 -0
- sqltidy-0.2.2/sqltidy.egg-info/SOURCES.txt +29 -0
- sqltidy-0.2.2/sqltidy.egg-info/dependency_links.txt +1 -0
- sqltidy-0.2.2/sqltidy.egg-info/entry_points.txt +2 -0
- sqltidy-0.2.2/sqltidy.egg-info/top_level.txt +1 -0
sqltidy-0.2.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Jayden Rasband
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
sqltidy-0.2.2/PKG-INFO
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sqltidy
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: SQL Formatting Made Easy
|
|
5
|
+
Author: Jayden Rasband
|
|
6
|
+
Project-URL: Source, https://github.com/jrasband-dev/sqltidy
|
|
7
|
+
Project-URL: Documentation, https://jrasband-dev.github.io/sqltidy/
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Dynamic: license-file
|
|
11
|
+
|
|
12
|
+
# SQLTidy
|
|
13
|
+
|
|
14
|
+
[](https://pypi.org/project/sqltidy/)
|
|
15
|
+
[](LICENSE)
|
|
16
|
+
[](https://jrasband-dev.github.io/sqltidy/)
|
|
17
|
+
|
|
18
|
+
Tidy your SQL Scripts
|
|
19
|
+
|
|
20
|
+
For full details and usage, see the [official documentation](https://jrasband-dev.github.io/sqltidy/).
|
|
21
|
+
|
|
22
|
+
## Getting Started
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install sqltidy
|
|
26
|
+
```
|
sqltidy-0.2.2/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# SQLTidy
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/sqltidy/)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://jrasband-dev.github.io/sqltidy/)
|
|
6
|
+
|
|
7
|
+
Tidy your SQL Scripts
|
|
8
|
+
|
|
9
|
+
For full details and usage, see the [official documentation](https://jrasband-dev.github.io/sqltidy/).
|
|
10
|
+
|
|
11
|
+
## Getting Started
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install sqltidy
|
|
15
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[tool.setuptools.dynamic]
|
|
6
|
+
version = {attr = "sqltidy.__version__"}
|
|
7
|
+
|
|
8
|
+
[project]
|
|
9
|
+
name = "sqltidy"
|
|
10
|
+
dynamic = ["version"]
|
|
11
|
+
description = "SQL Formatting Made Easy"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
authors = [{name = "Jayden Rasband"}]
|
|
14
|
+
dependencies = []
|
|
15
|
+
|
|
16
|
+
[project.urls]
|
|
17
|
+
"Source" = "https://github.com/jrasband-dev/sqltidy"
|
|
18
|
+
"Documentation" = "https://jrasband-dev.github.io/sqltidy/"
|
|
19
|
+
|
|
20
|
+
[project.scripts]
|
|
21
|
+
sqltidy = "sqltidy.cli:main"
|
sqltidy-0.2.2/setup.cfg
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from .config import TidyConfig
|
|
2
|
+
from .rules.base import BaseRule
|
|
3
|
+
from .core import SQLFormatter
|
|
4
|
+
|
|
5
|
+
# In-memory list to hold extra plugin rules registered at runtime
|
|
6
|
+
_extra_plugins = []
|
|
7
|
+
|
|
8
|
+
def register_plugin(rule: BaseRule):
|
|
9
|
+
"""
|
|
10
|
+
Register a plugin rule at runtime.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
rule (BaseRule): An instance of a rule to apply.
|
|
14
|
+
"""
|
|
15
|
+
if not isinstance(rule, BaseRule):
|
|
16
|
+
raise TypeError("Plugin must be an instance of BaseRule")
|
|
17
|
+
_extra_plugins.append(rule)
|
|
18
|
+
|
|
19
|
+
def clear_plugins():
|
|
20
|
+
"""
|
|
21
|
+
Clear all runtime-registered plugin rules.
|
|
22
|
+
"""
|
|
23
|
+
_extra_plugins.clear()
|
|
24
|
+
|
|
25
|
+
def format_sql(sql: str, config: TidyConfig = None) -> str:
|
|
26
|
+
"""
|
|
27
|
+
Format a SQL string using all registered rules, including runtime plugins.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
sql (str): The SQL string to format.
|
|
31
|
+
config (TidyConfig, optional): Formatter configuration.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
str: Formatted SQL string.
|
|
35
|
+
"""
|
|
36
|
+
formatter = SQLFormatter(config=config)
|
|
37
|
+
|
|
38
|
+
# Inject runtime plugins into the formatter
|
|
39
|
+
formatter.rules.extend(_extra_plugins)
|
|
40
|
+
|
|
41
|
+
return formatter.format(sql)
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import sys
|
|
3
|
+
import json
|
|
4
|
+
from . import __version__
|
|
5
|
+
from .api import format_sql
|
|
6
|
+
from .config import TidyConfig, RewriteConfig
|
|
7
|
+
from .generator import run_generator, load_config_file
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def create_tidy_config_from_file(config_file: str) -> TidyConfig:
|
|
11
|
+
"""
|
|
12
|
+
Load TidyConfig from a JSON configuration file.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
config_file: Path to the configuration JSON file
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
TidyConfig: Configuration object with loaded values
|
|
19
|
+
"""
|
|
20
|
+
try:
|
|
21
|
+
config_data = load_config_file(config_file)
|
|
22
|
+
|
|
23
|
+
# Create TidyConfig with loaded values (no nesting needed)
|
|
24
|
+
return TidyConfig(**config_data)
|
|
25
|
+
except (FileNotFoundError, json.JSONDecodeError) as e:
|
|
26
|
+
print(f"Error loading config file: {e}", file=sys.stderr)
|
|
27
|
+
sys.exit(1)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def create_rewrite_config_from_file(config_file: str) -> RewriteConfig:
|
|
31
|
+
"""
|
|
32
|
+
Load RewriteConfig from a JSON configuration file.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
config_file: Path to the configuration JSON file
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
RewriteConfig: Configuration object with loaded values
|
|
39
|
+
"""
|
|
40
|
+
try:
|
|
41
|
+
config_data = load_config_file(config_file)
|
|
42
|
+
|
|
43
|
+
# Create RewriteConfig with loaded values (no nesting needed)
|
|
44
|
+
return RewriteConfig(**config_data)
|
|
45
|
+
except (FileNotFoundError, json.JSONDecodeError) as e:
|
|
46
|
+
print(f"Error loading config file: {e}", file=sys.stderr)
|
|
47
|
+
sys.exit(1)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def main():
|
|
51
|
+
parser = argparse.ArgumentParser(
|
|
52
|
+
description="A SQL formatting tool"
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
parser.add_argument(
|
|
56
|
+
"-v", "--version",
|
|
57
|
+
action="version",
|
|
58
|
+
version=f"%(prog)s {__version__}"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# create subparsers for subcommands
|
|
62
|
+
subparsers = parser.add_subparsers(title='Commands', dest="command", required=True)
|
|
63
|
+
|
|
64
|
+
# -------------------
|
|
65
|
+
# tidy Command
|
|
66
|
+
# -------------------
|
|
67
|
+
tidy_parser = subparsers.add_parser(
|
|
68
|
+
name="tidy",
|
|
69
|
+
help="Format a SQL file",
|
|
70
|
+
description="Format SQL with tidy rules."
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
tidy_input_group = tidy_parser.add_argument_group(title='Input')
|
|
74
|
+
tidy_input_group.add_argument("input", nargs="?", help="SQL file to format")
|
|
75
|
+
|
|
76
|
+
tidy_parameter_group = tidy_parser.add_argument_group('Parameters')
|
|
77
|
+
tidy_parameter_group.add_argument("-o", "--output", help="Output file")
|
|
78
|
+
tidy_parameter_group.add_argument("-cfg","--rules", help="Path to custom rules json file")
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# -------------------
|
|
83
|
+
# rewrite Command
|
|
84
|
+
# -------------------
|
|
85
|
+
|
|
86
|
+
rewrite_parser = subparsers.add_parser(
|
|
87
|
+
"rewrite",
|
|
88
|
+
help="Rewrite SQL queries",
|
|
89
|
+
description="Rewrite SQL queries according to specified rules"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
rewrite_input_group = rewrite_parser.add_argument_group(title='Input')
|
|
93
|
+
rewrite_input_group.add_argument("input", nargs="?", help="SQL file to rewrite")
|
|
94
|
+
|
|
95
|
+
rewrite_parameter_group = rewrite_parser.add_argument_group('Parameters')
|
|
96
|
+
rewrite_parameter_group.add_argument("-o", "--output", help="Output file")
|
|
97
|
+
rewrite_parameter_group.add_argument("-cfg", "--rules", help="Path to custom rules json file")
|
|
98
|
+
# Use config.py defaults for rewrite behavior. No CLI enable/disable flags are provided.
|
|
99
|
+
rewrite_parameter_group.add_argument("--tidy", action="store_true", help="Apply tidy rules after rewriting")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
# -------------------
|
|
103
|
+
# config Command
|
|
104
|
+
# -------------------
|
|
105
|
+
config_parser = subparsers.add_parser(
|
|
106
|
+
"config",
|
|
107
|
+
help="Interactive config generator",
|
|
108
|
+
description="Launch an interactive configuration generator for sqltidy"
|
|
109
|
+
)
|
|
110
|
+
# You can add config-specific arguments here if needed
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# -------------------
|
|
119
|
+
# parse arguments
|
|
120
|
+
# -------------------
|
|
121
|
+
args = parser.parse_args()
|
|
122
|
+
|
|
123
|
+
if args.command == "config":
|
|
124
|
+
run_generator()
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
# tidy command
|
|
128
|
+
if args.command == "tidy":
|
|
129
|
+
if args.input:
|
|
130
|
+
with open(args.input, "r", encoding="utf-8") as f:
|
|
131
|
+
sql = f.read()
|
|
132
|
+
else:
|
|
133
|
+
sql = sys.stdin.read()
|
|
134
|
+
|
|
135
|
+
# Load config from file if provided, otherwise use defaults
|
|
136
|
+
if args.rules:
|
|
137
|
+
config = create_tidy_config_from_file(args.rules)
|
|
138
|
+
else:
|
|
139
|
+
config = TidyConfig()
|
|
140
|
+
|
|
141
|
+
formatted_sql = format_sql(sql, config=config)
|
|
142
|
+
|
|
143
|
+
if args.output:
|
|
144
|
+
with open(args.output, "w", encoding="utf-8") as f:
|
|
145
|
+
f.write(formatted_sql)
|
|
146
|
+
elif args.input:
|
|
147
|
+
# overwrite input file if no output specified
|
|
148
|
+
with open(args.input, "w", encoding="utf-8") as f:
|
|
149
|
+
f.write(formatted_sql)
|
|
150
|
+
else:
|
|
151
|
+
print(formatted_sql)
|
|
152
|
+
|
|
153
|
+
# rewrite command
|
|
154
|
+
if args.command == "rewrite":
|
|
155
|
+
if args.input:
|
|
156
|
+
with open(args.input, "r", encoding="utf-8") as f:
|
|
157
|
+
sql = f.read()
|
|
158
|
+
else:
|
|
159
|
+
sql = sys.stdin.read()
|
|
160
|
+
|
|
161
|
+
# Load config from file if provided, otherwise use defaults
|
|
162
|
+
if args.rules:
|
|
163
|
+
config = create_rewrite_config_from_file(args.rules)
|
|
164
|
+
else:
|
|
165
|
+
config = RewriteConfig()
|
|
166
|
+
|
|
167
|
+
formatted_sql = format_sql(sql, config=config)
|
|
168
|
+
|
|
169
|
+
# Apply tidy rules if requested
|
|
170
|
+
if args.tidy:
|
|
171
|
+
tidy_config = TidyConfig()
|
|
172
|
+
formatted_sql = format_sql(formatted_sql, config=tidy_config)
|
|
173
|
+
|
|
174
|
+
if args.output:
|
|
175
|
+
with open(args.output, "w", encoding="utf-8") as f:
|
|
176
|
+
f.write(formatted_sql)
|
|
177
|
+
elif args.input:
|
|
178
|
+
# overwrite input file if no output specified
|
|
179
|
+
with open(args.input, "w", encoding="utf-8") as f:
|
|
180
|
+
f.write(formatted_sql)
|
|
181
|
+
else:
|
|
182
|
+
print(formatted_sql)
|
|
183
|
+
|
|
184
|
+
if __name__ == "__main__":
|
|
185
|
+
main()
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Default Rules
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
|
|
5
|
+
@dataclass
|
|
6
|
+
class TidyConfig:
|
|
7
|
+
uppercase_keywords: bool = True
|
|
8
|
+
newline_after_select: bool = True
|
|
9
|
+
compact: bool = True
|
|
10
|
+
leading_commas: bool = True
|
|
11
|
+
indent_select_columns: bool = True
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class RewriteConfig:
|
|
17
|
+
enable_subquery_to_cte: bool = True
|
|
18
|
+
enable_alias_style_abc: bool = False
|
|
19
|
+
enable_alias_style_t_numeric: bool = False
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# sqltidy/core.py
|
|
2
|
+
import re
|
|
3
|
+
from typing import List
|
|
4
|
+
from .config import TidyConfig
|
|
5
|
+
from .tokenizer import TOKEN_RE
|
|
6
|
+
|
|
7
|
+
class SQLFormatter:
|
|
8
|
+
"""Main SQL formatting engine."""
|
|
9
|
+
|
|
10
|
+
def __init__(self, config: TidyConfig = None):
|
|
11
|
+
from .rules import load_rules
|
|
12
|
+
from .rules.base import FormatterContext
|
|
13
|
+
self.ctx = FormatterContext(config or TidyConfig())
|
|
14
|
+
self.rules = load_rules()
|
|
15
|
+
|
|
16
|
+
def tokenize(self, sql: str) -> List[str]:
|
|
17
|
+
"""Convert raw SQL into proper tokens without external dependencies."""
|
|
18
|
+
tokens = []
|
|
19
|
+
for groups in TOKEN_RE.findall(sql):
|
|
20
|
+
# Find the first non-empty capturing group
|
|
21
|
+
for t in groups:
|
|
22
|
+
if t == "":
|
|
23
|
+
continue
|
|
24
|
+
# normalize whitespace
|
|
25
|
+
if t.isspace():
|
|
26
|
+
if "\n" in t:
|
|
27
|
+
tokens.append("\n")
|
|
28
|
+
else:
|
|
29
|
+
tokens.append(" ")
|
|
30
|
+
else:
|
|
31
|
+
tokens.append(t)
|
|
32
|
+
break
|
|
33
|
+
|
|
34
|
+
return tokens
|
|
35
|
+
|
|
36
|
+
def format(self, sql: str) -> str:
|
|
37
|
+
tokens = self.tokenize(sql)
|
|
38
|
+
|
|
39
|
+
# Apply your custom rules
|
|
40
|
+
for rule in sorted(self.rules, key=lambda r: getattr(r, "order", 100)):
|
|
41
|
+
tokens = rule.apply(tokens, self.ctx)
|
|
42
|
+
|
|
43
|
+
return self.join_tokens(tokens)
|
|
44
|
+
|
|
45
|
+
def join_tokens(self, tokens: List[str]) -> str:
|
|
46
|
+
"""Reassemble tokens into formatted SQL text."""
|
|
47
|
+
return "".join(tokens).strip()
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Interactive configuration generator for sqltidy.
|
|
3
|
+
Generates config files that can override default settings.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
from dataclasses import asdict
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Dict, Any
|
|
10
|
+
from .config import TidyConfig, RewriteConfig
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
TIDY_DESCRIPTIONS = {
|
|
14
|
+
"uppercase_keywords": "Convert SQL keywords to uppercase? (e.g., SELECT, FROM, WHERE)",
|
|
15
|
+
"newline_after_select": "Add newline after SELECT keyword?",
|
|
16
|
+
"compact": "Use compact formatting (reduce unnecessary whitespace)?",
|
|
17
|
+
"leading_commas": "Use leading commas in column lists? (e.g., col1\\n , col2\\n , col3)",
|
|
18
|
+
"indent_select_columns": "Indent SELECT columns on separate lines?",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
REWRITE_DESCRIPTIONS = {
|
|
22
|
+
"enable_subquery_to_cte": "Convert subqueries to Common Table Expressions (CTEs)?",
|
|
23
|
+
"enable_alias_style_abc": "Apply uppercase A, B, C ... table aliases?",
|
|
24
|
+
"enable_alias_style_t_numeric": "Apply uppercase T1, T2, T3 ... table aliases?",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def prompt_yes_no(question: str, default: bool = True) -> bool:
|
|
29
|
+
"""
|
|
30
|
+
Prompt user for a yes/no question.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
question: The question to ask
|
|
34
|
+
default: The default value if user just presses enter
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
bool: The user's choice
|
|
38
|
+
"""
|
|
39
|
+
default_str = "[Y/n]" if default else "[y/N]"
|
|
40
|
+
while True:
|
|
41
|
+
response = input(f"{question} {default_str}: ").strip().lower()
|
|
42
|
+
if not response:
|
|
43
|
+
return default
|
|
44
|
+
if response in ("y", "yes"):
|
|
45
|
+
return True
|
|
46
|
+
if response in ("n", "no"):
|
|
47
|
+
return False
|
|
48
|
+
print("Please enter 'y' or 'n'")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def generate_tidy_config() -> Dict[str, Any]:
|
|
52
|
+
"""
|
|
53
|
+
Interactively generate a TidyConfig.
|
|
54
|
+
Automatically includes any new boolean fields added to TidyConfig.
|
|
55
|
+
"""
|
|
56
|
+
print("\n" + "=" * 60)
|
|
57
|
+
print("TIDY CONFIGURATION GENERATOR")
|
|
58
|
+
print("=" * 60)
|
|
59
|
+
print("Configure SQL formatting rules for the 'tidy' command.\n")
|
|
60
|
+
|
|
61
|
+
config = {}
|
|
62
|
+
default_config = asdict(TidyConfig())
|
|
63
|
+
|
|
64
|
+
for field_name, default_value in default_config.items():
|
|
65
|
+
if not isinstance(default_value, bool):
|
|
66
|
+
continue # only prompt for boolean options
|
|
67
|
+
question = TIDY_DESCRIPTIONS.get(
|
|
68
|
+
field_name,
|
|
69
|
+
f"Enable {field_name.replace('_', ' ')}?"
|
|
70
|
+
)
|
|
71
|
+
config[field_name] = prompt_yes_no(question, default=default_value)
|
|
72
|
+
|
|
73
|
+
return config
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def generate_rewrite_config() -> Dict[str, Any]:
|
|
77
|
+
"""
|
|
78
|
+
Interactively generate a RewriteConfig.
|
|
79
|
+
Automatically includes any new boolean fields added to RewriteConfig.
|
|
80
|
+
"""
|
|
81
|
+
print("\n" + "=" * 60)
|
|
82
|
+
print("REWRITE CONFIGURATION GENERATOR")
|
|
83
|
+
print("=" * 60)
|
|
84
|
+
print("Configure SQL rewriting rules for the 'rewrite' command.\n")
|
|
85
|
+
|
|
86
|
+
config = {}
|
|
87
|
+
default_config = asdict(RewriteConfig())
|
|
88
|
+
|
|
89
|
+
for field_name, default_value in default_config.items():
|
|
90
|
+
if not isinstance(default_value, bool):
|
|
91
|
+
continue # only prompt for boolean options
|
|
92
|
+
question = REWRITE_DESCRIPTIONS.get(
|
|
93
|
+
field_name,
|
|
94
|
+
f"Enable {field_name.replace('_', ' ')}?"
|
|
95
|
+
)
|
|
96
|
+
config[field_name] = prompt_yes_no(question, default=default_value)
|
|
97
|
+
|
|
98
|
+
return config
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def select_config_type() -> str:
|
|
102
|
+
"""
|
|
103
|
+
Prompt user to select which config type to generate.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
str: "tidy" or "rewrite"
|
|
107
|
+
"""
|
|
108
|
+
print("\n" + "=" * 60)
|
|
109
|
+
print("SQLTIDY CONFIGURATION GENERATOR")
|
|
110
|
+
print("=" * 60)
|
|
111
|
+
print("\nSelect which configuration to generate:\n")
|
|
112
|
+
print("1. Tidy configuration")
|
|
113
|
+
print("2. Rewrite configuration")
|
|
114
|
+
|
|
115
|
+
while True:
|
|
116
|
+
choice = input("\nEnter your choice (1-2): ").strip()
|
|
117
|
+
if choice == "1":
|
|
118
|
+
return "tidy"
|
|
119
|
+
elif choice == "2":
|
|
120
|
+
return "rewrite"
|
|
121
|
+
print("Please enter 1 or 2")
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def get_output_filename(config_type: str) -> str:
|
|
125
|
+
"""
|
|
126
|
+
Prompt user for output filename.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
config_type: Type of config ("tidy" or "rewrite")
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
str: Output filename
|
|
133
|
+
"""
|
|
134
|
+
if config_type == "tidy":
|
|
135
|
+
default = "tidy_config.json"
|
|
136
|
+
else:
|
|
137
|
+
default = "rewrite_config.json"
|
|
138
|
+
|
|
139
|
+
filename = input(f"\nOutput filename [{default}]: ").strip()
|
|
140
|
+
return filename if filename else default
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def save_config(config_data: Dict[str, Any], filename: str) -> Path:
|
|
144
|
+
"""
|
|
145
|
+
Save configuration to a JSON file.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
config_data: Configuration dictionary
|
|
149
|
+
filename: Output filename
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
Path: Path to the saved file
|
|
153
|
+
"""
|
|
154
|
+
filepath = Path(filename)
|
|
155
|
+
|
|
156
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
157
|
+
json.dump(config_data, f, indent=2)
|
|
158
|
+
|
|
159
|
+
return filepath
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def run_generator():
|
|
163
|
+
"""
|
|
164
|
+
Run the interactive configuration generator.
|
|
165
|
+
"""
|
|
166
|
+
try:
|
|
167
|
+
config_type = select_config_type()
|
|
168
|
+
|
|
169
|
+
if config_type == "tidy":
|
|
170
|
+
config_data = generate_tidy_config()
|
|
171
|
+
else:
|
|
172
|
+
config_data = generate_rewrite_config()
|
|
173
|
+
|
|
174
|
+
# Get output filename
|
|
175
|
+
output_file = get_output_filename(config_type)
|
|
176
|
+
|
|
177
|
+
# Save config
|
|
178
|
+
filepath = save_config(config_data, output_file)
|
|
179
|
+
|
|
180
|
+
print("\n" + "=" * 60)
|
|
181
|
+
print("✓ Configuration saved successfully!")
|
|
182
|
+
print(f"File: {filepath.absolute()}")
|
|
183
|
+
print("=" * 60)
|
|
184
|
+
print("\nUsage:")
|
|
185
|
+
if config_type == "tidy":
|
|
186
|
+
print(f" sqltidy tidy -cfg {output_file} <input_file>")
|
|
187
|
+
else:
|
|
188
|
+
print(f" sqltidy rewrite -cfg {output_file} <input_file>")
|
|
189
|
+
print()
|
|
190
|
+
|
|
191
|
+
except KeyboardInterrupt:
|
|
192
|
+
print("\n\nConfiguration generation cancelled.")
|
|
193
|
+
return
|
|
194
|
+
except Exception as e:
|
|
195
|
+
print(f"\nError: {e}")
|
|
196
|
+
raise
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def load_config_file(filepath: str) -> Dict[str, Any]:
|
|
200
|
+
"""
|
|
201
|
+
Load configuration from a JSON file.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
filepath: Path to the configuration file
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
dict: Configuration data
|
|
208
|
+
"""
|
|
209
|
+
with open(filepath, "r", encoding="utf-8") as f:
|
|
210
|
+
return json.load(f)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
from ..config import TidyConfig
|
|
3
|
+
|
|
4
|
+
class FormatterContext:
|
|
5
|
+
"""Holds configuration for the formatting run."""
|
|
6
|
+
def __init__(self, config: TidyConfig):
|
|
7
|
+
self.config = config
|
|
8
|
+
|
|
9
|
+
class BaseRule:
|
|
10
|
+
"""All rules must inherit from this."""
|
|
11
|
+
order = 100
|
|
12
|
+
rule_type = None # "tidy" or "rewrite"
|
|
13
|
+
def apply(self, tokens: List[str], ctx: FormatterContext) -> List[str]:
|
|
14
|
+
raise NotImplementedError
|