+sub featattrs ($attr) {
+ $attr or return '';
+ ref $attr or $attr = {eg => $attr};
+ my $title;
+ if (defined $attr->{experimental}) {
+ $title = 'experimental';
+ }
+ if (defined $attr->{dropped}) {
+ no warnings 'exiting';
+ next unless exists $get{v}; # skip containing feature
+ $title = sprintf 'removed in %vd', $_ for $attr->{dropped} || ();
+ }
+ elsif ($attr->{stable}) {
+ $title .= sprintf ' until %vd', $attr->{stable};
+ }
+ if ($attr->{experimental}) {
+ $title = sprintf '<span title="experimental::%s">%s</span>',
+ $attr->{experimental}, $title;
+ $attr->{name} //= $attr->{experimental};
+ }
+ if ($attr->{feature}) {
+ my $prefix = sprintf '<span title="%s">feature</span>',
+ $attr->{feature};
+ $title = join ', ', $prefix, $title // ();
+ $attr->{name} //= $attr->{feature};
+ }
+ if (defined $attr->{bundle}) {
+ if ($attr->{stable} and !$attr->{bundle}) {
+ $title .= ' then bundled';
+ }
+ else {
+ my $v = 'bundled';
+ $v .= ' in ' . join(' and ',
+ # specific version(s) distinct from stable or current
+ map { sprintf '%vd', $_ }
+ map { ref eq 'ARRAY' ? @{$_} : $_ } $_
+ ) for $attr->{bundle} || ();
+ $title = join ', ', $title // (), $v;
+ }
+ }
+ $title = $title ? sprintf ' <em class="ex">(%s)</em>', $title : '';
+
+ if (my $eg = $attr->{eg}) {
+ my $pre = Entity($eg);
+ $pre =~ s<\N{ZERO WIDTH SPACE}>{</code><wbr/><code>}g;
+ $pre = " <small>{<code>$pre</code>}</small>";
+ $title = $pre . $title;
+ }
+ return $title;
+}