@@ -14,7 +14,6 @@ import (
1414 "errors"
1515 "fmt"
1616 "io"
17- "path/filepath"
1817 "runtime"
1918 "strconv"
2019 "strings"
@@ -94,43 +93,7 @@ func (c Call) Format(s fmt.State, verb rune) {
9493 case s .Flag ('#' ):
9594 // done
9695 case s .Flag ('+' ):
97- // Here we want to get the source file path relative to the
98- // compile time GOPATH. As of Go 1.4.x there is no direct way to
99- // know the compiled GOPATH at runtime, but we can infer the
100- // number of path segments in the GOPATH. We note that fn.Name()
101- // returns the function name qualified by the import path, which
102- // does not include the GOPATH. Thus we can trim segments from the
103- // beginning of the file path until the number of path separators
104- // remaining is one more than the number of path separators in the
105- // function name. For example, given:
106- //
107- // GOPATH /home/user
108- // file /home/user/src/pkg/sub/file.go
109- // fn.Name() pkg/sub.Type.Method
110- //
111- // We want to produce:
112- //
113- // pkg/sub/file.go
114- //
115- // From this we can easily see that fn.Name() has one less path
116- // separator than our desired output. We count separators from the
117- // end of the file path until it finds two more than in the
118- // function name and then move one character forward to preserve
119- // the initial path segment without a leading separator.
120- const sep = "/"
121- goal := strings .Count (c .fn .Name (), sep ) + 2
122- pathCnt := 0
123- i := len (file )
124- for pathCnt < goal {
125- i = strings .LastIndex (file [:i ], sep )
126- if i == - 1 {
127- i = - len (sep )
128- break
129- }
130- pathCnt ++
131- }
132- // get back to 0 or trim the leading seperator
133- file = file [i + len (sep ):]
96+ file = file [pkgIndex (file , c .fn .Name ()):]
13497 default :
13598 const sep = "/"
13699 if i := strings .LastIndex (file , sep ); i != - 1 {
@@ -308,12 +271,59 @@ func (cs CallStack) TrimAbove(c Call) CallStack {
308271 return cs
309272}
310273
311- var goroot string
274+ // pkgIndex returns the index that results in file[index:] being the path of
275+ // file relative to the compile time GOPATH, and file[:index] being the
276+ // $GOPATH/src/ portion of file. funcName must be the name of a function in
277+ // file as returned by runtime.Func.Name.
278+ func pkgIndex (file , funcName string ) int {
279+ // As of Go 1.6.2 there is no direct way to know the compile time GOPATH
280+ // at runtime, but we can infer the number of path segments in the GOPATH.
281+ // We note that runtime.Func.Name() returns the function name qualified by
282+ // the import path, which does not include the GOPATH. Thus we can trim
283+ // segments from the beginning of the file path until the number of path
284+ // separators remaining is one more than the number of path separators in
285+ // the function name. For example, given:
286+ //
287+ // GOPATH /home/user
288+ // file /home/user/src/pkg/sub/file.go
289+ // fn.Name() pkg/sub.Type.Method
290+ //
291+ // We want to produce:
292+ //
293+ // file[:idx] == /home/user/src/
294+ // file[idx:] == pkg/sub/file.go
295+ //
296+ // From this we can easily see that fn.Name() has one less path separator
297+ // than our desired result for file[idx:]. We count separators from the
298+ // end of the file path until it finds two more than in the function name
299+ // and then move one character forward to preserve the initial path
300+ // segment without a leading separator.
301+ const sep = "/"
302+ i := len (file )
303+ for n := strings .Count (funcName , sep ) + 2 ; n > 0 ; n -- {
304+ i = strings .LastIndex (file [:i ], sep )
305+ if i == - 1 {
306+ i = - len (sep )
307+ break
308+ }
309+ }
310+ // get back to 0 or trim the leading seperator
311+ return i + len (sep )
312+ }
313+
314+ var runtimePath string
312315
313316func init () {
314- goroot = filepath .ToSlash (runtime .GOROOT ())
317+ var pcs [1 ]uintptr
318+ runtime .Callers (0 , pcs [:])
319+ fn := runtime .FuncForPC (pcs [0 ])
320+ file , _ := fn .FileLine (pcs [0 ])
321+
322+ idx := pkgIndex (file , fn .Name ())
323+
324+ runtimePath = file [:idx ]
315325 if runtime .GOOS == "windows" {
316- goroot = strings .ToLower (goroot )
326+ runtimePath = strings .ToLower (runtimePath )
317327 }
318328}
319329
@@ -325,7 +335,7 @@ func inGoroot(c Call) bool {
325335 if runtime .GOOS == "windows" {
326336 file = strings .ToLower (file )
327337 }
328- return strings .HasPrefix (file , goroot ) || strings .HasSuffix (file , "/_testmain.go" )
338+ return strings .HasPrefix (file , runtimePath ) || strings .HasSuffix (file , "/_testmain.go" )
329339}
330340
331341// TrimRuntime returns a slice of the CallStack with the topmost entries from
0 commit comments