@@ -14,7 +14,6 @@ import (
14
14
"errors"
15
15
"fmt"
16
16
"io"
17
- "path/filepath"
18
17
"runtime"
19
18
"strconv"
20
19
"strings"
@@ -94,43 +93,7 @@ func (c Call) Format(s fmt.State, verb rune) {
94
93
case s .Flag ('#' ):
95
94
// done
96
95
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 ()):]
134
97
default :
135
98
const sep = "/"
136
99
if i := strings .LastIndex (file , sep ); i != - 1 {
@@ -308,12 +271,59 @@ func (cs CallStack) TrimAbove(c Call) CallStack {
308
271
return cs
309
272
}
310
273
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
312
315
313
316
func 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 ]
315
325
if runtime .GOOS == "windows" {
316
- goroot = strings .ToLower (goroot )
326
+ runtimePath = strings .ToLower (runtimePath )
317
327
}
318
328
}
319
329
@@ -325,7 +335,7 @@ func inGoroot(c Call) bool {
325
335
if runtime .GOOS == "windows" {
326
336
file = strings .ToLower (file )
327
337
}
328
- return strings .HasPrefix (file , goroot ) || strings .HasSuffix (file , "/_testmain.go" )
338
+ return strings .HasPrefix (file , runtimePath ) || strings .HasSuffix (file , "/_testmain.go" )
329
339
}
330
340
331
341
// TrimRuntime returns a slice of the CallStack with the topmost entries from
0 commit comments