Skip to content

Commit fb54a34

Browse files
committed
initial import
0 parents  commit fb54a34

16 files changed

+2889
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*.pyc
2+
*.cache
3+
*.sublime-project
4+
.DS_Store

FormatSQL.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import sublime
2+
import sublime_plugin
3+
import re
4+
from os.path import basename
5+
import sqlparse
6+
7+
class FormatSqlCommand(sublime_plugin.TextCommand):
8+
def run(self, edit):
9+
view = self.view
10+
regions = view.sel()
11+
# if there are more than 1 region or region one and it's not empty
12+
if len(regions) > 1 or not regions[0].empty():
13+
for region in view.sel():
14+
if not region.empty():
15+
s = view.substr(region)
16+
s = self._run(s)
17+
view.replace(edit, region, s)
18+
else: #format all text
19+
alltextreg = sublime.Region(0, view.size())
20+
s = view.substr(alltextreg)
21+
s = self._run(s)
22+
view.replace(edit, alltextreg, s)
23+
24+
def _run(self, s):
25+
settings = self.view.settings()
26+
#indent_char = " " if settings.get("translate_tabs_to_spaces") else "\t"
27+
indent_char = " " #TODO indent by TAB (currently not supported in python-sqlparse)
28+
indent_size = int(settings.get("tab_size")) if indent_char == " " else 1
29+
s = s.encode("utf-8")
30+
return sqlparse.format(s, keyword_case="upper", reindent=True, indent_width=indent_size)

LICENSE.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
python-sqlparse and this code is on 2-clauses BSD http://www.opensource.org/licenses/bsd-license.php

