1#!/usr/bin/env python3 2 3from pathlib import Path 4 5 6try: 7 # VS Code settings allow comments and trailing commas, which are not valid JSON. 8 import json5 as json # type: ignore[import] 9 10 HAS_JSON5 = True 11except ImportError: 12 import json # type: ignore[no-redef] 13 14 HAS_JSON5 = False 15 16 17ROOT_FOLDER = Path(__file__).absolute().parent.parent 18VSCODE_FOLDER = ROOT_FOLDER / ".vscode" 19RECOMMENDED_SETTINGS = VSCODE_FOLDER / "settings_recommended.json" 20SETTINGS = VSCODE_FOLDER / "settings.json" 21 22 23# settings can be nested, so we need to recursively update the settings. 24def deep_update(d: dict, u: dict) -> dict: # type: ignore[type-arg] 25 for k, v in u.items(): 26 if isinstance(v, dict): 27 d[k] = deep_update(d.get(k, {}), v) 28 elif isinstance(v, list): 29 d[k] = d.get(k, []) + v 30 else: 31 d[k] = v 32 return d 33 34 35def main() -> None: 36 recommended_settings = json.loads(RECOMMENDED_SETTINGS.read_text()) 37 try: 38 current_settings_text = SETTINGS.read_text() 39 except FileNotFoundError: 40 current_settings_text = "{}" 41 42 try: 43 current_settings = json.loads(current_settings_text) 44 except ValueError as ex: # json.JSONDecodeError is a subclass of ValueError 45 if HAS_JSON5: 46 raise SystemExit("Failed to parse .vscode/settings.json.") from ex 47 raise SystemExit( 48 "Failed to parse .vscode/settings.json. " 49 "Maybe it contains comments or trailing commas. " 50 "Try `pip install json5` to install an extended JSON parser." 51 ) from ex 52 53 settings = deep_update(current_settings, recommended_settings) 54 55 SETTINGS.write_text( 56 json.dumps( 57 settings, 58 indent=4, 59 ) 60 + "\n", # add a trailing newline 61 encoding="utf-8", 62 ) 63 64 65if __name__ == "__main__": 66 main() 67