[PATCH] kernel-doc: add support for asciidoc output

From: Jani Nikula
Date: Mon Jan 18 2016 - 03:41:30 EST


Add new -asciidoc option to produce asciidoc output from kernel-doc. The
output is formatted internally, with no dependencies on external
tools. Any asciidoc formatting present in kernel-doc will naturally be
present in the resulting asciidoc as well.

Highlighting of functions(), &struct structures, &enum enumerations,
@parameters, etc. will be done by means of asciidoc. Anchors and
cross-references are added as well, providing hyperlinking support in
the result processed by asciidoc(1).

This support is non-conflicting and orthogonal to the patches adding
asciidoc support to the kernel-doc DocBook output [1]. Those patches
bolt on to the current document generation pipeline; there is currently
none for native asciidoc in the kernel (though ideas have been discussed
[2]). At this time, this patch should be considered a worthwhile
standalone improvement to kernel-doc, independent of the rest.

[1] http://mid.gmane.org/1448471279-19748-1-git-send-email-daniel.vetter@xxxxxxxx
[2] http://mid.gmane.org/20160114131823.2ff43a0c@xxxxxxx

Cc: Jonathan Corbet <corbet@xxxxxxx>
Cc: Daniel Vetter <daniel@xxxxxxxx>
Signed-off-by: Jani Nikula <jani.nikula@xxxxxxxxx>

---

WARNING: I do not know perl. This was all cargo-culted over a couple of
evenings.

I tested this mostly on drm/i915. I had to drop a few totally bogus
kernel-doc comments for everything to work cleanly, series at
http://patchwork.freedesktop.org/series/2581/.

With those out of the way, IMHO the result is astonishing for the amount
of time invested. With default installs and templates and stylesheets of
asciidoc and asciidoctor on debian:

