@@ -29,65 +29,138 @@ def has_overlay_annotations(lines):
2929 return any (ann in line for ann in annotations for line in lines )
3030
3131
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 ):
3337 '''
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 .
3640 '''
37- out_lines = []
38- status = 0
41+ comment = False
42+ for i , line in enumerate (lines ):
43+ trimmed = line .strip ()
3944
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
4553
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
48120
49- out_lines = []
50- empty_line_buffer = []
51- status = 0
52121 for line in lines :
53122 trimmed = line .strip ()
54- if not trimmed :
55- empty_line_buffer . append ( line )
123+
124+ if not trimmed or is_line_comment ( trimmed ):
56125 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 )
86153
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 )
89161
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 )
91164
92165
93166def insert_overlay_caller_annotations (lines ):
0 commit comments