@@ -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+ pathCnt := 0
95+ i := len (file )
96+ for pathCnt < goal {
97+ i = strings .LastIndex (file [:i ], sep )
98+ if i == - 1 {
99+ i = - len (sep )
100+ break
101+ }
102+ pathCnt ++
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