Skip to content

Commit 01634dd

Browse files
committed
[SQL] Add default SQL syntax selector command
This commit adds a naive command for users to choose default SQL dialect to be assigned to `source.sql`. The `sql_set_default_syntax` basically works for any syntax, but is prefixed `sql_` as it is located in and dedicated to SQL syntax package. Alternatively a general purpose `set_default_syntax` could be implemented by ST core or Default package. Ideally, dialect syntaxes could be identified by attributes in `Syntax()` class, but until this becomes true, rely on a static list of files specified via `dialect_file` argument to choose from.
1 parent a26c02c commit 01634dd

File tree

3 files changed

+136
-0
lines changed

3 files changed

+136
-0
lines changed

SQL/.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.8

SQL/Default.sublime-commands

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[
2+
{
3+
"caption": "SQL: Set Default Syntax",
4+
"command": "sql_set_default_syntax",
5+
"args": {
6+
"syntax_file": "Packages/SQL/SQL.sublime-syntax",
7+
"dialect_file": [
8+
"Packages/SQL/Cassandra.sublime-syntax",
9+
"Packages/SQL/MySQL.sublime-syntax",
10+
"Packages/SQL/PostgreSQL.sublime-syntax",
11+
"Packages/SQL/TSQL.sublime-syntax",
12+
]
13+
}
14+
}
15+
]

SQL/default_syntax.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
from __future__ import annotations
2+
import re
3+
4+
import sublime
5+
import sublime_plugin
6+
7+
from pathlib import Path
8+
9+
10+
class DialectFileInputHandler(sublime_plugin.ListInputHandler):
11+
def __init__(self, default_syntax: sublime.Syntax, dialect_files: list[str]):
12+
super().__init__()
13+
self.default_syntax = default_syntax
14+
self.dialect_files = dialect_files
15+
16+
def placeholder(self) -> str:
17+
return "Choose the dialect to use as default syntax."
18+
19+
def list_items(self) -> list[sublime.ListInputItem]:
20+
match = re.search(
21+
r"^extends:\s+(?:-\s+)?(Packages/.+)",
22+
sublime.load_resource(self.default_syntax.path),
23+
flags=re.MULTILINE,
24+
)
25+
selected_syntax = sublime.syntax_from_path(match.group(1)) if match else None
26+
selected_kind = (sublime.KindId.AMBIGUOUS, "✓", "Selected")
27+
28+
items = []
29+
for dialect_file in self.dialect_files:
30+
syntax = sublime.syntax_from_path(dialect_file)
31+
if not syntax:
32+
continue
33+
if syntax == self.default_syntax:
34+
continue
35+
items.append(
36+
sublime.ListInputItem(
37+
text=syntax.name,
38+
value=syntax.path,
39+
kind=selected_kind
40+
if syntax == selected_syntax
41+
else sublime.KIND_AMBIGUOUS,
42+
)
43+
)
44+
return items
45+
46+
47+
class SqlSetDefaultSyntaxCommand(sublime_plugin.WindowCommand):
48+
"""
49+
This class implements the `sql_set_default_syntax` command.
50+
51+
This command manipulates a syntax file (e.g.: SQL.sublime-syntax) to make
52+
it point to a desired dialect (e.g.: MySQL.sublime-syntax). As a result
53+
the dialect is used whenever the default syntax's scope (e.g.: `source.sql`)
54+
is addressed. This way it is possible to specify the syntax to use in
55+
embedded code blocks.
56+
57+
Usage:
58+
======
59+
60+
.. code-block:: json
61+
{
62+
"caption": "SQL: Set Default Syntax",
63+
"command": "sql_set_default_syntax",
64+
"args": {
65+
"syntax_file": "Packages/SQL/SQL.sublime-syntax",
66+
"dialect_file": [
67+
"Packages/SQL/Cassandra.sublime-syntax",
68+
"Packages/SQL/MySQL.sublime-syntax",
69+
"Packages/SQL/PostgreSQL.sublime-syntax",
70+
"Packages/SQL/TSQL.sublime-syntax"
71+
]
72+
}
73+
}
74+
"""
75+
76+
def input(self, args: dict[str, str]) -> DialectFileInputHandler | None:
77+
dialect_files = args.get("dialect_file")
78+
if isinstance(dialect_files, list):
79+
syntax_file = sublime.syntax_from_path(args.get("syntax_file", ""))
80+
if syntax_file:
81+
return DialectFileInputHandler(syntax_file, dialect_files)
82+
return None
83+
84+
def input_description(self) -> str:
85+
return "Syntax:"
86+
87+
def run(self, syntax_file: str, dialect_file: str) -> None:
88+
# validate target syntax
89+
default_syntax = sublime.syntax_from_path(syntax_file)
90+
if not default_syntax:
91+
sublime.error_message(f'Error: "{syntax_file}" is no valid target syntax!')
92+
return
93+
94+
# validate dialect syntax
95+
dialect_syntax = sublime.syntax_from_path(dialect_file)
96+
if not dialect_syntax:
97+
sublime.error_message(f'Error: "{dialect_file}" is no valid syntax!')
98+
return
99+
100+
# open existing target syntax and replace `extends:` value with dialect syntax
101+
old_content = sublime.load_resource(default_syntax.path)
102+
new_content = re.sub(
103+
r"(^extends:\s+(?:-\s+)?)Packages/.+",
104+
rf"\1{dialect_syntax.path}",
105+
old_content,
106+
flags=re.MULTILINE,
107+
)
108+
# don't touch syntax file if content is unchanged in order to
109+
# avoid re-indexing all open files and folders
110+
if old_content == new_content:
111+
return
112+
113+
# write modified default syntax to extracted Packages path
114+
with open(
115+
file=Path(sublime.packages_path()).parent / default_syntax.path,
116+
mode="w",
117+
encoding="utf-8",
118+
newline="\n",
119+
) as out:
120+
out.write(new_content)

0 commit comments

Comments
 (0)