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