spf13 / cobra

A Commander for modern Go CLI interactions
https://cobra.dev
Apache License 2.0
38.19k stars 2.85k forks source link

ANSI escape sequences and GenMarkdownTree #1878

Open chrboe opened 1 year ago

chrboe commented 1 year ago

Are ANSI escape sequences "officially" supported in Long descriptions?

Consider the following minimal example:

package main

import (
    "github.com/fatih/color"
    "github.com/spf13/cobra"
    "github.com/spf13/cobra/doc"
)

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
    Use:   "example",
    Short: "An example of cobra",
    Long: `This command does nothing.
` + color.RedString("NOTE") + ` however this important limitation: ...`,
}

func main() {
    RootCmd.Help()
    if err := doc.GenMarkdownTree(RootCmd, "docs"); err != nil {
        panic(err)
    }
}

This, of course, renders just fine in the terminal:

image

The markdown, however, looks predictably broken:

image

Is there a way to filter out "unprintable" characters like this when generating documentation?

BrunoQuaresma commented 1 year ago

Having a similar issue, did you find any workaround?

BrunoQuaresma commented 1 year ago

As a workaround, I iterate file by file and used the following function:

func stripANSI(str string) string {
    const ansi = "[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))"

    var re = regexp.MustCompile(ansi)

    return re.ReplaceAllString(str, "")
}

It is from https://github.com/acarl005/stripansi/blob/master/stripansi.go

macie commented 1 year ago

ANSI escape sequences are no more than markup language tags with obscure syntax. For example \x1b[1mbold\x1b[m is equal to HTML <b>bold</b>. And for any markup language you need an engine capable to interpret that markup. The most common engine is terminal emulator based on terminals from specific VT family.

But what if you want to show text in a client that don't understand any markup, eg. notepad or doc.GenMarkdownTree()? You will see text content mixed with tags (as in first comment).

As someone who dealt with this problem in the past I strongly recommend to use plain uppercase for important description. It's simple and compatible with any client at any operating system (I'm looking at you, Windows).

But if someone will be brave enough to implement this feature inside cobra, I can provide few hints:

  1. Read Wikipedia article before you start: _ANSI escape code_
  2. Test carefully on most popular terminals from golang supported OSs (go tool dist list).
  3. Don't rely on TERM environment variable only. This is common mistake made by popular libraries (also by mentioned fatih/color). You should rely on terminal capabilities - in POSIX world you can use tput command; on Windows it's not so easy. Also consider support for popular environment variables: CI, NO_COLOR, CLICOLOR.
  4. If command is run inside pipeline, it shouldn't print escape character. You can check field descriptor number (as in fatih/color), but test that against Windows.
  5. Support all valid escape characters: \e, \27, \033, \x1b. I believe, that most popular is \x1b but most portable is \033. I'm not sure, if stripANSI() workaround works for all of them.