Skip to content

Commit 87dd7e4

Browse files
committed
* initial commit
1 parent 5d450c1 commit 87dd7e4

File tree

8 files changed

+1149
-0
lines changed

8 files changed

+1149
-0
lines changed

gen_grammar.py

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
from dataclasses import dataclass
2+
import argparse
3+
import json
4+
import pathlib
5+
6+
7+
@dataclass
8+
class Spec:
9+
lang: str
10+
trigger: str
11+
source: str
12+
13+
14+
REPOSITORY = {
15+
"f-escape": {
16+
"name": "constant.character.escape.python",
17+
"match": r"(\{\{|\}\})",
18+
},
19+
"f-expression": {
20+
"begin": r"(\{)",
21+
# "end": r"(\})",
22+
"end": r"""(?x)
23+
(
24+
(?:
25+
(?: =?)
26+
(?: ![rsa])?
27+
)
28+
(?:
29+
:
30+
\w?
31+
[<>=^]?
32+
[-+]?
33+
\#?
34+
\d*
35+
,?
36+
(?: \.\d+)?
37+
[bcdeEfFgGnosxX%]?
38+
)
39+
)?
40+
(\})""",
41+
"name": "meta.embedded.inline.f-expression.python",
42+
"beginCaptures": {
43+
"1": {"name": "constant.character.format.placeholder.other.python"},
44+
},
45+
"endCaptures": {
46+
"1": {"name": "storage.type.format.python"},
47+
"2": {"name": "constant.character.format.placeholder.other.python"},
48+
},
49+
"patterns": [{"include": "source.python#f-expression"}],
50+
},
51+
}
52+
53+
54+
SPECS: list[Spec] = [
55+
Spec("sql", "sql", "source.sql.inline"),
56+
Spec("html", "html", "text.html.basic"),
57+
Spec("css", "css", "source.css"),
58+
Spec("js", "js", "source.js"),
59+
]
60+
61+
62+
def scopes(*s: str):
63+
return " ".join(s[::-1])
64+
65+
66+
def make_lang(spec: Spec):
67+
return {
68+
"begin": rf"""(?x)
69+
(?<!def\s+)
70+
\b
71+
({ spec.trigger })
72+
\s*
73+
(\()""",
74+
"end": r"""(?x)
75+
(\))
76+
""",
77+
"name": scopes(
78+
"meta.function-call.python",
79+
),
80+
"contentName": scopes(
81+
"meta.function-call.arguments.python",
82+
),
83+
"beginCaptures": {
84+
2: {
85+
"name": scopes(
86+
"punctuation.definition.arguments.begin.python",
87+
)
88+
},
89+
},
90+
"endCaptures": {
91+
1: {
92+
"name": scopes(
93+
"punctuation.definition.arguments.end.python",
94+
)
95+
},
96+
},
97+
"patterns": [
98+
{
99+
"begin": rf"([r|R]?f|F)('''|\"\"\"|'|\")(\\$)?",
100+
"end": r"(\2)",
101+
"name": scopes(
102+
"string.quoted.python",
103+
"string.interpolated.python",
104+
"meta.fstring.python",
105+
),
106+
"contentName": scopes(
107+
f"meta.embedded.inline.{ spec.lang }",
108+
),
109+
"beginCaptures": {
110+
1: {
111+
"name": scopes(
112+
"storage.type.string.python",
113+
)
114+
},
115+
2: {
116+
"name": scopes(
117+
"punctuation.definition.string.begin.python",
118+
)
119+
},
120+
3: {
121+
"name": scopes(
122+
"constant.language.python",
123+
)
124+
},
125+
},
126+
"endCaptures": {
127+
1: {
128+
"name": scopes(
129+
"punctuation.definition.string.end.python",
130+
)
131+
},
132+
},
133+
"patterns": [{"include": "#f-escape"}, {"include": "#f-expression"}, {"include": spec.source}],
134+
},
135+
{
136+
"begin": rf"(r|R)?('''|\"\"\"|'|\")(\\$)?",
137+
"end": r"(\2)",
138+
"name": scopes(
139+
"string.quoted.python",
140+
),
141+
"contentName": scopes(
142+
f"meta.embedded.inline.{ spec.lang }",
143+
),
144+
"beginCaptures": {
145+
1: {
146+
"name": scopes(
147+
"storage.type.string.python",
148+
)
149+
},
150+
2: {
151+
"name": scopes(
152+
"punctuation.definition.string.begin.python",
153+
)
154+
},
155+
3: {
156+
"name": scopes(
157+
"constant.language.python",
158+
)
159+
},
160+
},
161+
"endCaptures": {
162+
1: {
163+
"name": scopes(
164+
"punctuation.definition.string.end.python",
165+
)
166+
},
167+
},
168+
"patterns": [{"include": spec.source}],
169+
},
170+
],
171+
}
172+
173+
174+
def main():
175+
parser = argparse.ArgumentParser()
176+
parser.add_argument(
177+
"--output", help="generated grammar", type=pathlib.Path, default=pathlib.Path("./syntaxes/embed-in-python.json")
178+
)
179+
180+
args = parser.parse_args()
181+
tgt: pathlib.Path = args.output
182+
183+
grammar = {
184+
"comment": "This file is autogenerated. Run grammar.py to recreate it.",
185+
"scopeName": "embed-in-python",
186+
"fileTypes": [],
187+
"injectionSelector": "L:source -comment -(string -meta.embedded)",
188+
"patterns": [make_lang(spec) for spec in SPECS],
189+
"repository": REPOSITORY,
190+
}
191+
192+
with tgt.open("w") as f:
193+
json.dump(grammar, f, indent=4)
194+
195+
196+
main()

