2022-09-22 21:29:07 +03:00
|
|
|
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>
|
|
|
|
|
|
|
|
package cli
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
2024-05-04 12:04:40 +03:00
|
|
|
"slices"
|
2022-09-22 21:29:07 +03:00
|
|
|
"strings"
|
2023-11-11 14:39:23 +03:00
|
|
|
"time"
|
2022-09-22 21:29:07 +03:00
|
|
|
|
|
|
|
"golang.org/x/sys/unix"
|
|
|
|
|
|
|
|
"kitty"
|
|
|
|
"kitty/tools/cli/markup"
|
|
|
|
"kitty/tools/tty"
|
2023-11-11 14:39:23 +03:00
|
|
|
"kitty/tools/utils"
|
2022-09-22 21:29:07 +03:00
|
|
|
"kitty/tools/utils/style"
|
|
|
|
)
|
|
|
|
|
|
|
|
var _ = fmt.Print
|
|
|
|
|
|
|
|
func ShowError(err error) {
|
|
|
|
formatter := markup.New(tty.IsTerminal(os.Stderr.Fd()))
|
|
|
|
msg := formatter.Prettify(err.Error())
|
|
|
|
fmt.Fprintln(os.Stderr, formatter.Err("Error")+":", msg)
|
|
|
|
}
|
|
|
|
|
2022-09-25 10:33:54 +03:00
|
|
|
func (self *Command) version_string(formatter *markup.Context) string {
|
|
|
|
return fmt.Sprintln(formatter.Italic(self.CommandStringForUsage()), formatter.Opt(kitty.VersionString), "created by", formatter.Title("Kovid Goyal"))
|
|
|
|
}
|
|
|
|
|
2022-09-22 21:29:07 +03:00
|
|
|
func (self *Command) ShowVersion() {
|
|
|
|
formatter := markup.New(tty.IsTerminal(os.Stdout.Fd()))
|
2022-09-25 10:33:54 +03:00
|
|
|
fmt.Fprint(os.Stdout, self.version_string(formatter))
|
2022-09-22 21:29:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func format_with_indent(output io.Writer, text string, indent string, screen_width int) {
|
2023-03-29 18:26:24 +03:00
|
|
|
indented := style.WrapText(text, screen_width, style.WrapOptions{Indent: indent, Ignore_lines_containing: "#placeholder_for_formatting#", Trim_whitespace: true})
|
2022-09-22 21:29:07 +03:00
|
|
|
io.WriteString(output, indented)
|
2023-03-20 18:48:53 +03:00
|
|
|
io.WriteString(output, "\n")
|
2022-09-22 21:29:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (self *Command) FormatSubCommands(output io.Writer, formatter *markup.Context, screen_width int) {
|
|
|
|
for _, g := range self.SubCommandGroups {
|
|
|
|
if !g.HasVisibleSubCommands() {
|
|
|
|
continue
|
|
|
|
}
|
2022-09-25 09:53:52 +03:00
|
|
|
title := g.Title
|
|
|
|
if title == "" {
|
|
|
|
title = "Commands"
|
2022-09-22 21:29:07 +03:00
|
|
|
}
|
2022-09-25 09:53:52 +03:00
|
|
|
fmt.Fprintln(output)
|
|
|
|
fmt.Fprintln(output, formatter.Title(title)+":")
|
2022-09-22 21:29:07 +03:00
|
|
|
for _, c := range g.SubCommands {
|
|
|
|
if c.Hidden {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
fmt.Fprintln(output, " ", formatter.Opt(c.Name))
|
|
|
|
format_with_indent(output, formatter.Prettify(c.ShortDescription), " ", screen_width)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-11-11 14:39:23 +03:00
|
|
|
func (self *Option) FormatOptionForMan(output io.Writer) {
|
|
|
|
fmt.Fprintln(output, ".TP")
|
|
|
|
fmt.Fprint(output, ".BI \"")
|
|
|
|
for i, a := range self.Aliases {
|
|
|
|
fmt.Fprint(output, a.String())
|
|
|
|
if i != len(self.Aliases)-1 {
|
|
|
|
fmt.Fprint(output, ", ")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Fprint(output, "\" ")
|
|
|
|
defval := self.Default
|
|
|
|
switch self.OptionType {
|
|
|
|
case StringOption:
|
|
|
|
if self.IsList {
|
|
|
|
defval = ""
|
|
|
|
}
|
|
|
|
case BoolOption, CountOption:
|
|
|
|
defval = ""
|
|
|
|
}
|
|
|
|
|
|
|
|
if defval != "" {
|
|
|
|
fmt.Fprintf(output, "\" [=%s]\"", escape_text_for_man(defval))
|
|
|
|
}
|
|
|
|
fmt.Fprintln(output)
|
|
|
|
fmt.Fprintln(output, escape_help_for_man(self.Help))
|
|
|
|
if self.Choices != nil {
|
|
|
|
fmt.Fprintln(output)
|
|
|
|
fmt.Fprintln(output, "Choices: "+strings.Join(self.Choices, ", "))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-22 21:29:07 +03:00
|
|
|
func (self *Option) FormatOption(output io.Writer, formatter *markup.Context, screen_width int) {
|
2022-09-23 10:00:41 +03:00
|
|
|
fmt.Fprint(output, " ")
|
2022-09-22 21:29:07 +03:00
|
|
|
for i, a := range self.Aliases {
|
|
|
|
fmt.Fprint(output, formatter.Opt(a.String()))
|
|
|
|
if i != len(self.Aliases)-1 {
|
|
|
|
fmt.Fprint(output, ", ")
|
|
|
|
}
|
|
|
|
}
|
2022-09-25 09:53:52 +03:00
|
|
|
defval := self.Default
|
2022-09-22 21:29:07 +03:00
|
|
|
switch self.OptionType {
|
|
|
|
case StringOption:
|
|
|
|
if self.IsList {
|
|
|
|
defval = ""
|
|
|
|
}
|
2022-09-25 09:53:52 +03:00
|
|
|
case BoolOption, CountOption:
|
|
|
|
defval = ""
|
2022-09-22 21:29:07 +03:00
|
|
|
}
|
|
|
|
if defval != "" {
|
|
|
|
fmt.Fprintf(output, " [=%s]", formatter.Italic(defval))
|
|
|
|
}
|
|
|
|
fmt.Fprintln(output)
|
|
|
|
format_with_indent(output, formatter.Prettify(prepare_help_text_for_display(self.Help)), " ", screen_width)
|
2022-09-25 09:53:52 +03:00
|
|
|
if self.Choices != nil {
|
|
|
|
format_with_indent(output, "Choices: "+strings.Join(self.Choices, ", "), " ", screen_width)
|
|
|
|
}
|
2022-09-22 21:29:07 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
func (self *Command) ShowHelp() {
|
2022-10-13 18:37:17 +03:00
|
|
|
self.ShowHelpWithCommandString(strings.TrimSpace(self.CommandStringForUsage()))
|
|
|
|
}
|
|
|
|
|
2022-11-16 18:14:13 +03:00
|
|
|
func ShowHelpInPager(text string) {
|
|
|
|
pager := exec.Command(kitty.DefaultPager[0], kitty.DefaultPager[1:]...)
|
|
|
|
pager.Stdin = strings.NewReader(text)
|
|
|
|
pager.Stdout = os.Stdout
|
|
|
|
pager.Stderr = os.Stderr
|
2023-11-11 06:15:39 +03:00
|
|
|
_ = pager.Run()
|
2022-11-16 18:14:13 +03:00
|
|
|
}
|
|
|
|
|
2023-11-11 14:39:23 +03:00
|
|
|
func (self *Command) GenerateManPages(level int, recurse bool) (err error) {
|
|
|
|
var names []string
|
|
|
|
p := self
|
|
|
|
for p != nil {
|
|
|
|
names = append(names, p.Name)
|
|
|
|
p = p.Parent
|
|
|
|
}
|
|
|
|
slices.Reverse(names)
|
|
|
|
name := strings.Join(names, "-")
|
|
|
|
outf, err := os.Create(fmt.Sprintf("%s.%d", name, level))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer outf.Close()
|
|
|
|
fmt.Fprintf(outf, `.TH "%s" "1" "%s" "%s" "%s"`, name, time.Now().Format("Jan 02, 2006"), kitty.VersionString, "kitten Manual")
|
|
|
|
fmt.Fprintln(outf)
|
|
|
|
fmt.Fprintln(outf, ".SH Name")
|
|
|
|
fmt.Fprintln(outf, name, "\\-", escape_text_for_man(self.ShortDescription))
|
|
|
|
fmt.Fprintln(outf, ".SH Usage")
|
|
|
|
fmt.Fprintln(outf, ".SY", `"`+self.CommandStringForUsage()+` `+self.Usage+`"`)
|
|
|
|
fmt.Fprintln(outf, ".YS")
|
|
|
|
if self.HelpText != "" {
|
|
|
|
fmt.Fprintln(outf, ".SH Description")
|
|
|
|
fmt.Fprintln(outf, escape_help_for_man(self.HelpText))
|
|
|
|
}
|
|
|
|
|
|
|
|
if self.HasVisibleSubCommands() {
|
|
|
|
for _, g := range self.SubCommandGroups {
|
|
|
|
if !g.HasVisibleSubCommands() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
title := g.Title
|
|
|
|
if title == "" {
|
|
|
|
title = "Commands"
|
|
|
|
}
|
|
|
|
fmt.Fprintln(outf, ".SH", title)
|
|
|
|
|
|
|
|
for _, c := range utils.Sort(g.SubCommands, func(a, b *Command) int { return strings.Compare(a.Name, b.Name) }) {
|
|
|
|
if c.Hidden {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if recurse {
|
|
|
|
if err = c.GenerateManPages(level, recurse); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Fprintln(outf, ".TP", "2")
|
|
|
|
fmt.Fprintln(outf, c.Name)
|
|
|
|
fmt.Fprintln(outf, escape_text_for_man(c.ShortDescription)+".", "See: ")
|
|
|
|
fmt.Fprintf(outf, ".MR %s %d\n", name+"-"+c.Name, level)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fmt.Fprintln(outf, ".PP")
|
|
|
|
fmt.Fprintln(outf, "Get help for an individual command by running:")
|
|
|
|
fmt.Fprintln(outf, ".SY", self.CommandStringForUsage())
|
|
|
|
fmt.Fprintln(outf, "command", "-h")
|
|
|
|
fmt.Fprintln(outf, ".YS")
|
|
|
|
}
|
|
|
|
|
|
|
|
group_titles, gmap := self.GetVisibleOptions()
|
|
|
|
if len(group_titles) > 0 {
|
|
|
|
for _, title := range group_titles {
|
|
|
|
ptitle := title
|
|
|
|
if title == "" {
|
|
|
|
ptitle = "Options"
|
|
|
|
}
|
|
|
|
fmt.Fprintln(outf, ".SH", ptitle)
|
|
|
|
for _, opt := range gmap[title] {
|
|
|
|
opt.FormatOptionForMan(outf)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2022-10-13 18:37:17 +03:00
|
|
|
func (self *Command) ShowHelpWithCommandString(cs string) {
|
2022-09-22 21:29:07 +03:00
|
|
|
formatter := markup.New(tty.IsTerminal(os.Stdout.Fd()))
|
|
|
|
screen_width := 80
|
|
|
|
if formatter.EscapeCodesAllowed() {
|
|
|
|
var sz *unix.Winsize
|
|
|
|
var tty_size_err error
|
|
|
|
for {
|
|
|
|
sz, tty_size_err = unix.IoctlGetWinsize(int(os.Stdout.Fd()), unix.TIOCGWINSZ)
|
|
|
|
if tty_size_err != unix.EINTR {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if tty_size_err == nil && sz.Col < 80 {
|
|
|
|
screen_width = int(sz.Col)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var output strings.Builder
|
|
|
|
|
2022-10-13 18:37:17 +03:00
|
|
|
fmt.Fprintln(&output, formatter.Title("Usage")+":", formatter.Exe(cs), strings.TrimSpace(formatter.Prettify(self.Usage)))
|
2022-09-22 21:29:07 +03:00
|
|
|
fmt.Fprintln(&output)
|
2022-09-23 10:00:41 +03:00
|
|
|
if self.HelpText != "" {
|
|
|
|
format_with_indent(&output, formatter.Prettify(prepare_help_text_for_display(self.HelpText)), "", screen_width)
|
|
|
|
} else if self.ShortDescription != "" {
|
|
|
|
format_with_indent(&output, formatter.Prettify(self.ShortDescription), "", screen_width)
|
|
|
|
}
|
2022-09-22 21:29:07 +03:00
|
|
|
|
|
|
|
if self.HasVisibleSubCommands() {
|
|
|
|
self.FormatSubCommands(&output, formatter, screen_width)
|
|
|
|
fmt.Fprintln(&output)
|
|
|
|
format_with_indent(&output, "Get help for an individual command by running:", "", screen_width)
|
|
|
|
fmt.Fprintln(&output, " ", strings.TrimSpace(self.CommandStringForUsage()), formatter.Italic("command"), "-h")
|
|
|
|
}
|
|
|
|
|
|
|
|
group_titles, gmap := self.GetVisibleOptions()
|
|
|
|
if len(group_titles) > 0 {
|
|
|
|
fmt.Fprintln(&output)
|
|
|
|
for _, title := range group_titles {
|
2022-09-25 09:53:52 +03:00
|
|
|
ptitle := title
|
|
|
|
if title == "" {
|
|
|
|
ptitle = "Options"
|
2022-09-22 21:29:07 +03:00
|
|
|
}
|
2022-09-25 09:53:52 +03:00
|
|
|
fmt.Fprintln(&output, formatter.Title(ptitle)+":")
|
2022-09-22 21:29:07 +03:00
|
|
|
for _, opt := range gmap[title] {
|
|
|
|
opt.FormatOption(&output, formatter, screen_width)
|
|
|
|
fmt.Fprintln(&output)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-09-25 10:33:54 +03:00
|
|
|
output.WriteString(self.version_string(formatter))
|
2022-09-22 21:29:07 +03:00
|
|
|
output_text := output.String()
|
|
|
|
// fmt.Printf("%#v\n", output_text)
|
|
|
|
if formatter.EscapeCodesAllowed() {
|
2022-11-16 18:14:13 +03:00
|
|
|
ShowHelpInPager(output_text)
|
2022-09-22 21:29:07 +03:00
|
|
|
} else {
|
2022-11-16 18:14:13 +03:00
|
|
|
os.Stdout.WriteString(output_text)
|
2022-09-22 21:29:07 +03:00
|
|
|
}
|
|
|
|
}
|