blob: 263336efe324353e497575814b24403dd68caff8 [file] [log] [blame]
James Feist3cb5fec2018-01-23 14:41:51 -08001#!/usr/bin/python3
2# all arguments to this script are considered as json files
3# and attempted to be formatted alphabetically
4
5import json
James Feist5ffd8b42019-10-25 11:22:10 -07006import os
Potin Lai0f3a4d92023-12-05 00:13:55 +08007import re
James Feist3cb5fec2018-01-23 14:41:51 -08008from sys import argv
Potin Lai0f3a4d92023-12-05 00:13:55 +08009from typing import List, Tuple, Union
10
11# Trying to parse JSON comments and then being able to re-insert them into
12# the correct location on a re-emitted and sorted JSON would be very difficult.
13# To make this somewhat manageable, we take a few shortcuts here:
14#
15# - Single-line style comments (//) can be on a new line or at the end of
16# a line with contents.
17#
18# - Multi-line style comments (/* */) use the must be free-standing.
19#
20# - Comments will get inserted back into the file in the line they came
21# from. If keys are resorted or the number of lines change, all bets
22# for correctness are off.
23#
24# - No attempts to re-indent multi-line comments will be made.
25#
26# In light of this, it is highly recommended to use a JSON formatter such as
27# prettier before using this script and planning to move multi-line comments
28# around after key resorting.
29
30
31class CommentTracker:
32 # Regex patterns used.
33 single_line_pattern = re.compile(r"\s*//.*$")
34 multi_line_start_pattern = re.compile(r"/\*")
35 multi_line_end_pattern = re.compile(r".*\*/", re.MULTILINE | re.DOTALL)
36
37 def __init__(self) -> None:
38 self.comments: List[Tuple[bool, int, str]] = []
39
40 # Extract out the comments from a JSON-like string and save them away.
41 def extract_comments(self, contents: str) -> str:
42 result = []
43
44 multi_line_segment: Union[str, None] = None
45 multi_line_start = 0
46
47 for idx, line in enumerate(contents.split("\n")):
48 single = CommentTracker.single_line_pattern.search(line)
49 if single:
50 do_append = False if line.startswith(single.group(0)) else True
51 line = line[: single.start(0)]
52 self.comments.append((do_append, idx, single.group(0)))
53
54 multi_start = CommentTracker.multi_line_start_pattern.search(line)
55 if not multi_line_segment and multi_start:
56 multi_line_start = idx
57 multi_line_segment = line
58 elif multi_line_segment:
59 multi_line_segment = multi_line_segment + "\n" + line
60
61 if not multi_line_segment:
62 result.append(line)
63 continue
64
65 multi_end = CommentTracker.multi_line_end_pattern.search(
66 multi_line_segment
67 )
68 if multi_end:
69 self.comments.append(
70 (False, multi_line_start, multi_end.group(0))
71 )
72 result.append(multi_line_segment[multi_end.end(0) :])
73 multi_line_segment = None
74
75 return "\n".join(result)
76
77 # Re-insert the saved off comments into a JSON-like string.
78 def insert_comments(self, contents: str) -> str:
79 result = contents.split("\n")
80
81 for append, idx, string in self.comments:
82 if append:
83 result[idx] = result[idx] + string
84 else:
85 result = result[:idx] + string.split("\n") + result[idx:]
86
87 return "\n".join(result)
88
James Feist3cb5fec2018-01-23 14:41:51 -080089
Patrick Williamsae74f232024-09-05 14:51:04 -040090files = []
James Feist5ffd8b42019-10-25 11:22:10 -070091
Patrick Williamsae74f232024-09-05 14:51:04 -040092for file in argv[1:]:
93 if not os.path.isdir(file):
94 files.append(file)
95 continue
96 for root, _, filenames in os.walk(file):
97 for f in filenames:
98 files.append(os.path.join(root, f))
James Feist5ffd8b42019-10-25 11:22:10 -070099
100for file in files:
Patrick Williamscad2d1f2022-12-04 14:38:16 -0600101 if not file.endswith(".json"):
Brad Bishopca000e52019-12-19 15:43:06 -0500102 continue
James Feistc4e56942019-04-19 12:15:19 -0700103 print("formatting file {}".format(file))
Potin Lai0f3a4d92023-12-05 00:13:55 +0800104
105 comments = CommentTracker()
106
107 with open(file) as fp:
108 j = json.loads(comments.extract_comments(fp.read()))
James Feist3cb5fec2018-01-23 14:41:51 -0800109
James Feistc4e56942019-04-19 12:15:19 -0700110 if isinstance(j, list):
111 for item in j:
112 item["Exposes"] = sorted(item["Exposes"], key=lambda k: k["Type"])
113 else:
114 j["Exposes"] = sorted(j["Exposes"], key=lambda k: k["Type"])
115
Potin Lai0f3a4d92023-12-05 00:13:55 +0800116 with open(file, "w") as fp:
117 contents = json.dumps(
118 j, indent=4, sort_keys=True, separators=(",", ": ")
Patrick Williamscad2d1f2022-12-04 14:38:16 -0600119 )
Potin Lai0f3a4d92023-12-05 00:13:55 +0800120
121 fp.write(comments.insert_comments(contents))
122 fp.write("\n")