$ scripts/kernel-doc -asciidoc drivers/gpu/drm/i915/*.[ch] | asciidoc - > i915-asciidoc.html
See http://people.freedesktop.org/~jani/i915-asciidoc.html

$ scripts/kernel-doc -asciidoc drivers/gpu/drm/i915/*.[ch] | asciidoctor - > i915-asciidoctor.html
See http://people.freedesktop.org/~jani/i915-asciidoctor.html

At this point, running kernel-doc on *.[ch] is of course just a random
collection of glorified comments. For proper documentation, this will
need a high level asciidoc and a way to include named sections and
functions etc. I'll be giving that some thought next, but again, I think
this is worthwhile on its own.

BR,
Jani.
---
scripts/kernel-doc | 237 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 237 insertions(+)

diff --git a/scripts/kernel-doc b/scripts/kernel-doc
index 638a38e1b419..a9a38374cb49 100755
--- a/scripts/kernel-doc
+++ b/scripts/kernel-doc
@@ -201,6 +201,8 @@ my $type_param = '\@(\w+)';
my $type_struct = '\&((struct\s*)*[_\w]+)';
my $type_struct_xml = '\\&amp;((struct\s*)*[_\w]+)';
my $type_env = '(\$\w+)';
+my $type_enum_full = '\&(enum)\s*([_\w]+)';
+my $type_struct_full = '\&(struct)\s*([_\w]+)';

# Output conversion substitutions.
# One for each output format
@@ -266,6 +268,17 @@ my @highlights_text = (
);
my $blankline_text = "";

+# asciidoc-mode
+my @highlights_asciidoc = (
+ [$type_constant, "`\$1`"],
+ [$type_func, "<<func_\$1,`\$1()`>>"],
+ [$type_struct_full, "<<\$1_\$2,`\$1 \$2`>>"],
+ [$type_enum_full, "<<\$1_\$2,`\$1 \$2`>>"],
+ [$type_struct, "<<struct_\$1,`\$1`>>"],
+ [$type_param, "*\$1*"]
+ );
+my $blankline_asciidoc = "\n";
+
# list mode
my @highlights_list = (
[$type_constant, "\$1"],
@@ -402,6 +415,10 @@ while ($ARGV[0] =~ m/^-(.*)/) {
$output_mode = "text";
@highlights = @highlights_text;
$blankline = $blankline_text;
+ } elsif ($cmd eq "-asciidoc") {
+ $output_mode = "asciidoc";
+ @highlights = @highlights_asciidoc;
+ $blankline = $blankline_asciidoc;
} elsif ($cmd eq "-docbook") {
$output_mode = "xml";
@highlights = @highlights_xml;
@@ -1713,6 +1730,214 @@ sub output_blockhead_text(%) {
}
}

+##
+# output in asciidoc
+sub output_highlight_asciidoc {
+ my $contents = join "\n",@_;
+ my $line;
+
+ # undo the evil effects of xml_escape() earlier
+ $contents = xml_unescape($contents);
+
+ eval $dohighlight;
+ die $@ if $@;
+
+ foreach $line (split "\n", $contents) {
+ if ($line eq "") {
+ print $lineprefix, $blankline;
+ } else {
+ $line =~ s/\\\\\\/\&/g;
+ print $lineprefix, $line;
+ }
+ print "\n";
+ }
+}
+
+sub output_function_asciidoc(%) {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+ my $start;
+
+ print "[[func_$args{'function'}]]\n";
+ print "=== " . $args{'function'} . " ===\n";
+ print $args{'purpose'} . "\n\n";
+
+ print "----------\n";
+ if ($args{'functiontype'} ne "") {
+ $start = $args{'functiontype'} . " " . $args{'function'} . " (";
+ } else {
+ $start = $args{'function'} . " (";
+ }
+ print $start;
+
+ my $count = 0;
+ foreach my $parameter (@{$args{'parameterlist'}}) {
+ $type = $args{'parametertypes'}{$parameter};
+ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
+ # pointer-to-function
+ print $1 . $parameter . ") (" . $2;
+ } else {
+ print $type . " " . $parameter;
+ }
+ if ($count != $#{$args{'parameterlist'}}) {
+ $count++;
+ print ",\n";
+ print " " x length($start);
+ } else {
+ print ");\n\n";
+ }
+ }
+ print "----------\n";
+
+ print ".Parameters\n";
+ print "[horizontal]\n";
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ my $parameter_name = $parameter;
+ $parameter_name =~ s/\[.*//;
+ $type = $args{'parametertypes'}{$parameter};
+
+ print "`$type $parameter`::\n";
+ if ($args{'parameterdescs'}{$parameter_name} ne $undescribed) {
+ $blankline = "+";
+ output_highlight_asciidoc($args{'parameterdescs'}{$parameter_name});
+ $blankline = "\n";
+ } else {
+ print "_undescribed_\n";
+ }
+ print "\n";
+ }
+ output_section_asciidoc(@_);
+}
+
+sub output_section_asciidoc(%) {
+ my %args = %{$_[0]};
+ my $section;
+
+ foreach $section (@{$args{'sectionlist'}}) {
+ print ".$section\n\n";
+ output_highlight_asciidoc($args{'sections'}{$section});
+ print "\n";
+ }
+ print "\n";
+}
+
+sub output_enum_asciidoc(%) {
+ my %args = %{$_[0]};
+ my ($parameter);
+ my $count;
+
+ print "[[enum_$args{'enum'}]]\n";
+ print "=== enum " . $args{'enum'} . " ===\n";
+ print $args{'purpose'} . "\n\n";
+
+ print "----------\n";
+ print "enum " . $args{'enum'} . " {\n";
+ $count = 0;
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ print "\t$parameter";
+ if ($count != $#{$args{'parameterlist'}}) {
+ $count++;
+ print ",";
+ }
+ print "\n";
+ }
+ print "};\n";
+ print "----------\n";
+
+ print ".Constants\n";
+ print "[horizontal]\n";
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ print "`$parameter`::\n";
+ if ($args{'parameterdescs'}{$parameter} ne $undescribed) {
+ $blankline = "+";
+ output_highlight_asciidoc($args{'parameterdescs'}{$parameter});
+ $blankline = "\n";
+ } else {
+ print "_undescribed_\n";
+ }
+ print "\n";
+ }
+
+ output_section_asciidoc(@_);
+}
+
+sub output_typedef_asciidoc(%) {
+ my %args = %{$_[0]};
+ my ($parameter);
+ my $count;
+
+ print "[[$args{'typedef'}]]\n";
+ print "=== typedef " . $args{'typedef'} . " ===\n";
+ print $args{'purpose'} . "\n\n";
+
+ output_section_asciidoc(@_);
+}
+
+sub output_struct_asciidoc(%) {
+ my %args = %{$_[0]};
+ my ($parameter);
+
+ print "[[$args{'type'}_$args{'struct'}]]\n";
+ print "=== " . $args{'type'} . " " . $args{'struct'} . " ===\n";
+ print $args{'purpose'} . "\n\n";
+
+ print "----------\n";
+ print $args{'type'} . " " . $args{'struct'} . " {\n";
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ if ($parameter =~ /^#/) {
+ print "$parameter\n";
+ next;
+ }
+
+ my $parameter_name = $parameter;
+ $parameter_name =~ s/\[.*//;
+
+ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
+ $type = $args{'parametertypes'}{$parameter};
+ if ($type =~ m/([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)/) {
+ # pointer-to-function
+ print "\t$1 $parameter) ($2);\n";
+ } elsif ($type =~ m/^(.*?)\s*(:.*)/) {
+ # bitfield
+ print "\t$1 $parameter$2;\n";
+ } else {
+ print "\t" . $type . " " . $parameter . ";\n";
+ }
+ }
+ print "};\n";
+ print "----------\n";
+
+ print ".Members\n";
+ print "[horizontal]\n";
+ foreach $parameter (@{$args{'parameterlist'}}) {
+ ($parameter =~ /^#/) && next;
+
+ my $parameter_name = $parameter;
+ $parameter_name =~ s/\[.*//;
+
+ ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next;
+ $type = $args{'parametertypes'}{$parameter};
+ print "`$type $parameter`" . "::\n";
+ $blankline = "+";
+ output_highlight_asciidoc($args{'parameterdescs'}{$parameter_name});
+ $blankline = "\n";
+ print "\n";
+ }
+ print "\n";
+ output_section_asciidoc(@_);
+}
+
+sub output_blockhead_asciidoc(%) {
+ my %args = %{$_[0]};
+ my ($parameter, $section);
+
+ foreach $section (@{$args{'sectionlist'}}) {
+ print "=== $section ===\n";
+ output_highlight_asciidoc($args{'sections'}{$section});
+ print "\n";
+ }
+}
+
## list mode output functions

sub output_function_list(%) {
@@ -2411,6 +2636,18 @@ sub xml_escape($) {
return $text;
}

+# xml_unescape: reverse the effects of xml_escape
+sub xml_unescape($) {
+ my $text = shift;
+ if (($output_mode eq "text") || ($output_mode eq "man")) {
+ return $text;
+ }
+ $text =~ s/\\\\\\amp;/\&/g;
+ $text =~ s/\\\\\\lt;/</g;
+ $text =~ s/\\\\\\gt;/>/g;
+ return $text;
+}
+
# convert local escape strings to html
# local escape strings look like: '\\\\menmonic:' (that's 4 backslashes)
sub local_unescape($) {
--
2.1.4