cli icon indicating copy to clipboard operation
cli copied to clipboard

How to actually implement word wrapping?

Open autarch opened this issue 3 years ago • 8 comments

I copied some code from the tests and got this:

func main() {
	cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
		funcMap := map[string]interface{}{
			"wrapAt": func() int {
				return 80
			},
		}

		cli.HelpPrinterCustom(w, templ, data, funcMap)
	}

	app := &cli.App{...}
	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}

But when I look at the help output, nothing is wrapped. Am I doing this right?

It'd be really nice if this was better documented. Right now there's just has a passing mention of wrapAt in the docs but no examples.

autarch avatar Aug 30 '22 17:08 autarch

Specifically which content will be wrapped, you can refer to cli.AppHelpTemplate: https://github.com/urfave/cli/blob/254c38ea72e18e58f2ba8a4e25c7c553f19161e6/template.go#L3-L38

Dokiys avatar Sep 01 '22 03:09 Dokiys

Hi @Dokiys, that's not what I was getting at. I'm saying that how to inject wrapping wasn't documented, nor is what will be wrapped. Looking at a template doesn't really help that much.

autarch avatar Sep 01 '22 03:09 autarch

Hi @Dokiys, that's not what I was getting at. I'm saying that how to inject wrapping wasn't documented, nor is what will be wrapped. Looking at a template doesn't really help that much.

Hi @autarch I think if you want to custom HelpPrinter by default template and implementation of HelpPrinterCustom, you should know about what its handling. About 'how to inject wrapping', there are notes here: https://github.com/urfave/cli/blob/ca9df40abd0db61673fb645f9826d8fbb061b6bb/help.go#L289-L293 About 'what will be wrapped', you can find in AppHelpTemplate and here: https://github.com/urfave/cli/blob/ca9df40abd0db61673fb645f9826d8fbb061b6bb/help.go#L52-L53 But I think the method name "wrap" is really confusing.

Dokiys avatar Sep 01 '22 06:09 Dokiys

Hi @autarch

You can try this

package main

import (
	"log"
	"os"

	"github.com/urfave/cli/v2"
)

func main() {

	app := &cli.App{
		Name:  "long",
		Usage: "Long command description cli",

		Commands: []*cli.Command{
			{
				Name:    "help",
				Aliases: []string{"h"},
				Action: func(cCtx *cli.Context) error {
					_ = ShowAppHelp(cCtx)
					return nil
				},
			},
			{
				Name:    "testing",
				Aliases: []string{"t"},
				Usage:   "aaaaaaaaa aaaaaaaaa aaaaa aaaaaaaaa aaaaaaaaaaaaaaa aaaaa aaaaaaaaaaaa aaa aa aaaaaa aa aaaa",
			},
		},
	}

	if err := app.Run(os.Args); err != nil {
		log.Fatal(err)
	}
}

func ShowAppHelp(c *cli.Context) error {
	template := c.App.CustomAppHelpTemplate
	if template == "" {
		template = cli.AppHelpTemplate
	}

	customAppData := func() map[string]interface{} {
		return map[string]interface{}{
			"wrapAt": func() int {
				return 80
			},
		}
	}
	cli.HelpPrinterCustom(c.App.Writer, template, c.App, customAppData())
	return nil
}

This result will be the following:

NAME:
   long - Long command description cli

USAGE:
   long  command [command options] [arguments...]

COMMANDS:
   help, h     
   testing, t  aaaaaaaaa aaaaaaaaa aaaaa aaaaaaaaa aaaaaaaaaaaaaaa aaaaa
               aaaaaaaaaaaa aaa aa aaaaaa aa aaaa

Still it doesn't work quite will with Flags since the template doesn't handle flag usage wrapping. To fix it you can use the following template:

var AppHelpTemplate = `NAME:
   {{$v := offset .Name 6}}{{wrap .Name 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}}

USAGE:
   {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}

VERSION:
   {{.Version}}{{end}}{{end}}{{if .Description}}

DESCRIPTION:
   {{wrap .Description 3}}{{end}}{{if len .Authors}}

AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
   {{range $index, $author := .Authors}}{{if $index}}
   {{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}}

COMMANDS:{{range .VisibleCategories}}{{if .Name}}
   {{.Name}}:{{range .VisibleCommands}}
     {{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{else}}{{ $cv := offsetCommands .VisibleCommands 5}}{{range .VisibleCommands}}
   {{$s := join .Names ", "}}{{$s}}{{ $sp := subtract $cv (offset $s 3) }}{{ indent $sp ""}}{{wrap .Usage $cv}}{{end}}{{end}}{{end}}{{end}}{{if .VisibleFlagCategories}}

GLOBAL OPTIONS:{{range .VisibleFlagCategories}}
   {{if .Name}}{{.Name}}
   {{end}}{{range .Flags}}{{$v := offset .Name 6}}{{wrap .Name 3}}{{if .Usage}} - {{wrap .Usage $v}}{{end}}
   {{end}}{{end}}{{else}}{{if .VisibleFlags}}

GLOBAL OPTIONS:
   {{range $index, $option := .VisibleFlags}}{{if $index}}
   {{end}}{{wrap $option.String 3}}{{end}}{{end}}{{end}}{{if .Copyright}}

COPYRIGHT:
   {{wrap .Copyright 3}}{{end}}
`

Cheers,

abousselmi avatar Sep 08 '22 22:09 abousselmi

Documentation added for this.

dearchap avatar Oct 27 '22 12:10 dearchap