44import json
55import re
66import yaml
7- from yaml .representer import SafeRepresenter
87
9- CompletionItem : dict [str , str | list [str ]]
10- CompletionMetadata : dict [str , str | list [str ]]
11- CompletionSet : dict [str , CompletionItem | CompletionMetadata ]
12- CryptoSet : dict [str , str | list [str ]]
8+ from typing import Callable , Literal , NotRequired , TypedDict
9+ from yaml .representer import SafeRepresenter
1310
1411SYNTAX_STEM = 'Packages/SSH Config/syntax/'
1512TEST_STEM = '../test/generated/syntax_test_'
1613SUPPORT_STEM = '../support/generated/'
1714
1815
16+ class CompletionMetadata (TypedDict ):
17+ scope : str
18+ kind : list [str ]
19+ annotation : str
20+
21+
22+ class CompletionItem (TypedDict ):
23+ kind : list [str ] | Literal ['snippet' ]
24+ annotation : str
25+ trigger : str
26+ contents : str
27+ details : NotRequired [str ]
28+
29+
30+ class CompletionFillins (TypedDict ):
31+ details : str
32+ values : str | list [str ]
33+
34+
35+ class CompletionSet (TypedDict ):
36+ completions : CompletionMetadata
37+ items : dict [str ,CompletionFillins ]
38+
39+
40+ class CompletionYaml (TypedDict ):
41+ scope : str
42+ completions : list [CompletionItem ]
43+
44+
45+ class CryptoFillins (TypedDict ):
46+ scope : str
47+ items : list [str ]
48+
49+
50+ class CryptoSet (TypedDict ):
51+ completions : CompletionMetadata
52+ active : CryptoFillins
53+ deprecated : CryptoFillins
54+
55+
56+ class SyntaxContext (TypedDict ):
57+ include : NotRequired [str ]
58+ match : NotRequired [str ]
59+ scope : NotRequired [str ]
60+ captures : NotRequired [dict [int ,str ]]
61+ pop : NotRequired [bool | int ]
62+ push : NotRequired [str | list [str ] | list [SyntaxContext ]]
63+
64+
65+ class SyntaxYaml (TypedDict ):
66+ scope : str
67+ name : NotRequired [str ]
68+ hidden : NotRequired [bool ]
69+ file_extensions : NotRequired [list [str ]]
70+ hidden_file_extensions : NotRequired [list [str ]]
71+ extends : NotRequired [str | list [str ]]
72+ version : NotRequired [int ]
73+ variables : NotRequired [dict [str , str ]]
74+ contexts : dict [str , list [SyntaxContext ]]
75+
76+
1977def build_ssh_options ():
2078 with open ('options.yaml' , 'r' ) as stream :
21- ssh_options_input : dict [str , CompletionSet ] = yaml .load (
79+ ssh_options_input : dict [str , CompletionSet ] = yaml .load ( # pyright: ignore[reportAssignmentType]
2280 stream , Loader = yaml .BaseLoader )
2381
2482 for domain , settings in ssh_options_input .items ():
25- completions = {
83+ completions_yaml : CompletionYaml = {
2684 'scope' : settings ['completions' ]['scope' ],
2785 'completions' : [],
2886 }
87+ completions : list [CompletionItem ] = []
2988 snippet_spacer : str = settings ['completions' ].get ('snippet_spacer' , ' ' )
3089 default_kind : list [str ] = settings ['completions' ]['kind' ]
3190 annotation : str = settings ['completions' ]['annotation' ]
3291
3392 for keyword , options in settings ['items' ].items ():
3493 details : str = options .get ('details' , '' ) if options else ''
35- _ = completions [ 'completions' ] .append ({
94+ _ = completions .append ({
3695 'trigger' : keyword ,
3796 'contents' : keyword ,
3897 'annotation' : annotation ,
@@ -47,21 +106,23 @@ def build_ssh_options():
47106 value_string = values
48107 else :
49108 value_string = f'${{0:{{ { " | " .join (values )} \\ }}}}'
50- _ = completions [ 'completions' ] .append ({
109+ _ = completions .append ({
51110 'trigger' : keyword .lower (),
52111 'contents' : f'{ keyword } { snippet_spacer } { value_string } ' ,
53112 'annotation' : annotation ,
54113 'kind' : 'snippet' ,
55114 'details' : details ,
56115 })
57116
117+ completions_yaml ['completions' ] = completions
118+
58119 with open (f'{ SUPPORT_STEM } { domain } .sublime-completions' , 'w' ) as f :
59- json .dump (completions , f , indent = 4 )
120+ json .dump (completions_yaml , f , indent = 4 )
60121
61122
62123def build_sshd_index_test ():
63124 with open ('options.yaml' , 'r' ) as stream :
64- ssh_options_input : dict [str , CompletionSet ] = yaml .load (
125+ ssh_options_input : dict [str , CompletionSet ] = yaml .load ( # pyright: ignore[reportAssignmentType]
65126 stream , Loader = yaml .BaseLoader )
66127
67128 test_content = [
@@ -91,8 +152,9 @@ def build_crypto():
91152 # Set up YAML dump style
92153 class literal_str (str ): pass
93154
94- def change_style (style , representer ):
95- def new_representer (dumper , data ):
155+ def change_style (style : Literal ['"' , '|' , '>' ],
156+ representer : Callable ['...' , yaml .ScalarNode ]):
157+ def new_representer (dumper : yaml .Dumper , data : str ) -> yaml .ScalarNode :
96158 scalar = representer (dumper , data )
97159 scalar .style = style
98160 return scalar
@@ -103,12 +165,13 @@ def new_representer(dumper, data):
103165
104166 # Process
105167 with open ('crypto.yaml' , 'r' ) as stream :
106- crypto_input = yaml .load (stream , Loader = yaml .BaseLoader )
168+ crypto_input : dict [str , CryptoSet ] = yaml .load ( # pyright: ignore[reportAssignmentType]
169+ stream , Loader = yaml .BaseLoader )
107170
108- test_content = [
171+ test_content : list [ str ] = [
109172 f'# SYNTAX TEST "{ SYNTAX_STEM } SSH Crypto.sublime-syntax"\n ' ,
110173 ]
111- syntax_content = {
174+ syntax_content : SyntaxYaml = {
112175 'name' : 'SSH Crypto' ,
113176 'hidden' : True ,
114177 'scope' : 'text.ssh.crypto' ,
@@ -117,19 +180,22 @@ def new_representer(dumper, data):
117180 'hidden_file_extensions' : [
118181 'syntax_test_crypto' ,
119182 ],
120- 'contexts' : {
121- 'main' : [
122- {'include' : 'comments' },
123- ],
124- },
183+ 'contexts' : {},
125184 'variables' : {},
126185 }
186+ syntax_contexts : dict [str , list [SyntaxContext ]] = {
187+ 'main' : [
188+ {'include' : 'comments' },
189+ ],
190+ }
191+ syntax_variables : dict [str , str ] = {}
127192
128193 for domain , settings in crypto_input .items ():
129- completions = {
194+ completions_yaml : CompletionYaml = {
130195 'scope' : settings ['completions' ]['scope' ].strip (),
131196 'completions' : [],
132197 }
198+ completions : list [CompletionItem ] = []
133199 default_kind : list [str ] = settings ['completions' ]['kind' ]
134200 annotation : str = settings ['completions' ]['annotation' ]
135201 active_scope : str = settings ['active' ]['scope' ]
@@ -138,14 +204,14 @@ def new_representer(dumper, data):
138204 domain_ = domain .replace ('-' , '_' )
139205
140206 test_content .append (f'\n ###[ { domain + " ]" :#<74} \n ' )
141- _ = syntax_content [ 'contexts' ] ['main' ].append ({
207+ _ = syntax_contexts ['main' ].append ({
142208 'match' : fr'^{ annotation } :' ,
143209 'push' : [
144210 {'include' : 'pop-before-nl' },
145211 {'include' : f'ssh-{ domain } ' }
146212 ]
147213 })
148- syntax_content [ 'contexts' ] [f'ssh-{ domain } ' ] = [
214+ syntax_contexts [f'ssh-{ domain } ' ] = [
149215 {
150216 'match' : fr'\b{{{{{ domain_ } _active}}}}(?=[,\s\"])' ,
151217 'scope' : active_scope ,
@@ -155,21 +221,21 @@ def new_representer(dumper, data):
155221 'scope' : deprec_scope ,
156222 },
157223 ]
158- syntax_content [ 'variables' ] [f'{ domain_ } _active' ] = literal_str (
224+ syntax_variables [f'{ domain_ } _active' ] = literal_str (
159225 fr"""(?x:{ '\n ' } { '\n | ' .join (
160226 re .escape (i ) for i in
161227 sorted (settings ['active' ]['items' ], reverse = True )
162228 )} { '\n ' } )"""
163229 )
164- syntax_content [ 'variables' ] [f'{ domain_ } _deprec' ] = literal_str (
230+ syntax_variables [f'{ domain_ } _deprec' ] = literal_str (
165231 fr"""(?x:{ '\n ' } { '\n | ' .join (
166232 re .escape (i ) for i in
167233 sorted (settings ['deprecated' ]['items' ], reverse = True )
168234 )} { '\n ' } )"""
169235 )
170236
171237 for item in settings ['active' ]['items' ]:
172- _ = completions [ 'completions' ] .append ({
238+ _ = completions .append ({
173239 'trigger' : item ,
174240 'contents' : item ,
175241 'annotation' : annotation ,
@@ -180,7 +246,7 @@ def new_representer(dumper, data):
180246 f'#{ " " * len (annotation )} { "^" * len (item )} { active_scope } ' )
181247
182248 for item in settings ['deprecated' ]['items' ]:
183- _ = completions [ 'completions' ] .append ({
249+ _ = completions .append ({
184250 'trigger' : item ,
185251 'contents' : item ,
186252 'annotation' : f'deprecated { annotation } ' ,
@@ -191,12 +257,17 @@ def new_representer(dumper, data):
191257 test_content .append (
192258 f'#{ " " * len (annotation )} { "^" * len (item )} { deprec_scope } ' )
193259
260+ completions_yaml ['completions' ] = completions
261+
194262 with open (f'{ SUPPORT_STEM } { domain } .sublime-completions' , 'w' ) as f :
195- json .dump (completions , f , indent = 4 )
263+ json .dump (completions_yaml , f , indent = 4 )
264+
265+ syntax_content ['contexts' ] = syntax_contexts
266+ syntax_content ['variables' ] = syntax_variables
196267
197268 with open ('../syntax/SSH Crypto.sublime-syntax' , 'w' ) as syntax_file :
198269 _ = syntax_file .write ('%YAML 1.2\n ---\n ' )
199- yaml .dump (syntax_content , syntax_file )
270+ _ = yaml .dump (syntax_content , syntax_file )
200271
201272 with open (f'{ TEST_STEM } crypto' , 'w' ) as test_file :
202273 _ = test_file .write ('\n ' .join (test_content ))
0 commit comments