提交 82ece547 编写于 作者: A Alessandro Arzilli 提交者: Derek Parker

docs: Documentation for command line frontend (#518)

* documentation: copied old documentation from wiki

* command: better online documentation

Help without arguments prints just a short summary for each command,
help followed by a command prints the command's syntax and a longer
explanation.

* documentation: automatically generate Documentation/cli/README.md
上级 c4e01da5
......@@ -4,6 +4,7 @@ Documentation for the project will reside in this directory.
- [Installation](installation)
- [Usage](usage)
- [Command Line Interface](cli)
- [API](api)
- [Internal](internal)
- [Editor Integration](EditorIntegration.md)
# Commands
Command | Description
--------|------------
[help](#help) | Prints the help message.
[break](#break) | Sets a breakpoint.
[trace](#trace) | Set tracepoint.
[restart](#restart) | Restart process.
[continue](#continue) | Run until breakpoint or program termination.
[step](#step) | Single step through program.
[step-instruction](#step-instruction) | Single step a single cpu instruction.
[next](#next) | Step over to next source line.
[threads](#threads) | Print out info for every traced thread.
[thread](#thread) | Switch to the specified thread.
[clear](#clear) | Deletes breakpoint.
[clearall](#clearall) | Deletes multiple breakpoints.
[goroutines](#goroutines) | List program goroutines.
[goroutine](#goroutine) | Shows or changes current goroutine
[breakpoints](#breakpoints) | Print out info for active breakpoints.
[print](#print) | Evaluate an expression.
[set](#set) | Changes the value of a variable.
[sources](#sources) | Print list of source files.
[funcs](#funcs) | Print list of functions.
[types](#types) | Print list of types
[args](#args) | Print function arguments.
[locals](#locals) | Print local variables.
[vars](#vars) | Print package variables.
[regs](#regs) | Print contents of CPU registers.
[exit](#exit) | Exit the debugger.
[list](#list) | Show source code.
[stack](#stack) | Print stack trace.
[frame](#frame) | Executes command on a different frame.
[source](#source) | Executes a file containing a list of delve commands
[disassemble](#disassemble) | Disassembler.
[on](#on) | Executes a command when a breakpoint is hit.
[condition](#condition) | Set breakpoint condition.
## help
Prints the help message.
help [command]
Type "help" followed by the name of a command for more information about it.
Aliases: h
## break
Sets a breakpoint.
break [name] <linespec>
See [Documentation/cli/locspec.md](//github.com/derekparker/delve/tree/master/Documentation/cli/locspec.md) for the syntax of linespec.
See also: "help on", "help cond" and "help clear"
Aliases: b
## trace
Set tracepoint.
trace [name] <linespec>
A tracepoint is a breakpoint that does not stop the execution of the program, instead when the tracepoint is hit a notification is displayed. See [Documentation/cli/locspec.md](//github.com/derekparker/delve/tree/master/Documentation/cli/locspec.md) for the syntax of linespec.
See also: "help on", "help cond" and "help clear"
Aliases: t
## restart
Restart process.
Aliases: r
## continue
Run until breakpoint or program termination.
Aliases: c
## step
Single step through program.
Aliases: s
## step-instruction
Single step a single cpu instruction.
Aliases: si
## next
Step over to next source line.
Aliases: n
## threads
Print out info for every traced thread.
## thread
Switch to the specified thread.
thread <id>
Aliases: tr
## clear
Deletes breakpoint.
clear <breakpoint name or id>
## clearall
Deletes multiple breakpoints.
clearall [<linespec>]
If called with the linespec argument it will delete all the breakpoints matching the linespec. If linespec is omitted all breakpoints are deleted.
## goroutines
List program goroutines.
goroutines [-u (default: user location)|-r (runtime location)|-g (go statement location)]
Print out info for every goroutine. The flag controls what information is shown along with each goroutine:
-u displays location of topmost stackframe in user code
-r displays location of topmost stackframe (including frames inside private runtime functions)
-g displays location of go instruction that created the goroutine
If no flag is specified the default is -u.
## goroutine
Shows or changes current goroutine
goroutine
goroutine <id>
goroutine <id> <command>
Called without arguments it will show information about the current goroutine.
Called with a single argument it will switch to the specified goroutine.
Called with more arguments it will execute a command on the specified goroutine.
## breakpoints
Print out info for active breakpoints.
Aliases: bp
## print
Evaluate an expression.
[goroutine <n>] [frame <m>] print <expression>
See [Documentation/cli/expr.md](//github.com/derekparker/delve/tree/master/Documentation/cli/expr.md) for a description of supported expressions.
Aliases: p
## set
Changes the value of a variable.
[goroutine <n>] [frame <m>] set <variable> = <value>
See [Documentation/cli/expr.md](//github.com/derekparker/delve/tree/master/Documentation/cli/expr.md) for a description of supported expressions. Only numerical variables and pointers can be changed.
## sources
Print list of source files.
sources [<regex>]
If regex is specified only the source files matching it will be returned.
## funcs
Print list of functions.
funcs [<regex>]
If regex is specified only the functions matching it will be returned.
## types
Print list of types
types [<regex>]
If regex is specified only the functions matching it will be returned.
## args
Print function arguments.
[goroutine <n>] [frame <m>] args [-v] [<regex>]
If regex is specified only function arguments with a name matching it will be returned. If -v is specified more information about each function argument will be shown.
## locals
Print local variables.
[goroutine <n>] [frame <m>] locals [-v] [<regex>]
If regex is specified only local variables with a name matching it will be returned. If -v is specified more information about each local variable will be shown.
## vars
Print package variables.
vars [-v] [<regex>]
If regex is specified only package variables with a name matching it will be returned. If -v is specified more information about each package variable will be shown.
## regs
Print contents of CPU registers.
## exit
Exit the debugger.
Aliases: quit q
## list
Show source code.
[goroutine <n>] [frame <m>] list [<linespec>]
Show source around current point or provided linespec.
Aliases: ls
## stack
Print stack trace.
[goroutine <n>] [frame <m>] stack [<depth>] [-full]
If -full is specified every stackframe will be decorated by the value of its local variables and function arguments.
Aliases: bt
## frame
Executes command on a different frame.
frame <frame index> <command>.
## source
Executes a file containing a list of delve commands
source <path>
## disassemble
Disassembler.
[goroutine <n>] [frame <m>] disassemble [-a <start> <end>] [-l <locspec>]
If no argument is specified the function being executed in the selected stack frame will be executed.
-a <start> <end> disassembles the specified address range
-l <locspec> disassembles the specified function
Aliases: disass
## on
Executes a command when a breakpoint is hit.
on <breakpoint name or id> <command>.
Supported commands: print, stack and goroutine)
## condition
Set breakpoint condition.
condition <breakpoint name or id> <boolean expression>.
Specifies that the breakpoint or tracepoint should break only if the boolean expression is true.
Aliases: cond
# Expressions
Delve can evaluate a subset of go expression language, specifically the following features are supported:
- All (binary and unary) on basic types except <-, ++ and --
- Comparison operators on any type
- Type casts between numeric types
- Type casts of integer constants into any pointer type
- Struct member access (i.e. `somevar.memberfield`)
- Slicing and indexing operators on arrays, slices and strings
- Map access
- Pointer dereference
- Calls to builtin functions: `cap`, `len`, `complex`, `imag` and `real`
- Type assertion on interface variables (i.e. `somevar.(concretetype)`)
# Nesting limit
When delve evaluates a memory address it will automatically return the value of nested struct members, array and slice items and dereference pointers.
However to limit the size of the output evaluation will be limited to two levels deep. Beyond two levels only the address of the item will be returned, for example:
```
(dlv) print c1
main.cstruct {
pb: *struct main.bstruct {
a: (*main.astruct)(0xc82000a430),
},
sa: []*main.astruct len: 3, cap: 3, [
*(*main.astruct)(0xc82000a440),
*(*main.astruct)(0xc82000a450),
*(*main.astruct)(0xc82000a460),
],
}
```
To see the contents of the first item of the slice `c1.sa` there are two possibilities:
1. Execute `print c1.sa[0]`
2. Use the address directly, executing: `print *(*main.astruct)(0xc82000a440)`
# Elements limit
For arrays, slices, strings and maps delve will only return a maximum of 64 elements at a time:
```
(dlv) print ba
[]int len: 200, cap: 200, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+136 more]
```
To see more values use the slice operator:
```
(dlv) print ba[64:]
[]int len: 136, cap: 136, [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,...+72 more]
```
For this purpose delve allows use of the slice operator on maps, `m[64:]` will return the key/value pairs of map `m` that follow the first 64 key/value pairs (note that delve iterates over maps using a fixed ordering).
# Interfaces
Interfaces will be printed using the following syntax:
```
<interface name>(<concrete type>) <value>
```
For example:
```
(dlv) p iface1
(dlv) p iface1
interface {}(*struct main.astruct) *{A: 1, B: 2}
(dlv) p iface2
interface {}(*struct string) *"test"
(dlv) p err1
error(*struct main.astruct) *{A: 1, B: 2}
```
To use a field of a struct contained inside an interface variable use a type assertion:
```
(dlv) p iface1.(*main.astruct).B
2
```
# Location Specifiers
Several delve commands take a program location as an argument, the syntax accepted by this commands is:
* `*<address>` Specifies the location of memory address *address*. *address* can be specified as a decimal, hexadecimal or octal number
* `<filename>:<line>` Specifies the line *line* in *filename*. *filename* can be the partial path to a file or even just the base name as long as the expression remains unambiguous.
* `<line>` Specifies the line *line* in the current file
* `+<offset>` Specifies the line *offset* lines after the current one
* `-<offset>` Specifies the line *offset* lines before the current one
* `<function>[:<line>]` Specifies the line *line* inside *function*. The full syntax for *function* is `<package>.(*<receiver type>).<function name>` however the only required element is the function name, everything else can be omitted as long as the expression remains unambiguous.
* `/<regex>/` Specifies the location of all the functions matching *regex*
......@@ -11,7 +11,7 @@ else
endif
# Workaround for GO15VENDOREXPERIMENT bug (https://github.com/golang/go/issues/11659)
ALL_PACKAGES=$(shell go list ./... | grep -v /vendor/)
ALL_PACKAGES=$(shell go list ./... | grep -v /vendor/ | grep -v /scripts)
# We must compile with -ldflags="-s" to omit
# DWARF info on OSX when compiling with the
......
package main
import (
"bufio"
"log"
"os"
"github.com/derekparker/delve/terminal"
)
func main() {
fh, err := os.Create(os.ExpandEnv("$GOPATH/src/github.com/derekparker/delve/Documentation/cli/README.md"))
if err != nil {
log.Fatalf("could not create README.md: %v", err)
}
defer fh.Close()
w := bufio.NewWriter(fh)
defer w.Flush()
commands := terminal.DebugCommands(nil)
commands.WriteMarkdown(w)
}
......@@ -72,38 +72,139 @@ func DebugCommands(client service.Client) *Commands {
c := &Commands{client: client}
c.cmds = []command{
{aliases: []string{"help", "h"}, cmdFn: c.help, helpMsg: "Prints the help message."},
{aliases: []string{"break", "b"}, cmdFn: breakpoint, helpMsg: "break [name] <linespec>"},
{aliases: []string{"trace", "t"}, cmdFn: tracepoint, helpMsg: "Set tracepoint, takes the same arguments as break."},
{aliases: []string{"help", "h"}, cmdFn: c.help, helpMsg: `Prints the help message.
help [command]
Type "help" followed by the name of a command for more information about it.`},
{aliases: []string{"break", "b"}, cmdFn: breakpoint, helpMsg: `Sets a breakpoint.
break [name] <linespec>
See $GOPATH/src/github.com/derekparker/delve/Documentation/cli/locspec.md for the syntax of linespec.
See also: "help on", "help cond" and "help clear"`},
{aliases: []string{"trace", "t"}, cmdFn: tracepoint, helpMsg: `Set tracepoint.
trace [name] <linespec>
A tracepoint is a breakpoint that does not stop the execution of the program, instead when the tracepoint is hit a notification is displayed. See $GOPATH/src/github.com/derekparker/delve/Documentation/cli/locspec.md for the syntax of linespec.
See also: "help on", "help cond" and "help clear"`},
{aliases: []string{"restart", "r"}, cmdFn: restart, helpMsg: "Restart process."},
{aliases: []string{"continue", "c"}, cmdFn: cont, helpMsg: "Run until breakpoint or program termination."},
{aliases: []string{"step", "s"}, cmdFn: step, helpMsg: "Single step through program."},
{aliases: []string{"step-instruction", "si"}, cmdFn: stepInstruction, helpMsg: "Single step a single cpu instruction."},
{aliases: []string{"next", "n"}, cmdFn: next, helpMsg: "Step over to next source line."},
{aliases: []string{"threads"}, cmdFn: threads, helpMsg: "Print out info for every traced thread."},
{aliases: []string{"thread", "tr"}, cmdFn: thread, helpMsg: "Switch to the specified thread."},
{aliases: []string{"clear"}, cmdFn: clear, helpMsg: "Deletes breakpoint."},
{aliases: []string{"clearall"}, cmdFn: clearAll, helpMsg: "clearall [<linespec>]. Deletes all breakpoints. If <linespec> is provided, only matching breakpoints will be deleted."},
{aliases: []string{"goroutines"}, cmdFn: goroutines, helpMsg: "goroutines [-u (default: user location)|-r (runtime location)|-g (go statement location)] Print out info for every goroutine."},
{aliases: []string{"goroutine"}, allowedPrefixes: onPrefix | scopePrefix, cmdFn: c.goroutine, helpMsg: "Sets current goroutine."},
{aliases: []string{"thread", "tr"}, cmdFn: thread, helpMsg: `Switch to the specified thread.
thread <id>`},
{aliases: []string{"clear"}, cmdFn: clear, helpMsg: `Deletes breakpoint.
clear <breakpoint name or id>`},
{aliases: []string{"clearall"}, cmdFn: clearAll, helpMsg: `Deletes multiple breakpoints.
clearall [<linespec>]
If called with the linespec argument it will delete all the breakpoints matching the linespec. If linespec is omitted all breakpoints are deleted.`},
{aliases: []string{"goroutines"}, cmdFn: goroutines, helpMsg: `List program goroutines.
goroutines [-u (default: user location)|-r (runtime location)|-g (go statement location)]
Print out info for every goroutine. The flag controls what information is shown along with each goroutine:
-u displays location of topmost stackframe in user code
-r displays location of topmost stackframe (including frames inside private runtime functions)
-g displays location of go instruction that created the goroutine
If no flag is specified the default is -u.`},
{aliases: []string{"goroutine"}, allowedPrefixes: onPrefix | scopePrefix, cmdFn: c.goroutine, helpMsg: `Shows or changes current goroutine
goroutine
goroutine <id>
goroutine <id> <command>
Called without arguments it will show information about the current goroutine.
Called with a single argument it will switch to the specified goroutine.
Called with more arguments it will execute a command on the specified goroutine.`},
{aliases: []string{"breakpoints", "bp"}, cmdFn: breakpoints, helpMsg: "Print out info for active breakpoints."},
{aliases: []string{"print", "p"}, allowedPrefixes: onPrefix | scopePrefix, cmdFn: printVar, helpMsg: "Evaluate a variable."},
{aliases: []string{"set"}, allowedPrefixes: scopePrefix, cmdFn: setVar, helpMsg: "Changes the value of a variable."},
{aliases: []string{"sources"}, cmdFn: sources, helpMsg: "Print list of source files, optionally filtered by a regexp."},
{aliases: []string{"funcs"}, cmdFn: funcs, helpMsg: "Print list of functions, optionally filtered by a regexp."},
{aliases: []string{"types"}, cmdFn: types, helpMsg: "Print list of types, optionally filtered by a regexp."},
{aliases: []string{"args"}, allowedPrefixes: scopePrefix | onPrefix, cmdFn: args, helpMsg: "args [-v] <filter>. Print function arguments, optionally filtered by a regexp."},
{aliases: []string{"locals"}, allowedPrefixes: scopePrefix | onPrefix, cmdFn: locals, helpMsg: "locals [-v] <filter>. Print function locals, optionally filtered by a regexp."},
{aliases: []string{"vars"}, cmdFn: vars, helpMsg: "vars [-v] <filter>. Print package variables, optionally filtered by a regexp."},
{aliases: []string{"print", "p"}, allowedPrefixes: onPrefix | scopePrefix, cmdFn: printVar, helpMsg: `Evaluate an expression.
[goroutine <n>] [frame <m>] print <expression>
See $GOPATH/src/github.com/derekparker/delve/Documentation/cli/expr.md for a description of supported expressions.`},
{aliases: []string{"set"}, allowedPrefixes: scopePrefix, cmdFn: setVar, helpMsg: `Changes the value of a variable.
[goroutine <n>] [frame <m>] set <variable> = <value>
See $GOPATH/src/github.com/derekparker/delve/Documentation/cli/expr.md for a description of supported expressions. Only numerical variables and pointers can be changed.`},
{aliases: []string{"sources"}, cmdFn: sources, helpMsg: `Print list of source files.
sources [<regex>]
If regex is specified only the source files matching it will be returned.`},
{aliases: []string{"funcs"}, cmdFn: funcs, helpMsg: `Print list of functions.
funcs [<regex>]
If regex is specified only the functions matching it will be returned.`},
{aliases: []string{"types"}, cmdFn: types, helpMsg: `Print list of types
types [<regex>]
If regex is specified only the functions matching it will be returned.`},
{aliases: []string{"args"}, allowedPrefixes: scopePrefix | onPrefix, cmdFn: args, helpMsg: `Print function arguments.
[goroutine <n>] [frame <m>] args [-v] [<regex>]
If regex is specified only function arguments with a name matching it will be returned. If -v is specified more information about each function argument will be shown.`},
{aliases: []string{"locals"}, allowedPrefixes: scopePrefix | onPrefix, cmdFn: locals, helpMsg: `Print local variables.
[goroutine <n>] [frame <m>] locals [-v] [<regex>]
If regex is specified only local variables with a name matching it will be returned. If -v is specified more information about each local variable will be shown.`},
{aliases: []string{"vars"}, cmdFn: vars, helpMsg: `Print package variables.
vars [-v] [<regex>]
If regex is specified only package variables with a name matching it will be returned. If -v is specified more information about each package variable will be shown.`},
{aliases: []string{"regs"}, cmdFn: regs, helpMsg: "Print contents of CPU registers."},
{aliases: []string{"exit", "quit", "q"}, cmdFn: exitCommand, helpMsg: "Exit the debugger."},
{aliases: []string{"list", "ls"}, allowedPrefixes: scopePrefix, cmdFn: listCommand, helpMsg: "list <linespec>. Show source around current point or provided linespec."},
{aliases: []string{"stack", "bt"}, allowedPrefixes: scopePrefix | onPrefix, cmdFn: stackCommand, helpMsg: "stack [<depth>] [-full]. Prints stack."},
{aliases: []string{"frame"}, allowedPrefixes: scopePrefix, cmdFn: c.frame, helpMsg: "frame <frame index> <command>. Executes command on the specified stack frame"},
{aliases: []string{"source"}, cmdFn: c.sourceCommand, helpMsg: "Executes a file containing a list of delve commands"},
{aliases: []string{"disassemble", "disass"}, allowedPrefixes: scopePrefix, cmdFn: disassCommand, helpMsg: "Displays disassembly of specific function or address range: disassemble [-a <start> <end>] [-l <locspec>]"},
{aliases: []string{"on"}, cmdFn: c.onCmd, helpMsg: "on <breakpoint name or id> <command>. Executes command when the specified breakpoint is hit (supported commands: print <expression>, stack [<depth>] [-full] and goroutine)"},
{aliases: []string{"condition", "cond"}, cmdFn: conditionCmd, helpMsg: "cond <breakpoint name or id> <boolean expression>. Specifies that the breakpoint or tracepoint should break only if the boolean expression is true."},
{aliases: []string{"list", "ls"}, allowedPrefixes: scopePrefix, cmdFn: listCommand, helpMsg: `Show source code.
[goroutine <n>] [frame <m>] list [<linespec>]
Show source around current point or provided linespec.`},
{aliases: []string{"stack", "bt"}, allowedPrefixes: scopePrefix | onPrefix, cmdFn: stackCommand, helpMsg: `Print stack trace.
[goroutine <n>] [frame <m>] stack [<depth>] [-full]
If -full is specified every stackframe will be decorated by the value of its local variables and function arguments.`},
{aliases: []string{"frame"}, allowedPrefixes: scopePrefix, cmdFn: c.frame, helpMsg: `Executes command on a different frame.
frame <frame index> <command>.`},
{aliases: []string{"source"}, cmdFn: c.sourceCommand, helpMsg: `Executes a file containing a list of delve commands
source <path>`},
{aliases: []string{"disassemble", "disass"}, allowedPrefixes: scopePrefix, cmdFn: disassCommand, helpMsg: `Disassembler.
[goroutine <n>] [frame <m>] disassemble [-a <start> <end>] [-l <locspec>]
If no argument is specified the function being executed in the selected stack frame will be executed.
-a <start> <end> disassembles the specified address range
-l <locspec> disassembles the specified function`},
{aliases: []string{"on"}, cmdFn: c.onCmd, helpMsg: `Executes a command when a breakpoint is hit.
on <breakpoint name or id> <command>.
Supported commands: print, stack and goroutine)`},
{aliases: []string{"condition", "cond"}, cmdFn: conditionCmd, helpMsg: `Set breakpoint condition.
condition <breakpoint name or id> <boolean expression>.
Specifies that the breakpoint or tracepoint should break only if the boolean expression is true.`},
}
return c
......@@ -165,8 +266,10 @@ func (c *Commands) Merge(allAliases map[string][]string) {
}
}
var noCmdError = errors.New("command not available")
func noCmdAvailable(t *Term, ctx callContext, args string) error {
return fmt.Errorf("command not available")
return noCmdError
}
func nullCommand(t *Term, ctx callContext, args string) error {
......@@ -174,17 +277,37 @@ func nullCommand(t *Term, ctx callContext, args string) error {
}
func (c *Commands) help(t *Term, ctx callContext, args string) error {
if args != "" {
for _, cmd := range c.cmds {
for _, alias := range cmd.aliases {
if alias == args {
fmt.Println(cmd.helpMsg)
return nil
}
}
}
return noCmdError
}
fmt.Println("The following commands are available:")
w := new(tabwriter.Writer)
w.Init(os.Stdout, 0, 8, 0, '-', 0)
for _, cmd := range c.cmds {
h := cmd.helpMsg
if idx := strings.Index(h, "\n"); idx >= 0 {
h = h[:idx]
}
if len(cmd.aliases) > 1 {
fmt.Fprintf(w, " %s (alias: %s) \t %s\n", cmd.aliases[0], strings.Join(cmd.aliases[1:], " | "), cmd.helpMsg)
fmt.Fprintf(w, " %s (alias: %s) \t %s\n", cmd.aliases[0], strings.Join(cmd.aliases[1:], " | "), h)
} else {
fmt.Fprintf(w, " %s \t %s\n", cmd.aliases[0], cmd.helpMsg)
fmt.Fprintf(w, " %s \t %s\n", cmd.aliases[0], h)
}
}
return w.Flush()
if err := w.Flush(); err != nil {
return err
}
fmt.Println("Type help followed by a command for full documentation.")
return nil
}
type byThreadID []*api.Thread
......
......@@ -400,7 +400,6 @@ func TestOnPrefixLocals(t *testing.T) {
})
}
func countOccourences(s string, needle string) int {
count := 0
for {
......
package terminal
import (
"fmt"
"io"
"strings"
)
func replaceDocPath(s string) string {
const docpath = "$GOPATH/src/github.com/derekparker/delve/"
for {
start := strings.Index(s, docpath)
if start < 0 {
return s
}
var end int
for end = start + len(docpath); end < len(s); end++ {
if s[end] == ' ' {
break
}
}
text := s[start+len(docpath) : end]
s = s[:start] + fmt.Sprintf("[%s](//github.com/derekparker/delve/tree/master/%s)", text, text) + s[end:]
}
}
func (commands *Commands) WriteMarkdown(w io.Writer) {
fmt.Fprintf(w, "# Commands\n\n")
fmt.Fprintf(w, "Command | Description\n")
fmt.Fprintf(w, "--------|------------\n")
for _, cmd := range commands.cmds {
h := cmd.helpMsg
if idx := strings.Index(h, "\n"); idx >= 0 {
h = h[:idx]
}
fmt.Fprintf(w, "[%s](#%s) | %s\n", cmd.aliases[0], cmd.aliases[0], h)
}
fmt.Fprintf(w, "\n")
for _, cmd := range commands.cmds {
fmt.Fprintf(w, "## %s\n%s\n\n", cmd.aliases[0], replaceDocPath(cmd.helpMsg))
if len(cmd.aliases) > 1 {
fmt.Fprintf(w, "Aliases:")
for _, alias := range cmd.aliases[1:] {
fmt.Fprintf(w, " %s", alias)
}
fmt.Fprintf(w, "\n")
}
fmt.Fprintf(w, "\n")
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册