Followed the Migration Guide at https://cli.urfave.org/migrate-v1-to-v2/ The major changes not pointed out in the migration guide are: - context.Args() no longer produces a []slice, so context.Args().Slice() in substitued - All cli.Global***** are deprecated (the migration guide is somewhat unclear on this) Signed-off-by: Derek Nola <derek.nola@suse.com> Vendor in urfave cli/v2 Signed-off-by: Derek Nola <derek.nola@suse.com> Fix NewStringSlice calls Signed-off-by: Derek Nola <derek.nola@suse.com>
		
			
				
	
	
		
			204 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			204 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
//go:build !urfave_cli_no_docs
 | 
						|
// +build !urfave_cli_no_docs
 | 
						|
 | 
						|
package cli
 | 
						|
 | 
						|
import (
 | 
						|
	"bytes"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"sort"
 | 
						|
	"strings"
 | 
						|
	"text/template"
 | 
						|
 | 
						|
	"github.com/cpuguy83/go-md2man/v2/md2man"
 | 
						|
)
 | 
						|
 | 
						|
// ToMarkdown creates a markdown string for the `*App`
 | 
						|
// The function errors if either parsing or writing of the string fails.
 | 
						|
func (a *App) ToMarkdown() (string, error) {
 | 
						|
	var w bytes.Buffer
 | 
						|
	if err := a.writeDocTemplate(&w, 0); err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	return w.String(), nil
 | 
						|
}
 | 
						|
 | 
						|
// ToMan creates a man page string with section number for the `*App`
 | 
						|
// The function errors if either parsing or writing of the string fails.
 | 
						|
func (a *App) ToManWithSection(sectionNumber int) (string, error) {
 | 
						|
	var w bytes.Buffer
 | 
						|
	if err := a.writeDocTemplate(&w, sectionNumber); err != nil {
 | 
						|
		return "", err
 | 
						|
	}
 | 
						|
	man := md2man.Render(w.Bytes())
 | 
						|
	return string(man), nil
 | 
						|
}
 | 
						|
 | 
						|
// ToMan creates a man page string for the `*App`
 | 
						|
// The function errors if either parsing or writing of the string fails.
 | 
						|
func (a *App) ToMan() (string, error) {
 | 
						|
	man, err := a.ToManWithSection(8)
 | 
						|
	return man, err
 | 
						|
}
 | 
						|
 | 
						|
type cliTemplate struct {
 | 
						|
	App          *App
 | 
						|
	SectionNum   int
 | 
						|
	Commands     []string
 | 
						|
	GlobalArgs   []string
 | 
						|
	SynopsisArgs []string
 | 
						|
}
 | 
						|
 | 
						|
