提交 35bc789b 编写于 作者: E Evgeny L 提交者: Derek Parker

config, terminal, command: Add substitute-path parameter to configuration file. (#640)

Allows to rewrite a source path stored in program's debug information,
if the sources were moved to a different place between compilation and
debugging.
上级 0f4b5150
......@@ -15,9 +15,23 @@ const (
configFile string = "config.yml"
)
// Describes a rule for substitution of path to source code file.
type SubstitutePathRule struct {
// Directory path will be substituted if it matches `From`.
From string
// Path to which substitution is performed.
To string
}
// Slice of source code path substitution rules.
type SubstitutePathRules []SubstitutePathRule
// Config defines all configuration options available to be set through the config file.
type Config struct {
Aliases map[string][]string
// Commands aliases.
Aliases map[string][]string
// Source code path substitution rules.
SubstitutePath SubstitutePathRules `yaml:"substitute-path"`
}
// LoadConfig attempts to populate a Config object from the config.yml file.
......@@ -89,6 +103,14 @@ func writeDefaultConfig(f *os.File) error {
# Provided aliases will be added to the default aliases for a given command.
aliases:
# command: ["alias1", "alias2"]
# Define sources path substitution rules. Can be used to rewrite a source path stored
# in program's debug information, if the sources were moved to a different place
# between compilation and debugging.
# Note that substitution rules will not be used for paths passed to "break" and "trace"
# commands.
substitute-path:
# - {from: path, to: path}
`)
return err
}
......
......@@ -1252,7 +1252,7 @@ func printcontextThread(t *Term, th *api.Thread) {
}
func printfile(t *Term, filename string, line int, showArrow bool) error {
file, err := os.Open(filename)
file, err := os.Open(t.substitutePath(filename))
if err != nil {
return err
}
......
......@@ -5,6 +5,7 @@ import (
"io"
"os"
"os/signal"
"runtime"
"strings"
"syscall"
......@@ -24,6 +25,7 @@ const (
// Term represents the terminal running dlv.
type Term struct {
client service.Client
conf *config.Config
prompt string
line *liner.State
cmds *Commands
......@@ -49,9 +51,10 @@ func New(client service.Client, conf *config.Config) *Term {
}
return &Term{
client: client,
conf: conf,
prompt: "(dlv) ",
line: liner.NewLiner(),
client: client,
cmds: cmds,
dumb: dumb,
stdout: w,
......@@ -150,6 +153,46 @@ func (t *Term) Println(prefix, str string) {
fmt.Fprintf(t.stdout, "%s%s\n", prefix, str)
}
// Substitues directory to source file.
//
// Ensures that only directory is substitued, for example:
// substitute from `/dir/subdir`, substitute to `/new`
// for file path `/dir/subdir/file` will return file path `/new/file`.
// for file path `/dir/subdir-2/file` substitution will not be applied.
//
// If more than one substitution rule is defined, the rules are applied
// in the order they are defined, first rule that matches is used for
// substitution.
func (t *Term) substitutePath(path string) string {
path = crossPlatformPath(path)
if t.conf == nil {
return path
}
separator := string(os.PathSeparator)
for _, r := range t.conf.SubstitutePath {
from := crossPlatformPath(r.From)
to := r.To
if !strings.HasSuffix(from, separator) {
from = from + separator
}
if !strings.HasSuffix(to, separator) {
to = to + separator
}
if strings.HasPrefix(path, from) {
return strings.Replace(path, from, to, 1)
}
}
return path
}
func crossPlatformPath(path string) string {
if runtime.GOOS == "darwin" || runtime.GOOS == "windows" {
return strings.ToLower(path)
}
return path
}
func (t *Term) promptForInput() (string, error) {
l, err := t.line.Prompt(t.prompt)
if err != nil {
......
package terminal
import (
"testing"
"runtime"
"github.com/derekparker/delve/config"
)
type tRule struct {
from string
to string
}
type tCase struct {
rules []tRule
path string
res string
}
func platformCases() []tCase {
casesUnix := []tCase{
// Should not depend on separator at the end of rule path
{[]tRule{{"/tmp/path", "/new/path2"}}, "/tmp/path/file.go", "/new/path2/file.go"},
{[]tRule{{"/tmp/path/", "/new/path2/"}}, "/tmp/path/file.go", "/new/path2/file.go"},
{[]tRule{{"/tmp/path/", "/new/path2"}}, "/tmp/path/file.go", "/new/path2/file.go"},
{[]tRule{{"/tmp/path", "/new/path2/"}}, "/tmp/path/file.go", "/new/path2/file.go"},
// Should apply only for directory names
{[]tRule{{"/tmp/path", "/new/path2"}}, "/tmp/path-2/file.go", "/tmp/path-2/file.go"},
// First matched rule should be used
{[]tRule{
{"/tmp/path1", "/new/path1"},
{"/tmp/path2", "/new/path2"},
{"/tmp/path2", "/new/path3"}}, "/tmp/path2/file.go", "/new/path2/file.go"},
}
casesLinux := []tCase{
// Should be case-sensitive
{[]tRule{{"/tmp/path", "/new/path2"}}, "/TmP/path/file.go", "/TmP/path/file.go"},
}
casesDarwin := []tCase{
// Should be case-insensitive
{[]tRule{{"/tmp/path", "/new/path2"}}, "/TmP/PaTh/file.go", "/new/path2/file.go"},
}
casesWindows := []tCase{
// Should not depend on separator at the end of rule path
{[]tRule{{`c:\tmp\path`, `d:\new\path2`}}, `c:\tmp\path\file.go`, `d:\new\path2\file.go`},
{[]tRule{{`c:\tmp\path\`, `d:\new\path2\`}}, `c:\tmp\path\file.go`, `d:\new\path2\file.go`},
{[]tRule{{`c:\tmp\path`, `d:\new\path2\`}}, `c:\tmp\path\file.go`, `d:\new\path2\file.go`},
{[]tRule{{`c:\tmp\path\`, `d:\new\path2`}}, `c:\tmp\path\file.go`, `d:\new\path2\file.go`},
// Should apply only for directory names
{[]tRule{{`c:\tmp\path`, `d:\new\path2`}}, `c:\tmp\path-2\file.go`, `c:\tmp\path-2\file.go`},
// Should be case-insensitive
{[]tRule{{`c:\tmp\path`, `d:\new\path2`}}, `C:\TmP\PaTh\file.go`, `d:\new\path2\file.go`},
}
if runtime.GOOS == "windows" {
return casesWindows
}
if runtime.GOOS == "darwin" {
return append(casesUnix, casesDarwin...)
}
if runtime.GOOS == "linux" {
return append(casesUnix, casesLinux...)
}
return casesUnix
}
func TestSubstitutePath(t *testing.T) {
for _, c := range(platformCases()) {
var subRules config.SubstitutePathRules
for _, r := range(c.rules) {
subRules = append(subRules, config.SubstitutePathRule{From: r.from, To: r.to})
}
res := New(nil, &config.Config{SubstitutePath: subRules}).substitutePath(c.path)
if c.res != res {
t.Errorf("terminal.SubstitutePath(%q) => %q, want %q", c.path, res, c.res)
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册