Main.sublime-menu

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[
2+
{
3+
"id": "selection",
4+
"caption": "Selection",
5+
"children":
6+
[
7+
{
8+
"id": "format",
9+
"caption": "Format",
10+
"children":
11+
[
12+
{
13+
"caption": "Format SQL Statement",
14+
"command": "format_sql"
15+
}
16+
]
17+
}
18+
]
19+
}
20+
]

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
## Summary
2+
3+
FormatSQL formats long SQL statement to a more readable form by using [python-sqlparse library](http://code.google.com/p/python-sqlparse/).
4+
5+
6+
## How to Use
7+
8+
select sql and click menu Selection -> Format -> SQL
9+
10+
11+
### Configure key binding
12+
13+
add the following line to keymap settings
14+
15+
{ "keys": ["super+k", "super+s"], "command": "format_sql" },
16+
17+
18+
## License
19+
20+
[python-sqlparse library](http://code.google.com/p/python-sqlparse/) and this code are both on [2-clauses BSD](http://www.opensource.org/licenses/bsd-license.php)

sqlparse/__init__.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Copyright (C) 2008 Andi Albrecht, albrecht.andi@gmail.com
2+
#
3+
# This module is part of python-sqlparse and is released under
4+
# the BSD License: http://www.opensource.org/licenses/bsd-license.php.
5+
6+
"""Parse SQL statements."""
7+
8+
9+
__version__ = '0.1.3'
10+
11+
12+
class SQLParseError(Exception):
13+
"""Base class for exceptions in this module."""
14+
15+
16+
# Setup namespace
17+
from sqlparse import engine
18+
from sqlparse import filters
19+
from sqlparse import formatter
20+
21+
22+
def parse(sql):
23+
"""Parse sql and return a list of statements.
24+
25+
*sql* is a single string containting one or more SQL statements.
26+
27+
Returns a tuple of :class:`~sqlparse.sql.Statement` instances.
28+
"""
29+
stack = engine.FilterStack()
30+
stack.full_analyze()
31+
return tuple(stack.run(sql))
32+
33+
34+
def format(sql, **options):
35+
"""Format *sql* according to *options*.
36+
37+
Available options are documented in :ref:`formatting`.
38+
39+
Returns the formatted SQL statement as string.
40+
"""
41+
stack = engine.FilterStack()
42+
options = formatter.validate_options(options)
43+
stack = formatter.build_filter_stack(stack, options)
44+
stack.postprocess.append(filters.SerializerUnicode())
45+
return ''.join(stack.run(sql))
46+
47+
48+
def split(sql):
49+
"""Split *sql* into single statements.
50+
51+
Returns a list of strings.
52+
"""
53+
stack = engine.FilterStack()
54+
stack.split_statements = True
55+
return [unicode(stmt) for stmt in stack.run(sql)]
56+
57+
58+
from sqlparse.engine.filter import StatementFilter
59+
def split2(stream):
60+
splitter = StatementFilter()
61+
return list(splitter.process(None, stream))

sqlparse/engine/__init__.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Copyright (C) 2008 Andi Albrecht, albrecht.andi@gmail.com
2+
#
3+
# This module is part of python-sqlparse and is released under
4+
# the BSD License: http://www.opensource.org/licenses/bsd-license.php.
5+
6+
"""filter"""
7+
8+
from sqlparse import lexer
9+
from sqlparse.engine import grouping
10+
from sqlparse.engine.filter import StatementFilter
11+
12+
# XXX remove this when cleanup is complete
13+
Filter = object
14+
15+
16+
class FilterStack(object):
17+
18+
def __init__(self):
19+
self.preprocess = []
20+
self.stmtprocess = []
21+
self.postprocess = []
22+
self.split_statements = False
23+
self._grouping = False
24+
25+
def _flatten(self, stream):
26+
for token in stream:
27+
if token.is_group():
28+
for t in self._flatten(token.tokens):
29+
yield t
30+
else:
31+
yield token
32+
33+
def enable_grouping(self):
34+
self._grouping = True
35+
36+
def full_analyze(self):
37+
self.enable_grouping()
38+
39+
def run(self, sql):
40+
stream = lexer.tokenize(sql)
41+
# Process token stream
42+
if self.preprocess:
43+
for filter_ in self.preprocess:
44+
stream = filter_.process(self, stream)
45+
46+
if (self.stmtprocess or self.postprocess or self.split_statements
47+
or self._grouping):
48+
splitter = StatementFilter()
49+
stream = splitter.process(self, stream)
50+
51+
if self._grouping:
52+
53+
def _group(stream):
54+
for stmt in stream:
55+
grouping.group(stmt)
56+
yield stmt
57+
stream = _group(stream)
58+
59+
if self.stmtprocess:
60+
61+
def _run1(stream):
62+
ret = []
63+
for stmt in stream:
64+
for filter_ in self.stmtprocess:
65+
filter_.process(self, stmt)
66+
ret.append(stmt)
67+
return ret
68+
stream = _run1(stream)
69+
70+
if self.postprocess:
71+
72+
def _run2(stream):
73+
for stmt in stream:
74+
stmt.tokens = list(self._flatten(stmt.tokens))
75+
for filter_ in self.postprocess:
76+
stmt = filter_.process(self, stmt)
77+
yield stmt
78+
stream = _run2(stream)
79+
80+
return stream

sqlparse/engine/filter.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# -*- coding: utf-8 -*-
2+
3+
from sqlparse.sql import Statement, Token
4+
from sqlparse import tokens as T
5+
6+
7+
class TokenFilter(object):
8+
9+
def __init__(self, **options):
10+
self.options = options
11+
12+
def process(self, stack, stream):
13+
"""Process token stream."""
14+
raise NotImplementedError
15+
16+
17+
class StatementFilter(TokenFilter):
18+
19+
def __init__(self):
20+
TokenFilter.__init__(self)
21+
self._in_declare = False
22+
self._in_dbldollar = False
23+
self._is_create = False
24+
self._begin_depth = 0
25+
26+
def _reset(self):
27+
self._in_declare = False
28+
self._in_dbldollar = False
29+
self._is_create = False
30+
self._begin_depth = 0
31+
32+
def _change_splitlevel(self, ttype, value):
33+
# PostgreSQL
34+
if (ttype == T.Name.Builtin
35+
and value.startswith('$') and value.endswith('$')):
36+
if self._in_dbldollar:
37+
self._in_dbldollar = False
38+
return -1
39+
else:
40+
self._in_dbldollar = True
41+
return 1
42+
elif self._in_dbldollar:
43+
return 0
44+
45+
# ANSI
46+
if ttype not in T.Keyword:
47+
return 0
48+
49+
unified = value.upper()
50+
51+
if unified == 'DECLARE' and self._is_create:
52+
self._in_declare = True
53+
return 1
54+
55+
if unified == 'BEGIN':
56+
self._begin_depth += 1
57+
if self._in_declare: # FIXME(andi): This makes no sense.
58+
return 0
59+
return 0
60+
61+
if unified == 'END':
62+
# Should this respect a preceeding BEGIN?
63+
# In CASE ... WHEN ... END this results in a split level -1.
64+
self._begin_depth = max(0, self._begin_depth - 1)
65+
return -1
66+
67+
if ttype is T.Keyword.DDL and unified.startswith('CREATE'):
68+
self._is_create = True
69+
return 0
70+
71+
if (unified in ('IF', 'FOR')
72+
and self._is_create and self._begin_depth > 0):
73+
return 1
74+
75+
# Default
76+
return 0
77+
78+
def process(self, stack, stream):
79+
splitlevel = 0
80+
stmt = None
81+
consume_ws = False
82+
stmt_tokens = []
83+
for ttype, value in stream:
84+
# Before appending the token
85+
if (consume_ws and ttype is not T.Whitespace
86+
and ttype is not T.Comment.Single):
87+
consume_ws = False
88+
stmt.tokens = stmt_tokens
89+
yield stmt
90+
self._reset()
91+
stmt = None
92+
splitlevel = 0
93+
if stmt is None:
94+
stmt = Statement()
95+
stmt_tokens = []
96+
splitlevel += self._change_splitlevel(ttype, value)
97+
# Append the token
98+
stmt_tokens.append(Token(ttype, value))
99+
# After appending the token
100+
if (splitlevel <= 0 and ttype is T.Punctuation
101+
and value == ';'):
102+
consume_ws = True
103+
if stmt is not None:
104+
stmt.tokens = stmt_tokens
105+
yield stmt

0 commit comments

Comments
 (0)