* Bug 6911 fixed: help_from_sci dit not support ω in heading comments
[scilab.git] / scilab / modules / helptools / macros / help_from_sci.sci
1 // Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
2 // Copyright (C) 2008-2009 - T. Pettersen
3 // Copyright (C) 2010 - DIGITEO - Allan CORNET
4 // Copyright (C) 2011 - DIGITEO - Michael Baudin
5 // Copyright (C) 2017 - Samuel GOUGEON
6 //
7 // Copyright (C) 2012 - 2016 - Scilab Enterprises
8 //
9 // This file is hereby licensed under the terms of the GNU GPL v2.0,
10 // pursuant to article 5.3.4 of the CeCILL v.2.1.
11 // This file was originally licensed under the terms of the CeCILL v2.1,
12 // and continues to be available under such terms.
13 // For more information, see the COPYING file which you should have received
14 // along with this program.
15 //==============================================================================
16
17 function [helptxt,demotxt]=help_from_sci(funname,helpdir,demodir)
18     // Generate help files and demo files from the head comments section of a .sci source file.
19     //
20     // Syntax
21     //  help_from_sci() // generate an empty function template
22     //  helptxt = help_from_sci() // generate an empty function template
23     //  help_from_sci(funname,helpdir) // generate helpdir/funname.xml from funname.sci.
24     //  help_from_sci(dirname,helpdir) // process dirname/*.sci and create helpdir/*.xml help files.
25     //  help_from_sci(dirname,helpdir,demodir) // as above but also creating demodir/*.dem.sce demo files.
26     //  [helptxt,demotxt]=help_from_sci(funname) // return funname.xml and funname.dem.sce code as two text matrices.
27     // Parameters
28     //  funname: the name of a single .sci source file to be processed.
29     //  dirname: directory name where all .sci files will be processed.
30     //  helpdir: optional path where the .xml help file will be created.
31     //  demodir: optional path where .dem.sce demo files will be created based on code from the Examples section.
32     //  helptxt: returns the XML help code if helpdir is empty, or the path to the .xml file.
33     //  demotxt: returns the demo code if demodir is empty, or the path to the .dem.sce file.
34     //
35     // Description
36     //  The help_from_sci function generates .xml help files based on the head comments section
37     //  of .sci source files. Optionally .dem.sce demo files can be generated based on
38     //  code from the Examples section in the head comments section of .sci files.
39     //
40     //  In order for help_from_sci to format the .xml file properly the
41     //  head comments section should comply with some simple formatting rules.
42     //
43     //  The first comment line following the function definition should contain a short description
44     //  of the function.
45     //
46     //  The remaining comments are formatted according to the following (optional) headlines:
47     //  "Syntax", "Parameters", "Description", "Examples", "See also", "Used functions",
48     //  "Authors" and "Bibliography".
49     //
50     //  The following guidelines should be used when writing the source code comments:
51     //  <itemizedlist>
52     //    <listitem><para><literal>Syntax</literal> - one example pr. line.</para></listitem>
53     //    <listitem><para><literal>Parameters</literal> - separate parameter name and
54     //    description by a ":". Keep the description of each parameter on the same line.</para></listitem>
55     //    <listitem><para><literal>Description</literal> - formatting of the text can be done
56     //    using XML commands. Compare the output of head_comments("help_from_sci") with help("help_from_sci")
57     //    to get some hints.
58     //    Adding an empty comment line in the Description section is interpreted as the
59     //    start of a new paragraph.</para></listitem>
60     //    <listitem><para><literal>See also</literal> - list one function name pr line.</para></listitem>
61     //    <listitem><para><literal>Authors</literal> - write one author on each line following
62     //    the Authors headline. Use ";" to separate the authors name
63     //    from any add additional information.</para></listitem>
64     //    <listitem><para><literal>Bibliography</literal> - write one reference pr line
65     //    following the References headline.</para></listitem>
66     //  </itemizedlist>
67     //
68     // Examples
69     //  help_from_sci()   // Open an empty source code template in the scinotes editor.
70     //  // Save this template as test_fun.sci in the current directory before running
71     //  // the next example commands.
72     //
73     //  help_from_sci("test_fun")        // return the xml skeleton as a text string
74     //
75     //  help_from_sci("test_fun",".")    // create the xml help file in the current directory.
76     //
77     //  // create both a xml help file and a demo file in the current directory.
78     //  help_from_sci("test_fun",".",".")
79     //
80     //  // From a toolbox root directory a typical syntax would be:
81     //  // help_from_sci("macros","help\en_US","demos")
82     //  // This command would process all .sci files in the macros directory
83     //  // and use the head comments section to update all .xml help files in the
84     //  // help\en_US directory an rebuild the .dem.sce files in the demos\ directory.
85     //
86     // See also
87     //  help
88     //  help_skeleton
89     //  head_comments
90     //
91     // Authors
92     // Copyright (C) 2008-2009 - T. Pettersen
93     // Copyright (C) 2010 - DIGITEO - Allan CORNET
94     // Copyright (C) 2011 - DIGITEO - Michael Baudin
95
96     if argn(2) == 0 then
97         helptxt = [..
98         "function [z] = function_template(x,y)"
99         "// Short description on the first line following the function header."
100         "//"
101         "// Syntax"
102         "//   [z] = template_function(x,y) // calling examples, one pr. line"
103         "//"
104         "// Parameters"
105         "// x: the x parameter   // single line description of each parameter."
106         "// y: the y parameter   // parameter name and description must be "
107         "// z: the z parameter   // separated by "":""."
108         "//"
109         "// Description"
110         "// Here is a description of the function."
111         "// Add an empty comment line to format the text into separate paragraphs."
112         "//"
113         "// XML format commands may also be used directly in the text, "
114         "// as in the following examples."
115         "//"
116         "// This is an example of a itemized list:"
117         "// <itemizedlist>"
118         "// <listitem><para>An itemized list is shown here</para></listitem>"
119         "// </itemizedlist>"
120         "// The help text for help_from_sci was generated from the head comments section of help_from_sci.sci"
121         "// Compare the output from head_comments(""help_from_sci"") and help(""help_from_sci"")"
122         "// to see more examples on how to write the head comments section."
123         "//"
124         "// This is an example of the programlisting tag:"
125         "//   <programlisting>z = test_fun(x, y)</programlisting>"
126         "//"
127         "// This is an example of a latex equation:"
128         "//   <latex>"
129         "//   \begin{eqnarray}"
130         "//   m = |M| \beta^{1-p},"
131         "//   \end{eqnarray}"
132         "//   </latex>"
133         "//"
134         "// Examples"
135         "// [z] = test_fun(1, 2) // examples of use"
136         "//"
137         "// // An empty comment line in the Examples section will add a halt() statement"
138         "// // in the demo file test_fun.dem.sce generated by help_from_sci."
139         "//"
140         "// See also"
141         "//  help_from_sci"
142         "//  help_skeleton"
143         "//"
144         "// Authors"
145         "//  Author name ; should be listed one pr line. Use "";"" to separate names from additional information "
146         "//"
147         "// Bibliography"
148         "//   Literature references one pr. line"
149         ""
150         "// start of coding after on empty line to mark the end of the head_comments section"
151         "z = sin(x).*cos(x + y);"
152         "endfunction"
153         ];
154
155         mputl(helptxt, TMPDIR + filesep() + "function_template.sci");
156         if (isdef("editor") | (funptr("editor") <> 0)) then
157             editor(TMPDIR + filesep() + "function_template.sci");
158         end
159         [helptxt, demotxt] = help_from_sci("TMPDIR/function_template");
160         return;
161     end
162
163     if argn(2) < 3 then demodir = []; end
164     if argn(2) < 2 then helpdir = []; end
165
166     if ~isempty(demodir) & ~isdir(demodir) then
167         error(sprintf(gettext("%s: Wrong value for input argument #%d: A valid existing directory is expected.\n"), "help_from_sci", 3));
168     end
169
170     if isdir(funname) then
171         printf(gettext("%s: Reading from directory %s\n"), "help_from_sci", funname);
172         files = findfiles(funname, "*.sci");   // read *.sci files.
173         for i = 1:size(files, "r")
174             [tmp, out] = fileparts(files(i));
175             if isempty(helpdir) then
176                 help_from_sci(funname + filesep() + files(i), ".", demodir);
177                 printf(gettext("%s: Processing of file: %s to %s\n"), "help_from_sci", funname + "/" + out, out);
178             else
179                 help_from_sci(funname + filesep() + files(i), helpdir, demodir);
180                 printf(gettext("%s: Processing of file: %s to %s\n"), "help_from_sci", funname + "/" + out, helpdir + "/" + out);
181             end
182             if ~isempty(demodir) then
183                 printf(gettext("%s: Processing of file: %s\n"), "help_from_sci" , demodir + "/" + out + ".dem.sce");
184             else
185                 printf("\n");
186             end
187         end
188         printf(gettext("%s: processed %i files.\n"), "help_from_sci", i);
189         helptxt = "";
190         return;
191     end
192
193     out = tokens(pathconvert(funname), filesep());
194     out = out($);
195     out = tokens(out,".");
196     out = out(1);      // remove .sci (...wont work for fil.name.sci)
197     outxml = strsubst(out, "/^%/","percent", "r");
198
199     demotxt = ["mode(1)"
200     "//"
201     "// Demo of "+out+".sci"
202     "//"
203     ""];
204
205     verno = ver();
206     verno = verno(1,2);
207     helptxt = [
208     "<?xml version=""1.0"" encoding=""UTF-8""?>"
209     ""
210     "<!--"
211     " *"
212     " * This help file was generated from "+out+".sci using help_from_sci()."
213     " *"
214     " -->"
215     ""
216     "<refentry version=""5.0-subset Scilab"" xml:id="""+outxml+""" xml:lang=""en"""
217     "          xmlns=""http://docbook.org/ns/docbook"""
218     "          xmlns:xlink=""http://www.w3.org/1999/xlink"""
219     "          xmlns:svg=""http://www.w3.org/2000/svg"""
220     "          xmlns:ns3=""http://www.w3.org/1999/xhtml"""
221     "          xmlns:mml=""http://www.w3.org/1998/Math/MathML"""
222     "          xmlns:scilab=""http://www.scilab.org"""
223     "          xmlns:db=""http://docbook.org/ns/docbook"">"
224     ""
225     ];
226
227     if isempty(strindex(funname, ".sci")) then funname = funname + ".sci"; end;
228     if isempty(fileinfo(funname)) then
229         error(sprintf(gettext("%s: The file %s does not exist.\n"),"help_from_sci",funname));
230     end;
231     f = mopen(funname, "rt");
232     if isempty(f) then
233         error(sprintf(gettext("%s: Cannot open file %s.\n"), "help_from_sci", funname + ".sci"));
234     end
235     line = " ";
236     doc = [];
237
238     while isempty(strindex(line, "function ")) & ~meof(f), line = mgetl(f, 1); end
239
240     line = mgetl(f,1);
241     line = replaceTabBySpace(line);
242     short_descr = stripblanks(strsubst(line, "//", ""), %T);
243     short_descr = helpfromsci_protects_ampersand(short_descr);
244     helptxt = [helptxt;
245     "  <refnamediv>"
246     "    <refname>"+out+"</refname>"
247     "    <refpurpose>"+short_descr+"</refpurpose>"
248     "  </refnamediv>"
249     ];
250
251     cmds = ["SYNTAX", "PARAMETERS", "DESCRIPTION", "EXAMPLES", "SEE ALSO", ..
252             "AUTHORS", "BIBLIOGRAPHY", "USED FUNCTIONS" ];
253
254     doing = "search";
255     i = strindex(line, "//");
256     line = mgetl(f, 1);
257     line = replaceTabBySpace(line);
258     // Continue until empty line or end of file or a scilab command line (Bug#5487)
259     while (~isempty(stripblanks(line)) & ~meof(f)) & ~isempty(regexp(stripblanks(line),"/^\/\/*/"))
260         if stripblanks(line) == "//" then
261             if doing == "Description" then
262                 in = "new_descr_param";
263             else
264                 in = "";
265             end
266         else
267
268             in = strsplit(line, i(1) + 1);
269             in = stripblanks(in(2));
270             code = in;  // store original line for the demos.
271             if (doing ~= "Examples") then // Replacing characters like <, > or & should not be done in the Examples
272                 // Replacing "&" that are not prefixing HTML entities, with "&amp;":
273                 in = helpfromsci_protects_ampersand(in);
274                 //
275                 in = strsubst(in, "< ", "&lt; ");
276                 if strindex(in ,"<") then
277                     if ~helpfromsci_isxmlstr(in) then
278                         in = strsubst(in, "<", "&lt;");
279                     end;
280                 end
281                 in = strsubst(in, " >", " &gt;");
282                 if strindex(in, ">") then
283                     if ~helpfromsci_isxmlstr(in) then
284                         in = strsubst(in, ">", "&gt;");
285                     end;
286                 end
287             end
288         end
289
290         IN = convstr(in, "u");
291         if find(cmds == IN) then
292             [add_txt, doing] = change_activity(doing, in);
293             helptxt = [helptxt; add_txt];
294         else
295             if doing == "Syntax" then
296                 helptxt = [helptxt;"   " + in];
297             elseif doing == "Parameters" then
298                 i = strindex(in, ":");
299                 if ~isempty(i) then
300                     if length(in) > i(1) then
301                         in = strsplit(in,i(1));
302                         par_name = in(1);
303                         par_descr = in(2);
304                     else
305                         par_name = in;
306                         par_descr = " ";
307                     end
308                     helptxt = [helptxt
309                                "        <varlistentry>"
310                                "            <term>" + par_name + "</term>"];
311                     helptxt = [helptxt
312                       "            <listitem><para>" + par_descr + "</para></listitem>"
313                       "        </varlistentry>"];
314                 end
315             elseif doing == "Description" & in == "new_descr_param" then
316                 helptxt = [helptxt;"   </para>";"   <para>"];
317             elseif doing == "Description" then
318                 helptxt = [helptxt; in];
319             elseif doing == "Examples" & convstr(in, "u") ~= "EXAMPLES" then
320                 if isempty(stripblanks(in)) then
321                     demotxt = [demotxt; "halt()   // Press return to continue"; " "];
322                 else
323                     demotxt = [demotxt; code];
324                 end
325                 helptxt = [helptxt; in];
326             elseif doing == "See also" & convstr(in, "u") ~= "SEE ALSO" & ~isempty(stripblanks(in)) then
327                 str = stripblanks(in);
328                 i = strindex(str, " ");
329                 if i <> [] then
330                     str = stripblanks(strsplit(str, i(1)));
331                 else
332                     str = [str str];
333                 end
334                 helptxt = [helptxt; "   <member><link linkend=""" + str(1) + """>" + str(2) + "</link></member>"];
335             elseif doing == "Authors" & convstr(in, "u") ~= "AUTHORS" & ~isempty(stripblanks(in)) then
336                 [name, ref] = chop(in, ";");
337                 if isempty(ref) then
338                     helptxt = [helptxt; "   <member>" + name + "</member>"];
339                 else
340                     helptxt = [helptxt; "   <member>" + name + "</member><listitem><para>" + ref + "</para></listitem>"];
341                 end
342             elseif doing == "Bibliography" & convstr(in, "u") ~= "BIBLIOGRAPHY" & ~isempty(stripblanks(in)) then
343                 helptxt = [helptxt;"   <para>" + in + "</para>"];
344             elseif doing == "Used functions" & convstr(in, "u") ~= "USED FUNCTIONS" & ~isempty(stripblanks(in)) then
345                 helptxt = [helptxt;"   <para>" + in + "</para>"];
346             end
347         end
348         line = mgetl(f,1);
349         line = replaceTabBySpace(line);
350         i = strindex(line, "//");
351     end
352
353     helptxt = [helptxt; change_activity(doing, "FINISHED")];
354     mclose(f);
355
356     if ~isempty(helpdir) then
357         fnme = pathconvert(helpdir, %t, %f) + outxml + ".xml";
358         answ = 1;
359         if isfile(fnme) then  // file exists...
360             answ = messagebox(fnme + " exists!", "Warning - help_from_sci", "warning", ["Create anyway" "Skip file"], "modal");
361         end
362         if answ == 1 then
363             mputl(helptxt, fnme);
364             helptxt = fnme;
365         else
366             printf(gettext("%s: File skipped %s."), "help_from_sci", outxml + ".xml");
367             helptxt = "";
368         end
369     end
370
371     demotxt = [demotxt; "//========= E N D === O F === D E M O =========//"];
372     if ~isempty(demodir) then
373         fnme = demodir + filesep() + out + ".dem.sce";
374         answ = 1;
375         if isfile(fnme) then
376             answ = messagebox(fnme + " exists!", "Warning - help_from_sci", "warning", ["Create anyway" "Skip file"], "modal");
377         end
378         if answ == 1 then
379             mputl(demotxt, fnme);
380             demotxt = fnme;
381         else
382             printf(gettext("%s: File skipped %s."), "help_from_sci", out + ".demo.sce");
383             demotxt = "";
384         end
385     end
386 endfunction
387 //==============================================================================
388 function str = helpfromsci_protects_ampersand(str)
389     // Replaces "&" that are not prefixing HTML entities, with "&amp;":
390     strPrev = "";
391     while str ~= strPrev
392         strPrev = str;
393         str = strsubst(str, "/&(?!([a-zA-Z]+|#[0-9]+);)/", "&amp;", "r");
394     end
395 endfunction
396 //==============================================================================
397 function tf = helpfromsci_isxmlstr(str)
398     // Returns %t if the current string is a xml line
399     if ( ~isempty(regexp(str, "/\<*[a-z]\>/")) ) then
400         tf=%t
401     elseif ( ~isempty(regexp(str, "/\<(.*)\/\>/")) ) then
402         tf=%t
403     else
404         tf=%f
405     end
406 endfunction
407 //==============================================================================
408 function [head, tail] = chop(str, tok)
409     i = regexp(str, "/" + tok + "/", "o");
410     if isempty(i) then
411         head = str;
412         tail = [];
413     else
414         head = part(str, 1:i - 1);
415         tail = part(str, i + 1:length(str));
416     end
417 endfunction
418 //==============================================================================
419 function strOut = replaceTabBySpace(strIn)
420     strOut = strsubst(strIn, ascii(9), part(" ",1:4));
421 endfunction
422 //==============================================================================
423 function [txt, doing] = change_activity(currently_doing, start_doing)
424     doing = start_doing;
425     select convstr(currently_doing,"u")
426     case "SYNTAX" then
427         txt = ["   </synopsis>"; "</refsynopsisdiv>"];
428     case "PARAMETERS" then
429         txt = ["   </variablelist>"; "</refsection>"];
430     case "DESCRIPTION" then
431         txt = ["</para>"; "</refsection>"];
432     case "EXAMPLES" then
433         txt = ["   ]]></programlisting>"; "</refsection>"];
434     case "SEE ALSO" then
435         txt = ["   </simplelist>"; "</refsection>"];
436     case "AUTHORS" then
437         txt = ["   </simplelist>"; "</refsection>"];
438     case "BIBLIOGRAPHY" then
439         txt = ["</refsection>"];
440     case "USED FUNCTIONS" then
441         txt = ["</refsection>"];
442     else
443         txt = "";
444     end
445
446     select convstr(start_doing, "u"),
447     case "SYNTAX"
448         txt = [txt; ""; "<refsynopsisdiv>"; "   <title>Syntax</title>"; "   <synopsis>"];
449     case "PARAMETERS"
450         txt = [txt; ""; "<refsection>"; "   <title>Parameters</title>"; "   <variablelist>"];
451     case "DESCRIPTION"
452         txt = [txt; ""; "<refsection>"; "   <title>Description</title>"; "   <para>"];
453     case "EXAMPLES"
454         txt = [txt; ""; "<refsection>"; "   <title>Examples</title>"; "   <programlisting role=""example""><![CDATA["];
455     case "SEE ALSO"
456         txt = [txt; ""; "<refsection>"; "   <title>See also</title>"; "   <simplelist type=""inline"">"];
457     case "AUTHORS"
458         txt = [txt; ""; "<refsection>"; "   <title>Authors</title>"; "   <simplelist type=""vert"">"];
459     case "BIBLIOGRAPHY"
460         txt = [txt; ""; "<refsection>"; "   <title>Bibliography</title>"];
461     case "USED FUNCTIONS"
462         txt = [txt; ""; "<refsection>"; "   <title>Used functions</title>"];
463     case "FINISHED"
464         txt = [txt; "</refentry>"];
465     end
466 endfunction
467 //==============================================================================