| 21 | var tabReplacer = strings.NewReplacer("\t", " ") |
| 22 | |
| 23 | func (e *Error) Bind(source Source) *Error { |
| 24 | src := source.String() |
| 25 | |
| 26 | var runeCount, lineStart int |
| 27 | e.Line = 1 |
| 28 | e.Column = 0 |
| 29 | for i, r := range src { |
| 30 | if runeCount == e.From { |
| 31 | break |
| 32 | } |
| 33 | if r == '\n' { |
| 34 | lineStart = i + 1 |
| 35 | e.Line++ |
| 36 | e.Column = 0 |
| 37 | } else { |
| 38 | e.Column++ |
| 39 | } |
| 40 | runeCount++ |
| 41 | } |
| 42 | |
| 43 | lineEnd := lineStart + strings.IndexByte(src[lineStart:], '\n') |
| 44 | if lineEnd < lineStart { |
| 45 | lineEnd = len(src) |
| 46 | } |
| 47 | if lineStart == lineEnd { |
| 48 | return e |
| 49 | } |
| 50 | |
| 51 | const prefix = "\n | " |
| 52 | line := src[lineStart:lineEnd] |
| 53 | snippet := new(strings.Builder) |
| 54 | snippet.Grow(2*len(prefix) + len(line) + e.Column + 1) |
| 55 | snippet.WriteString(prefix) |
| 56 | tabReplacer.WriteString(snippet, line) |
| 57 | snippet.WriteString(prefix) |
| 58 | for i := 0; i < e.Column; i++ { |
| 59 | snippet.WriteByte('.') |
| 60 | } |
| 61 | snippet.WriteByte('^') |
| 62 | e.Snippet = snippet.String() |
| 63 | return e |
| 64 | } |
| 65 | |
| 66 | func (e *Error) Unwrap() error { |
| 67 | return e.Prev |