1
2
3
4
5 package astutil
6
7 import (
8 "fmt"
9 "go/ast"
10 "go/token"
11 "strconv"
12 "unicode/utf8"
13 )
14
15
16
17 func RangeInStringLiteral(lit *ast.BasicLit, start, end int) (Range, error) {
18 startPos, err := PosInStringLiteral(lit, start)
19 if err != nil {
20 return Range{}, fmt.Errorf("start: %v", err)
21 }
22 endPos, err := PosInStringLiteral(lit, end)
23 if err != nil {
24 return Range{}, fmt.Errorf("end: %v", err)
25 }
26 return Range{startPos, endPos}, nil
27 }
28
29
30
31
32 func PosInStringLiteral(lit *ast.BasicLit, offset int) (token.Pos, error) {
33 raw := lit.Value
34
35 value, err := strconv.Unquote(raw)
36 if err != nil {
37 return 0, err
38 }
39 if !(0 <= offset && offset <= len(value)) {
40 return 0, fmt.Errorf("invalid offset")
41 }
42
43 pos, _ := walkStringLiteral(lit, lit.End(), offset)
44 return pos, nil
45 }
46
47
48
49 func OffsetInStringLiteral(lit *ast.BasicLit, pos token.Pos) (int, error) {
50 if !NodeContainsPos(lit, pos) {
51 return 0, fmt.Errorf("invalid position")
52 }
53
54 raw := lit.Value
55
56 value, err := strconv.Unquote(raw)
57 if err != nil {
58 return 0, err
59 }
60
61 _, offset := walkStringLiteral(lit, pos, len(value))
62 return offset, nil
63 }
64
65
66
67
68
69
70 func walkStringLiteral(lit *ast.BasicLit, targetPos token.Pos, targetOffset int) (token.Pos, int) {
71 raw := lit.Value
72 norm := int(lit.End()-lit.Pos()) > len(lit.Value)
73
74
75 quote := raw[0]
76 raw = raw[1 : len(raw)-1]
77
78 var (
79 i = 0
80 pos = lit.Pos() + 1
81 )
82
83 for raw != "" {
84 r, _, rest, _ := strconv.UnquoteChar(raw, quote)
85 sz := len(raw) - len(rest)
86
87 nextPos := pos + token.Pos(sz)
88 if norm && r == '\n' {
89 nextPos++
90 }
91 nextI := i + utf8.RuneLen(r)
92
93 if nextPos > targetPos || nextI > targetOffset {
94 break
95 }
96
97 raw = raw[sz:]
98 i = nextI
99 pos = nextPos
100 }
101
102 return pos, i
103 }
104
View as plain text