@@ -106,7 +106,7 @@ def __post_init__(self):
106106
107107
108108_loaders : dict [str , Callable [[str ], dict ]] = {}
109- _dumpers : dict [str , Callable [[dict , int ], str ]] = {}
109+ _dumpers : dict [str , Callable [[dict , int , str | None ], tuple [ str , bool ] ]] = {}
110110
111111
112112@dataclass
@@ -144,7 +144,7 @@ def handle(m: re.Match[str]):
144144
145145 raise ValueError (f"Unsupported file format: { path .suffix } " )
146146
147- def dumper (self , path : Path , save_path : Path , data : dict , indent : int ):
147+ def dumper (self , path : Path , save_path : Path , data : dict , indent : int , apply_schema : bool ):
148148 if not path .exists ():
149149 return
150150 origin = self .loader (path )
@@ -153,12 +153,15 @@ def dumper(self, path: Path, save_path: Path, data: dict, indent: int):
153153 else :
154154 origin = data
155155 end = save_path .suffix .split ("." )[- 1 ]
156+ schema_file = None
157+ if apply_schema :
158+ schema_file = f"{ save_path .stem } .schema.json"
156159 if end in _dumpers :
157- ans = _dumpers [end ](origin , indent )
160+ ans , applied = _dumpers [end ](origin , indent , schema_file )
158161 if self ._env_replaced :
159162 lines = ans .splitlines (keepends = True )
160163 for i , line in self ._env_replaced .items ():
161- lines [i ] = line
164+ lines [i + applied ] = line
162165 ans = "" .join (lines )
163166 with save_path .open ("w" , encoding = "utf-8" ) as f :
164167 f .write (ans )
@@ -243,11 +246,11 @@ def dump(self, indent: int = 2):
243246 for file in self .plugin_extra_files :
244247 path = Path (file )
245248 if path .is_file ():
246- self .dumper (path , path , self ._clean (self .plugin .pop (path .stem )), indent )
249+ self .dumper (path , path , self ._clean (self .plugin .pop (path .stem )), indent , False )
247250 else :
248251 for _path in path .iterdir ():
249252 if _path .is_file ():
250- self .dumper (_path , _path , self ._clean (self .plugin .pop (_path .stem )), indent )
253+ self .dumper (_path , _path , self ._clean (self .plugin .pop (_path .stem )), indent , False )
251254 for key in list (self .plugin .keys ()):
252255 if key .startswith ("$" ):
253256 continue
@@ -261,9 +264,9 @@ def dump(self, indent: int = 2):
261264 self .plugin [key ] = self ._clean (value )
262265 return self ._origin_data
263266
264- def save (self , path : str | os .PathLike [str ] | None = None , indent : int = 2 ):
267+ def save (self , path : str | os .PathLike [str ] | None = None , indent : int = 2 , apply_schema : bool = False ):
265268 self .save_flag = True
266- self .dumper (self .path , Path (path or self .path ), self .dump (indent ), indent )
269+ self .dumper (self .path , Path (path or self .path ), self .dump (indent ), indent , apply_schema )
267270
268271 @classmethod
269272 def load (cls , path : str | os .PathLike [str ] | None = None ) -> "EntariConfig" :
@@ -352,7 +355,7 @@ def decorator(func: Callable[[str], dict]):
352355def register_dumper (* ext : str ):
353356 """Register a dumper for a specific file extension."""
354357
355- def decorator (func : Callable [[dict , int ], str ]):
358+ def decorator (func : Callable [[dict , int , str | None ], tuple [ str , bool ] ]):
356359 for e in ext :
357360 _dumpers [e ] = func
358361 return func
@@ -366,8 +369,12 @@ def json_loader(text: str) -> dict:
366369
367370
368371@register_dumper ("json" )
369- def json_dumper (origin : dict , indent : int ):
370- return json .dumps (origin , indent = indent , ensure_ascii = False )
372+ def json_dumper (origin : dict , indent : int , schema_file : str | None = None ) -> tuple [str , bool ]:
373+ schema_applied = False
374+ if schema_file and "$schema" not in origin :
375+ origin = {"$schema" : f"{ schema_file } " , ** origin }
376+ schema_applied = True
377+ return json .dumps (origin , indent = indent , ensure_ascii = False ), schema_applied
371378
372379
373380@register_loader ("yaml" , "yml" )
@@ -381,7 +388,7 @@ def yaml_loader(text: str) -> dict:
381388
382389
383390@register_dumper ("yaml" , "yml" )
384- def yaml_dumper (origin : dict , indent : int ) :
391+ def yaml_dumper (origin : dict , indent : int , schema_file : str | None = None ) -> tuple [ str , bool ] :
385392 if YAML is None :
386393 raise RuntimeError ("yaml is not installed. Please install with `arclet-entari[yaml]`" )
387394 yaml = YAML ()
@@ -390,4 +397,15 @@ def yaml_dumper(origin: dict, indent: int):
390397 yaml .width = 4096
391398 sio = StringIO ()
392399 yaml .dump (origin , sio )
393- return sio .getvalue ()
400+ ans = sio .getvalue ()
401+ schema_applied = False
402+ if schema_file :
403+ root = Path .cwd ()
404+ if (root / ".vscode" ).exists ():
405+ if not ans .startswith ("# yaml-language-server: $schema=" ):
406+ ans = f"# yaml-language-server: $schema={ schema_file } \n { ans } "
407+ schema_applied = True
408+ elif not ans .startswith ("# $schema:" ):
409+ ans = f"# $schema: { schema_file } \n { ans } "
410+ schema_applied = True
411+ return ans , schema_applied
0 commit comments