Files
dolthub--dolt/go/cmd/dolt/cli/documentation_helper.go
wehub-resource-sync 5357c39144
Fuzzer / Run Fuzzer (push) Has been cancelled
Race tests / Go race tests (ubuntu-22.04) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:01:40 +08:00

308 lines
8.5 KiB
Go

// Copyright 2020 Dolthub, Inc.
//
// 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.
package cli
import (
"bytes"
"fmt"
"strings"
"text/template"
"github.com/dolthub/dolt/go/libraries/utils/argparser"
)
type commandDocumentForMarkdown struct {
Command string
ShortDesc string
Synopsis string
Description string
Options string
}
var cmdMdDocTempl = "## `{{.Command}}`\n\n" +
"{{.ShortDesc}}\n\n" +
"**Synopsis**\n\n" +
"{{.Synopsis}}\n\n" +
"**Description**\n\n" +
"{{.Description}}\n\n" +
"**Arguments and options**\n\n" +
"{{.Options}}\n\n"
func (cmdDoc CommandDocumentation) CmdDocToMd() (string, error) {
return cmdDoc.executeTemplate(cmdMdDocTempl, false)
}
var globalCmdMdDocTempl = "## Global Arguments\n" +
"{{.ShortDesc}}\n" +
"{{.Synopsis}}\n\n" +
"Specific dolt options:\n\n" +
"{{.Options}}\n"
func (cmdDoc CommandDocumentation) GlobalCmdDocToMd() (string, error) {
return cmdDoc.executeTemplate(globalCmdMdDocTempl, true)
}
func (cmdDoc CommandDocumentation) executeTemplate(cmdTempl string, includeValDesc bool) (string, error) {
// Accumulate the options and args in a string
options := ""
if len(cmdDoc.ArgParser.Supported) > 0 || len(cmdDoc.ArgParser.ArgListHelp) > 0 {
// Iterate across arguments and template them
for _, kvTuple := range cmdDoc.ArgParser.ArgListHelp {
arg, desc := kvTuple[0], kvTuple[1]
templatedDesc, err := templateDocStringHelper(desc, MarkdownFormat)
if err != nil {
return "", err
}
argStruct := argument{arg, templatedDesc}
outputStr, err := templateArgument(argStruct)
if err != nil {
return "", err
}
options += outputStr
}
// Iterate across supported options, templating each one of them
for _, supOpt := range cmdDoc.ArgParser.Supported {
templatedDesc, err := templateDocStringHelper(supOpt.Desc, MarkdownFormat)
if err != nil {
return "", err
}
argStruct := supported{supOpt.Abbrev, supOpt.Name, "", templatedDesc}
if includeValDesc {
argStruct.ValDesc = supOpt.ValDesc
}
outputStr, err := templateSupported(argStruct)
if err != nil {
return "", err
}
options += outputStr
}
} else {
options = `No options for this command.`
}
cmdMdDoc, cmdMdDocErr := cmdDoc.cmdDocToCmdDocMd(options)
if cmdMdDocErr != nil {
return "", cmdMdDocErr
}
templ, templErr := template.New("shortDesc").Parse(cmdTempl)
if templErr != nil {
return "", templErr
}
var templBuffer bytes.Buffer
if err := templ.Execute(&templBuffer, cmdMdDoc); err != nil {
return "", err
}
ret := strings.Replace(templBuffer.String(), "HEAD~", "HEAD\\~", -1)
return ret, nil
}
// A struct that represents all the data structures required to create the documentation for a command.
type CommandDocumentation struct {
// The command/sub-command string passed to a command by the caller
CommandStr string
// The short description of the command
ShortDesc string
// The long description of the command
LongDesc string
// The synopsis, an array of strings showing how to use the command
Synopsis []string
// A structure that
ArgParser *argparser.ArgParser
}
func (cmdDoc CommandDocumentation) cmdDocToCmdDocMd(options string) (commandDocumentForMarkdown, error) {
longDesc, longDescErr := cmdDoc.GetLongDesc(MarkdownFormat)
if longDescErr != nil {
return commandDocumentForMarkdown{}, longDescErr
}
synopsis, synopsisErr := cmdDoc.GetSynopsis(SynopsisMarkdownFormat)
if synopsisErr != nil {
return commandDocumentForMarkdown{}, synopsisErr
}
return commandDocumentForMarkdown{
Command: cmdDoc.CommandStr,
ShortDesc: cmdDoc.GetShortDesc(),
Synopsis: transformSynopsisToMarkdown(cmdDoc.CommandStr, synopsis),
Description: longDesc,
Options: options,
}, nil
}
// NewCommandDocumentation returns a |CommandDocumentation| for the content and arg parser given.
// Does not include a command string, which must be filled in separately.
func NewCommandDocumentation(cmdDoc CommandDocumentationContent, argParser *argparser.ArgParser) *CommandDocumentation {
return &CommandDocumentation{
ShortDesc: cmdDoc.ShortDesc,
LongDesc: cmdDoc.LongDesc,
Synopsis: cmdDoc.Synopsis,
ArgParser: argParser,
}
}
// CommandDocsForCommandString returns a |CommandDocumentation| for the command string, doc contents, and arg
// parser given.
func CommandDocsForCommandString(command string, cmdDoc CommandDocumentationContent, argParser *argparser.ArgParser) *CommandDocumentation {
return &CommandDocumentation{
CommandStr: command,
ShortDesc: cmdDoc.ShortDesc,
LongDesc: cmdDoc.LongDesc,
Synopsis: cmdDoc.Synopsis,
ArgParser: argParser,
}
}
// Returns the ShortDesc field of the receiver CommandDocumentation with the passed DocFormat injected into the template
func (cmdDoc CommandDocumentation) GetShortDesc() string {
return cmdDoc.ShortDesc
}
// Returns the LongDesc field of the receiver CommandDocumentation with the passed DocFormat injected into the template
func (cmdDoc CommandDocumentation) GetLongDesc(format docFormat) (string, error) {
return templateDocStringHelper(cmdDoc.LongDesc, format)
}
func templateDocStringHelper(docString string, docFormat docFormat) (string, error) {
templ, err := template.New("description").Parse(docString)
if err != nil {
return "", err
}
var templBuffer bytes.Buffer
if err := templ.Execute(&templBuffer, docFormat); err != nil {
return "", err
}
return templBuffer.String(), nil
}
// Returns the synopsis iterating over each element and injecting the supplied DocFormat
func (cmdDoc CommandDocumentation) GetSynopsis(format docFormat) ([]string, error) {
lines := cmdDoc.Synopsis
for i, line := range lines {
formatted, err := templateDocStringHelper(line, format)
if err != nil {
return []string{}, err
}
lines[i] = formatted
}
return lines, nil
}
type docFormat struct {
LessThan string
GreaterThan string
EmphasisLeft string
EmphasisRight string
}
// mdx format
var MarkdownFormat = docFormat{"`<", ">`", "`", "`"}
// Shell help output format
var CliFormat = docFormat{"<", ">", "<b>", "</b>"}
// Synopsis is an mdx format, but already inside a code block
var SynopsisMarkdownFormat = docFormat{"<", ">", "`", "`"}
// Format that deletes all template options
var EmptyFormat = docFormat{"", "", "", ""}
func transformSynopsisToMarkdown(commandStr string, synopsis []string) string {
if len(synopsis) == 0 {
return ""
}
synopsisStr := fmt.Sprintf("%s %s\n", commandStr, synopsis[0])
if len(synopsis) > 1 {
temp := make([]string, len(synopsis)-1)
for i, el := range synopsis[1:] {
temp[i] = fmt.Sprintf("%s %s\n", commandStr, el)
}
synopsisStr += strings.Join(temp, "")
}
markdown := "```bash\n%s```"
return fmt.Sprintf(markdown, synopsisStr)
}
type argument struct {
Name string
Description string
}
func templateArgument(supportedArg argument) (string, error) {
var formatString string
if supportedArg.Description == "" {
formatString = "`<{{.Name}}>`\n\n"
} else {
formatString = "`<{{.Name}}>`: {{.Description}}\n\n"
}
templ, err := template.New("argString").Parse(formatString)
if err != nil {
return "", err
}
var templBuffer bytes.Buffer
if err := templ.Execute(&templBuffer, supportedArg); err != nil {
return "", err
}
ret := templBuffer.String()
return ret, nil
}
type supported struct {
Abbreviation string
Name string
ValDesc string
Description string
}
func templateSupported(supported supported) (string, error) {
abbr := ""
if supported.Abbreviation != "" {
abbr = "-{{.Abbreviation}}"
if supported.ValDesc != "" {
abbr += " <{{.ValDesc}}>"
}
abbr = "`" + abbr + "`, "
}
name := "--{{.Name}}"
if supported.ValDesc != "" {
name += "=<{{.ValDesc}}>"
}
name = "`" + name + "`"
desc := ""
if supported.Description != "" {
desc = ":\n{{.Description}}"
}
formatString := fmt.Sprintf("%s%s%s\n\n", abbr, name, desc)
templ, err := template.New("argString").Parse(formatString)
if err != nil {
return "", err
}
var templBuffer bytes.Buffer
if err := templ.Execute(&templBuffer, supported); err != nil {
return "", err
}
ret := templBuffer.String()
return ret, nil
}