1package commands
2
3import (
4	"fmt"
5	"os"
6	"os/exec"
7	"path/filepath"
8	"sort"
9	"strings"
10
11	"github.com/github/hub/git"
12	"github.com/github/hub/ui"
13	"github.com/github/hub/utils"
14	"github.com/kballard/go-shellquote"
15)
16
17var cmdHelp = &Command{
18	Run:          runHelp,
19	GitExtension: true,
20	Usage: `
21help hub
22help <COMMAND>
23help hub-<COMMAND> [--plain-text]
24`,
25	Long: `Show the help page for a command.
26
27## Options:
28	hub-<COMMAND>
29		Use this format to view help for hub extensions to an existing git command.
30
31	--plain-text
32		Skip man page lookup mechanism and display raw help text.
33
34## See also:
35
36hub(1), git-help(1)
37`,
38}
39
40var cmdListCmds = &Command{
41	Key:          "--list-cmds",
42	Run:          runListCmds,
43	GitExtension: true,
44}
45
46func init() {
47	CmdRunner.Use(cmdHelp, "--help")
48	CmdRunner.Use(cmdListCmds)
49}
50
51func runHelp(helpCmd *Command, args *Args) {
52	if args.IsParamsEmpty() {
53		args.AfterFn(func() error {
54			ui.Println(helpText)
55			return nil
56		})
57		return
58	}
59
60	p := utils.NewArgsParser()
61	p.RegisterBool("--all", "-a")
62	p.RegisterBool("--plain-text")
63	p.RegisterBool("--man", "-m")
64	p.RegisterBool("--web", "-w")
65	p.Parse(args.Params)
66
67	if p.Bool("--all") {
68		args.AfterFn(func() error {
69			ui.Printf("\nhub custom commands\n\n  %s\n", strings.Join(customCommands(), "  "))
70			return nil
71		})
72		return
73	}
74
75	isWeb := func() bool {
76		if p.Bool("--web") {
77			return true
78		}
79		if p.Bool("--man") {
80			return false
81		}
82		if f, err := git.Config("help.format"); err == nil {
83			return f == "web" || f == "html"
84		}
85		return false
86	}
87
88	cmdName := ""
89	if words := args.Words(); len(words) > 0 {
90		cmdName = words[0]
91	}
92
93	if cmdName == "hub" {
94		err := displayManPage("hub", args, isWeb())
95		utils.Check(err)
96		return
97	}
98
99	foundCmd := lookupCmd(cmdName)
100	if foundCmd == nil {
101		return
102	}
103
104	if p.Bool("--plain-text") {
105		ui.Println(foundCmd.HelpText())
106		os.Exit(0)
107	}
108
109	manPage := fmt.Sprintf("hub-%s", foundCmd.Name())
110	err := displayManPage(manPage, args, isWeb())
111	utils.Check(err)
112}
113
114func runListCmds(cmd *Command, args *Args) {
115	listOthers := false
116	parts := strings.SplitN(args.Command, "=", 2)
117	for _, kind := range strings.Split(parts[1], ",") {
118		if kind == "others" {
119			listOthers = true
120			break
121		}
122	}
123
124	if listOthers {
125		args.AfterFn(func() error {
126			ui.Println(strings.Join(customCommands(), "\n"))
127			return nil
128		})
129	}
130}
131
132// On systems where `man` was found, invoke:
133//   MANPATH={PREFIX}/share/man:$MANPATH man <page>
134//
135// otherwise:
136//   less -R {PREFIX}/share/man/man1/<page>.1.txt
137func displayManPage(manPage string, args *Args, isWeb bool) error {
138	programPath, err := utils.CommandPath(args.ProgramPath)
139	if err != nil {
140		return err
141	}
142
143	if isWeb {
144		manPage += ".1.html"
145		manFile := filepath.Join(programPath, "..", "..", "share", "doc", "hub-doc", manPage)
146		args.Replace(args.Executable, "web--browse", manFile)
147		return nil
148	}
149
150	var manArgs []string
151	manProgram, _ := utils.CommandPath("man")
152	if manProgram != "" {
153		manArgs = []string{manProgram}
154	} else {
155		manPage += ".1.txt"
156		if manProgram = os.Getenv("PAGER"); manProgram != "" {
157			var err error
158			manArgs, err = shellquote.Split(manProgram)
159			if err != nil {
160				return err
161			}
162		} else {
163			manArgs = []string{"less", "-R"}
164		}
165	}
166
167	env := os.Environ()
168	if strings.HasSuffix(manPage, ".txt") {
169		manFile := filepath.Join(programPath, "..", "..", "share", "man", "man1", manPage)
170		manArgs = append(manArgs, manFile)
171	} else {
172		manArgs = append(manArgs, manPage)
173		manPath := filepath.Join(programPath, "..", "..", "share", "man")
174		env = append(env, fmt.Sprintf("MANPATH=%s:%s", manPath, os.Getenv("MANPATH")))
175	}
176
177	c := exec.Command(manArgs[0], manArgs[1:]...)
178	c.Stdin = os.Stdin
179	c.Stdout = os.Stdout
180	c.Stderr = os.Stderr
181	c.Env = env
182	if err := c.Run(); err != nil {
183		return err
184	}
185	os.Exit(0)
186	return nil
187}
188
189func lookupCmd(name string) *Command {
190	if strings.HasPrefix(name, "hub-") {
191		return CmdRunner.Lookup(strings.TrimPrefix(name, "hub-"))
192	} else {
193		cmd := CmdRunner.Lookup(name)
194		if cmd != nil && !cmd.GitExtension {
195			return cmd
196		} else {
197			return nil
198		}
199	}
200}
201
202func customCommands() []string {
203	cmds := []string{}
204	for n, c := range CmdRunner.All() {
205		if !c.GitExtension && !strings.HasPrefix(n, "--") {
206			cmds = append(cmds, n)
207		}
208	}
209
210	sort.Strings(cmds)
211
212	return cmds
213}
214
215var helpText = `
216These GitHub commands are provided by hub:
217
218   api            Low-level GitHub API request interface
219   browse         Open a GitHub page in the default browser
220   ci-status      Show the status of GitHub checks for a commit
221   compare        Open a compare page on GitHub
222   create         Create this repository on GitHub and add GitHub as origin
223   delete         Delete a repository on GitHub
224   fork           Make a fork of a remote repository on GitHub and add as remote
225   gist           Make a gist
226   issue          List or create GitHub issues
227   pr             List or checkout GitHub pull requests
228   pull-request   Open a pull request on GitHub
229   release        List or create GitHub releases
230   sync           Fetch git objects from upstream and update branches
231`
232