diff --git a/Documentation/README.md b/Documentation/README.md index 04ddb85037a403fb146b96b505b34ad4ef8107c6..cf601d33f06ebd114851f7d536274f597a1e5736 100644 --- a/Documentation/README.md +++ b/Documentation/README.md @@ -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) diff --git a/Documentation/cli/README.md b/Documentation/cli/README.md new file mode 100644 index 0000000000000000000000000000000000000000..6c9e17360de7ea4606f50bf4e2b8c7e240ca70d9 --- /dev/null +++ b/Documentation/cli/README.md @@ -0,0 +1,282 @@ +# 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] + +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] + +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 + +Aliases: tr + +## clear +Deletes breakpoint. + + clear + + +## clearall +Deletes multiple breakpoints. + + clearall [] + +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 + goroutine + +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 ] [frame ] print + +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 ] [frame ] set = + +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 [] + +If regex is specified only the source files matching it will be returned. + + +## funcs +Print list of functions. + + funcs [] + +If regex is specified only the functions matching it will be returned. + + +## types +Print list of types + + types [] + +If regex is specified only the functions matching it will be returned. + + +## args +Print function arguments. + + [goroutine ] [frame ] args [-v] [] + +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 ] [frame ] locals [-v] [] + +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] [] + +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 ] [frame ] list [] + +Show source around current point or provided linespec. + +Aliases: ls + +## stack +Print stack trace. + + [goroutine ] [frame ] stack [] [-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 . + + +## source +Executes a file containing a list of delve commands + + source + + +## disassemble +Disassembler. + + [goroutine ] [frame ] disassemble [-a ] [-l ] + +If no argument is specified the function being executed in the selected stack frame will be executed. + + -a disassembles the specified address range + -l disassembles the specified function + +Aliases: disass + +## on +Executes a command when a breakpoint is hit. + + on . + +Supported commands: print, stack and goroutine) + + +## condition +Set breakpoint condition. + + condition . + +Specifies that the breakpoint or tracepoint should break only if the boolean expression is true. + +Aliases: cond + diff --git a/Documentation/cli/expr.md b/Documentation/cli/expr.md new file mode 100644 index 0000000000000000000000000000000000000000..573daaedd3e44c6d338c2cae2f35a43af6ebfb1b --- /dev/null +++ b/Documentation/cli/expr.md @@ -0,0 +1,82 @@ +# 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: +``` +() +``` + +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 +``` diff --git a/Documentation/cli/locspec.md b/Documentation/cli/locspec.md new file mode 100644 index 0000000000000000000000000000000000000000..0d0902f09f4c182b01f8182e69b50ae6ef7d6a79 --- /dev/null +++ b/Documentation/cli/locspec.md @@ -0,0 +1,11 @@ +# Location Specifiers + +Several delve commands take a program location as an argument, the syntax accepted by this commands is: + +* `*
` Specifies the location of memory address *address*. *address* can be specified as a decimal, hexadecimal or octal number +* `:` 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. +* `` Specifies the line *line* in the current file +* `+` Specifies the line *offset* lines after the current one +* `-` Specifies the line *offset* lines before the current one +* `[:]` Specifies the line *line* inside *function*. The full syntax for *function* is `.(*).` however the only required element is the function name, everything else can be omitted as long as the expression remains unambiguous. +* `//` Specifies the location of all the functions matching *regex* diff --git a/Makefile b/Makefile index 165a0e8a13e6479640763dba81c325c8273576ce..2108bfa65ca8b137de1c2be4f217efe7a9795b80 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/scripts/gen-cli-docs.go b/scripts/gen-cli-docs.go new file mode 100644 index 0000000000000000000000000000000000000000..6dac753b21a7cdfaf4983871f0f961cfed5fe5bc --- /dev/null +++ b/scripts/gen-cli-docs.go @@ -0,0 +1,23 @@ +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) +} diff --git a/terminal/command.go b/terminal/command.go index c8f16a5112e8d9b7a5bf49bdf1db16a93ca7f6bd..7c8a75c30333cfcfd3ebc50a08ac865f7612b7cb 100644 --- a/terminal/command.go +++ b/terminal/command.go @@ -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] "}, - {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] + +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] + +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 []. Deletes all breakpoints. If 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 `}, + {aliases: []string{"clear"}, cmdFn: clear, helpMsg: `Deletes breakpoint. + + clear `}, + {aliases: []string{"clearall"}, cmdFn: clearAll, helpMsg: `Deletes multiple breakpoints. + + clearall [] + +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 + goroutine + +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] . Print function arguments, optionally filtered by a regexp."}, - {aliases: []string{"locals"}, allowedPrefixes: scopePrefix | onPrefix, cmdFn: locals, helpMsg: "locals [-v] . Print function locals, optionally filtered by a regexp."}, - {aliases: []string{"vars"}, cmdFn: vars, helpMsg: "vars [-v] . Print package variables, optionally filtered by a regexp."}, + {aliases: []string{"print", "p"}, allowedPrefixes: onPrefix | scopePrefix, cmdFn: printVar, helpMsg: `Evaluate an expression. + + [goroutine ] [frame ] print + +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 ] [frame ] set = + +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 [] + +If regex is specified only the source files matching it will be returned.`}, + {aliases: []string{"funcs"}, cmdFn: funcs, helpMsg: `Print list of functions. + + funcs [] + +If regex is specified only the functions matching it will be returned.`}, + {aliases: []string{"types"}, cmdFn: types, helpMsg: `Print list of types + + types [] + +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 ] [frame ] args [-v] [] + +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 ] [frame ] locals [-v] [] + +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] [] + +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 . Show source around current point or provided linespec."}, - {aliases: []string{"stack", "bt"}, allowedPrefixes: scopePrefix | onPrefix, cmdFn: stackCommand, helpMsg: "stack [] [-full]. Prints stack."}, - {aliases: []string{"frame"}, allowedPrefixes: scopePrefix, cmdFn: c.frame, helpMsg: "frame . 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 ] [-l ]"}, - {aliases: []string{"on"}, cmdFn: c.onCmd, helpMsg: "on . Executes command when the specified breakpoint is hit (supported commands: print , stack [] [-full] and goroutine)"}, - {aliases: []string{"condition", "cond"}, cmdFn: conditionCmd, helpMsg: "cond . 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 ] [frame ] list [] + +Show source around current point or provided linespec.`}, + {aliases: []string{"stack", "bt"}, allowedPrefixes: scopePrefix | onPrefix, cmdFn: stackCommand, helpMsg: `Print stack trace. + + [goroutine ] [frame ] stack [] [-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 .`}, + {aliases: []string{"source"}, cmdFn: c.sourceCommand, helpMsg: `Executes a file containing a list of delve commands + + source `}, + {aliases: []string{"disassemble", "disass"}, allowedPrefixes: scopePrefix, cmdFn: disassCommand, helpMsg: `Disassembler. + + [goroutine ] [frame ] disassemble [-a ] [-l ] + +If no argument is specified the function being executed in the selected stack frame will be executed. + + -a disassembles the specified address range + -l disassembles the specified function`}, + {aliases: []string{"on"}, cmdFn: c.onCmd, helpMsg: `Executes a command when a breakpoint is hit. + + on . + +Supported commands: print, stack and goroutine)`}, + {aliases: []string{"condition", "cond"}, cmdFn: conditionCmd, helpMsg: `Set breakpoint condition. + + condition . + +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 diff --git a/terminal/command_test.go b/terminal/command_test.go index 213d0b312c60f6a2c30f6b1661c878a0e8c730a5..e8fa6890847476ec7941b0acd91d804149849cb9 100644 --- a/terminal/command_test.go +++ b/terminal/command_test.go @@ -400,7 +400,6 @@ func TestOnPrefixLocals(t *testing.T) { }) } - func countOccourences(s string, needle string) int { count := 0 for { diff --git a/terminal/docgen.go b/terminal/docgen.go new file mode 100644 index 0000000000000000000000000000000000000000..a9632d9b885d9802dcb3d09ab3a65ff1141affce --- /dev/null +++ b/terminal/docgen.go @@ -0,0 +1,54 @@ +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") + } +}