add doxygen files

This commit is contained in:
Laurent Perron
2019-05-15 14:55:43 +02:00
parent 844a8c9903
commit 43d7aa291e
3 changed files with 2937 additions and 813 deletions

View File

@@ -1,566 +1,378 @@
/* The standard CSS for doxygen
modified by RossCairns.com
*/
/* Google's standard stylesheet for publishing to developers.google.com */
/* Settings in this file add to or override settings in the default stylesheet */
/* Specify this filename with the HTML_EXTRA_STYLESHEET setting in Doxyfile */
body, table, div, p, dl {
font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif;
font-size: 12px;
/* author: doog@google.com */
/* Limitations due to HTML and id design: */
/* * The paramtype and param padding styles affect both the top and bottom */
/* spacing of method prototypes that have no parameters */
/* (e.g. closeActiveNativeShareDialog in Google+ API), and also spacing */
/* in between parameters (e.g. handleURL). I would like to make the */
/* shaded rectangle slightly larger for prototypes with no parameter, */
/* but keep the parameter lines withini a single prototype tight. */
/* */
/* * Property detail prototype has a one-cell table for the name */
/* (e.g. delegate) instead of a two-cell table for both the name and */
/* arguments. This makes the style definitions complicated. */
/* OVERVIEW PAGE (index.html) */
.icon, .icona {
display: none;
}
p, div, dl {
color:#3E3E3E;
.directory {
margin-top: 1em;
}
div.contents p {
font-family:Monaco, courier;
/* Set gray background on all rows (not just even) but does NOT work */
.directory tr.even, .directory td.entry, .directory td.desc {
}
/* @group Heading Levels */
h1 {
margin-bottom: 10px;
font-size: 30px;
padding: 0px 0px 20px 0px ;
border-bottom:1px dotted #E0E0E0;
}
h2 {
padding-top: 30px;
font-size: 17px;
color:#42657B;
font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif;
}
h3 {
font-size: 17px;
font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif;
}
dt {
font-weight: bold;
}
div.multicol {
-moz-column-gap: 1em;
-webkit-column-gap: 1em;
-moz-column-count: 3;
-webkit-column-count: 3;
}
p.startli, p.startdd, p.starttd {
margin-top: 2px;
}
p.endli {
margin-bottom: 0px;
}
p.enddd {
margin-bottom: 4px;
}
p.endtd {
margin-bottom: 2px;
}
/* @end */
caption {
font-weight: bold;
}
span.legend {
font-size: 70%;
text-align: center;
}
h3.version {
font-size: 90%;
padding-bottom:10px;
border-bottom:1px dotted #E0E0E0;
}
div.qindex, div.navtab{
background-color: #e8eef2;
border: 1px solid #84b0c7;
text-align: center;
margin: 2px;
padding: 2px;
}
div.qindex, div.navpath {
width: 100%;
line-height: 140%;
}
div.navtab {
margin-right: 15px;
}
/* @group Link Styling */
a {
color: #153788;
font-weight: normal;
text-decoration: none;
}
.contents a:visited {
color: #1b77c5;
}
a:hover {
text-decoration: underline;
}
a.qindex {
font-weight: bold;
}
a.qindexHL {
font-weight: bold;
background-color: #6666cc;
color: #ffffff;
border: 1px double #9295C2;
}
.contents a.qindexHL:visited {
color: #ffffff;
}
a.el {
font-weight: bold;
}
a.elRef {
}
a.code {
color: #3030f0;
}
a.codeRef {
color: #3030f0;
}
/* @end */
dl.el {
margin-left: -1cm;
.directory td.entry {
font-weight: bold;
white-space: nowrap;
}
.fragment {
font-family: monospace, fixed;
font-size: 105%;
white-space: pre;
font: 14px/20px Roboto Mono,monospace;
color: #37474f;
background: #f7f7f7;
margin: 16px 0;
padding: 8px;
overflow-x: auto;
}
pre.fragment {
border: 1px solid #CCCCCC;
background-color: #f5f5f5;
padding: 4px 6px;
margin: 4px 8px 4px 2px;
overflow: auto;
word-wrap: break-word;
font-size: 9pt;
line-height: 125%;
}
/* TOP OF CLASS PAGES */
div.ah {
background-color: black;
font-weight: bold;
color: #ffffff;
margin-bottom: 3px;
margin-top: 3px
div.summary {
margin-top: -1em;
margin-bottom: 1em;
}
div.groupHeader {
margin-left: 16px;
margin-top: 12px;
margin-bottom: 6px;
font-weight: bold;
div.headertitle {
display: none;
}
div.groupText {
margin-left: 16px;
font-style: italic;
div.textblock p {
margin-bottom: 0.6em;
}
body {
background: white;
color: black;
margin-right: 20px;
margin-left: 20px;
}
/* ALL TABLES */
td.indexkey {
background-color: #F1F5F9;
font-weight: bold;
border: 1px solid #CCCCCC;
margin: 2px 0px 2px 0;
padding: 2px 10px;
table {
border-collapse: collapse;
border: none;
}
td.indexvalue {
background-color: #F1F5F9;
border: 1px solid #CCCCCC;
padding: 2px 10px;
margin: 2px 0px;
table tr.heading {
border: none;
}
tr.memlist {
background-color: #f0f0f0;
table tr.heading td {
border: none;
padding-left: 0;
}
p.formulaDsp {
text-align: center;
table td.memSeparator {
display: none;
}
img.formulaDsp {
table td {
border: none;
}
img.formulaInl {
vertical-align: middle;
table {
margin-bottom: 0;
}
div.center {
text-align: center;
margin-top: 0px;
margin-bottom: 0px;
padding: 0px;
}
div.center img {
border: 0px;
}
/* CLASS PAGE - SUMMARY TABLES */
img.footer {
border: 0px;
vertical-align: middle;
.groupheader {
padding-left: 8px;
}
/* @group Code Colorization */
span.keyword {
color: #008000
table td.memItemLeft {
width: 10%;
text-align: right;
padding-top: 7px;
padding-right: 3px;
padding-bottom: 0;
padding-left: 10px;
white-space: nowrap;
}
span.keywordtype {
color: #604020
table td.memItemRight {
font-weight: bold;
padding-top: 7px;
padding-right: 3px;
padding-bottom: 0;
padding-left: 9px;
}
span.keywordflow {
color: #e08000
table td.mdescLeft {
padding-top: 0;
padding-bottom: 10px;
}
span.comment {
color: #800000
table td.mdescRight {
padding-left: 20px;
padding-top: 0;
padding-bottom: 6px;
}
span.preprocessor {
color: #806020
table.memberdecls tr {
border: none;
}
span.stringliteral {
color: #002080
table.memberdecls h2 {
font: 400 24px/32px Roboto,sans-serif;
}
span.charliteral {
color: #008080
}
span.vhdldigit {
color: #ff00ff
}
/* CLASS PAGE - DETAILED SECTION */
span.vhdlchar {
color: #000000
div.memproto {
margin-top: 22px;
margin-bottom: 12px;
}
span.vhdlkeyword {
color: #700070
table.memname {
border: none;
}
span.vhdllogic {
color: #ff0000
table.memname tr {
border: none;
}
/* @end */
.search {
color: #003399;
font-weight: bold;
table.memname td {
font: 400 16px/24px Roboto,sans-serif;
border: none;
}
form.search {
margin-bottom: 0px;
margin-top: 0px;
table.memname td.memname,
table.memname td.paramkey {
font-size: 17px;
font-weight: bold;
text-align: right;
padding-top: 0px;
padding-right: 0px;
padding-bottom: 2px;
white-space: nowrap;
width: 85px;
}
input.search {
font-size: 75%;
color: #000080;
font-weight: normal;
background-color: #F1F5F9;
table.memname td.paramtype {
padding-left: 0;
padding-top: 0;
width: 85px;
white-space: nowrap;
}
td.tiny {
font-size: 75%;
table.memname td.paramname {
min-width: 170px;
padding-top: 0;
}
.dirtab {
padding: 4px;
border-collapse: collapse;
border: 1px solid #84b0c7;
table.memname tr:first-child td.memname {
text-align: left;
}
th.dirtab {
background: #F1F5F9;
font-weight: bold;
table.memname tr:first-child td.memname,
table.memname tr:first-child td.paramtype,
table.memname tr:first-child td.paramname {
padding-top: 10px;
padding-bottom: 10px;
}
hr {
height: 0;
border: none;
border-top: 1px solid #666;
div.memdoc p {
margin-left: 35px;
margin-bottom: 0.7em;
}
/* @group Member Descriptions */
.mdescLeft, .mdescRight,
.memItemLeft, .memItemRight,
.memTemplItemLeft, .memTemplItemRight, .memTemplParams {
background-color: #FAFAFA;
border: none;
margin: 4px;
padding: 1px 0 0 8px;
div.memdoc dl.since {
margin-left: 35px;
}
.mdescLeft, .mdescRight {
padding: 0px 8px 4px 8px;
color: #555;
dl.since dt,
dl.since dd {
display: inline;
}
.memItemLeft, .memItemRight, .memTemplParams {
border-top: 1px solid #ccc;
background-color: #F9F9F9;
dl.deprecated {
padding-left: 35px;
font-weight: 500;
}
.memItemLeft, .memTemplItemLeft {
white-space: nowrap;
dl.deprecated dt,
dl.deprecated dd {
display: inline;
}
.memTemplParams {
color: #606060;
white-space: nowrap;
dl.deprecated dd {
padding-left: 12px;
font-weight: normal;
}
/* @end */
/* @group Member Details */
/* Styles for detailed member documentation */
.memtemplate {
font-size: 80%;
color: #606060;
font-weight: normal;
margin-left: 3px;
/* Make unneeded links black in detail tables */
td.memname a.el,
td.memname a:visited.el {
color: #000;
}
.memnav {
background-color: #F1F5F9;
border: 1px solid #84b0c7;
text-align: center;
margin: 2px;
margin-right: 15px;
padding: 2px;
dl.params, dl.return {
padding-left: 35px;
}
.memitem {
padding: 0;
margin-bottom: 30px;
dl.params dt, dl.return dt {
font-weight: 500;
}
.memname {
white-space: nowrap;
font-weight: bold;
color:#42657B;
padding:3px 5px;
dl.params dd, dl.return dd {
padding-left: 0px;
}
.memproto, .memdoc {
border: 1px dotted #E0E0E0;
}
.memproto {
padding: 0;
background-color: #F9F9F9;
font-weight: bold;
-webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
-moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
/* CLASS FILE - PROPERTY DETAILS */
td.mlabels-left td.memname {
padding-left: 0;
}
.memdoc {
padding: 2px 20px 20px;
background-color: #FFFFFF;
border-top-width: 0;
-webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
-moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
table.memname {
margin-top: 12px;
}
.paramkey {
text-align: right;
table.mlabels {
margin-top: 10px;
}
.paramtype {
white-space: nowrap;
table.mlabels, table.memname {
margin-bottom: 0;
}
.paramname {
color: #885656;
white-space: nowrap;
}
.paramname em {
font-style: normal;
table.mlabels tr {
border: 0;
}
/* @end */
/* @group Directory (tree) */
/* for the tree view */
.ftvtree {
font-family: sans-serif;
margin: 0.5em;
table.mlabels td.mlabels-left {
font-size: 17px/24px;
padding-top: 0;
padding-bottom: 0;
width: 125px;
}
/* these are for tree view when used as main index */
.directory {
font-size: 9pt;
font-weight: bold;
table.mlabels td.mlabels-right {
font-size: 17px/24px;
margin-bottom: 0;
padding-top: 0;
padding-bottom: 0;
vertical-align: middle;
width: 600px;
}
.directory h3 {
margin: 0px;
margin-top: 1em;
font-size: 11pt;
}
/*
The following two styles can be used to replace the root node title
with an image of your choice. Simply uncomment the next two styles,
specify the name of your image and be sure to set 'height' to the
proper pixel height of your image.
*/
/*
.directory h3.swap {
height: 61px;
background-repeat: no-repeat;
background-image: url("yourimage.gif");
table.mlabels span.mlabel {
padding-left: 2px;
padding-right: 8px;
}
.directory h3.swap span {
display: none;
}
*/
.directory > h3 {
margin-top: 0;
}
/* OLD DESIGN COMPATIBILITY */
.directory p {
margin: 0px;
white-space: nowrap;
body.docs table {
border-collapse: collapse;
border: none;
}
.directory div {
display: none;
margin: 0px;
body.docs table tr.heading td {
border: none;
padding-left: 0;
}
.directory img {
vertical-align: -30%;
body.docs table td.memSeparator {
display: none;
}
/* these are for tree view when not used as main index */
.directory-alt {
font-size: 100%;
font-weight: bold;
body.docs table td {
border: none;
}
.directory-alt h3 {
margin: 0px;
margin-top: 1em;
font-size: 11pt;
body.docs table {
margin-bottom: 0;
}
.directory-alt > h3 {
margin-top: 0;
body.docs table td.memItemLeft {
background-color: #F9FAFC;
width: 10%;
text-align: right;
padding-top: 7px;
padding-right: 3px;
padding-bottom: 0;
padding-left: 10px;
white-space: nowrap;
}
.directory-alt p {
margin: 0px;
white-space: nowrap;
body.docs table td.memItemRight {
background-color: #F9FAFC;
font-weight: bold;
padding-top: 7px;
padding-right: 3px;
padding-bottom: 0;
padding-left: 9px;
}
.directory-alt div {
display: none;
margin: 0px;
body.docs table td.mdescLeft {
background-color: #F9FAFC;
padding-top: 0;
padding-bottom: 10px;
}
.directory-alt img {
vertical-align: -30%;
body.docs table td.mdescRight {
background-color: #F9FAFC;
padding-left: 20px;
padding-top: 0;
padding-bottom: 6px;
}
/* @end */
address {
font-style: normal;
color: #333;
body.docs table.memname {
border: none;
}
table.doxtable {
border-collapse:collapse;
body.docs table.memname td {
background-color: #E2E8F2;
border: none;
}
table.doxtable td, table.doxtable th {
border: 1px solid #153788;
padding: 3px 7px 2px;
body.docs table.memname td.memname,
body.docs table.memname td.paramkey {
font-weight: bold;
text-align: right;
padding-top: 0px;
padding-right: 0px;
padding-bottom: 2px;
white-space: nowrap;
width: 85px;
}
table.doxtable th {
background-color: #254798;
color: #FFFFFF;
font-size: 110%;
padding-bottom: 4px;
padding-top: 5px;
text-align:left;
body.docs table.memname td.paramtype {
padding-left: 0;
padding-top: 0px;
padding-bottom: 2px;
width: 85px;
white-space: nowrap;
}
hr {
border-top:1px dotted #E0E0E0;
border-bottom:1px dotted #E0E0E0;
margin-top:30px;
padding-top:10px;
body.docs table.memname td.paramname {
min-width: 330px;
padding-top: 0px;
padding-bottom: 2px;
}
.contents {
padding-top: 30px;
body.docs td.mlabels-left td.memname {
padding-left: 0;
}
h1 {
margin-top:0;
}
.contents .dynsection {
margin-top:10px;
}
/* END OF OLD DESIGN COMPATIBILITY */

View File

@@ -1,408 +1,209 @@
#! /usr/bin/python2.4
#
# Copyright 2010-2014 Google
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This here steaming pile of Python works as a Doxygen input filter which
# automatically translates the C++ code with doxygen /// comments into
# the format Doxygen wants, complete
#!/usr/bin/python
"""Doxygen pre-filter script for ion.
This filter processes code and adds Doxygen-compatible markup in various places
to enable Doxygen to read the docs more fully. Unlike some other Doxygen
filters, it is designed to work with Doxygen's newer markdown syntax.
In order to ensure proper syntax coloring of indented code blocks, make sure
there is a blank (commented) line both above and below the block. For example:
// Comment comment comment.
//
// int CodeBlock() {
// Goes here;
// }
//
// More comment.
"""
import re
import sys
# Class which reads from an input file one line at a time and allows you to
# match that line against regexes.
class Reader:
def __init__(self, file):
self.file = file
self.line = file.readline()
def Eof(self):
return self.line == ""
class DoxygenFormatter(object):
"""Transforms lines of a source file to make them doxygen-friendly."""
def LookingAt(self, pattern):
return re.match(pattern, self.line) != None
ANYWHERE = 'anywhere'
COMMENT = 'comment'
def ConsumeAndWrite(self, out):
out.write(self.line)
self.line = self.file.readline()
def __init__(self, outfile):
# The file-like object to which we will write lines.
self.out = outfile
def Consume(self):
line = self.line
self.line = self.file.readline()
# A buffer for storing empty lines which we can use later if we need to
# retroactively insert markup without causing line number offset problems.
self.empty_line_buffer = []
# Whether we are currently inside an indented code block.
self.in_code_block = False
self.CompileExpressions()
def CompileExpressions(self):
"""Pre-compiles frequently used regexps for improved performance.
The regexps are arranged as a list of 3-tuples, where the second value is
the replacement string (which may include backreferences) and the third
value is one of the context constants ANYWHERE or COMMENT. This is a list
of tuples instead of a dictionary because order matters: earlier regexps
will be applied first, and the resulting text (not the original) will be
what is seen by subsequent regexps.
"""
self.comment_regex = re.compile(r'^\s*//')
self.substitutions = [
# Remove copyright lines.
(re.compile(r'^\s*//\s*[Cc]opyright.*Google.*'), r'', self.ANYWHERE),
# Remove any comment lines that consist of only punctuation (banners).
# We only allow a maximum of two spaces before the punctuation so we
# don't accidentally get rid of code examples with bare braces and
# whatnot.
(re.compile(r'(^\s*)//\s{0,2}[-=#/]+$'), r'\1//\n', self.ANYWHERE),
# If we find something that looks like a list item that is indented four
# or more spaces, pull it back to the left so doxygen's Markdown engine
# doesn't treat it like a code block.
(re.compile(r'(^\s*)//\s{4,}([-\d*].*)'), r'\1 \2', self.COMMENT),
# Replace TODO(someone) in a comment with @todo (someone)
(re.compile(r'TODO'), r'@todo ', self.COMMENT),
# Replace leading 'Note:' or 'Note that' in a comment with @note
(re.compile(r'(\/\/\s+)Note(?:\:| that)', re.I), r'\1@note',
self.COMMENT),
# Replace leading 'Warning:' in a comment with @warning
(re.compile(r'(\/\/\s+)Warning:', re.I), r'\1@warning', self.COMMENT),
# Replace leading 'Deprecated' in a comment with @deprecated
(re.compile(r'(\/\/\s+)Deprecated[^\w\s]*', re.I), r'\1@deprecated',
self.COMMENT),
# Replace pipe-delimited parameter names with backtick-delimiters
(re.compile(r'\|(\w+)\|'), r'`\1`', self.COMMENT),
# Convert standalone comment lines to Doxygen style.
(re.compile(r'(^\s*)//(?=[^/])'), r'\1///', self.ANYWHERE),
# Strip trailing comments from preprocessor directives.
(re.compile(r'(^#.*)//.*'), r'\1', self.ANYWHERE),
# Convert remaining trailing comments to doxygen style, unless they are
# documenting the end of a block.
(re.compile(r'([^} ]\s+)//(?=[^/])'), r'\1///<', self.ANYWHERE),
]
def Transform(self, line):
"""Performs the regexp transformations defined by self.substitutions.
Args:
line: The line to transform.
Returns:
The resulting line.
"""
for (regex, repl, where) in self.substitutions:
if where is self.COMMENT and not self.comment_regex.match(line):
return line
line = regex.sub(repl, line)
return line
def GetIndent(self):
return re.match(" *", self.line).end()
def AppendToBufferedLine(self, text):
"""Appends text to the last buffered empty line.
# If we insert additional lines into the output, we will want to remove lines
# somewhere else so that definitions generally appear on the line number they
# were on originally. Otherwise, when Doxygen links into the orignial headers,
# it links to the wrong line numbers. "offset" counts the number of lines we
# need to make up.
class OffsetTrackingFile:
def __init__(self, file):
self.file = file
self.offset = 0
def write(self, text):
self.file.write(text)
def writelines(self, lines):
self.file.writelines(lines)
Empty lines are buffered rather than being written out directly. This lets
us retroactively rewrite buffered lines to include markup that affects the
following line, while avoiding the line number offset that would result from
inserting a line that wasn't in the original source.
# This class keeps tracks of what member groups we've opened, so that we can
# write their end tags later on. Doxygen does not allow nested groups, though
# nested classes can have their own groups. Thus, we can keep track of groups
# according to their indentation levels.
class GroupTracker:
def __init__(self):
self.levels = []
Args:
text: The text to append to the line.
# Start a new group indented by the given number of spaces.
def StartGroup(self, indent, out):
self.EndGroupsDeeperThan(indent - 1, out)
out.write(" " * indent + "/// @{\n")
out.offset += 1
self.levels.append(indent)
# Write end tags for all open groups who are indented more than the given
# number of spaces.
def EndGroupsDeeperThan(self, indent, out):
while self.levels and self.levels[-1] > indent:
out.write(" " * self.levels[-1] + "/// @}\n")
out.offset += 1
self.levels.pop()
# Converts a regular comment block into a Doxygen-format comment block and
# writes it to the output.
#
# Other than converting all the "//"s to "///"s, we also try to detect code
# fragments embedded inside comments. Throughout the comments in net/proto2,
# such fragments are always indented two spaces, e.g. like this:
# // Here is a code fragment:
# // int foo = Foo();
# // // A comment within the code fragment.
# // Bar(foo);
# // That was a code fragment!
# We try to detect this and put the fragment in a @code block so that Doxygen
# will format it nicely. However, not everything which is indented is a code
# block. Another common case is lists, which look like:
# // Here is a list:
# // * List item 1.
# // This is still part of list item 1.
# // Notice the indent.
# // * List item 2.
# // The list is over now.
# So, we want to detect lists and *not* interpret them as code.
def FormatDocComment(comment, out):
min_indent_for_code = 3
in_code = False
empty_lines = []
for line in comment:
# Doxygen gets confused when it sees "*/" inside a // comment and silently
# fails to parse anything in the file. Thanks, Doxygen.
line = line.replace("*/", "* /")
# Separate the comment text from the pre-comment indent.
(outer_indent, content) = line.split("//", 1)
if content.strip() == "":
# The line was empty. Buffer it for now.
empty_lines.append(outer_indent + "///" + content)
continue
# How many spaces is this line indented *after* the comment start? E.g.
# the line " // foo" has a two-space outer indent and a one-space
# inner_indent.
inner_indent = re.match(" *", content).end()
if inner_indent >= min_indent_for_code:
# Content is indented enough to be a code fragment.
if not in_code:
# Begin a code block.
# Flush empty lines first.
out.writelines(empty_lines)
empty_lines = []
out.write("%s///%s@code\n" %
(outer_indent, " " * (min_indent_for_code - 2)))
out.offset += 1
in_code = True
# Doxygen takes all the raw bytes between @code and @encode and copies
# them directly into the final web page. It does not look for newlines
# in this range at all. So, if you have something reasonable like:
# /// @code
# /// int foo = 1;
# /// @endcode
# Your code fragment ends up being:
# /// int foo = 1;
# ///
# That is, it includes the stupid "///"s, which obviously aren't supposed
# to be part of the fragment. Instead, you have to do:
# /// @code
# int foo = 1;
# @endcode
# Which is, of course, totally invalid C++. But luckily we don't need
# this to compile as C++. We just need Doxygen to read it.
#
# Before you ask, yes, I tried doing this:
# /** @code
# int foo = 1;
# @endcode */
# For whatever reason, Doxygen didn't like it.
out.write("\n" * len(empty_lines))
empty_lines = []
out.write(content[min_indent_for_code:])
Returns:
True if there was an available empty line to which text could be
appended, and False otherwise.
"""
if self.empty_line_buffer:
last_line = self.empty_line_buffer.pop().rstrip()
last_line += text + '\n'
self.empty_line_buffer.append(last_line)
return True
else:
if in_code:
# End a code block. Do not flush empty lines; we don't want to
# include them in the block.
out.write("@endcode\n")
out.offset += 1
in_code = False
return False
# Replace '*' bullet style with '-', since Doxygen only recognizes the
# latter.
content = re.sub("^( +)[*] ", "\\1- ", content)
def ConvertCodeBlock(self, line):
"""Converts any code block that may begin or end on this line.
if re.match(" +- ", content):
# This line starts a bullet list. Subsequent lines will be indented,
# but should not be code.
min_indent_for_code = inner_indent + 4
elif re.match(".*\\w.*:.*\\w.*$", content):
# This line starts a note. Subsequent lines will be indented, but
# should not be code.
#
# Note: "Note" comments look like this. They start with a word
# followed by a colon (e.g. "Note:", "TODO(kenton):", etc.). A
# line which merely ends with a colon, however, should *not* be
# considered a note, because indented code blocks are often
# preceeded by such lines.
min_indent_for_code = inner_indent + 4
else:
# Nothing special here. If the next line is indented then we will
# put it in a code block.
min_indent_for_code = inner_indent + 2
Doxygen has (at least) two kinds of code blocks. Any block indented at
least four spaces gets formatted as code, but (for some reason) no syntax
highlighting is applied. Any block surrounded by "~~~" on both sides is
also treated as code, but these are syntax highlighted intelligently
depending on the file type. We typically write code blocks in the former
style, but we'd like them to be highlighted, so this function converts them
to the latter style by adding in the ~~~ lines.
out.writelines(empty_lines)
empty_lines = []
out.write(outer_indent + "///" + content)
To make this a bit more complicated, we would really prefer not to insert
new lines into the file, since that will make the line numbers shown in
doxygen not match the line numbers in the actual source code. For this
reason, we only perform the conversion if at least one "blank" line (empty
comment line) appears before the start of the code block. If we get down to
the bottom of the block and there's no blank line after it, we will be
forced to add a line, since we can't go back and undo what we already did.
if in_code:
# End a code block. Do not flush empty lines; we don't want to
# include them in the block.
out.write("@endcode\n")
out.offset += 1
# Now flush empty lines.
out.writelines(empty_lines)
Args:
line: The line to process.
# Reads a comment block. Returns a list of lines.
def ReadComment(reader):
assert reader.LookingAt(" *//")
comment = []
while (reader.LookingAt(" *//") or
reader.LookingAt("#ifndef PROTO2_OPENSOURCE") or
reader.LookingAt("#endif // !PROTO2_OPENSOURCE")):
if reader.LookingAt(" *//"):
comment.append(reader.Consume())
Returns:
The converted line.
"""
if not self.in_code_block and re.match(r'\s*///\s{4,}', line):
if self.AppendToBufferedLine(' ~~~'):
# If this fails, we'll just leave it un-highlighted.
self.in_code_block = True
elif self.in_code_block and not re.match(r'\s*///\s{4,}', line):
if not self.AppendToBufferedLine(' ~~~'):
# This is bad. We don't have a buffered line to use to end the code
# block, so we'll have to insert one. This will cause the line
# numbers to stop matching the original source, unfortunately.
line = '/// ~~~\n' + line
self.in_code_block = False
return line
def ProcessLine(self, line):
"""Processes a line.
If the line is an empty line inside a comment, we buffer it for possible
rewriting later on. Otherwise, we transform it using our regexps and
write it (as well as any buffered blank lines) out to the output.
Args:
line: The line to process.
"""
line = self.Transform(line)
if line.strip() == '///':
# We may repurpose this empty line later, so don't write it out yet.
self.empty_line_buffer.append(line)
else:
# Replace PROTO2_OPENSOURCE ifdef with a blank comment line. These
# ifdefs are used to hide google-internal parts of doc comments from
# the open source release. The directives will actually be stripped
# entirely from the released code, so if we're seeing them, then we
# must be running against the internal code. This means we should
# include the comments they are guarding. We replace it with a
# blank comment that is indented equally to the previous comment line.
comment.append(" " * comment[-1].index("//") + "//\n")
reader.Consume()
return comment
line = self.ConvertCodeBlock(line)
# Flush the line buffer and write this line as well.
for buffered_line in self.empty_line_buffer:
self.out.write(buffered_line)
self.empty_line_buffer = []
self.out.write(line)
# Reads a comment block and writes it to the output. If the comment appears to
# be documenting something, translates it to Doxygen format in the process.
def HandleComment(reader, out, group_tracker):
if reader.Eof(): return
comment = ReadComment(reader)
def main(argv):
sourcefile = argv[1]
with open(sourcefile, 'r') as infile:
formatter = DoxygenFormatter(sys.stdout)
for line in infile:
formatter.ProcessLine(line)
# Figure out what to do with it.
if ((comment[0].count("----") > 0 or comment[0].count("====") > 0) and
comment[0].startswith(" ")):
# The comment is a divider. Put everything under it into a group.
group_tracker.StartGroup(comment[0].index("//"), out)
# Remove the -'s and ='s.
comment[0] = comment[0].rstrip(" -=\r\n") + "\n";
if comment[0] == "//":
# The first line is entirely composed of -'s or ='s. Remove it.
comment = comment[1:]
# We know the offset is at least 1 from the StartGroup() call, so we
# should be able to decrement it.
assert out.offset > 0
out.offset -= 1
else:
comment[0] = comment[0].replace("//", "// @name")
# If the comment is non-empty, we want to write it even if it is not
# followed by any content, since it documents the group.
if comment:
FormatDocComment(comment, out)
# We need to ensure there is a blank line here so that the group's
# doc comment doesn't run up against the doc comment for the first thing
# in it.
out.write("\n")
out.offset += 1
elif (# A line with alphanumeric characters is considered to be content.
reader.LookingAt(".*\\w.*") and
# Unless all the characters are in a comment.
not reader.LookingAt("\\W*//") and
# Also, class forward-declarations, namespace decls, and preprocessor
# directives should not be documented.
not reader.LookingAt(" *class +\\w+ *;") and
not reader.LookingAt(" *namespace +\\w+ *{") and
not reader.LookingAt(" *#") and
# And neither should inline method definitions declared outside
# the class definition.
not reader.LookingAt("[^ ].*\w+::\w+ *\\(\\)")):
# Line contains content. Transform the comment into a doc comment and
# write it.
if reader.LookingAt(".*typedef"):
# If a group contains a typedef, Doxygen annoyingly places that group
# above all other groups, rather than placing it in declaration order
# like normal. So, don't let typedefs be in groups.
group_tracker.EndGroupsDeeperThan(reader.GetIndent() - 1, out)
FormatDocComment(comment, out)
else:
# No content. The comment is not a doc comment. Just write it. But try
# to make up some offset lines while we're here.
if out.offset >= len(comment):
# The comment is shorter than the offset, so don't write it at all.
out.offset -= len(comment)
else:
out.writelines(comment[out.offset:])
out.offset = 0
# Skips code lines until a comment block is encountered.
def FindNextComment(reader, out, group_tracker):
while not reader.LookingAt(" *//") and not reader.Eof():
if (reader.LookingAt(".*[^ \n].*$") and
not reader.LookingAt(" *#")):
# This line containts some sort of content. End any groups which had
# deeper indentation levels than this line.
group_tracker.EndGroupsDeeperThan(reader.GetIndent(), out)
# By default, Doxygen puts class members into implicit groups like
# "Public Member Functions" and "Public Fields". This script sometimes
# produces explicit groups as well, in order to improve organization.
# Unfortunately, Doxygen does not handle things very well when some things
# are explicitly grouped and some aren't. In particular:
# * If the SUBGROUPING option is on, then the explicit groups will be
# nested within the implicit ones, but *only if* all members of the group
# belong in the same implicit group (e.g. they are all methods, or all
# fields, etc.). If you make an explicit group that crosses multiple
# implicit groups, the explicit group becomes a top-level group and
# sits along side the implicit ones. This is really confusing, because
# it makes it look like these groups are different from the ones that
# are nested.
# * If the SUBGROUPING option is off, then explicit groups always appear
# as peers to the implicit ones. Unfortunately, the explicit groups
# seem to appear *above* the implicit ones, even if the members in the
# implicit group are all defined before the explicit group.
# Unfortunately, methods which are not in any particular group are almost
# always more important than methods that are in groups, and thus
# should appear first.
# The only way we can solve both of these problems without giving up on
# groups altogether is to force *all* members to be in explicit groups.
# So, we start a group called "General Members" immediately after the
# "public:" line to pick up ungrouped stuff.
is_public_line = False
public_line_indent = 0
if reader.LookingAt(" *public:"):
is_public_line = True
public_line_indent = reader.GetIndent() + 1
if (reader.LookingAt(".*\\w.*//") and
not reader.LookingAt(" *namespace ") and
not reader.LookingAt(" *class +\\w+ *;") and
not reader.LookingAt(" *#")):
# This line has some content followed by a line comment, e.g.:
# FOO, // The FOO enum value.
# We want to convert the comment into a post-declaration comment, like:
# FOO, ///< The FOO enum value.
line = reader.Consume()
out.write(line.replace("//", "///<", 1))
# If subsequent lines have comments that are lined up with this one
# and only whitespace before those comments, they must be part of this
# comment.
indent = line.index("//")
while reader.LookingAt(" " * indent + "//"):
out.write(reader.Consume().replace("//", "///<", 1))
elif reader.LookingAt(".*ATTRIBUTE_ALWAYS_INLINE"):
# Hide this from Doxygen. Otherwise it actually documents its presence,
# which looks confusing.
out.write(reader.Consume().replace("ATTRIBUTE_ALWAYS_INLINE", ""))
elif out.offset > 0 and reader.LookingAt(" *$"):
# Skip empty line to make up an offset line.
out.offset -= 1
reader.Consume()
else:
# Normal line; just print it.
reader.ConsumeAndWrite(out)
# See the monstrous comment earlier in this function.
if is_public_line:
group_tracker.StartGroup(public_line_indent, out)
out.write(" " * public_line_indent + "/// @name General Members\n\n")
out.offset += 2
# Read the entire file and convert it to Doxygen format.
def HandleFile(reader, out):
out = OffsetTrackingFile(out)
if reader.LookingAt(".*[cC]opyright.*"):
# Skip copyright comment.
while reader.LookingAt("//..+$"):
reader.ConsumeAndWrite(out)
# Skip blank lines after copyright.
while reader.LookingAt("//$"):
reader.ConsumeAndWrite(out)
# Read the file-level docs.
file_comment = []
if (reader.LookingAt("//")):
file_comment = ReadComment(reader)
# Write the file-level docs as a Doxygen comment. Even if the file had no
# top-level comment, we want to write an empty Doxygen file comment.
# Otherwise, Doxygen will not generate a page listing the contents of this
# file, even if the contents are themselves documented. Silly Doxygen.
out.write("/// @file\n")
out.offset += 1
if file_comment:
FormatDocComment(file_comment, out)
# Loop through the rest of the file.
group_tracker = GroupTracker()
while not reader.Eof():
FindNextComment(reader, out, group_tracker)
HandleComment(reader, out, group_tracker)
# Doxygen expects an input filter to take the source file name as a
# command-line argument and write the filtered text to stdout.
if len(sys.argv) == 1:
# No arguments given. Read from stdin. Useful for debugging.
HandleFile(Reader(sys.stdin), sys.stdout)
else:
for filename in sys.argv[1:]:
f = file(filename, "r")
try:
HandleFile(Reader(f), sys.stdout)
finally:
f.close()
if __name__ == '__main__':
main(sys.argv)

2511
tools/ortools.doxy Normal file

File diff suppressed because it is too large Load Diff