mirror of
				https://github.com/KevinMidboe/linguist.git
				synced 2025-10-29 17:50:22 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			631 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Raku
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			631 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			Raku
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env perl6
 | |
| use v6;
 | |
| 
 | |
| # This script isn't in bin/ because it's not meant to be installed.
 | |
| 
 | |
| BEGIN say 'Initializing ...';
 | |
| 
 | |
| use Pod::To::HTML;
 | |
| use URI::Escape;
 | |
| use lib 'lib';
 | |
| use Perl6::TypeGraph;
 | |
| use Perl6::TypeGraph::Viz;
 | |
| use Perl6::Documentable::Registry;
 | |
| 
 | |
| my $*DEBUG = False;
 | |
| 
 | |
| my $tg;
 | |
| my %methods-by-type;
 | |
| my $footer = footer-html;
 | |
| my $head   = q[
 | |
| <link rel="icon" href="/favicon.ico" type="favicon.ico" />
 | |
| <link rel="stylesheet" type="text/css" href="/style.css" media="screen" title="default" />
 | |
| ];
 | |
| 
 | |
| 
 | |
| sub url-munge($_) {
 | |
|     return $_ if m{^ <[a..z]>+ '://'};
 | |
|     return "/type/$_" if m/^<[A..Z]>/;
 | |
|     return "/routine/$_" if m/^<[a..z]>/;
 | |
|     # poor man's <identifier>
 | |
|     if m/ ^ '&'( \w <[[\w'-]>* ) $/ {
 | |
|         return "/routine/$0";
 | |
|     }
 | |
|     return $_;
 | |
| }
 | |
| 
 | |
| sub p2h($pod) {
 | |
|     pod2html($pod, :url(&url-munge), :$footer, :$head);
 | |
| }
 | |
| 
 | |
| sub pod-gist(Pod::Block $pod, $level = 0) {
 | |
|     my $leading = ' ' x $level;
 | |
|     my %confs;
 | |
|     my @chunks;
 | |
|     for <config name level caption type> {
 | |
|         my $thing = $pod.?"$_"();
 | |
|         if $thing {
 | |
|             %confs{$_} = $thing ~~ Iterable ?? $thing.perl !! $thing.Str;
 | |
|         }
 | |
|     }
 | |
|     @chunks = $leading, $pod.^name, (%confs.perl if %confs), "\n";
 | |
|     for $pod.content.list -> $c {
 | |
|         if $c ~~ Pod::Block {
 | |
|             @chunks.push: pod-gist($c, $level + 2);
 | |
|         }
 | |
|         elsif $c ~~ Str {
 | |
|             @chunks.push: $c.indent($level + 2), "\n";
 | |
|         } elsif $c ~~ Positional {
 | |
|             @chunks.push: $c.map: {
 | |
|                 if $_ ~~ Pod::Block {
 | |
|                     *.&pod-gist
 | |
|                 } elsif $_ ~~ Str {
 | |
|                     $_
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     @chunks.join;
 | |
| }
 | |
| 
 | |
| sub recursive-dir($dir) {
 | |
|     my @todo = $dir;
 | |
|     gather while @todo {
 | |
|         my $d = @todo.shift;
 | |
|         for dir($d) -> $f {
 | |
|             if $f.f {
 | |
|                 take $f;
 | |
|             }
 | |
|             else {
 | |
|                 @todo.push($f.path);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub first-code-block(@pod) {
 | |
|     if @pod[1] ~~ Pod::Block::Code {
 | |
|         return @pod[1].content.grep(Str).join;
 | |
|     }
 | |
|     '';
 | |
| }
 | |
| 
 | |
| sub MAIN(Bool :$debug, Bool :$typegraph = False) {
 | |
|     $*DEBUG = $debug;
 | |
| 
 | |
|     say 'Creating html/ subdirectories ...';
 | |
|     for '', <type language routine images op op/prefix op/postfix op/infix
 | |
|              op/circumfix op/postcircumfix op/listop> {
 | |
|         mkdir "html/$_" unless "html/$_".IO ~~ :e;
 | |
|     }
 | |
| 
 | |
|     say 'Reading lib/ ...';
 | |
|     my @source  = recursive-dir('lib').grep(*.f).grep(rx{\.pod$});
 | |
|        @source .= map: {; .path.subst('lib/', '').subst(rx{\.pod$}, '').subst(:g, '/', '::') => $_ };
 | |
| 
 | |
|     say 'Reading type graph ...';
 | |
|     $tg = Perl6::TypeGraph.new-from-file('type-graph.txt');
 | |
|     {
 | |
|         my %h = $tg.sorted.kv.flat.reverse;
 | |
|         @source .= sort: { %h{.key} // -1 };
 | |
|     }
 | |
| 
 | |
|     my $dr = Perl6::Documentable::Registry.new;
 | |
| 
 | |
|     say 'Processing Pod files ...';
 | |
|     for (0..* Z @source) -> $num, $_ {
 | |
|         my $podname  = .key;
 | |
|         my $file     = .value;
 | |
|         my $what     = $podname ~~ /^<[A..Z]> | '::'/  ?? 'type' !! 'language';
 | |
|         printf "% 4d/%d: % -40s => %s\n", $num, +@source, $file.path, "$what/$podname";
 | |
| 
 | |
|         my $pod  = eval slurp($file.path) ~ "\n\$=pod";
 | |
|            $pod .= [0];
 | |
| 
 | |
|         if $what eq 'language' {
 | |
|             write-language-file(:$dr, :$what, :$pod, :$podname);
 | |
|         }
 | |
|         else {
 | |
|             say pod-gist($pod[0]) if $*DEBUG;
 | |
|             write-type-file(:$dr, :$what, :pod($pod[0]), :$podname);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     say 'Composing doc registry ...';
 | |
|     $dr.compose;
 | |
| 
 | |
|     write-disambiguation-files($dr);
 | |
|     write-op-disambiguation-files($dr);
 | |
|     write-operator-files($dr);
 | |
|     write-type-graph-images(:force($typegraph));
 | |
|     write-search-file($dr);
 | |
|     write-index-file($dr);
 | |
| 
 | |
|     say 'Writing per-routine files ...';
 | |
|     my %routine-seen;
 | |
|     for $dr.lookup('routine', :by<kind>).list -> $d {
 | |
|         next if %routine-seen{$d.name}++;
 | |
|         write-routine-file($dr, $d.name);
 | |
|         print '.'
 | |
|     }
 | |
|     say '';
 | |
| 
 | |
|     say 'Processing complete.';
 | |
| }
 | |
| 
 | |
| sub write-language-file(:$dr, :$what, :$pod, :$podname) {
 | |
|     spurt "html/$what/$podname.html", p2h($pod);
 | |
|     if $podname eq 'operators' {
 | |
|         my @chunks = chunks-grep($pod.content,
 | |
|                                  :from({ $_ ~~ Pod::Heading and .level == 2}),
 | |
|                                  :to({  $^b ~~ Pod::Heading and $^b.level <= $^a.level}),
 | |
|                                 );
 | |
|         for @chunks -> $chunk {
 | |
|             my $heading = $chunk[0].content[0].content[0];
 | |
|             next unless $heading ~~ / ^ [in | pre | post | circum | postcircum ] fix | listop /;
 | |
|             my $what = ~$/;
 | |
|             my $operator = $heading.split(' ', 2)[1];
 | |
|             $dr.add-new(
 | |
|                         :kind<operator>,
 | |
|                         :subkind($what),
 | |
|                         :name($operator),
 | |
|                         :pod($chunk),
 | |
|                         :!pod-is-complete,
 | |
|                        );
 | |
|         }
 | |
|     }
 | |
|     $dr.add-new(
 | |
|                 :kind<language>,
 | |
|                 :name($podname),
 | |
|                 :$pod,
 | |
|                 :pod-is-complete,
 | |
|                );
 | |
| }
 | |
| 
 | |
| sub write-type-file(:$dr, :$what, :$pod, :$podname) {
 | |
|     my @chunks = chunks-grep($pod.content,
 | |
|                              :from({ $_ ~~ Pod::Heading and .level == 2}),
 | |
|                              :to({  $^b ~~ Pod::Heading and $^b.level <= $^a.level}),
 | |
|                             );
 | |
| 
 | |
|     if $tg.types{$podname} -> $t {
 | |
|         $pod.content.push: Pod::Block::Named.new(
 | |
|             name    => 'Image',
 | |
|             content => [ "/images/type-graph-$podname.png"],
 | |
|         );
 | |
|         $pod.content.push: pod-link(
 | |
|             'Full-size type graph image as SVG',
 | |
|             "/images/type-graph-$podname.svg",
 | |
|         );
 | |
| 
 | |
|         my @mro = $t.mro;
 | |
|            @mro.shift; # current type is already taken care of
 | |
| 
 | |
|         for $t.roles -> $r {
 | |
|             next unless %methods-by-type{$r};
 | |
|             $pod.content.push:
 | |
|                 pod-heading("Methods supplied by role $r"),
 | |
|                 pod-block(
 | |
|                     "$podname does role ",
 | |
|                     pod-link($r.name, "/type/$r"),
 | |
|                     ", which provides the following methods:",
 | |
|                 ),
 | |
|                 %methods-by-type{$r}.list,
 | |
|                 ;
 | |
|         }
 | |
|         for @mro -> $c {
 | |
|             next unless %methods-by-type{$c};
 | |
|             $pod.content.push:
 | |
|                 pod-heading("Methods supplied by class $c"),
 | |
|                 pod-block(
 | |
|                     "$podname inherits from class ",
 | |
|                     pod-link($c.name, "/type/$c"),
 | |
|                     ", which provides the following methods:",
 | |
|                 ),
 | |
|                 %methods-by-type{$c}.list,
 | |
|                 ;
 | |
|             for $c.roles -> $r {
 | |
|                 next unless %methods-by-type{$r};
 | |
|                 $pod.content.push:
 | |
|                     pod-heading("Methods supplied by role $r"),
 | |
|                     pod-block(
 | |
|                         "$podname inherits from class ",
 | |
|                         pod-link($c.name, "/type/$c"),
 | |
|                         ", which does role ",
 | |
|                         pod-link($r.name, "/type/$r"),
 | |
|                         ", which provides the following methods:",
 | |
|                     ),
 | |
|                     %methods-by-type{$r}.list,
 | |
|                     ;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
|     my $d = $dr.add-new(
 | |
|         :kind<type>,
 | |
|         # TODO: subkind
 | |
|         :$pod,
 | |
|         :pod-is-complete,
 | |
|         :name($podname),
 | |
|     );
 | |
| 
 | |
|     for @chunks -> $chunk {
 | |
|         my $name = $chunk[0].content[0].content[0];
 | |
|         say "$podname.$name" if $*DEBUG;
 | |
|         next if $name ~~ /\s/;
 | |
|         %methods-by-type{$podname}.push: $chunk;
 | |
|         # determine whether it's a sub or method
 | |
|         my Str $subkind;
 | |
|         {
 | |
|             my %counter;
 | |
|             for first-code-block($chunk).lines {
 | |
|                 if ms/^ 'multi'? (sub|method)»/ {
 | |
|                     %counter{$0}++;
 | |
|                 }
 | |
|             }
 | |
|             if %counter == 1 {
 | |
|                 ($subkind,) = %counter.keys;
 | |
|             }
 | |
|             if %counter<method> {
 | |
|                 write-qualified-method-call(
 | |
|                     :$name,
 | |
|                     :pod($chunk),
 | |
|                     :type($podname),
 | |
|                 );
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         $dr.add-new(
 | |
|             :kind<routine>,
 | |
|             :$subkind,
 | |
|             :$name,
 | |
|             :pod($chunk),
 | |
|             :!pod-is-complete,
 | |
|             :origin($d),
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     spurt "html/$what/$podname.html", p2h($pod);
 | |
| }
 | |
| 
 | |
| sub chunks-grep(:$from!, :&to!, *@elems) {
 | |
|     my @current;
 | |
| 
 | |
|     gather {
 | |
|         for @elems -> $c {
 | |
|             if @current && ($c ~~ $from || to(@current[0], $c)) {
 | |
|                 take [@current];
 | |
|                 @current = ();
 | |
|                 @current.push: $c if $c ~~ $from;
 | |
|             }
 | |
|             elsif @current or $c ~~ $from {
 | |
|                 @current.push: $c;
 | |
|             }
 | |
|         }
 | |
|         take [@current] if @current;
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub pod-with-title($title, *@blocks) {
 | |
|     Pod::Block::Named.new(
 | |
|         name => "pod",
 | |
|         content => [
 | |
|             Pod::Block::Named.new(
 | |
|                 name => "TITLE",
 | |
|                 content => Array.new(
 | |
|                     Pod::Block::Para.new(
 | |
|                         content => [$title],
 | |
|                     )
 | |
|                 )
 | |
|             ),
 | |
|             @blocks.flat,
 | |
|         ]
 | |
|     );
 | |
| }
 | |
| 
 | |
| sub pod-block(*@content) {
 | |
|     Pod::Block::Para.new(:@content);
 | |
| }
 | |
| 
 | |
| sub pod-link($text, $url) {
 | |
|     Pod::FormattingCode.new(
 | |
|         type    => 'L',
 | |
|         content => [
 | |
|             join('|', $text, $url),
 | |
|         ],
 | |
|     );
 | |
| }
 | |
| 
 | |
| sub pod-item(*@content, :$level = 1) {
 | |
|     Pod::Item.new(
 | |
|         :$level,
 | |
|         :@content,
 | |
|     );
 | |
| }
 | |
| 
 | |
| sub pod-heading($name, :$level = 1) {
 | |
|     Pod::Heading.new(
 | |
|         :$level,
 | |
|         :content[pod-block($name)],
 | |
|     );
 | |
| }
 | |
| 
 | |
| sub write-type-graph-images(:$force) {
 | |
|     unless $force {
 | |
|         my $dest = 'html/images/type-graph-Any.svg'.path;
 | |
|         if $dest.e && $dest.modified >= 'type-graph.txt'.path.modified {
 | |
|             say "Not writing type graph images, it seems to be up-to-date";
 | |
|             say "To force writing of type graph images, supply the --typegraph";
 | |
|             say "option at the command line, or delete";
 | |
|             say "file 'html/images/type-graph-Any.svg'";
 | |
|             return;
 | |
|         }
 | |
|     }
 | |
|     say 'Writing type graph images to html/images/ ...';
 | |
|     for $tg.sorted -> $type {
 | |
|         my $viz = Perl6::TypeGraph::Viz.new-for-type($type);
 | |
|         $viz.to-file("html/images/type-graph-{$type}.svg", format => 'svg');
 | |
|         $viz.to-file("html/images/type-graph-{$type}.png", format => 'png', size => '8,3');
 | |
|         print '.'
 | |
|     }
 | |
|     say '';
 | |
| 
 | |
|     say 'Writing specialized visualizations to html/images/ ...';
 | |
|     my %by-group = $tg.sorted.classify(&viz-group);
 | |
|     %by-group<Exception>.push: $tg.types< Exception Any Mu >;
 | |
|     %by-group<Metamodel>.push: $tg.types< Any Mu >;
 | |
| 
 | |
|     for %by-group.kv -> $group, @types {
 | |
|         my $viz = Perl6::TypeGraph::Viz.new(:types(@types),
 | |
|                                             :dot-hints(viz-hints($group)),
 | |
|                                             :rank-dir('LR'));
 | |
|         $viz.to-file("html/images/type-graph-{$group}.svg", format => 'svg');
 | |
|         $viz.to-file("html/images/type-graph-{$group}.png", format => 'png', size => '8,3');
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub viz-group ($type) {
 | |
|     return 'Metamodel' if $type.name ~~ /^ 'Perl6::Metamodel' /;
 | |
|     return 'Exception' if $type.name ~~ /^ 'X::' /;
 | |
|     return 'Any';
 | |
| }
 | |
| 
 | |
| sub viz-hints ($group) {
 | |
|     return '' unless $group eq 'Any';
 | |
| 
 | |
|     return '
 | |
|     subgraph "cluster: Mu children" {
 | |
|         rank=same;
 | |
|         style=invis;
 | |
|         "Any";
 | |
|         "Junction";
 | |
|     }
 | |
|     subgraph "cluster: Pod:: top level" {
 | |
|         rank=same;
 | |
|         style=invis;
 | |
|         "Pod::Config";
 | |
|         "Pod::Block";
 | |
|     }
 | |
|     subgraph "cluster: Date/time handling" {
 | |
|         rank=same;
 | |
|         style=invis;
 | |
|         "Date";
 | |
|         "DateTime";
 | |
|         "DateTime-local-timezone";
 | |
|     }
 | |
|     subgraph "cluster: Collection roles" {
 | |
|         rank=same;
 | |
|         style=invis;
 | |
|         "Positional";
 | |
|         "Associative";
 | |
|         "Baggy";
 | |
|     }
 | |
| ';
 | |
| }
 | |
| 
 | |
| sub write-search-file($dr) {
 | |
|     say 'Writing html/search.html ...';
 | |
|     my $template = slurp("search_template.html");
 | |
|     my @items;
 | |
|     my sub fix-url ($raw) { $raw.substr(1) ~ '.html' };
 | |
|     @items.push: $dr.lookup('language', :by<kind>).sort(*.name).map({
 | |
|         "\{ label: \"Language: {.name}\", value: \"{.name}\", url: \"{ fix-url(.url) }\" \}"
 | |
|     });
 | |
|     @items.push: $dr.lookup('type', :by<kind>).sort(*.name).map({
 | |
|         "\{ label: \"Type: {.name}\", value: \"{.name}\", url: \"{ fix-url(.url) }\" \}"
 | |
|     });
 | |
|     my %seen;
 | |
|     @items.push: $dr.lookup('routine', :by<kind>).grep({!%seen{.name}++}).sort(*.name).map({
 | |
|         "\{ label: \"{ (.subkind // 'Routine').tclc }: {.name}\", value: \"{.name}\", url: \"{ fix-url(.url) }\" \}"
 | |
|     });
 | |
|     sub escape(Str $s) {
 | |
|         $s.trans([</ \\ ">] => [<\\/ \\\\ \\">]);
 | |
|     }
 | |
|     @items.push: $dr.lookup('operator', :by<kind>).map({
 | |
|         qq[\{ label: "$_.human-kind() {escape .name}", value: "{escape .name}", url: "{ fix-url .url }"\}]
 | |
|     });
 | |
| 
 | |
|     my $items = @items.join(",\n");
 | |
|     spurt("html/search.html", $template.subst("ITEMS", $items));
 | |
| }
 | |
| 
 | |
| my %operator_disambiguation_file_written;
 | |
| 
 | |
| sub write-disambiguation-files($dr) {
 | |
|     say 'Writing disambiguation files ...';
 | |
|     for $dr.grouped-by('name').kv -> $name, $p is copy {
 | |
|         print '.';
 | |
|         my $pod = pod-with-title("Disambiguation for '$name'");
 | |
|         if $p.elems == 1 {
 | |
|             $p.=[0] if $p ~~ Array;
 | |
|             if $p.origin -> $o {
 | |
|                 $pod.content.push:
 | |
|                     pod-block(
 | |
|                         pod-link("'$name' is a $p.human-kind()", $p.url),
 | |
|                         ' from ',
 | |
|                         pod-link($o.human-kind() ~ ' ' ~ $o.name, $o.url),
 | |
|                     );
 | |
|             }
 | |
|             else {
 | |
|                 $pod.content.push:
 | |
|                     pod-block(
 | |
|                         pod-link("'$name' is a $p.human-kind()", $p.url)
 | |
|                     );
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             $pod.content.push:
 | |
|                 pod-block("'$name' can be anything of the following"),
 | |
|                 $p.map({
 | |
|                     if .origin -> $o {
 | |
|                         pod-item(
 | |
|                             pod-link(.human-kind, .url),
 | |
|                             ' from ',
 | |
|                             pod-link($o.human-kind() ~ ' ' ~ $o.name, $o.url),
 | |
|                         )
 | |
|                     }
 | |
|                     else {
 | |
|                         pod-item( pod-link(.human-kind, .url) )
 | |
|                     }
 | |
|                 });
 | |
|         }
 | |
|         my $html = p2h($pod);
 | |
|         spurt "html/$name.html", $html;
 | |
|         if all($p>>.kind) eq 'operator' {
 | |
|             spurt "html/op/$name.html", $html;
 | |
|             %operator_disambiguation_file_written{$p[0].name} = 1;
 | |
|         }
 | |
|     }
 | |
|     say '';
 | |
| }
 | |
| 
 | |
| sub write-op-disambiguation-files($dr) {
 | |
|     say 'Writing operator disambiguation files ...';
 | |
|     for $dr.lookup('operator', :by<kind>).classify(*.name).kv -> $name, @ops {
 | |
|         next unless %operator_disambiguation_file_written{$name};
 | |
|         my $pod = pod-with-title("Disambiguation for '$name'");
 | |
|         if @ops == 1 {
 | |
|             my $p = @ops[0];
 | |
|             if $p.origin -> $o {
 | |
|                 $pod.content.push:
 | |
|                     pod-block(
 | |
|                         pod-link("'$name' is a $p.human-kind()", $p.url),
 | |
|                         ' from ',
 | |
|                         pod-link($o.human-kind() ~ ' ' ~ $o.name, $o.url),
 | |
|                     );
 | |
|             }
 | |
|             else {
 | |
|                 $pod.content.push:
 | |
|                     pod-block(
 | |
|                         pod-link("'$name' is a $p.human-kind()", $p.url)
 | |
|                     );
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             $pod.content.push:
 | |
|                 pod-block("'$name' can be anything of the following"),
 | |
|                 @ops.map({
 | |
|                     if .origin -> $o {
 | |
|                         pod-item(
 | |
|                             pod-link(.human-kind, .url),
 | |
|                             ' from ',
 | |
|                             pod-link($o.human-kind() ~ ' ' ~ $o.name, $o.url),
 | |
|                         )
 | |
|                     }
 | |
|                     else {
 | |
|                         pod-item( pod-link(.human-kind, .url) )
 | |
|                     }
 | |
|                 });
 | |
|         }
 | |
|         my $html = p2h($pod);
 | |
|         spurt "html/$name.html", $html;
 | |
|     }
 | |
| 
 | |
| }
 | |
| 
 | |
| sub write-operator-files($dr) {
 | |
|     say 'Writing operator files ...';
 | |
|     for $dr.lookup('operator', :by<kind>).list -> $doc {
 | |
|         my $what  = $doc.subkind;
 | |
|         my $op    = $doc.name;
 | |
|         my $pod   = pod-with-title(
 | |
|             "$what.tclc() $op operator",
 | |
|             pod-block(
 | |
|                 "Documentation for $what $op, extracted from ",
 | |
|                 pod-link("the operators language documentation", "/language/operators")
 | |
|             ),
 | |
|             @($doc.pod),
 | |
|         );
 | |
|         spurt "html/op/$what/$op.html", p2h($pod);
 | |
|     }
 | |
| }
 | |
| 
 | |
| sub write-index-file($dr) {
 | |
|     say 'Writing html/index.html ...';
 | |
|     my %routine-seen;
 | |
|     my $pod = pod-with-title('Perl 6 Documentation',
 | |
|         Pod::Block::Para.new(
 | |
|             content => ['Official Perl 6 documentation'],
 | |
|         ),
 | |
|         # TODO: add more
 | |
|         pod-heading("Language Documentation"),
 | |
|         $dr.lookup('language', :by<kind>).sort(*.name).map({
 | |
|             pod-item( pod-link(.name, .url) )
 | |
|         }),
 | |
|         pod-heading('Types'),
 | |
|         $dr.lookup('type', :by<kind>).sort(*.name).map({
 | |
|             pod-item(pod-link(.name, .url))
 | |
|         }),
 | |
|         pod-heading('Routines'),
 | |
|         $dr.lookup('routine', :by<kind>).sort(*.name).map({
 | |
|             next if %routine-seen{.name}++;
 | |
|             pod-item(pod-link(.name, .url))
 | |
|         }),
 | |
|     );
 | |
|     spurt 'html/index.html', p2h($pod);
 | |
| }
 | |
| 
 | |
| sub write-routine-file($dr, $name) {
 | |
|     say 'Writing html/routine/$name.html ...' if $*DEBUG;
 | |
|     my @docs = $dr.lookup($name, :by<name>).grep(*.kind eq 'routine');
 | |
|     my $subkind = 'routine';
 | |
|     {
 | |
|         my @subkinds = @docs>>.subkind;
 | |
|         $subkind = @subkinds[0] if all(@subkinds>>.defined) && [eq] @subkinds;
 | |
|     }
 | |
|     my $pod = pod-with-title("Documentation for $subkind $name",
 | |
|         pod-block("Documentation for $subkind $name, assembled from the
 | |
|             following types:"),
 | |
|         @docs.map({
 | |
|             pod-heading(.origin.name ~ '.' ~ .name),
 | |
|             pod-block("From ", pod-link(.origin.name, .origin.url ~ '#' ~ .name)),
 | |
|             .pod.list,
 | |
|         })
 | |
|     );
 | |
|     spurt "html/routine/$name.html", p2h($pod);
 | |
| }
 | |
| 
 | |
| sub write-qualified-method-call(:$name!, :$pod!, :$type!) {
 | |
|     my $p = pod-with-title(
 | |
|         "Documentation for method $type.$name",
 | |
|         pod-block('From ', pod-link($type, "/type/{$type}#$name")),
 | |
|         @$pod,
 | |
|     );
 | |
|     spurt "html/{$type}.{$name}.html", p2h($p);
 | |
| }
 | |
| 
 | |
| sub footer-html() {
 | |
|     state $dt = ~DateTime.now;
 | |
|     qq[
 | |
|     <div id="footer">
 | |
|         <p>
 | |
|             Generated on $dt from the sources at
 | |
|             <a href="https://github.com/perl6/doc">perl6/doc on github</a>.
 | |
|         </p>
 | |
|         <p>
 | |
|             This is a work in progress to document Perl 6, and known to be
 | |
|             incomplete. Your contribution is appreciated.
 | |
|         </p>
 | |
|     </div>
 | |
|     ];
 | |
| }
 |