icon.png

237 Bytes
Loading

inline-sql-configuration.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"comments": {
3+
"lineComment": "--",
4+
"blockComment": ["/*", "*/"]
5+
}
6+
}

package.json

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
{
2+
"name": "vscode-python-fstring-dsl",
3+
"publisher": "jkmnt",
4+
"engines": {
5+
"vscode": "^1.22.0"
6+
},
7+
"license": "MIT",
8+
"displayName": "Highlight f-strings",
9+
"icon": "icon.png",
10+
"description": "Python - highlight HTML, SQL, JS, CSS in f-strings",
11+
"categories": [
12+
"Programming Languages",
13+
"Themes"
14+
],
15+
"keywords": [
16+
"f-strings",
17+
"fstrings",
18+
"python",
19+
"template",
20+
"literal",
21+
"strings",
22+
"syntax",
23+
"highlighting",
24+
"html",
25+
"sql"
26+
],
27+
"contributes": {
28+
"languages": [
29+
{
30+
"id": "inlinesql",
31+
"configuration": "./inline-sql.configuration.json"
32+
}
33+
],
34+
"grammars": [
35+
{
36+
"language": "inlinesql",
37+
"scopeName": "source.sql.inline",
38+
"path": "./syntaxes/sql.tmLanguage.json"
39+
},
40+
{
41+
"injectTo": [
42+
"source.python"
43+
],
44+
"scopeName": "embed-in-python",
45+
"path": "./syntaxes/embed-in-python.json",
46+
"embeddedLanguages": {
47+
"meta.embedded.inline.sql": "sql",
48+
"meta.embedded.inline.html": "html",
49+
"meta.embedded.inline.css": "css",
50+
"meta.embedded.inline.js": "javascript"
51+
}
52+
},
53+
{
54+
"injectTo": [
55+
"source.python"
56+
],
57+
"scopeName": "embed-in-embedded-html",
58+
"path": "./syntaxes/embed-in-embedded-html.json",
59+
"embeddedLanguages": {
60+
"meta.embedded.inline.f-expression.python": "python"
61+
}
62+
},
63+
{
64+
"injectTo": [
65+
"source.python"
66+
],
67+
"scopeName": "embed-in-embedded-sql",
68+
"path": "./syntaxes/embed-in-embedded-sql.json",
69+
"embeddedLanguages": {
70+
"meta.embedded.inline.f-expression.python": "python"
71+
}
72+
}
73+
]
74+
},
75+
"repository": {
76+
"type": "git",
77+
"url": "https://github.com/jkmnt/vscode-python-fstring-dsl"
78+
}
79+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"scopeName": "embed-in-embedded-html",
3+
"fileTypes": [],
4+
"injectionSelector": "L:meta.fstring.python meta.embedded.inline.html meta.tag",
5+
"patterns": [
6+
{
7+
"include": "embed-in-python#f-escape"
8+
},
9+
{
10+
"include": "embed-in-python#f-expression"
11+
}
12+
]
13+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"scopeName": "embed-in-embedded-sql",
3+
"fileTypes": [],
4+
"injectionSelector": "L:meta.fstring.python meta.embedded.inline.sql string.quoted.single.sql, L:meta.fstring.python meta.embedded.inline.sql string.quoted.double.sql",
5+
"patterns": [
6+
{
7+
"include": "embed-in-python#f-escape"
8+
},
9+
{
10+
"include": "embed-in-python#f-expression"
11+
}
12+
]
13+
}

0 commit comments

Comments
 (0)