func (a *App) writeDocTemplate(w io.Writer, sectionNum int) error {
 | 
						|
	const name = "cli"
 | 
						|
	t, err := template.New(name).Parse(MarkdownDocTemplate)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	return t.ExecuteTemplate(w, name, &cliTemplate{
 | 
						|
		App:          a,
 | 
						|
		SectionNum:   sectionNum,
 | 
						|
		Commands:     prepareCommands(a.Commands, 0),
 | 
						|
		GlobalArgs:   prepareArgsWithValues(a.VisibleFlags()),
 | 
						|
		SynopsisArgs: prepareArgsSynopsis(a.VisibleFlags()),
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
func prepareCommands(commands []*Command, level int) []string {
 | 
						|
	var coms []string
 | 
						|
	for _, command := range commands {
 | 
						|
		if command.Hidden {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		usageText := prepareUsageText(command)
 | 
						|
 | 
						|
		usage := prepareUsage(command, usageText)
 | 
						|
 | 
						|
		prepared := fmt.Sprintf("%s %s\n\n%s%s",
 | 
						|
			strings.Repeat("#", level+2),
 | 
						|
			strings.Join(command.Names(), ", "),
 | 
						|
			usage,
 | 
						|
			usageText,
 | 
						|
		)
 | 
						|
 | 
						|
		flags := prepareArgsWithValues(command.VisibleFlags())
 | 
						|
		if len(flags) > 0 {
 | 
						|
			prepared += fmt.Sprintf("\n%s", strings.Join(flags, "\n"))
 | 
						|
		}
 | 
						|
 | 
						|
		coms = append(coms, prepared)
 | 
						|
 | 
						|
		// recursively iterate subcommands
 | 
						|
		if len(command.Subcommands) > 0 {
 | 
						|
			coms = append(
 | 
						|
				coms,
 | 
						|
				prepareCommands(command.Subcommands, level+1)...,
 | 
						|
			)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return coms
 | 
						|
}
 | 
						|
 | 
						|
func prepareArgsWithValues(flags []Flag) []string {
 | 
						|
	return prepareFlags(flags, ", ", "**", "**", `""`, true)
 | 
						|
}
 | 
						|
 | 
						|
func prepareArgsSynopsis(flags []Flag) []string {
 | 
						|
	return prepareFlags(flags, "|", "[", "]", "[value]", false)
 | 
						|
}
 | 
						|
 | 
						|
func prepareFlags(
 | 
						|
	flags []Flag,
 | 
						|
	sep, opener, closer, value string,
 | 
						|
	addDetails bool,
 | 
						|
) []string {
 | 
						|
	args := []string{}
 | 
						|
	for _, f := range flags {
 | 
						|
		flag, ok := f.(DocGenerationFlag)
 | 
						|
		if !ok {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		modifiedArg := opener
 | 
						|
 | 
						|
		for _, s := range flag.Names() {
 | 
						|
			trimmed := strings.TrimSpace(s)
 | 
						|
			if len(modifiedArg) > len(opener) {
 | 
						|
				modifiedArg += sep
 | 
						|
			}
 | 
						|
			if len(trimmed) > 1 {
 | 
						|
				modifiedArg += fmt.Sprintf("--%s", trimmed)
 | 
						|
			} else {
 | 
						|
				modifiedArg += fmt.Sprintf("-%s", trimmed)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		modifiedArg += closer
 | 
						|
		if flag.TakesValue() {
 | 
						|
			modifiedArg += fmt.Sprintf("=%s", value)
 | 
						|
		}
 | 
						|
 | 
						|
		if addDetails {
 | 
						|
			modifiedArg += flagDetails(flag)
 | 
						|
		}
 | 
						|
 | 
						|
		args = append(args, modifiedArg+"\n")
 | 
						|
 | 
						|
	}
 | 
						|
	sort.Strings(args)
 | 
						|
	return args
 | 
						|
}
 | 
						|
 | 
						|
// flagDetails returns a string containing the flags metadata
 | 
						|
func flagDetails(flag DocGenerationFlag) string {
 | 
						|
	description := flag.GetUsage()
 | 
						|
	if flag.TakesValue() {
 | 
						|
		defaultText := flag.GetDefaultText()
 | 
						|
		if defaultText == "" {
 | 
						|
			defaultText = flag.GetValue()
 | 
						|
		}
 | 
						|
		if defaultText != "" {
 | 
						|
			description += " (default: " + defaultText + ")"
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return ": " + description
 | 
						|
}
 | 
						|
 | 
						|
func prepareUsageText(command *Command) string {
 | 
						|
	if command.UsageText == "" {
 | 
						|
		return ""
 | 
						|
	}
 | 
						|
 | 
						|
	// Remove leading and trailing newlines
 | 
						|
	preparedUsageText := strings.Trim(command.UsageText, "\n")
 | 
						|
 | 
						|
	var usageText string
 | 
						|
	if strings.Contains(preparedUsageText, "\n") {
 | 
						|
		// Format multi-line string as a code block using the 4 space schema to allow for embedded markdown such
 | 
						|
		// that it will not break the continuous code block.
 | 
						|
		for _, ln := range strings.Split(preparedUsageText, "\n") {
 | 
						|
			usageText += fmt.Sprintf("    %s\n", ln)
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		// Style a single line as a note
 | 
						|
		usageText = fmt.Sprintf(">%s\n", preparedUsageText)
 | 
						|
	}
 | 
						|
 | 
						|
	return usageText
 | 
						|
}
 | 
						|
 | 
						|
func prepareUsage(command *Command, usageText string) string {
 | 
						|
	if command.Usage == "" {
 | 
						|
		return ""
 | 
						|
	}
 | 
						|
 | 
						|
	usage := command.Usage + "\n"
 | 
						|
	// Add a newline to the Usage IFF there is a UsageText
 | 
						|
	if usageText != "" {
 | 
						|
		usage += "\n"
 | 
						|
	}
 | 
						|
 | 
						|
	return usage
 | 
						|
}
 |