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