@@ -29,65 +29,138 @@ def has_overlay_annotations(lines):
29
29
return any (ann in line for ann in annotations for line in lines )
30
30
31
31
32
- def insert_toplevel_maybe_local_annotation (filename , lines ):
32
+ def is_line_comment (line ):
33
+ return line .startswith ("//" ) or (line .startswith ("/*" ) and line .endswith ("*/" ))
34
+
35
+
36
+ def find_file_level_module_declaration (lines ):
33
37
'''
34
- Find a suitable place to insert an overlay[local?] annotation at the top of the file.
35
- Return a pair: (string describing action taken, modified content as list of lines) .
38
+ Returns the index of the existing file-level module declaration if one
39
+ exists. Returns None otherwise .
36
40
'''
37
- out_lines = []
38
- status = 0
41
+ comment = False
42
+ for i , line in enumerate (lines ):
43
+ trimmed = line .strip ()
39
44
40
- for line in lines :
41
- if status == 0 and line .rstrip ().endswith ("module;" ):
42
- out_lines .append ("overlay[local?]\n " )
43
- status = 1
44
- out_lines .append (line )
45
+ if is_line_comment (trimmed ):
46
+ continue
47
+ elif trimmed .startswith ("/*" ):
48
+ comment = True
49
+ elif comment and trimmed .endswith ("*/" ):
50
+ comment = False
51
+ elif not comment and trimmed .endswith ("module;" ):
52
+ return i
45
53
46
- if status == 1 :
47
- return (f"Annotating \" { filename } \" via existing file-level module statement" , out_lines )
54
+ return None
55
+
56
+
57
+ def is_file_module_qldoc (i , lines ):
58
+ '''
59
+ Assuming a qldoc ended on line i, determine if it belongs to the implicit
60
+ file-level module. If it is followed by another qldoc or imports, then it
61
+ does and if it is followed by any other non-empty, non-comment lines, then
62
+ we assume that is a declaration of some kind and the qldoc is attached to
63
+ that declaration.
64
+ '''
65
+ comment = False
66
+
67
+ for line in lines [i + 1 :]:
68
+ trimmed = line .strip ()
69
+
70
+ if trimmed .startswith ("import " ) or trimmed .startswith ("private import " ) or trimmed .startswith ("/**" ):
71
+ return True
72
+ elif is_line_comment (trimmed ) or not trimmed :
73
+ continue
74
+ elif trimmed .startswith ("/*" ):
75
+ comment = True
76
+ elif comment and trimmed .endswith ("*/" ):
77
+ comment = False
78
+ elif not comment and trimmed :
79
+ return False
80
+
81
+ return True
82
+
83
+
84
+ def find_file_module_qldoc_declaration (lines ):
85
+ '''
86
+ Returns the index of last line of the implicit file module qldoc if one
87
+ exists. Returns None otherwise.
88
+ '''
89
+
90
+ qldoc = False
91
+ comment = False
92
+ for i , line in enumerate (lines ):
93
+ trimmed = line .strip ()
94
+
95
+ if trimmed .startswith ("//" ):
96
+ continue
97
+ elif (qldoc or trimmed .startswith ("/**" )) and trimmed .endswith ("*/" ):
98
+ # a qldoc just ended; determine if it belongs to the implicit file module
99
+ if is_file_module_qldoc (i , lines ):
100
+ return i
101
+ else :
102
+ return None
103
+ elif trimmed .startswith ("/**" ):
104
+ qldoc = True
105
+ elif trimmed .startswith ("/*" ):
106
+ comment = True
107
+ elif comment and trimmed .endswith ("*/" ):
108
+ comment = False
109
+ elif (not qldoc and not comment ) and trimmed :
110
+ return None
111
+
112
+ return None
113
+
114
+
115
+ def only_comments (lines ):
116
+ '''
117
+ Returns true if the lines contain only comments and empty lines.
118
+ '''
119
+ comment = False
48
120
49
- out_lines = []
50
- empty_line_buffer = []
51
- status = 0
52
121
for line in lines :
53
122
trimmed = line .strip ()
54
- if not trimmed :
55
- empty_line_buffer . append ( line )
123
+
124
+ if not trimmed or is_line_comment ( trimmed ):
56
125
continue
57
- if status <= 1 and trimmed .endswith ("*/" ):
58
- status = 2
59
- elif status == 0 and trimmed .startswith ("/**" ):
60
- status = 1
61
- elif status == 0 and not trimmed .startswith ("/*" ):
62
- out_lines .append ("overlay[local?]\n " )
63
- out_lines .append ("module;\n " )
64
- out_lines .append ("\n " )
65
- status = 3
66
- elif status == 2 and (trimmed .startswith ("import " ) or trimmed .startswith ("private import " )):
67
- out_lines .append ("overlay[local?]\n " )
68
- out_lines .append ("module;\n " )
69
- status = 3
70
- elif status == 2 and (trimmed .startswith ("class " ) or trimmed .startswith ("predicate " )
71
- or trimmed .startswith ("module " ) or trimmed .startswith ("signature " )):
72
- out_lines = ["overlay[local?]\n " , "module;\n " , "\n " ] + out_lines
73
- status = 3
74
- elif status == 2 and trimmed .startswith ("/*" ):
75
- out_lines .append ("overlay[local?]\n " )
76
- out_lines .append ("module;\n " )
77
- status = 3
78
- elif status == 2 :
79
- status = 4
80
- if empty_line_buffer :
81
- out_lines += empty_line_buffer
82
- empty_line_buffer = []
83
- out_lines .append (line )
84
- if status == 3 :
85
- out_lines += empty_line_buffer
126
+ elif trimmed .startswith ("/*" ):
127
+ comment = True
128
+ elif comment and trimmed .endswith ("*/" ):
129
+ comment = False
130
+ elif comment :
131
+ continue
132
+ elif trimmed :
133
+ return False
134
+
135
+ return True
136
+
137
+
138
+ def insert_toplevel_maybe_local_annotation (filename , lines ):
139
+ '''
140
+ Find a suitable place to insert an overlay[local?] annotation at the top of the file.
141
+ Returns a pair consisting of description and the modified lines or None if no overlay
142
+ annotation is necessary (e.g., for files that only contain comments).
143
+ '''
144
+ if only_comments (lines ):
145
+ return None
146
+
147
+ i = find_file_level_module_declaration (lines )
148
+ if not i == None :
149
+ out_lines = lines [:i ]
150
+ out_lines .append ("overlay[local?]\n " )
151
+ out_lines .extend (lines [i :])
152
+ return (f"Annotating \" { filename } \" via existing file-level module statement" , out_lines )
86
153
87
- if status == 3 :
88
- return (f"Annotating \" { filename } \" after file-level module qldoc" , out_lines )
154
+ i = find_file_module_qldoc_declaration (lines )
155
+ if not i == None :
156
+ out_lines = lines [:i + 1 ]
157
+ out_lines .append ("overlay[local?]\n " )
158
+ out_lines .append ("module;\n " )
159
+ out_lines .extend (lines [i + 1 :])
160
+ return (f"Annotating \" { filename } \" which has a file-level module qldoc" , out_lines )
89
161
90
- raise Exception (f"Failed to annotate \" { filename } \" as overlay[local?]." )
162
+ out_lines = ["overlay[local?]\n " , "module;\n " , "\n " ] + lines
163
+ return (f"Annotating \" { filename } \" without file-level module qldoc" , out_lines )
91
164
92
165
93
166
def insert_overlay_caller_annotations (lines ):
0 commit comments