blob: bd78e70ddbf5fc39af0f38baa43217af64c78a54 [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package docgen
import (
"fmt"
"go.fuchsia.dev/fuchsia/tools/cppdocgen/clangdoc"
"io"
)
// Writes either "()" for functions with no arguments, or "(...)" for functions with arguments.
// This is used to make things look like function calls without listing the arguments or incorrectly
// implying that they take no arguments.
func functionEllipsesParens(fn *clangdoc.FunctionInfo) string {
if len(fn.Params) == 0 {
return "()"
}
return "(…)"
}
func functionGroupEllipsesParens(g *FunctionGroup) string {
for _, fn := range g.Funcs {
if len(fn.Params) > 0 {
return "(…)"
}
}
return "()"
}
// If linkDest is nonempty this will make the function name a link. The number of visible characters
// in this prefix is supplied, which allows the caller to include HTML tags and escaped characters
// while keeping things aligned.
func writeFunctionDeclaration(fn *clangdoc.FunctionInfo, namePrefix string,
namePrefixVisibleCharLen int, includeReturnType bool, linkDest string,
f io.Writer) {
if fn.Template != nil {
writeTemplateDeclaration(*fn.Template, f)
fmt.Fprintf(f, "\n")
}
retTypeLen := 0
if includeReturnType {
qualType, n := getEscapedTypeName(fn.ReturnType.Reference.QualName)
fmt.Fprintf(f, "<span class=\"typ\">%s</span> ", qualType)
retTypeLen = n + 1 // Include space after.
}
// Name (optionally linked and with optional prefix).
if len(linkDest) > 0 {
fmt.Fprintf(f, "<a href=\"%s\">", linkDest)
}
fmt.Fprintf(f, "%s<b>%s</b>", namePrefix, fn.Name)
if len(linkDest) > 0 {
fmt.Fprintf(f, "</a>")
}
// Template specializations.
templateSpecCharLen := 0
if fn.Template != nil && fn.Template.Specialization != nil {
templateParams := ""
templateParams, templateSpecCharLen = getTemplateParameterList(
fn.Template.Specialization.Params, true)
fmt.Fprintf(f, "%s", templateParams)
}
fmt.Fprintf(f, "(")
// Indent is type + space + prefix + name + paren.
indent := makeIndent(retTypeLen + namePrefixVisibleCharLen + len(fn.Name) + templateSpecCharLen + 1)
for i, param := range fn.Params {
if i > 0 {
fmt.Fprintf(f, ",\n")
f.Write(indent)
}
tn, _ := getEscapedTypeName(param.TypeRef.QualName)
if len(param.Name) == 0 {
// Unnamed parameter.
fmt.Fprintf(f, "<span class=\"typ\">%s</span>", tn)
} else {
fmt.Fprintf(f, "<span class=\"typ\">%s</span> %s", tn, param.Name)
}
// Optional default parameter value.
if param.DefaultValue != "" {
fmt.Fprintf(f, " = %s", param.DefaultValue)
}
}
fmt.Fprintf(f, ");\n")
}
// Writes the body of a function reference. This is used for both standalone functions and member
// functions.
//
// The |namePrefix| is prepended to the definition for defining class or namespace information.
// This could be extracted from the function but this lets the caller decide which information to
// include. The number of visible characters in this prefix is supplied, which allows the caller to
// include HTML tags and escaped characters while keeping things aligned.
func writeFunctionGroupBody(settings WriteSettings, index *Index, g *FunctionGroup,
namePrefix string, namePrefixVisibleCharLen int,
includeReturnType bool, f io.Writer) {
// Use the first function's location for the definition location link.
if g.Funcs[0].GetLocation().Filename != "" {
fmt.Fprintf(f, "[Declaration source code](%s)\n\n",
settings.locationSourceLink(g.Funcs[0].GetLocation()))
}
if !commentContains(g.Funcs[0].Description, NoDeclTag) {
// Write the declaration.
writePreHeader(f)
for _, fn := range g.Funcs {
writeFunctionDeclaration(fn, namePrefix, namePrefixVisibleCharLen,
includeReturnType, "", f)
}
writePreFooter(f)
}
// Any comment is on the first function in the group. If it has a heading, it will have
// been extracted and used as the title so we need to strip that to avoid duplicating.
_, commentWithNoH1 := extractCommentHeading1(g.Funcs[0].Description)
writeComment(index, commentWithNoH1, markdownHeading2, f)
fmt.Fprintf(f, "\n")
}
func functionGroupHtmlId(g *FunctionGroup) string {
// It seems devsite doesn't like more than one HTML ID for a heading. Until it is fixed
// or we can find a workaround, just use the first function's ID.
return functionHtmlId(g.Funcs[0])
}
// Writes the reference section for a standalone function.
func writeFunctionGroupSection(settings WriteSettings, index *Index, g *FunctionGroup, f io.Writer) {
if g.ExplicitTitle != "" {
fmt.Fprintf(f, "## %s {:#%s}\n\n", g.ExplicitTitle, functionGroupHtmlId(g))
} else {
fullName := functionFullName(g.Funcs[0])
fmt.Fprintf(f, "## %s {:#%s}\n\n",
titleWithTemplateSpecializations(fullName, g.Funcs[0].Template, functionGroupEllipsesParens(g), ""),
functionGroupHtmlId(g))
}
// Include the qualified namespaces as a prefix.
namespacePrefix := getScopeQualifier(g.Funcs[0].Namespace, true)
writeFunctionGroupBody(settings, index, g, namespacePrefix, len(namespacePrefix), true, f)
}
// Interface for sorting a function list by function name.
type functionByName []*clangdoc.FunctionInfo
func (f functionByName) Len() int {
return len(f)
}
func (f functionByName) Swap(i, j int) {
f[i], f[j] = f[j], f[i]
}
func (f functionByName) Less(i, j int) bool {
return f[i].Name < f[j].Name
}
// Interface for sorting a function list by declaration location.
type functionByLocation []*clangdoc.FunctionInfo
func (f functionByLocation) Len() int {
return len(f)
}
func (f functionByLocation) Swap(i, j int) {
f[i], f[j] = f[j], f[i]
}
func (f functionByLocation) Less(i, j int) bool {
// This assumes the file names are the same (since we're normally processing by file).
return f[i].GetLocation().LineNumber < f[j].GetLocation().LineNumber
}
func functionHtmlId(f *clangdoc.FunctionInfo) string {
// Use the fully-qualified function name. This can still produce collisions due to
// overloading but we don't have a way to differentiate this other than making all
// references by USR, which makes manual linking impossible.
//
// TODO(https://fxbug.dev/42070119) it would be nice to allow links to functions overloads and
// template specializations (the same name). If this function took an Index parameter, it
// could check for this case and use the USR id for all but the first instance.
return getScopeQualifier(f.Namespace, true) + f.Name
}
func functionLink(f *clangdoc.FunctionInfo) string {
return HeaderReferenceFile(f.GetLocation().Filename) + "#" + functionHtmlId(f)
}
func functionGroupLink(g *FunctionGroup) string {
return HeaderReferenceFile(g.Funcs[0].GetLocation().Filename) + "#" + functionGroupHtmlId(g)
}
// Returns the fully-qualified name of a function.
func functionFullName(f *clangdoc.FunctionInfo) string {
return getScopeQualifier(f.Namespace, true) + f.Name
}