Skip to content

Commit dc2e3dc

Browse files
committed
[#32] Add f-string interpolation support
1 parent 057beed commit dc2e3dc

2 files changed

Lines changed: 203 additions & 0 deletions

File tree

Syntax/Just.sublime-syntax

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,16 @@ contexts:
333333
###[ CHARACTERS ]##############################################################
334334

335335
all-strings:
336+
- include: format-strings
336337
- include: quoted-strings
337338
- include: backtick-strings
338339

340+
format-strings:
341+
- include: format-single-quote-block-strings
342+
- include: format-single-quote-strings
343+
- include: format-double-quote-block-strings
344+
- include: format-double-quote-strings
345+
339346
quoted-strings:
340347
- include: single-quote-block-strings
341348
- include: single-quote-strings
@@ -347,6 +354,71 @@ contexts:
347354
- include: backtick-quote-strings
348355

349356

357+
format-double-quote-block-strings:
358+
- match: 'f"""'
359+
scope: punctuation.definition.string.begin.just
360+
push:
361+
- meta_scope: meta.string.just string.quoted.double.block.just
362+
- meta_include_prototype: false
363+
- match: \\.
364+
scope: constant.character.escape.just
365+
- match: '"""'
366+
scope: punctuation.definition.string.end.just
367+
pop: 1
368+
- include: format-string-interpolations
369+
370+
format-double-quote-strings:
371+
- match: 'f"'
372+
scope: punctuation.definition.string.begin.just
373+
push:
374+
- meta_scope: meta.string.just string.quoted.double.just
375+
- meta_include_prototype: false
376+
- match: \\.
377+
scope: constant.character.escape.just
378+
- match: '"'
379+
scope: punctuation.definition.string.end.just
380+
pop: 1
381+
- include: format-string-interpolations
382+
383+
format-single-quote-block-strings:
384+
- match: "f'''"
385+
scope: punctuation.definition.string.begin.just
386+
push:
387+
- meta_scope: meta.string.just string.quoted.single.block.just
388+
- meta_include_prototype: false
389+
- match: "'''"
390+
scope: punctuation.definition.string.end.just
391+
pop: 1
392+
- include: format-string-interpolations
393+
394+
format-single-quote-strings:
395+
- match: "f'"
396+
scope: punctuation.definition.string.begin.just
397+
push:
398+
- meta_scope: meta.string.just string.quoted.single.just
399+
- meta_include_prototype: false
400+
- match: "'"
401+
scope: punctuation.definition.string.end.just
402+
pop: 1
403+
- include: format-string-interpolations
404+
405+
format-string-interpolations:
406+
- match: \{\{\{\{
407+
scope: constant.character.escape.just
408+
comment: Escaped double brace - literal {{ in output
409+
- match: \{\{(?!\{)
410+
scope: punctuation.section.interpolation.begin.just
411+
push: format-string-interpolation-body
412+
413+
format-string-interpolation-body:
414+
- clear_scopes: 1
415+
- meta_scope: meta.interpolation.just
416+
- match: \}\}
417+
scope: punctuation.section.interpolation.end.just
418+
pop: 1
419+
- include: just-expressions
420+
421+
350422
backtick-quote-block-strings:
351423
- match: '```'
352424
scope: punctuation.section.interpolation.begin.just

Syntax/tests/syntax_test_just.quotes.just

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,134 @@ test
7676
"""):
7777
# <- string.quoted.double.block.just punctuation.definition.string.end.just
7878
#^^ string.quoted.double.block.just punctuation.definition.string.end.just
79+
80+
81+
82+
######
83+
# f-strings
84+
######
85+
name := "Yorke"
86+
87+
# Basic interpolation
88+
greeting := f'Hello, {{name}}!'
89+
# ^^ punctuation.section.interpolation.begin.just
90+
# ^^^^ variable.other.just
91+
# ^^ punctuation.section.interpolation.end.just
92+
93+
greeting := f"Hello, {{name}}!"
94+
# ^^ punctuation.section.interpolation.begin.just
95+
# ^^^^ variable.other.just
96+
# ^^ punctuation.section.interpolation.end.just
97+
98+
# Escaped brace produces literal {{ — no interpolation
99+
literal_braces := f'I {{{{love}}}} curly braces'
100+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.string.just string.quoted.single.just
101+
# ^^punctuation.definition.string.begin.just
102+
# ^^^^ constant.character.escape.just
103+
# ^ punctuation.definition.string.end.just
104+
105+
# Escaped brace immediately before a real interpolation
106+
tricky := f'{{{{{{name}}}}}}'
107+
# ^^^^^^^^^^^^^^^^^^^ meta.string.just
108+
# ^^string.quoted.single.just punctuation.definition.string.begin.just
109+
# ^^^^string.quoted.single.just constant.character.escape.just
110+
# ^^ punctuation.section.interpolation.begin.just
111+
# ^^^^ variable.other.just
112+
# ^^ punctuation.section.interpolation.end.just
113+
# ^^^^string.quoted.single.just
114+
# ^string.quoted.single.just punctuation.definition.string.end.just
115+
116+
# Empty interpolation (degenerate case)
117+
empty := f'before {{}} after'
118+
# ^^ punctuation.section.interpolation.begin.just
119+
# ^^ punctuation.section.interpolation.end.just
120+
# ^^^^^ string.quoted.single.just
121+
122+
# Interpolation with a function call
123+
home := f'Home is {{env("HOME")}}'
124+
125+
# Concatenation of format string with plain string
126+
path := f'{{name}}' + '/suffix'
127+
# ^^ string.quoted.single.just punctuation.definition.string.begin.just
128+
# ^^ punctuation.section.interpolation.begin.just
129+
# ^^^^ variable.other.just
130+
# ^^ punctuation.section.interpolation.end.just
131+
# ^ string.quoted.single.just punctuation.definition.string.end.just
132+
# ^ keyword.operator.arithmetic.just
133+
# ^ string.quoted.single.just punctuation.definition.string.begin.just
134+
# ^^^^^^^ string.quoted.single.just
135+
# ^ string.quoted.single.just punctuation.definition.string.end.just
136+
137+
# Nested quotes: double-quote chars inside single-quoted f-string
138+
msg := f'He said "{{greeting}}" loudly'
139+
# ^^ string.quoted.single.just punctuation.definition.string.begin.just
140+
# ^^^^^^^^^ string.quoted.single.just
141+
# ^^ punctuation.section.interpolation.begin.just
142+
# ^^^^^^^^ variable.other.just
143+
# ^^ punctuation.section.interpolation.end.just
144+
# ^^^^^^^^ string.quoted.single.just
145+
# ^ string.quoted.single.just punctuation.definition.string.end.just
146+
147+
# Escape sequences are NOT processed in single-quoted f-strings
148+
raw := f'\t\n{{name}}\r'
149+
# ^^ string.quoted.single.just punctuation.definition.string.begin.just
150+
# ^^^^ string.quoted.single.just
151+
# ^^ string.quoted.single.just
152+
# ^ string.quoted.single.just punctuation.definition.string.end.just
153+
154+
# Escape sequences ARE processed in double-quoted f-strings
155+
escaped := f"\t\n{{name}}\r"
156+
# ^^ punctuation.definition.string.begin.just
157+
# ^^^^ string.quoted.double.just constant.character.escape.just
158+
# ^^ punctuation.section.interpolation.begin.just
159+
# ^^^^ variable.other.just
160+
# ^^ punctuation.section.interpolation.end.just
161+
# ^^ string.quoted.double.just constant.character.escape.just
162+
# ^ string.quoted.double.just punctuation.definition.string.end.just
163+
164+
# Multiple interpolations in one string
165+
full_name := f'{{first}} {{last}}'
166+
# ^^^^^^^^^ meta.string.just meta.interpolation.just
167+
# ^^^^^^^^ meta.string.just meta.interpolation.just
168+
169+
# Block single-quoted: no escape processing
170+
block_single := f'''line one\nstill line one\n{{name}}'''
171+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.string.just
172+
# ^^^^ punctuation.definition.string.begin.just
173+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^ string.quoted.single.block.just - constant.character.escape.just
174+
# ^^ punctuation.section.interpolation.begin.just
175+
# ^^^^ variable.other.just
176+
# ^^ punctuation.section.interpolation.end.just
177+
# ^^^ punctuation.definition.string.end.just
178+
179+
# Block double-quoted: escape processing active
180+
block_double := f"""line one\nline two\n{{name}}"""
181+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.string.just
182+
# ^^^^ punctuation.definition.string.begin.just
183+
# ^^^^^^^^^^^^^^^^^^^^ string.quoted.double.block.just
184+
# ^^ constant.character.escape.just
185+
# ^^ constant.character.escape.just
186+
# ^^ punctuation.section.interpolation.begin.just
187+
# ^^^^ variable.other.just
188+
# ^^ punctuation.section.interpolation.end.just
189+
# ^^^ punctuation.definition.string.end.just
190+
191+
# Interpolation at the very start and end of string
192+
bookended := f'{{open}}middle{{close}}'
193+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.string.just
194+
# ^^ punctuation.section.interpolation.begin.just
195+
# ^^^^ variable.other.just
196+
# ^^ punctuation.section.interpolation.end.just
197+
# ^^^^^^ string.quoted.single.just
198+
# ^^ punctuation.section.interpolation.begin.just
199+
# ^^^^^ variable.other.just
200+
# ^^ punctuation.section.interpolation.end.just
201+
202+
# Only escaped braces, no real interpolations
203+
only_escaped := f'{{{{}}}}{{{{}}}}{{{{}}}}{{{{}}}}{{{{}}}}'
204+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.string.just string.quoted.single.just
205+
# ^^^^ constant.character.escape.just
206+
# ^^^^constant.character.escape.just
207+
# ^^^^ constant.character.escape.just
208+
# ^^^^ constant.character.escape.just
209+
# ^^^^ constant.character.escape.just

0 commit comments

Comments
 (0)