@@ -65,11 +65,45 @@ func (l loc) Location() (string, int) {
6565 return "unknown" , 0
6666 }
6767
68- _ , prefix , _ , _ := runtime .Caller (0 )
6968 file , line := fn .FileLine (pc )
70- if i := strings .LastIndex (prefix , "github.com/pkg/errors" ); i > 0 {
71- file = file [i :]
69+
70+ // Here we want to get the source file path relative to the compile time
71+ // GOPATH. As of Go 1.6.x there is no direct way to know the compiled
72+ // GOPATH at runtime, but we can infer the number of path segments in the
73+ // GOPATH. We note that fn.Name() returns the function name qualified by
74+ // the import path, which does not include the GOPATH. Thus we can trim
75+ // segments from the beginning of the file path until the number of path
76+ // separators remaining is one more than the number of path separators in
77+ // the function name. For example, given:
78+ //
79+ // GOPATH /home/user
80+ // file /home/user/src/pkg/sub/file.go
81+ // fn.Name() pkg/sub.Type.Method
82+ //
83+ // We want to produce:
84+ //
85+ // pkg/sub/file.go
86+ //
87+ // From this we can easily see that fn.Name() has one less path separator
88+ // than our desired output. We count separators from the end of the file
89+ // path until it finds two more than in the function name and then move
90+ // one character forward to preserve the initial path segment without a
91+ // leading separator.
92+ const sep = "/"
93+ goal := strings .Count (fn .Name (), sep ) + 2
94+ i := len (file )
95+ for n := 0 ; n < goal ; n ++ {
96+ i = strings .LastIndex (file [:i ], sep )
97+ if i == - 1 {
98+ // not enough separators found, set i so that the slice expression
99+ // below leaves file unmodified
100+ i = - len (sep )
101+ break
102+ }
72103 }
104+ // get back to 0 or trim the leading seperator
105+ file = file [i + len (sep ):]
106+
73107 return file , line
74108}
75109
0 commit comments