parent
55e44c9dc0
commit
48288edd18
@ -11,6 +11,7 @@ Command | Description
|
||||
[clear-checkpoint](#clear-checkpoint) | Deletes checkpoint.
|
||||
[clearall](#clearall) | Deletes multiple breakpoints.
|
||||
[condition](#condition) | Set breakpoint condition.
|
||||
[config](#config) | Changes configuration parameters.
|
||||
[continue](#continue) | Run until breakpoint or program termination.
|
||||
[disassemble](#disassemble) | Disassembler.
|
||||
[exit](#exit) | Exit the debugger.
|
||||
@ -106,6 +107,32 @@ Specifies that the breakpoint or tracepoint should break only if the boolean exp
|
||||
|
||||
Aliases: cond
|
||||
|
||||
## config
|
||||
Changes configuration parameters.
|
||||
|
||||
config -list
|
||||
|
||||
Show all configuration parameters.
|
||||
|
||||
config -save
|
||||
|
||||
Saves the configuration file to disk, overwriting the current configuration file.
|
||||
|
||||
config <parameter> <value>
|
||||
|
||||
Changes the value of a configuration parameter.
|
||||
|
||||
config subistitute-path <from> <to>
|
||||
config subistitute-path <from>
|
||||
|
||||
Adds or removes a path subistitution rule.
|
||||
|
||||
config alias <command> <alias>
|
||||
config alias <alias>
|
||||
|
||||
Defines <alias> as an alias to <command> or removes an alias.
|
||||
|
||||
|
||||
## continue
|
||||
Run until breakpoint or program termination.
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package cmds
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
@ -12,7 +11,6 @@ import (
|
||||
"runtime"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"unicode"
|
||||
|
||||
"github.com/derekparker/delve/pkg/config"
|
||||
"github.com/derekparker/delve/pkg/goversion"
|
||||
@ -534,7 +532,7 @@ func execute(attachPid int, processArgs []string, conf *config.Config, coreFile
|
||||
func gobuild(debugname, pkg string) error {
|
||||
args := []string{"-gcflags", "-N -l", "-o", debugname}
|
||||
if BuildFlags != "" {
|
||||
args = append(args, splitQuotedFields(BuildFlags)...)
|
||||
args = append(args, config.SplitQuotedFields(BuildFlags, '\'')...)
|
||||
}
|
||||
if ver, _ := goversion.Installed(); ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) {
|
||||
// after go1.9 building with -gcflags='-N -l' and -a simultaneously works
|
||||
@ -547,7 +545,7 @@ func gobuild(debugname, pkg string) error {
|
||||
func gotestbuild(pkg string) error {
|
||||
args := []string{"-gcflags", "-N -l", "-c", "-o", testdebugname}
|
||||
if BuildFlags != "" {
|
||||
args = append(args, splitQuotedFields(BuildFlags)...)
|
||||
args = append(args, config.SplitQuotedFields(BuildFlags, '\'')...)
|
||||
}
|
||||
if ver, _ := goversion.Installed(); ver.Major < 0 || ver.AfterOrEqual(goversion.GoVersion{1, 9, -1, 0, 0, ""}) {
|
||||
// after go1.9 building with -gcflags='-N -l' and -a simultaneously works
|
||||
@ -565,63 +563,6 @@ func gocommand(command string, args ...string) error {
|
||||
return goBuild.Run()
|
||||
}
|
||||
|
||||
// Like strings.Fields but ignores spaces inside areas surrounded
|
||||
// by single quotes.
|
||||
// To specify a single quote use backslash to escape it: '\''
|
||||
func splitQuotedFields(in string) []string {
|
||||
type stateEnum int
|
||||
const (
|
||||
inSpace stateEnum = iota
|
||||
inField
|
||||
inQuote
|
||||
inQuoteEscaped
|
||||
)
|
||||
state := inSpace
|
||||
r := []string{}
|
||||
var buf bytes.Buffer
|
||||
|
||||
for _, ch := range in {
|
||||
switch state {
|
||||
case inSpace:
|
||||
if ch == '\'' {
|
||||
state = inQuote
|
||||
} else if !unicode.IsSpace(ch) {
|
||||
buf.WriteRune(ch)
|
||||
state = inField
|
||||
}
|
||||
|
||||
case inField:
|
||||
if ch == '\'' {
|
||||
state = inQuote
|
||||
} else if unicode.IsSpace(ch) {
|
||||
r = append(r, buf.String())
|
||||
buf.Reset()
|
||||
} else {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
|
||||
case inQuote:
|
||||
if ch == '\'' {
|
||||
state = inField
|
||||
} else if ch == '\\' {
|
||||
state = inQuoteEscaped
|
||||
} else {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
|
||||
case inQuoteEscaped:
|
||||
buf.WriteRune(ch)
|
||||
state = inQuote
|
||||
}
|
||||
}
|
||||
|
||||
if buf.Len() != 0 {
|
||||
r = append(r, buf.String())
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// SafeRemoveAll removes dir and its contents but only as long as dir does
|
||||
// not contain directories.
|
||||
func SafeRemoveAll(dir string) {
|
||||
|
||||
@ -1,21 +0,0 @@
|
||||
package cmds
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSplitQuotedFields(t *testing.T) {
|
||||
in := `field'A' 'fieldB' fie'l\'d'C fieldD 'another field' fieldE`
|
||||
tgt := []string{"fieldA", "fieldB", "fiel'dC", "fieldD", "another field", "fieldE"}
|
||||
out := splitQuotedFields(in)
|
||||
|
||||
if len(tgt) != len(out) {
|
||||
t.Fatalf("expected %#v, got %#v (len mismatch)", tgt, out)
|
||||
}
|
||||
|
||||
for i := range tgt {
|
||||
if tgt[i] != out[i] {
|
||||
t.Fatalf(" expected %#v, got %#v (mismatch at %d)", tgt, out, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -20,7 +20,7 @@ type SubstitutePathRule struct {
|
||||
// Directory path will be substituted if it matches `From`.
|
||||
From string
|
||||
// Path to which substitution is performed.
|
||||
To string
|
||||
To string
|
||||
}
|
||||
|
||||
// Slice of source code path substitution rules.
|
||||
@ -29,9 +29,16 @@ type SubstitutePathRules []SubstitutePathRule
|
||||
// Config defines all configuration options available to be set through the config file.
|
||||
type Config struct {
|
||||
// Commands aliases.
|
||||
Aliases map[string][]string
|
||||
Aliases map[string][]string `yaml:"aliases"`
|
||||
// Source code path substitution rules.
|
||||
SubstitutePath SubstitutePathRules `yaml:"substitute-path"`
|
||||
|
||||
// MaxStringLen is the maximum string length that the commands print,
|
||||
// locals, args and vars should read (in verbose mode).
|
||||
MaxStringLen *int `yaml:"max-string-len,omitempty"`
|
||||
// MaxArrayValues is the maximum number of array items that the commands
|
||||
// print, locals, args and vars should read (in verbose mode).
|
||||
MaxArrayValues *int `yaml:"max-array-values,omitempty"`
|
||||
}
|
||||
|
||||
// LoadConfig attempts to populate a Config object from the config.yml file.
|
||||
@ -75,6 +82,27 @@ func LoadConfig() *Config {
|
||||
return &c
|
||||
}
|
||||
|
||||
func SaveConfig(conf *Config) error {
|
||||
fullConfigFile, err := GetConfigFilePath(configFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
out, err := yaml.Marshal(*conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.Create(fullConfigFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = f.Write(out)
|
||||
return err
|
||||
}
|
||||
|
||||
func createDefaultConfig(path string) {
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
|
||||
63
pkg/config/split.go
Normal file
63
pkg/config/split.go
Normal file
@ -0,0 +1,63 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Like strings.Fields but ignores spaces inside areas surrounded
|
||||
// by the specified quote character.
|
||||
// To specify a single quote use backslash to escape it: '\''
|
||||
func SplitQuotedFields(in string, quote rune) []string {
|
||||
type stateEnum int
|
||||
const (
|
||||
inSpace stateEnum = iota
|
||||
inField
|
||||
inQuote
|
||||
inQuoteEscaped
|
||||
)
|
||||
state := inSpace
|
||||
r := []string{}
|
||||
var buf bytes.Buffer
|
||||
|
||||
for _, ch := range in {
|
||||
switch state {
|
||||
case inSpace:
|
||||
if ch == quote {
|
||||
state = inQuote
|
||||
} else if !unicode.IsSpace(ch) {
|
||||
buf.WriteRune(ch)
|
||||
state = inField
|
||||
}
|
||||
|
||||
case inField:
|
||||
if ch == quote {
|
||||
state = inQuote
|
||||
} else if unicode.IsSpace(ch) {
|
||||
r = append(r, buf.String())
|
||||
buf.Reset()
|
||||
} else {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
|
||||
case inQuote:
|
||||
if ch == quote {
|
||||
state = inField
|
||||
} else if ch == '\\' {
|
||||
state = inQuoteEscaped
|
||||
} else {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
|
||||
case inQuoteEscaped:
|
||||
buf.WriteRune(ch)
|
||||
state = inQuote
|
||||
}
|
||||
}
|
||||
|
||||
if buf.Len() != 0 {
|
||||
r = append(r, buf.String())
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
37
pkg/config/split_test.go
Normal file
37
pkg/config/split_test.go
Normal file
@ -0,0 +1,37 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSplitQuotedFields(t *testing.T) {
|
||||
in := `field'A' 'fieldB' fie'l\'d'C fieldD 'another field' fieldE`
|
||||
tgt := []string{"fieldA", "fieldB", "fiel'dC", "fieldD", "another field", "fieldE"}
|
||||
out := SplitQuotedFields(in, '\'')
|
||||
|
||||
if len(tgt) != len(out) {
|
||||
t.Fatalf("expected %#v, got %#v (len mismatch)", tgt, out)
|
||||
}
|
||||
|
||||
for i := range tgt {
|
||||
if tgt[i] != out[i] {
|
||||
t.Fatalf(" expected %#v, got %#v (mismatch at %d)", tgt, out, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSplitDoubleQuotedFields(t *testing.T) {
|
||||
in := `field"A" "fieldB" fie"l'd"C "field\"D" "yet another field"`
|
||||
tgt := []string{"fieldA", "fieldB", "fiel'dC", "field\"D", "yet another field"}
|
||||
out := SplitQuotedFields(in, '"')
|
||||
|
||||
if len(tgt) != len(out) {
|
||||
t.Fatalf("expected %#v, got %#v (len mismatch)", tgt, out)
|
||||
}
|
||||
|
||||
for i := range tgt {
|
||||
if tgt[i] != out[i] {
|
||||
t.Fatalf(" expected %#v, got %#v (mismatch at %d)", tgt, out, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -41,6 +41,7 @@ type cmdfunc func(t *Term, ctx callContext, args string) error
|
||||
|
||||
type command struct {
|
||||
aliases []string
|
||||
builtinAliases []string
|
||||
allowedPrefixes cmdPrefix
|
||||
helpMsg string
|
||||
cmdFn cmdfunc
|
||||
@ -222,6 +223,29 @@ Supported commands: print, stack and goroutine)`},
|
||||
condition <breakpoint name or id> <boolean expression>.
|
||||
|
||||
Specifies that the breakpoint or tracepoint should break only if the boolean expression is true.`},
|
||||
{aliases: []string{"config"}, cmdFn: configureCmd, helpMsg: `Changes configuration parameters.
|
||||
|
||||
config -list
|
||||
|
||||
Show all configuration parameters.
|
||||
|
||||
config -save
|
||||
|
||||
Saves the configuration file to disk, overwriting the current configuration file.
|
||||
|
||||
config <parameter> <value>
|
||||
|
||||
Changes the value of a configuration parameter.
|
||||
|
||||
config subistitute-path <from> <to>
|
||||
config subistitute-path <from>
|
||||
|
||||
Adds or removes a path subistitution rule.
|
||||
|
||||
config alias <command> <alias>
|
||||
config alias <alias>
|
||||
|
||||
Defines <alias> as an alias to <command> or removes an alias.`},
|
||||
}
|
||||
|
||||
if client == nil || client.Recorded() {
|
||||
@ -318,8 +342,17 @@ func (c *Commands) Call(cmdstr string, t *Term) error {
|
||||
|
||||
// Merge takes aliases defined in the config struct and merges them with the default aliases.
|
||||
func (c *Commands) Merge(allAliases map[string][]string) {
|
||||
for i := range c.cmds {
|
||||
if c.cmds[i].builtinAliases != nil {
|
||||
c.cmds[i].aliases = append(c.cmds[i].aliases[:0], c.cmds[i].builtinAliases...)
|
||||
}
|
||||
}
|
||||
for i := range c.cmds {
|
||||
if aliases, ok := allAliases[c.cmds[i].aliases[0]]; ok {
|
||||
if c.cmds[i].builtinAliases == nil {
|
||||
c.cmds[i].builtinAliases = make([]string, len(c.cmds[i].aliases))
|
||||
copy(c.cmds[i].builtinAliases, c.cmds[i].aliases)
|
||||
}
|
||||
c.cmds[i].aliases = append(c.cmds[i].aliases, aliases...)
|
||||
}
|
||||
}
|
||||
@ -906,7 +939,7 @@ func printVar(t *Term, ctx callContext, args string) error {
|
||||
ctx.Breakpoint.Variables = append(ctx.Breakpoint.Variables, args)
|
||||
return nil
|
||||
}
|
||||
val, err := t.client.EvalVariable(ctx.Scope, args, LongLoadConfig)
|
||||
val, err := t.client.EvalVariable(ctx.Scope, args, t.loadConfig())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -1001,19 +1034,19 @@ func types(t *Term, ctx callContext, args string) error {
|
||||
return printSortedStrings(t.client.ListTypes(args))
|
||||
}
|
||||
|
||||
func parseVarArguments(args string) (filter string, cfg api.LoadConfig) {
|
||||
func parseVarArguments(args string, t *Term) (filter string, cfg api.LoadConfig) {
|
||||
if v := strings.SplitN(args, " ", 2); len(v) >= 1 && v[0] == "-v" {
|
||||
if len(v) == 2 {
|
||||
return v[1], LongLoadConfig
|
||||
return v[1], t.loadConfig()
|
||||
} else {
|
||||
return "", LongLoadConfig
|
||||
return "", t.loadConfig()
|
||||
}
|
||||
}
|
||||
return args, ShortLoadConfig
|
||||
}
|
||||
|
||||
func args(t *Term, ctx callContext, args string) error {
|
||||
filter, cfg := parseVarArguments(args)
|
||||
filter, cfg := parseVarArguments(args, t)
|
||||
if ctx.Prefix == onPrefix {
|
||||
if filter != "" {
|
||||
return fmt.Errorf("filter not supported on breakpoint")
|
||||
@ -1029,7 +1062,7 @@ func args(t *Term, ctx callContext, args string) error {
|
||||
}
|
||||
|
||||
func locals(t *Term, ctx callContext, args string) error {
|
||||
filter, cfg := parseVarArguments(args)
|
||||
filter, cfg := parseVarArguments(args, t)
|
||||
if ctx.Prefix == onPrefix {
|
||||
if filter != "" {
|
||||
return fmt.Errorf("filter not supported on breakpoint")
|
||||
@ -1045,7 +1078,7 @@ func locals(t *Term, ctx callContext, args string) error {
|
||||
}
|
||||
|
||||
func vars(t *Term, ctx callContext, args string) error {
|
||||
filter, cfg := parseVarArguments(args)
|
||||
filter, cfg := parseVarArguments(args, t)
|
||||
vars, err := t.client.ListPackageVariables(filter, cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@ -14,6 +14,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/derekparker/delve/pkg/config"
|
||||
"github.com/derekparker/delve/pkg/proc/test"
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
@ -121,7 +122,7 @@ func withTestTerminal(name string, t testing.TB, fn func(*FakeTerminal)) {
|
||||
|
||||
ft := &FakeTerminal{
|
||||
t: t,
|
||||
Term: New(client, nil),
|
||||
Term: New(client, &config.Config{}),
|
||||
}
|
||||
fn(ft)
|
||||
}
|
||||
@ -604,3 +605,75 @@ func TestIssue827(t *testing.T) {
|
||||
term.MustExec("goroutine 1")
|
||||
})
|
||||
}
|
||||
|
||||
func findCmdName(c *Commands, cmdstr string, prefix cmdPrefix) string {
|
||||
for _, v := range c.cmds {
|
||||
if v.match(cmdstr) {
|
||||
if prefix != noPrefix && v.allowedPrefixes&prefix == 0 {
|
||||
continue
|
||||
}
|
||||
return v.aliases[0]
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
var term Term
|
||||
term.conf = &config.Config{}
|
||||
term.cmds = DebugCommands(nil)
|
||||
|
||||
err := configureCmd(&term, callContext{}, "nonexistent-parameter 10")
|
||||
if err == nil {
|
||||
t.Fatalf("expected error executing configureCmd(nonexistent-parameter)")
|
||||
}
|
||||
|
||||
err = configureCmd(&term, callContext{}, "max-string-len 10")
|
||||
if err != nil {
|
||||
t.Fatalf("error executing configureCmd(max-string-len): %v", err)
|
||||
}
|
||||
if term.conf.MaxStringLen == nil {
|
||||
t.Fatalf("expected MaxStringLen 10, got nil")
|
||||
}
|
||||
if *term.conf.MaxStringLen != 10 {
|
||||
t.Fatalf("expected MaxStringLen 10, got: %d", *term.conf.MaxStringLen)
|
||||
}
|
||||
|
||||
err = configureCmd(&term, callContext{}, "substitute-path a b")
|
||||
if err != nil {
|
||||
t.Fatalf("error executing configureCmd(substitute-path a b): %v", err)
|
||||
}
|
||||
if len(term.conf.SubstitutePath) != 1 || (term.conf.SubstitutePath[0] != config.SubstitutePathRule{"a", "b"}) {
|
||||
t.Fatalf("unexpected SubstitutePathRules after insert %v", term.conf.SubstitutePath)
|
||||
}
|
||||
|
||||
err = configureCmd(&term, callContext{}, "substitute-path a")
|
||||
if err != nil {
|
||||
t.Fatalf("error executing configureCmd(substitute-path a): %v", err)
|
||||
}
|
||||
if len(term.conf.SubstitutePath) != 0 {
|
||||
t.Fatalf("unexpected SubstitutePathRules after delete %v", term.conf.SubstitutePath)
|
||||
}
|
||||
|
||||
err = configureCmd(&term, callContext{}, "alias print blah")
|
||||
if err != nil {
|
||||
t.Fatalf("error executing configureCmd(alias print blah): %v", err)
|
||||
}
|
||||
if len(term.conf.Aliases["print"]) != 1 {
|
||||
t.Fatalf("aliases not changed after configure command %v", term.conf.Aliases)
|
||||
}
|
||||
if findCmdName(term.cmds, "blah", noPrefix) != "print" {
|
||||
t.Fatalf("new alias not found")
|
||||
}
|
||||
|
||||
err = configureCmd(&term, callContext{}, "alias blah")
|
||||
if err != nil {
|
||||
t.Fatalf("error executing configureCmd(alias blah): %v", err)
|
||||
}
|
||||
if len(term.conf.Aliases["print"]) != 0 {
|
||||
t.Fatalf("alias not removed after configure command %v", term.conf.Aliases)
|
||||
}
|
||||
if findCmdName(term.cmds, "blah", noPrefix) != "" {
|
||||
t.Fatalf("new alias found after delete")
|
||||
}
|
||||
}
|
||||
|
||||
188
pkg/terminal/config.go
Normal file
188
pkg/terminal/config.go
Normal file
@ -0,0 +1,188 @@
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/derekparker/delve/pkg/config"
|
||||
)
|
||||
|
||||
func configureCmd(t *Term, ctx callContext, args string) error {
|
||||
switch args {
|
||||
case "-list":
|
||||
return configureList(t)
|
||||
case "-save":
|
||||
return config.SaveConfig(t.conf)
|
||||
case "":
|
||||
return fmt.Errorf("wrong number of arguments to \"config\"")
|
||||
default:
|
||||
return configureSet(t, args)
|
||||
}
|
||||
}
|
||||
|
||||
type configureIterator struct {
|
||||
cfgValue reflect.Value
|
||||
cfgType reflect.Type
|
||||
i int
|
||||
}
|
||||
|
||||
func iterateConfiguration(conf *config.Config) *configureIterator {
|
||||
cfgValue := reflect.ValueOf(conf).Elem()
|
||||
cfgType := cfgValue.Type()
|
||||
|
||||
return &configureIterator{cfgValue, cfgType, -1}
|
||||
}
|
||||
|
||||
func (it *configureIterator) Next() bool {
|
||||
it.i++
|
||||
return it.i < it.cfgValue.NumField()
|
||||
}
|
||||
|
||||
func (it *configureIterator) Field() (name string, field reflect.Value) {
|
||||
name = it.cfgType.Field(it.i).Tag.Get("yaml")
|
||||
if comma := strings.Index(name, ","); comma >= 0 {
|
||||
name = name[:comma]
|
||||
}
|
||||
field = it.cfgValue.Field(it.i)
|
||||
return
|
||||
}
|
||||
|
||||
func configureFindFieldByName(conf *config.Config, name string) reflect.Value {
|
||||
it := iterateConfiguration(conf)
|
||||
for it.Next() {
|
||||
fieldName, field := it.Field()
|
||||
if fieldName == name {
|
||||
return field
|
||||
}
|
||||
}
|
||||
return reflect.ValueOf(nil)
|
||||
}
|
||||
|
||||
func configureList(t *Term) error {
|
||||
w := new(tabwriter.Writer)
|
||||
w.Init(os.Stdout, 0, 8, 1, ' ', 0)
|
||||
|
||||
it := iterateConfiguration(t.conf)
|
||||
for it.Next() {
|
||||
fieldName, field := it.Field()
|
||||
if fieldName == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if !field.IsNil() {
|
||||
if field.Kind() == reflect.Ptr {
|
||||
fmt.Fprintf(w, "%s\t%v\n", fieldName, field.Elem())
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\t%v\n", fieldName, field)
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(w, "%s\t<not defined>\n", fieldName)
|
||||
}
|
||||
}
|
||||
return w.Flush()
|
||||
}
|
||||
|
||||
func configureSet(t *Term, args string) error {
|
||||
v := strings.SplitN(args, " ", 2)
|
||||
|
||||
cfgname := v[0]
|
||||
var rest string
|
||||
if len(v) == 2 {
|
||||
rest = v[1]
|
||||
}
|
||||
|
||||
if cfgname == "alias" {
|
||||
return configureSetAlias(t, rest)
|
||||
}
|
||||
|
||||
field := configureFindFieldByName(t.conf, cfgname)
|
||||
if !field.CanAddr() {
|
||||
return fmt.Errorf("%q is not a configuration parameter", cfgname)
|
||||
}
|
||||
|
||||
if field.Kind() == reflect.Slice && field.Type().Elem().Name() == "SubstitutePathRule" {
|
||||
return configureSetSubstituePath(t, rest)
|
||||
}
|
||||
|
||||
simpleArg := func(typ reflect.Type) (reflect.Value, error) {
|
||||
switch typ.Kind() {
|
||||
case reflect.Int:
|
||||
n, err := strconv.Atoi(rest)
|
||||
if err != nil {
|
||||
return reflect.ValueOf(nil), fmt.Errorf("argument to %q must be a number", cfgname)
|
||||
}
|
||||
return reflect.ValueOf(&n), nil
|
||||
default:
|
||||
return reflect.ValueOf(nil), fmt.Errorf("unsupported type for configuration key %q", cfgname)
|
||||
}
|
||||
}
|
||||
|
||||
if field.Kind() == reflect.Ptr {
|
||||
val, err := simpleArg(field.Type().Elem())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field.Set(val)
|
||||
} else {
|
||||
val, err := simpleArg(field.Type())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
field.Set(val.Elem())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureSetSubstituePath(t *Term, rest string) error {
|
||||
argv := config.SplitQuotedFields(rest, '"')
|
||||
switch len(argv) {
|
||||
case 1: // delete substitute-path rule
|
||||
for i := range t.conf.SubstitutePath {
|
||||
if t.conf.SubstitutePath[i].From == argv[0] {
|
||||
copy(t.conf.SubstitutePath[i:], t.conf.SubstitutePath[i+1:])
|
||||
t.conf.SubstitutePath = t.conf.SubstitutePath[:len(t.conf.SubstitutePath)-1]
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("could not find rule for %q", argv[0])
|
||||
case 2: // add substitute-path rule
|
||||
for i := range t.conf.SubstitutePath {
|
||||
if t.conf.SubstitutePath[i].From == argv[0] {
|
||||
t.conf.SubstitutePath[i].To = argv[1]
|
||||
return nil
|
||||
}
|
||||
}
|
||||
t.conf.SubstitutePath = append(t.conf.SubstitutePath, config.SubstitutePathRule{argv[0], argv[1]})
|
||||
default:
|
||||
return fmt.Errorf("too many arguments to \"config substitute-path\"")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureSetAlias(t *Term, rest string) error {
|
||||
argv := config.SplitQuotedFields(rest, '"')
|
||||
switch len(argv) {
|
||||
case 1: // delete alias rule
|
||||
for k := range t.conf.Aliases {
|
||||
v := t.conf.Aliases[k]
|
||||
for i := range v {
|
||||
if v[i] == argv[0] {
|
||||
copy(v[i:], v[i+1:])
|
||||
t.conf.Aliases[k] = v[:len(v)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
case 2: // add alias rule
|
||||
alias, cmd := argv[1], argv[0]
|
||||
if t.conf.Aliases == nil {
|
||||
t.conf.Aliases = make(map[string][]string)
|
||||
}
|
||||
t.conf.Aliases[cmd] = append(t.conf.Aliases[cmd], alias)
|
||||
}
|
||||
t.cmds.Merge(t.conf.Aliases)
|
||||
return nil
|
||||
}
|
||||
@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/derekparker/delve/pkg/config"
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -240,3 +241,18 @@ func (t *Term) handleExit() (int, error) {
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// loadConfig returns an api.LoadConfig with the parameterss specified in
|
||||
// the configuration file.
|
||||
func (t *Term) loadConfig() api.LoadConfig {
|
||||
r := api.LoadConfig{true, 1, 64, 64, -1}
|
||||
|
||||
if t.conf.MaxStringLen != nil {
|
||||
r.MaxStringLen = *t.conf.MaxStringLen
|
||||
}
|
||||
if t.conf.MaxArrayValues != nil {
|
||||
r.MaxArrayValues = *t.conf.MaxArrayValues
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user