2 # -*- coding: utf-8 -*-
5 # This file is part of LyX, the document processor.
6 # Licence details can be found in the file COPYING.
10 # Full author contact details are available in file CREDITS
13 # gen_lfuns.py <path/to/LyXAction.cpp> <where/to/save/LFUNs.lyx>
17 from datetime
import date
20 sys
.stderr
.write(message
+ '\n')
24 return "Usage: %s <path/to/LyXAction.cpp> [<where/to/save/LFUNs.lyx>]" % prog_name
29 LYX_NEWLINE
= "\n\\begin_inset Newline newline\n\\end_inset\n\n"
30 LYX_BACKSLASH
= "\n\\backslash\n"
32 HTMLONLY_START
= "\\htmlonly"
33 HTMLONLY_END
= "\\endhtmlonly"
34 LFUN_NAME_ID
= "\\var lyx::FuncCode lyx::"
35 LFUN_ACTION_ID
= "\\li Action: "
36 LFUN_NOTION_ID
= "\\li Notion: "
37 LFUN_SYNTAX_ID
= "\\li Syntax: "
38 LFUN_PARAMS_ID
= "\\li Params: "
39 LFUN_SAMPLE_ID
= "\\li Sample: "
40 LFUN_ORIGIN_ID
= "\\li Origin: "
41 LFUN_ENDVAR
= "\\endvar"
43 ID_DICT
= dict(name
=LFUN_NAME_ID
, action
=LFUN_ACTION_ID
, notion
=LFUN_NOTION_ID
,
44 syntax
=LFUN_SYNTAX_ID
, params
=LFUN_PARAMS_ID
, sample
=LFUN_SAMPLE_ID
, origin
=LFUN_ORIGIN_ID
)
46 LFUNS_HEADER
= """# gen_lfuns.py generated this file. For more info see http://www.lyx.org/
52 \\renewcommand{\\descriptionlabel}[1]{\\hspace\\labelsep\\upshape\\bfseries #1:}
53 \\renewenvironment{description}{\\list{}{%
54 \\setlength{\\itemsep}{-2pt}
55 \\advance\\leftmargini6\\p@ \\itemindent-12\\p@
56 \\labelwidth\\z@ \\let\\makelabel\\descriptionlabel}%
61 \\use_default_options false
66 \\font_typewriter default
67 \\font_default_family default
74 \\paperfontsize default
83 \\paperorientation portrait
90 \\paragraph_separation indent
92 \\quotes_language english
95 \\paperpagestyle default
96 \\tracking_changes false
97 \\output_changes false
104 \\begin_layout Section*""" + "\nLFUNs documentation automatically generated " + str(date
.today()) + """
107 \\begin_layout Standard
111 \\begin_layout Plain Layout
121 \\begin_inset VSpace 1cm
127 LFUNS_FOOTER
= """\\end_body
132 """Takes a comment block (str) and parses it for fields describing the LFUN. Returns a dict containing the fields."""
134 lfun
= dict(name
="", action
="", notion
="", syntax
="", params
="", sample
="", origin
="")
136 lines
= str.splitlines()
137 # strip leading whitespace and * from the lines of the comment to get
138 # rid of unimportant characters
139 for i
in range(0, len(lines
)):
140 lines
[i
] = lines
[i
].strip(" *")
142 for i
in range(0, len(lines
) - 1):
143 # work out what field is being read if none of these is found, the line will be added
144 # to the last field edited
145 # since the field identifier is not included skip it out if it's found, otherwise skip
146 # nothing as an existing field is being added to
147 # if a field id is found, then its the first line of the field so set the pre_space to ""
148 # so that the first line isn't prespaced
149 if lines
[i
].startswith(LFUN_NAME_ID
):
152 skip
= len(ID_DICT
[field
])
153 elif lines
[i
].startswith(LFUN_ACTION_ID
):
156 skip
= len(ID_DICT
[field
])
157 elif lines
[i
].startswith(LFUN_NOTION_ID
):
160 skip
= len(ID_DICT
[field
])
161 elif lines
[i
].startswith(LFUN_SYNTAX_ID
):
164 skip
= len(ID_DICT
[field
])
165 elif lines
[i
].startswith(LFUN_PARAMS_ID
):
168 skip
= len(ID_DICT
[field
])
169 elif lines
[i
].startswith(LFUN_SAMPLE_ID
):
172 skip
= len(ID_DICT
[field
])
173 elif lines
[i
].startswith(LFUN_ORIGIN_ID
):
176 skip
= len(ID_DICT
[field
])
177 elif lines
[i
].startswith(LFUN_ENDVAR
):
181 # if a manual line break was found last line, don't prespace this line
182 if i
> 1 and lines
[i
-1].endswith("\\n"):
187 # add the line to the field, processing it for \ characters and \n
188 # which, if occurring at the end of a line, must become a LYX_NEWLINE
189 line
= lines
[i
][skip
:]
191 # deal with \htmlonly
192 # TODO: convert chars found in htmlonly to unicode
193 start
= line
.find(HTMLONLY_START
)
195 # if removing the htmlonly element leaves a double space, go back one to remove it
196 if line
[start
-1] == " ":
198 end
= line
.find(HTMLONLY_END
)
200 end
= line
.find(HTMLONLY_END
) + len(HTMLONLY_END
)
201 line
= line
[:start
] + line
[end
:]
203 # TODO: if HTMLONLY_END is not found, look on the next line
204 # TODO: in the current LyXAction.cpp there are no htmlonly fields which go over a line break
206 # deal with \ but leave \n if at the end of the line
207 slash_idx
= line
.find("\\")
208 while slash_idx
>= 0:
209 if slash_idx
< len(line
)-2 \
210 or slash_idx
== len(line
)-1:
211 # true when the \ is not the last or second last char
212 # or when the slash is the last char of the line
214 # slash must be interpreted literaly so swap it for a LYX_BACKSLASH
215 line
= line
[:slash_idx
] + LYX_BACKSLASH
+ line
[slash_idx
+1:]
216 # skip the index ahead beyond the added text
217 slash_idx
= slash_idx
+ len(LYX_BACKSLASH
)
218 elif line
[slash_idx
+1] != "n": # only evaluated if the line ends "\x" where 'x' != 'n'
219 line
= line
[:slash_idx
] + LYX_BACKSLASH
+ line
[slash_idx
+1:]
220 # skip the index ahead beyond the added text
221 slash_idx
= slash_idx
+ len(LYX_BACKSLASH
)
222 # look for the next \
223 slash_idx
= line
.find("\\", slash_idx
+1)
225 # \n at the end of lines will not be processed by the above while loop
226 # so sort those out now
227 # sometime lines end " \n" so chop the space if its there
228 if line
.endswith(" \\n"):
229 line
= line
[:len(line
)-3] + LYX_NEWLINE
230 elif line
.endswith("\\n"):
231 line
= line
[:len(line
)-2] + LYX_NEWLINE
233 # any references to other LFUNs need the # removing
234 # TODO: actually insert a cross-reference here
235 line
= line
.replace("#LFUN", "LFUN")
237 # handle the few #lyx:: cases
238 line
= line
.replace("#lyx::", "lyx::")
240 # the first line might not have a field in it in which
241 # case the variable field won't have a value, so check
244 lfun
[field
] = lfun
[field
] + pre_space
+ line
246 # TODO: sort out chopping lines of more that 80 chars in length
250 def write_fields(file, lfun
):
251 """Writes the LFUN contained in the dict lfun to the file. Does not write a the file header or footer"""
252 # add lfun to LFUNs.lyx
253 file.write("\\begin_layout Subsection*\n")
254 file.write(lfun
["name"] + "\n")
255 file.write("\\end_layout\n")
257 if lfun
["action"] != "":
258 file.write("\\begin_layout Description\n")
259 file.write("Action " + lfun
["action"] + "\n")
261 file.write("\\end_layout\n")
263 if lfun
["notion"] != "":
264 file.write("\\begin_layout Description\n")
265 file.write("Notion " + lfun
["notion"] + "\n")
266 file.write("\\end_layout\n")
268 if lfun
["syntax"] != "":
269 file.write("\\begin_layout Description\n")
270 file.write("Syntax " + lfun
["syntax"] + "\n")
271 file.write("\\end_layout\n")
273 if lfun
["params"] != "":
274 file.write("\\begin_layout Description\n")
275 file.write("Params " + lfun
["params"] + "\n")
276 file.write("\\end_layout\n")
278 if lfun
["sample"] != "":
279 file.write("\\begin_layout Description\n")
280 file.write("Sample " + lfun
["sample"] + "\n")
281 file.write("\\end_layout\n")
283 if lfun
["origin"] != "":
284 file.write("\\begin_layout Description\n")
285 file.write("Origin " + lfun
["origin"] + "\n")
286 file.write("\\end_layout\n")
291 # parse command line arguments
292 script_path
, script_name
= os
.path
.split(argv
[0])
294 error(usage(script_name
))
296 lyxaction_path
= argv
[1]
297 if not os
.path
.exists(lyxaction_path
):
298 error(script_name
+ ": %s is not a valid path" % lyxaction_path
, usage(argv
[0]))
303 if os
.path
.isdir(lfuns_path
):
304 lfuns_path
= lfuns_path
+ "LFUNs.lyx"
305 elif os
.path
.exists(lfuns_path
):
306 error(script_name
+ ": %s already exists, delete it and rerun the script" % lfuns_path
)
307 lfuns_file
= open(lfuns_path
, 'wb')
309 lfuns_file
= sys
.stdout
311 sys
.stderr
.write(script_name
+ ": Start processing " + argv
[1] + '\n')
312 # Read the input file and write the output file
313 lyxaction_file
= open(lyxaction_path
, 'rb')
315 lyxaction_text
= lyxaction_file
.read()
317 lfuns_file
.write(LFUNS_HEADER
)
319 # seek to the important bit of LyXAction.cpp
321 start
= lyxaction_text
.index("ev_item const items[] = {")
323 lyxaction_file
.close()
325 error(script_name
+ ": LFUNs not found in " + lyxaction_file
)
330 # look for a doxygen comment
331 start
= lyxaction_text
.find(DOXYGEN_START
, start
)
332 end
= lyxaction_text
.find(DOXYGEN_END
, start
) + len(DOXYGEN_END
)
333 # parse the lfun if it is found
336 lfun
= parse_lfun(lyxaction_text
[start
:end
])
337 # write the lfun to the file
338 write_fields(lfuns_file
, lfun
)
339 # done adding current lfun to LFUNs.lyx so get the next one
342 # if no more lfuns are found, EOF reached
345 sys
.stderr
.write(script_name
+ ": Created documentation for " + str(count
) + " LFUNs\n")
347 # write the last part of LFUNs.lyx
348 lfuns_file
.write(LFUNS_FOOTER
)
350 lyxaction_file
.close()
353 sys
.stderr
.write(script_name
+ ": Finished\n")
355 if __name__
== "__main__":