#!/usr/bin/env python3 from pathlib import Path try: # VS Code settings allow comments and trailing commas, which are not valid JSON. import json5 as json # type: ignore[import] HAS_JSON5 = True except ImportError: import json # type: ignore[no-redef] HAS_JSON5 = False ROOT_FOLDER = Path(__file__).absolute().parent.parent VSCODE_FOLDER = ROOT_FOLDER / ".vscode" RECOMMENDED_SETTINGS = VSCODE_FOLDER / "settings_recommended.json" SETTINGS = VSCODE_FOLDER / "settings.json" # settings can be nested, so we need to recursively update the settings. def deep_update(d: dict, u: dict) -> dict: # type: ignore[type-arg] for k, v in u.items(): if isinstance(v, dict): d[k] = deep_update(d.get(k, {}), v) elif isinstance(v, list): d[k] = d.get(k, []) + v else: d[k] = v return d def main() -> None: recommended_settings = json.loads(RECOMMENDED_SETTINGS.read_text()) try: current_settings_text = SETTINGS.read_text() except FileNotFoundError: current_settings_text = "{}" try: current_settings = json.loads(current_settings_text) except ValueError as ex: # json.JSONDecodeError is a subclass of ValueError if HAS_JSON5: raise SystemExit("Failed to parse .vscode/settings.json.") from ex raise SystemExit( "Failed to parse .vscode/settings.json. " "Maybe it contains comments or trailing commas. " "Try `pip install json5` to install an extended JSON parser." ) from ex settings = deep_update(current_settings, recommended_settings) SETTINGS.write_text( json.dumps( settings, indent=4, ) + "\n", # add a trailing newline encoding="utf-8", ) if __name__ == "__main__": main()