click-free a-delay bypass/enable
[ardour.git] / tools / fmt-luadoc.php
1 #!/usr/bin/php
2 <?php
3 ## USAGE
4 #
5 ## generate doc/luadoc.json.gz (lua binding doc)
6 # ./waf configure --luadoc ....
7 # ./waf
8 # ./gtk2_ardour/arluadoc > doc/luadoc.json.gz
9 #
10 ## generate doc/ardourapi.json.gz (ardour header doxygen doc)
11 # cd ../../tools/doxy2json
12 # ./ardourdoc.sh
13 # cd -
14 #
15 ## format HTML (using this scripterl)
16 # php tools/fmt-luadoc.php > /tmp/luadoc.html
17 #
18
19 $options = getopt("m");
20 if (isset ($options['m'])) {
21         $HTMLOUTPUT = false; ## set to false to output ardour-manual
22 } else {
23         $HTMLOUTPUT = true; ## set to false to output ardour-manual
24 }
25
26 ################################################################################
27 ################################################################################
28
29 $json = gzdecode (file_get_contents (dirname (__FILE__).'/../doc/luadoc.json.gz'));
30 $doc = array ();
31 $ardourversion = '';
32 foreach (json_decode ($json, true) as $b) {
33         if (!isset ($b['type'])) {
34                 if (isset ($b['version'])) { $ardourversion = $b['version']; }
35                 continue;
36         }
37         # reserved lua words
38         $b ['lua'] = preg_replace ('/:_end/', ':end', $b ['lua']);
39         $b ['lua'] = preg_replace ('/:_type/', ':type', $b ['lua']);
40         $b ['ldec'] = preg_replace ('/ const/', '', preg_replace ('/ const&/', '', $b['decl']));
41         if (isset ($b['ret'])) {
42                 $b['ret'] = preg_replace ('/ const/', '', preg_replace ('/ const&/', '', $b['ret']));
43         }
44         $doc[] = $b;
45 }
46
47 if (count ($doc) == 0) {
48         fwrite (STDERR, "Failed to read luadoc.json\n");
49         exit (1);
50 }
51
52 ################################################################################
53 ## Global result variables
54 ################################################################################
55
56 $classlist = array ();
57 $constlist = array ();
58
59
60 ################################################################################
61 ## Pre-process the data, collect functions, parse arguments, cross reference
62 ################################################################################
63
64
65 ################################################################################
66 # some internal helper functions first
67
68 $funclist = array ();
69 $classes = array ();
70 $consts = array ();
71
72 function my_die ($msg) {
73         fwrite (STDERR, $msg."\n");
74         exit (1);
75 }
76
77 ##function ptr_strip ($ctype) {
78 #       # boost::shared_ptr<std::list<boost::shared_ptr<ARDOUR::Route>> > >
79 #       # -> std::list<ARDOUR::Route>
80 #       $ctype = preg_replace ('/boost::shared_ptr<([^>]*)[ ]*>/', '$1', $ctype);
81 #       return preg_replace ('/boost::shared_ptr<([^>]*)[ ]*>/', '$1', $ctype);
82 #}
83
84 function arg2lua ($argtype, $flags = 0) {
85         global $classes;
86         global $consts;
87
88         # LuaBridge abstracts C++ references
89         $flags |= preg_match ('/&$/', $argtype);
90         $arg = preg_replace ('/&$/', '', $argtype);
91         $arg = preg_replace ('/ $/', '', $arg);
92
93         # filter out basic types
94         $builtin = array ('float', 'double', 'bool', 'std::string', 'int', 'short', 'long', 'unsigned int', 'unsigned short', 'unsigned long', 'unsigned char', 'char', 'void', 'char*', 'unsigned char*', 'void*');
95         if (in_array ($arg, $builtin)) {
96                 return array ($arg => $flags);
97         }
98
99         # check Class declarations first
100         foreach (array_merge ($classes, $consts) as $b) {
101                 if ($b['ldec'] == $arg) {
102                         return array ($b['lua'] => $flags);
103                 }
104         }
105
106         # strip class pointers -- TODO Check C'tor for given class
107         $arg = preg_replace ('/[&*]*$/', '', $argtype);
108         foreach (array_merge ($classes, $consts) as $b) {
109                 if ($b['ldec'] == $arg) {
110                         return array ($b['lua'] => $flags);
111                 }
112         }
113         if ($flags & 2) {
114                 return array ($argtype => ($flags | 4));
115         } else {
116                 return array ('--MISSING (' . $argtype . ')--' => ($flags | 4));
117         }
118 }
119
120 function stripclass ($classname, $name) {
121         $classname .= ':';
122         if (strpos ($name, $classname) !== 0) {
123                 my_die ('invalid class prefix: ' .$classname. ' -- '. $name);
124         }
125         return substr ($name, strlen ($classname));
126 }
127
128 function datatype ($decl) {
129         # TODO handle spaces in type. Works because
130         # we don't yet have templated types (with_space <here >)
131         return substr ($decl, 0, strrpos ($decl, ' '));
132 }
133
134 function luafn2class ($lua) {
135         return substr ($lua, 0, strrpos ($lua, ':'));
136 }
137
138 function luafn2name ($lua) {
139         $fn = strrpos ($lua, ':');
140         if ($fn !== 0 && strlen($lua) > $fn + 1) {
141                 return substr ($lua, $fn + 1);
142         }
143         my_die ('invalid class prefix: '. $name);
144 }
145
146
147 function checkclass ($b) {
148         global $classlist;
149         if (!isset ($classlist[luafn2class ($b['lua'])])) {
150                 my_die ('MISSING CLASS FOR '. print_r ($b['lua'], true));
151         }
152 }
153
154 # parse functions argument list to lua-names
155 function decl2args ($decl) {
156         $start = strrpos ($decl, '(');
157         $end = strrpos ($decl, ')');
158         $args = substr ($decl, $start + 1, $end - $start - 1);
159         $arglist = preg_split ('/, */', $args);
160         $rv = array ();
161         foreach ($arglist as $a) {
162                 if (empty ($a)) { continue; }
163                 $rv[] = arg2lua ($a);
164         }
165         return $rv;
166 }
167
168 function canonical_ctor ($b) {
169         $rv = '';
170         if (preg_match('/[^(]*\(([^)*]*)\*\)(\(.*\))/', $b['decl'], $matches)) {
171                 $lc = luafn2class ($b['lua']);
172                 $cn = str_replace (':', '::', $lc);
173                 $fn = substr ($lc, 1 + strrpos ($lc, ':'));
174                 $rv = $cn . '::'. $fn . $matches[2];
175         }
176         return $rv;
177 }
178
179 function canonical_decl ($b) {
180         $rv = '';
181         $pfx = '';
182         # match clang's declatation format
183         if (preg_match('/[^(]*\(([^)*]*)\*\)\((.*)\)/', $b['decl'], $matches)) {
184                 if (strpos ($b['type'], 'Free Function') !== false) {
185                         $pfx = str_replace (':', '::', luafn2class ($b['lua'])) . '::';
186                 }
187                 $fn = substr ($b['lua'], 1 + strrpos ($b['lua'], ':'));
188                 $rv = $matches[1] . $fn . '(';
189                 $arglist = preg_split ('/, */', $matches[2]);
190                 $first = true;
191                 foreach ($arglist as $a) {
192                         if (!$first) { $rv .= ', '; }; $first = false;
193                         if (empty ($a)) { continue; }
194                         $a = preg_replace ('/([^>]) >/', '$1>', $a);
195                         $a = preg_replace ('/^Cairo::/', '', $a); // special case cairo enums
196                         $a = preg_replace ('/([^ ])&/', '$1 &', $a);
197                         $a = str_replace ('vector', 'std::vector', $a);
198                         $a = str_replace ('std::string', 'string', $a);
199                         $a = str_replace ('string const', 'const string', $a);
200                         $a = str_replace ('string', 'std::string', $a);
201                         $rv .= $a;
202                 }
203                 $rv .= ')';
204         }
205         return $pfx . $rv;
206 }
207
208 ################################################################################
209 # step 1: build class indices
210
211 foreach ($doc as $b) {
212         if (strpos ($b['type'], "[C] ") === 0) {
213                 $classes[] = $b;
214                 $classlist[$b['lua']] = $b;
215                 if (strpos ($b['type'], 'Pointer Class') === false) {
216                         $classdecl[$b['ldec']] = $b;
217                 }
218         }
219 }
220
221 foreach ($classes as $c) {
222         if (strpos ($c['type'], 'Pointer Class') !== false) { continue; }
223         if (isset ($c['parent'])) {
224                 if (isset ($classdecl[$c['parent']])) {
225                         $classlist[$c['lua']]['luaparent'][] = $classdecl[$c['parent']]['lua'];
226                 } else {
227                         my_die ('unknown parent class: ' . print_r ($c, true));
228                 }
229         }
230 }
231
232 # step 2: extract constants/enum
233 foreach ($doc as $b) {
234         switch ($b['type']) {
235         case "Constant/Enum":
236         case "Constant/Enum Member":
237                 if (strpos ($b['ldec'], '::') === false) {
238                         # for extern c enums, use the Lua Namespace
239                         $b['ldec'] = str_replace (':', '::', luafn2class ($b['lua']));
240                 }
241                 $ns = str_replace ('::', ':', $b['ldec']);
242                 $constlist[$ns][] = $b;
243                 # arg2lua lookup
244                 $b['lua'] = $ns;
245                 $consts[] = $b;
246                 break;
247         default:
248                 break;
249         }
250 }
251
252 # step 3: process functions
253 foreach ($doc as $b) {
254         switch ($b['type']) {
255         case "Constructor":
256         case "Weak/Shared Pointer Constructor":
257                 checkclass ($b);
258                 $classlist[luafn2class ($b['lua'])]['ctor'][] = array (
259                         'name' => luafn2class ($b['lua']),
260                         'args' => decl2args ($b['ldec']),
261                         'cand' => canonical_ctor ($b)
262                 );
263                 break;
264         case "Property":
265                 checkclass ($b);
266                 $classlist[luafn2class ($b['lua'])]['props'][] = array (
267                         'name' => $b['lua'],
268                         'ret'  => arg2lua (datatype ($b['ldec']))
269                 );
270                 break;
271         case "Data Member":
272                 checkclass ($b);
273                 $classlist[luafn2class ($b['lua'])]['data'][] = array (
274                         'name' => $b['lua'],
275                         'ret'  => arg2lua (datatype ($b['ldec']))
276                 );
277                 break;
278         case "Static C Function":
279                 checkclass ($b);
280                 if (strpos ($b['lua'], 'ARDOUR:DataType:') === 0) {
281                         # special case ARDOUR:DataType convenience c'tor
282                         $args = array ();
283                         $ret = array (luafn2class ($b['lua']) => 0);
284                         $canon = 'ARDOUR::LuaAPI::datatype_ctor_'.strtolower (luafn2name ($b['lua'])).'(lua_State*)';
285                 } else {
286                         my_die ('unhandled Static C: ' . print_r($b, true));
287                 }
288                 $classlist[luafn2class ($b['lua'])]['func'][] = array (
289                         'bind' => $b,
290                         'name' => $b['lua'],
291                         'args' => $args,
292                         'ret'  => $ret,
293                         'ref'  => false,
294                         'ext'  => false,
295                         'cand' => $canon
296                 );
297                 break;
298         case "C Function":
299                 # we required C functions to be in a class namespace
300         case "Ext C Function":
301                 checkclass ($b);
302                 $args = array (array ('--lua--' => 0));
303                 $ret = array ('...' => 0);
304                 $ns = luafn2class ($b['lua']);
305                 $cls = $classlist[$ns];
306                 if (preg_match ('/.*<([^>]*)[ ]*>/', $cls['ldec'], $templ)) {
307                         # std::vector, std::list types
308                         switch (stripclass($ns, $b['lua'])) {
309                         case 'add':
310                                 #$args = array (array ('LuaTable {'.$templ[1].'}' => 0));
311                                 $args = array (arg2lua ($templ[1], 2));
312                                 $ret = array ('LuaTable' => 0);
313                                 break;
314                         case 'iter':
315                                 $args = array ();
316                                 $ret = array ('LuaIter' => 0);
317                                 break;
318                         case 'table':
319                                 $args = array ();
320                                 $ret = array ('LuaTable' => 0);
321                                 break;
322                         default:
323                                 break;
324                         }
325                 } else if (strpos ($cls['type'], ' Array') !== false) {
326                         # catches  C:FloatArray, C:IntArray
327                         $templ = preg_replace ('/[&*]*$/', '', $cls['ldec']);
328                         switch (stripclass($ns, $b['lua'])) {
329                         case 'array':
330                                 $args = array ();
331                                 $ret = array ('LuaMetaTable' => 0);
332                                 break;
333                         case 'get_table':
334                                 $args = array ();
335                                 $ret = array ('LuaTable' => 0);
336                                 break;
337                         case 'set_table':
338                                 $args = array (array ('LuaTable {'.$templ.'}' => 0));
339                                 $ret = array ('void' => 0);
340                                 break;
341                         default:
342                                 break;
343                         }
344                 }
345                 $classlist[luafn2class ($b['lua'])]['func'][] = array (
346                         'bind' => $b,
347                         'name' => $b['lua'],
348                         'args' => $args,
349                         'ret'  => $ret,
350                         'ref'  => true,
351                         'ext'  => true,
352                         'cand' => canonical_decl ($b)
353                 );
354                 break;
355         case "Free C Function":
356                 $funclist[luafn2class ($b['lua'])][] = array (
357                         'bind' => $b,
358                         'name' => $b['lua'],
359                         'args' => $args,
360                         'ret'  => $ret,
361                         'ref'  => false,
362                         'ext'  => true,
363                         'cand' => str_replace (':', '::', $b['lua']).'(lua_State*)'
364                 );
365                 break;
366         case "Free Function":
367         case "Free Function RefReturn":
368                 $funclist[luafn2class ($b['lua'])][] = array (
369                         'bind' => $b,
370                         'name' => $b['lua'],
371                         'args' => decl2args ($b['ldec']),
372                         'ret'  => arg2lua ($b['ret']),
373                         'ref'  => (strpos ($b['type'], "RefReturn") !== false),
374                         'cand' => canonical_decl ($b)
375                 );
376                 break;
377         case "Member Function":
378         case "Member Function RefReturn":
379         case "Member Pointer Function":
380         case "Weak/Shared Pointer Function":
381         case "Weak/Shared Pointer Function RefReturn":
382         case "Weak/Shared Null Check":
383         case "Static Member Function":
384                 checkclass ($b);
385                 $classlist[luafn2class ($b['lua'])]['func'][] = array (
386                         'bind' => $b,
387                         'name' => $b['lua'],
388                         'args' => decl2args ($b['ldec']),
389                         'ret'  => arg2lua ($b['ret']),
390                         'ref'  => (strpos ($b['type'], "RefReturn") !== false),
391                         'cand' => canonical_decl ($b)
392                 );
393                 break;
394         case "Cast":
395         case "Weak/Shared Pointer Cast":
396                 checkclass ($b);
397                 $classlist[luafn2class ($b['lua'])]['cast'][] = array (
398                         'bind' => $b,
399                         'name' => $b['lua'],
400                         'args' => decl2args ($b['ldec']),
401                         'ret'  => arg2lua ($b['ret']),
402                         'ref'  => (strpos ($b['type'], "RefReturn") !== false),
403                         'cand' => canonical_decl ($b)
404                 );
405                 break;
406         case "Constant/Enum":
407         case "Constant/Enum Member":
408                 # already handled -> $consts
409                 break;
410         default:
411                 if (strpos ($b['type'], "[C] ") !== 0) {
412                         my_die ('unhandled type: ' . $b['type']);
413                 }
414                 break;
415         }
416 }
417
418
419 # step 4: collect/group/sort
420
421 # step 4a: unify weak/shared Ptr classes
422 foreach ($classlist as $ns => $cl) {
423         if (strpos ($cl['type'], ' Array') !== false) {
424                 $classlist[$ns]['arr'] = true;
425                 $classlist[$ns]['cdecl'] = $cl['decl'];
426                 continue;
427         }
428         foreach ($classes as $c) {
429                 if ($c['lua'] == $ns) {
430                         if (strpos ($c['type'], 'Pointer Class') !== false) {
431                                 $classlist[$ns]['ptr'] = true;
432                                 $classlist[$ns]['cdecl'] = 'boost::shared_ptr< '.$c['decl']. ' >, boost::weak_ptr< '.$c['decl']. ' >';
433                                 break;
434                         } else {
435                                 $classlist[$ns]['cdecl'] = $c['decl'];
436                         }
437                 }
438         }
439 }
440
441 # step4b: sanity check
442 foreach ($classlist as $ns => $cl) {
443         if (isset ($classes[$ns]['parent']) && !isset ($classlist[$ns]['luaparent'])) {
444                 my_die ('missing parent class: ' . print_r ($cl, true));
445         }
446 }
447
448 # step 4c: merge free functions into classlist
449 foreach ($funclist as $ns => $fl) {
450         if (isset ($classlist[$ns])) {
451                 my_die ('Free Funcion in existing namespace: '.$ns.' '. print_r ($ns, true));
452         }
453         $classlist[$ns]['func'] = $fl;
454         $classlist[$ns]['free'] = true;
455 }
456
457 # step 4d: order to chaos
458 # no array_multisort() here, sub-types are sorted after merging parents
459 ksort ($classlist);
460
461
462 ################################################################################
463 ################################################################################
464 ################################################################################
465
466
467 #### -- split here --  ####
468
469 # from here on, only $classlist and $constlist arrays are relevant.
470 # we also pull in C++ header annotation from doxygen to $api
471
472
473 # read documentation from doxygen
474 $json = gzdecode (file_get_contents (dirname (__FILE__).'/../doc/ardourapi.json.gz'));
475 $api = array ();
476 foreach (json_decode ($json, true) as $a) {
477         if (!isset ($a['decl'])) { continue; }
478         if (empty ($a['decl'])) { continue; }
479         $canon = str_replace (' *', '*', $a['decl']);
480         $api[$canon] = $a;
481 }
482
483 # keep track of found/missing doc
484 $dox_found = 0;
485 $dox_miss = 0;
486
487 # retrive a value from $api
488 function doxydoc ($canonical_declaration) {
489         global $api;
490         global $dox_found;
491         global $dox_miss;
492         if (isset ($api[$canonical_declaration])) {
493                 $dox_found++;
494                 return $api[$canonical_declaration]['doc'];
495         }
496         // remove template namespace e.g.
497         //  "ARDOUR::Track::bounceable(boost::shared_ptr<ARDOUR::Processor>"
498         //  "ARDOUR::Track::bounceable(boost::shared_ptr<Processor>"
499         $cn = preg_replace ('/<[^>]*::([^>]*)>/', '<$1>', $canonical_declaration);
500         if (isset ($api[$cn])) {
501                 $dox_found++;
502                 return $api[$cn]['doc'];
503         }
504         #fwrite (STDERR, $canonical_declaration."\n"); # XXX DEBUG
505
506         $dox_miss++;
507         return '';
508 }
509
510 ################################################################################
511 # OUTPUT
512 ################################################################################
513
514
515 ################################################################################
516 # Helper functions
517 define ('NL', "\n");
518
519 # constructors, enums (constants) use a dot.  (e.g. "LuaOSC.Address" -> "LuaOSC.Address" )
520 function ctorname ($name) {
521         return htmlentities (str_replace (':', '.', $name));
522 }
523
524 # strip class prefix (e.g "Evoral:MidiEvent:channel"  -> "channel")
525 function shortname ($name) {
526         return htmlentities (substr ($name, strrpos ($name, ':') + 1));
527 }
528
529 # retrieve variable name from    array["VARNAME"] => FLAGS
530 function varname ($a) {
531         return array_keys ($a)[0];
532 }
533
534 # recusively collect class parents (derived classes)
535 function traverse_parent ($ns, &$inherited) {
536         global $classlist;
537         $rv = '';
538         if (isset ($classlist[$ns]['luaparent'])) {
539                 $parents = array_unique ($classlist[$ns]['luaparent']);
540                 asort ($parents);
541                 foreach ($parents as $p) {
542                         if (!empty ($rv)) { $rv .= ', '; }
543                         $rv .= typelink ($p);
544                         $inherited[$p] = $classlist[$p];
545                         traverse_parent ($p, $inherited);
546                 }
547         }
548         return $rv;
549 }
550
551 # create a cross-reference to a type (class or enum)
552 # *all* <a> links are generated here, currently anchors on a single page.
553 function typelink ($a, $short = false, $argcls = '', $linkcls = '', $suffix = '') {
554         global $classlist;
555         global $constlist;
556         if (isset($classlist[$a]['free'])) {
557                 return '<a class="'.$linkcls.'" href="#'.htmlentities ($a).'">'.($short ? shortname($a) : ctorname($a)).$suffix.'</a>';
558         } else if (in_array ($a, array_keys ($classlist))) {
559                 return '<a class="'.$linkcls.'" href="#'.htmlentities($a).'">'.($short ? shortname($a) : htmlentities($a)).$suffix.'</a>';
560         } else if (in_array ($a, array_keys ($constlist))) {
561                 return '<a class="'.$linkcls.'" href="#'.ctorname ($a).'">'.($short ? shortname($a) : ctorname($a)).$suffix.'</a>';
562         } else {
563                 return '<span class="'.$argcls.'">'.htmlentities($a).$suffix.'</span>';
564         }
565 }
566
567 # output format function arguments
568 function format_args ($args) {
569         $rv = '<span class="functionargs"> (';
570         $first = true;
571         foreach ($args as $a) {
572                 if (!$first) { $rv .= ', '; }; $first = false;
573                 $flags = $a[varname ($a)];
574                 if ($flags & 2) {
575                         $rv .= '<em>LuaTable</em> {'.typelink (varname ($a), true, 'em').'}';
576                 }
577                 elseif ($flags & 1) {
578                         $rv .= typelink (varname ($a), true, 'em', '', '&amp;');
579                 }
580                 else {
581                         $rv .= typelink (varname ($a), true, 'em');
582                 }
583         }
584         $rv .= ')</span>';
585         return $rv;
586 }
587
588 # format doxygen documentation for class-definition
589 function format_doxyclass ($cl) {
590         $rv = '';
591         if (isset ($cl['decl'])) {
592                 $doc = doxydoc ($cl['decl']);
593                 if (!empty ($doc)) {
594                         $rv.= '<div class="classdox">'.$doc.'</div>'.NL;
595                 }
596         }
597         return $rv;
598 }
599
600 # format doxygen documentation for class-members
601 function format_doxydoc ($f) {
602         $rv = '';
603         if (isset ($f['cand'])) {
604                 $doc = doxydoc ($f['cand']);
605                 if (!empty ($doc)) {
606                         $rv.= '<tr><td></td><td class="doc" colspan="2"><div class="dox">'.$doc;
607                         $rv.= '</div></td></tr>'.NL;
608                 } else if (0) { # debug
609                         $rv.= '<tr><td></td><td class="doc" colspan="2"><p>'.htmlentities($f['cand']).'</p>';
610                         $rv.= '</td></tr>'.NL;
611                 }
612         }
613         return $rv;
614 }
615
616 # usort() callback for class-members
617 function name_sort_cb ($a, $b) {
618         return strcmp ($a['name'], $b['name']);
619 }
620
621 # main output function for every class
622 function format_class_members ($ns, $cl, &$dups) {
623         $rv = '';
624         # print contructor - if any
625         if (isset ($cl['ctor'])) {
626                 usort ($cl['ctor'], 'name_sort_cb');
627                 $rv.= ' <tr><th colspan="3">Constructor</th></tr>'.NL;
628                 foreach ($cl['ctor'] as $f) {
629                         $rv.= ' <tr><td class="def">&Copf;</td><td class="decl">';
630                         $rv.= '<span class="functionname">'.ctorname ($f['name']).'</span>';
631                         $rv.= format_args ($f['args']);
632                         $rv.= '</td><td class="fill"></td></tr>'.NL;
633                         # doxygen documentation (may be empty)
634                         $rv.= format_doxydoc($f);
635                 }
636         }
637
638         # strip duplicates (inherited or derived methods)
639         # e.g  AudioTrack -> Track -> Route -> SessionObject -> Stateful
640         # all 5 have "isnil()"
641         $nondups = array ();
642         if (isset ($cl['func'])) {
643                 foreach ($cl['func'] as $f) {
644                         if (in_array (stripclass ($ns, $f['name']), $dups)) { continue; }
645                         $nondups[] = $f;
646                 }
647         }
648
649         # print methods - if any
650         if (count ($nondups) > 0) {
651                 usort ($nondups, 'name_sort_cb');
652                 $rv.= ' <tr><th colspan="3">Methods</th></tr>'.NL;
653                 foreach ($nondups as $f) {
654                         $dups[] = stripclass ($ns, $f['name']);
655                         # return value/type
656                         $rv.= ' <tr><td class="def">';
657                         if ($f['ref'] && isset ($f['ext'])) {
658                                 # external C functions
659                                 $rv.= '<em>'.varname ($f['ret']).'</em>';
660                         } elseif ($f['ref'] && varname ($f['ret']) == 'void') {
661                                 # void functions with reference args
662                                 $rv.= '<em>LuaTable</em>(...)';
663                         } elseif ($f['ref']) {
664                                 # functions with reference args and return value
665                                 $rv.= '<em>LuaTable</em>('.typelink (varname ($f['ret']), true, 'em').', ...)';
666                         } else {
667                                 # normal class members
668                                 $rv.= typelink (varname ($f['ret']), true, 'em');
669                         }
670                         # function declaration and arguments
671                         $rv.= '</td><td class="decl">';
672                         $rv.= '<span class="functionname"><abbr title="'.htmlentities($f['bind']['decl']).'">'.stripclass ($ns, $f['name']).'</abbr></span>';
673                         $rv.= format_args ($f['args']);
674                         $rv.= '</td><td class="fill"></td></tr>'.NL;
675                         # doxygen documentation (may be empty)
676                         $rv.= format_doxydoc($f);
677                 }
678         }
679         # print cast - if any
680         if (isset ($cl['cast'])) {
681                 usort ($cl['cast'], 'name_sort_cb');
682                 $rv.= ' <tr><th colspan="3">Cast</th></tr>'.NL;
683                 foreach ($cl['cast'] as $f) {
684                         $rv.= ' <tr><td class="def">';
685                         $rv.= typelink (varname ($f['ret']), true, 'em');
686                         # function declaration and arguments
687                         $rv.= '</td><td class="decl">';
688                         $rv.= '<span class="functionname"><abbr title="'.htmlentities($f['bind']['decl']).'">'.stripclass ($ns, $f['name']).'</abbr></span>';
689                         $rv.= format_args ($f['args']);
690                         $rv.= '</td><td class="fill"></td></tr>'.NL;
691                         # doxygen documentation (may be empty)
692                         $rv.= format_doxydoc($f);
693                 }
694         }
695
696         # print properties - if any
697         if (isset ($cl['props'])) {
698                 usort ($cl['props'], 'name_sort_cb');
699                 $rv.= ' <tr><th colspan="3">Properties</th></tr>'.NL;
700                 foreach ($cl['props'] as $f) {
701                         $rv.= ' <tr><td class="def">'.typelink (array_keys ($f['ret'])[0], false, 'em').'</td><td class="decl">';
702                         $rv.= '<span class="functionname">'.stripclass ($ns, $f['name']).'</span>';
703                         $rv.= '</td><td class="fill"></td></tr>'.NL;
704                 }
705         }
706
707         # print data members - if any
708         if (isset ($cl['data'])) {
709                 usort ($cl['data'], 'name_sort_cb');
710                 $rv.= ' <tr><th colspan="3">Data Members</th></tr>'.NL;
711                 foreach ($cl['data'] as $f) {
712                         $rv.= ' <tr><td class="def">'.typelink (array_keys ($f['ret'])[0], false, 'em').'</td><td class="decl">';
713                         $rv.= '<span class="functionname">'.stripclass ($ns, $f['name']).'</span>';
714                         $rv.= '</td><td class="fill"></td></tr>'.NL;
715                 }
716         }
717         return $rv;
718 }
719
720
721 ################################################################################
722 # Start Output
723
724 if ($HTMLOUTPUT) {
725
726 ?><!DOCTYPE html>
727 <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
728 <head>
729 <title>Ardour Lua Bindings</title>
730 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
731 <style type="text/css">
732 div.header         { text-align:center; }
733 div.header h2      { margin:0; }
734 div.header p       { margin:.25em; text-align:center; }
735 div.luafooter      { text-align:center; font-size:80%; color: #888; margin: 2em 0; }
736 #luaref            { max-width:60em; margin: 1em auto; }
737
738 #luaref h2                 { margin:2em 0 0 0; padding:0em; border-bottom: 1px solid black; }
739 #luaref h3.cls             { margin:2em 0 0 0; padding: 0 0 0 1em; border: 1px dashed #6666ee; }
740 #luaref h3.cls abbr        { text-decoration:none; cursor:default; }
741 #luaref h4.cls             { margin:1em 0 0 0; }
742 #luaref h3.class           { background-color: #aaee66; }
743 #luaref h3.enum            { background-color: #aaaaaa; }
744 #luaref h3.pointerclass    { background-color: #eeaa66; }
745 #luaref h3.array           { background-color: #66aaee; }
746 #luaref h3.opaque          { background-color: #6666aa; }
747 #luaref p                  { text-align: justify; }
748 #luaref p.cdecl            { text-align: right; float:right; font-size:90%; margin:0; padding: 0 0 0 1em; }
749 #luaref ul.classindex      { columns: 2; -webkit-columns: 2; -moz-columns: 2; }
750 #luaref div.clear          { clear:both; }
751 #luaref p.classinfo        { margin: .25em 0; }
752 #luaref div.code           { width:80%; margin:.5em auto; }
753 #luaref div.code div       { width:45%; }
754 #luaref div.code pre       { line-height: 1.2em; margin: .25em 0; }
755 #luaref div.code samp      { color: green; font-weight: bold; background-color: #eee; }
756 #luaref div.classdox       { padding: .1em 1em; }
757 #luaref div.classdox p     { margin: .5em 0 .5em .6em; }
758 #luaref div.classdox p     { margin: .5em 0 .5em .6em; }
759 #luaref div.classdox       { padding: .1em 1em; }
760 #luaref div.classdox p     { margin: .5em 0 .5em .6em; }
761 #luaref table.classmembers { width: 100%; }
762 #luaref table.classmembers th      { text-align:left; border-bottom:1px solid black; padding-top:1em; }
763 #luaref table.classmembers td.def  { text-align:right; padding-right:.5em;  white-space: nowrap; }
764 #luaref table.classmembers td.decl { text-align:left; padding-left:.5em; white-space: nowrap; }
765 #luaref table.classmembers td.doc  { text-align:left; padding-left:.6em; line-height: 1.2em; font-size:80%; }
766 #luaref table.classmembers td.doc div.dox {background-color:#eee; padding: .1em 1em; }
767 #luaref table.classmembers td.doc p { margin: .5em 0; }
768 #luaref table.classmembers td.doc p.para-brief { font-size:120%; }
769 #luaref table.classmembers td.doc p.para-returns { font-size:120%; }
770 #luaref table.classmembers td.doc dl { font-size:120%; line-height: 1.3em; }
771 #luaref table.classmembers td.doc dt { font-style: italic; }
772 #luaref table.classmembers td.fill { width: 99%; }
773 #luaref table.classmembers span.em { font-style: italic; }
774 #luaref span.functionname abbr     { text-decoration:none; cursor:default; }
775 </style>
776 </head>
777 <body>
778 <div class="header">
779 <h2>Ardour Lua Bindings</h2>
780 <p>
781 <a href="#h_classes">Class Documentation</a>
782 &nbsp;|&nbsp;
783 <a href="#h_enum">Enum/Constants</a>
784 &nbsp;|&nbsp;
785 <a href="#h_index">Index</a>
786 </p>
787 </div>
788
789 <!-- #### SNIP #### !-->
790
791 <?php
792
793 } else {
794
795 ?>
796 ---
797 layout: default
798 style: luadoc
799 title: Class Reference
800 ---
801
802 <p class="warning">
803 This documentation is far from complete may be inaccurate and subject to change.
804 </p>
805
806 <?php
807 }
808 ?>
809
810 <div id="luaref">
811
812 <?php
813
814 ################################################################################
815 # some general documentation -- should really go elsehere
816
817 ?>
818
819 <h2 id="h_intro">Overview</h2>
820 <p>
821 The top-level entry point are <?=typelink('ARDOUR:Session')?> and <?=typelink('ArdourUI:Editor')?>.
822 Most other Classes are used indirectly starting with a Session function. e.g. Session:get_routes().
823 </p>
824 <p>
825 A few classes are dedicated to certain script types, e.g. Lua DSP processors have exclusive access to
826 <?=typelink('ARDOUR:DSP')?> and <?=typelink('ARDOUR:ChanMapping')?>. Action Hooks Scripts to
827 <?=typelink('LuaSignal:Set')?> etc.
828 </p>
829 <p>
830 Detailed documentation (parameter names, method description) is not yet available. Please stay tuned.
831 </p>
832 <h3>Short introduction to Ardour classes</h3>
833 <p>
834 Ardour's structure is object oriented. The main object is the Session. A Session contains Audio Tracks, Midi Tracks and Busses.
835 Audio and Midi tracks are derived from a more general "Track" Object,  which in turn is derived from a "Route" (aka Bus).
836 (We say "An Audio Track <em>is-a</em> Track <em>is-a</em> Route").
837 Tracks contain specifics. For Example a track <em>has-a</em> diskstream (for file i/o).
838 </p>
839 <p>
840 Operations are performed on objects. One gets a reference to an object and then calls a method.
841 e.g <code>obj = Session:route_by_name("Audio")   obj:set_name("Guitar")</code>.
842 </p>
843 <p>
844 Lua automatically follows C++ class inheritance. e.g one can directly call all SessionObject and Route methods on Track object. However lua does not automatically promote objects. A Route object which just happens to be a Track needs to be explicily cast to a Track. Methods for casts are provided with each class. Note that the cast may fail and return a <em>nil</em> reference.
845 </p>
846 <p>
847 Likewise multiple inheritance is a <a href="http://www.lua.org/pil/16.3.html">non-trivial issue</a> in lua. To avoid performance penalties involved with lookups, explicit casts are required in this case. One example is <?=typelink('ARDOUR:SessionObject')?> which is-a StatefulDestructible which inhertis from both Stateful and Destructible.
848 </p>
849 <p>
850 Object lifetimes are managed by the Session. Most Objects cannot be directly created, but one asks the Session to create or destroy them. This is mainly due to realtime constrains:
851 you cannot simply remove a track that is currently processing audio. There are various <em>factory</em> methods for object creation or removal.
852 </p>
853 <h3>Pass by Reference</h3>
854 <p>
855 Since lua functions are closures, C++ methods that pass arguments by reference cannot be used as-is.
856 All parameters passed to a C++ method which uses references are returned as Lua Table.
857 If the C++ method also returns a value it is prefixed. Two parameters are returned: the value and a Lua Table holding the parameters.
858 </p>
859
860 <div class="code">
861         <div style="float:left;">C++
862
863 <pre><code class="cxx">void set_ref (int&amp; var, long&amp; val)
864 {
865         printf ("%d %ld\n", var, val);
866         var = 5;
867         val = 7;
868 }
869 </code></pre>
870
871         </div>
872         <div style="float:right;">Lua
873
874 <pre><code class="lua">local var = 0;
875 ref = set_ref (var, 2);
876 -- output from C++ printf()
877 </code><samp class="lua">0 2</samp><code>
878 -- var is still 0 here
879 print (ref[1], ref[2])
880 </code><samp class="lua">5 7</samp></pre>
881
882         </div>
883 </div>
884 <div class="clear"></div>
885 <div class="code">
886         <div style="float:left;">
887
888 <pre><code class="cxx">int set_ref2 (int &amp;var, std::string unused)
889 {
890         var = 5;
891         return 3;
892 }
893 </code></pre>
894
895         </div>
896         <div style="float:right;">
897 <pre><code class="lua">rv, ref = set_ref2 (0, "hello");
898 print (rv, ref[1], ref[2])
899 </code><samp class="lua">3 5 hello</samp></pre>
900         </div>
901 </div>
902 <div class="clear"></div>
903
904 <h3>Pointer Classes</h3>
905 <p>
906 Libardour makes extensive use of reference counted <code>boost::shared_ptr</code> to manage lifetimes.
907 The Lua bindings provide a complete abstration of this. There are no pointers in lua.
908 For example a <?=typelink('ARDOUR:Route')?> is a pointer in C++, but lua functions operate on it like it was a class instance.
909 </p>
910 <p>
911 <code>shared_ptr</code> are reference counted. Once assigned to a lua variable, the C++ object will be kept and remains valid.
912 It is good practice to assign references to lua <code>local</code> variables or reset the variable to <code>nil</code> to drop the ref.
913 </p>
914 <p>
915 All pointer classes have a <code>isnil ()</code> method. This is for two cases:
916 Construction may fail. e.g. <code><?=typelink('ARDOUR:LuaAPI')?>.newplugin()</code>
917 may not be able to find the given plugin and hence cannot create an object.
918 </p>
919 <p>
920 The second case if for <code>boost::weak_ptr</code>. As opposed to <code>boost::shared_ptr</code> weak-pointers are not reference counted.
921 The object may vanish at any time.
922 If lua code calls a method on a nil object, the interpreter will raise an exception and the script will not continue.
923 This is not unlike <code>a = nil a:test()</code> which results in en error "<em>attempt to index a nil value</em>".
924 </p>
925 <p>
926 From the lua side of things there is no distinction between weak and shared pointers. They behave identically.
927 Below they're inidicated in orange and have an arrow to indicate the pointer type.
928 Pointer Classes cannot be created in lua scripts. It always requires a call to C++ to create the Object and obtain a reference to it.
929 </p>
930
931
932 <?php
933
934 #################################
935 # Main output function -- Classes
936
937 echo '<h2 id="h_classes">Class Documentation</h2>'.NL;
938 foreach ($classlist as $ns => $cl) {
939         $dups = array ();
940         $tbl =  format_class_members ($ns, $cl, $dups);
941
942         # format class title - depending on type
943         if (empty ($tbl)) {
944                 # classes with no members (no ctor, no methods, no data)
945                 echo '<h3 id="'.htmlentities ($ns).'" class="cls opaque"><abbr title="Opaque Object">&empty;</abbr>&nbsp;'.htmlentities ($ns).'</h3>'.NL;
946         }
947         else if (isset ($classlist[$ns]['free'])) {
948                 # free functions (no class)
949                 echo '<h3 id="'.htmlentities ($ns).'" class="cls freeclass"><abbr title="Namespace">&Nopf;</abbr>&nbsp;'.ctorname($ns).'</h3>'.NL;
950         }
951         else if (isset ($classlist[$ns]['arr'])) {
952                 # C Arrays
953                 echo '<h3 id="'.htmlentities ($ns).'" class="cls array"><abbr title="C Array">&ctdot;</abbr>&nbsp;'.htmlentities ($ns).'</h3>'.NL;
954         }
955         else if (isset ($classlist[$ns]['ptr'])) {
956                 # Pointer Classes
957                 echo '<h3 id="'.htmlentities ($ns).'" class="cls pointerclass"><abbr title="Pointer Class">&Rarr;</abbr>&nbsp;'. htmlentities ($ns).'</h3>'.NL;
958         }
959         else {
960                 # Normal Class
961                 echo '<h3 id="'.htmlentities ($ns).'" class="cls class"><abbr title="Class">&comp;</abbr>&nbsp;'.htmlentities ($ns).'</h3>'.NL;
962         }
963
964         # show original C++ declaration
965         if (isset ($cl['cdecl'])) {
966                 echo '<p class="cdecl"><em>C&#8225;</em>: '.htmlentities ($cl['cdecl']).'</p>'.NL;
967         }
968
969         # print class inheritance (direct parent *name* only)
970         $inherited = array ();
971         $isa = traverse_parent ($ns, $inherited);
972         if (!empty ($isa)) {
973                 echo ' <p class="classinfo">is-a: '.$isa.'</p>'.NL;
974         }
975         echo '<div class="clear"></div>'.NL;
976
977
978         # class documentation (if any)
979         echo format_doxyclass ($cl);
980
981         # member documentation
982         if (empty ($tbl)) {
983                 echo '<p class="classinfo">This class object is only used indirectly as return-value and function-parameter. It provides no methods by itself.</p>'.NL;
984         } else {
985                 echo '<table class="classmembers">'.NL;
986                 echo $tbl;
987                 echo ' </table>'.NL;
988         }
989
990         # traverse parent classes (all inherited members)
991         foreach ($inherited as $pns => $pcl) {
992                 $tbl = format_class_members ($pns, $pcl, $dups);
993                 if (!empty ($tbl)) {
994                         echo '<h4 class="cls">Inherited from '.$pns.'</h4>'.NL;
995                         echo '<table class="classmembers">'.NL;
996                         echo $tbl;
997                         echo '</table>'.NL;
998                 }
999         }
1000 }
1001
1002 ####################
1003 # Enum and Constants
1004
1005 echo '<h2 id="h_enum">Enum/Constants</h2>'.NL;
1006 foreach ($constlist as $ns => $cs) {
1007         echo '<h3 id="'.ctorname ($ns).'" class="cls enum"><abbr title="Enum">&isin;</abbr>&nbsp;'.ctorname ($ns).'</h3>'.NL;
1008         echo '<ul class="enum">'.NL;
1009         foreach ($cs as $c) {
1010                 echo '<li class="const">'.ctorname ($c['lua']).'</li>'.NL;
1011         }
1012         echo '</ul>'.NL;
1013 }
1014
1015 ######################
1016 # Index of all classes
1017
1018 echo '<h2 id="h_index" >Class Index</h2>'.NL;
1019 echo '<ul class="classindex">'.NL;
1020 foreach ($classlist as $ns => $cl) {
1021         echo '<li>'.typelink($ns).'</li>'.NL;
1022 }
1023 echo '</ul>'.NL;
1024
1025
1026 # see how far there is still to go...
1027 fwrite (STDERR, "Found $dox_found annotations. missing: $dox_miss\n");
1028 echo '<!-- '.$dox_found.' / '.$dox_miss.' !-->'.NL;
1029
1030 ?>
1031 </div>
1032 <div class="luafooter">Ardour <?=$ardourversion?> &nbsp;-&nbsp; <?=date('r')?></div>
1033 <?php
1034
1035 if ($HTMLOUTPUT) {
1036         echo '<!-- #### SNIP #### !-->'.NL;
1037         echo '</body>'.NL;
1038         echo '</html>'.NL;
1039 }