dispatch/vendor/github.com/tdewolff/parse/v2/position.go

98 lines
2.1 KiB
Go
Raw Normal View History

2018-12-17 13:41:24 +00:00
package parse
import (
"fmt"
"io"
"strings"
"github.com/tdewolff/parse/v2/buffer"
)
// Position returns the line and column number for a certain position in a file. It is useful for recovering the position in a file that caused an error.
// It only treates \n, \r, and \r\n as newlines, which might be different from some languages also recognizing \f, \u2028, and \u2029 to be newlines.
2019-06-09 00:01:48 +00:00
func Position(r io.Reader, offset int) (line, col int, context string) {
2018-12-17 13:41:24 +00:00
l := buffer.NewLexer(r)
line = 1
for {
c := l.Peek(0)
2019-06-09 00:01:48 +00:00
if c == 0 && l.Err() != nil || offset == l.Pos() {
2018-12-17 13:41:24 +00:00
col = l.Pos() + 1
context = positionContext(l, line, col)
return
}
2019-06-09 00:01:48 +00:00
nNewline := 0
2018-12-17 13:41:24 +00:00
if c == '\n' {
2019-06-09 00:01:48 +00:00
nNewline = 1
2018-12-17 13:41:24 +00:00
} else if c == '\r' {
if l.Peek(1) == '\n' {
2019-06-09 00:01:48 +00:00
nNewline = 2
2018-12-17 13:41:24 +00:00
} else {
2019-06-09 00:01:48 +00:00
nNewline = 1
}
} else if c >= 0xC0 {
if r, n := l.PeekRune(0); r == '\u2028' || r == '\u2029' {
nNewline = n
}
} else {
l.Move(1)
}
if nNewline > 0 {
if offset < l.Pos()+nNewline {
// move onto offset position, let next iteration handle it
l.Move(offset - l.Pos())
continue
2018-12-17 13:41:24 +00:00
}
2019-06-09 00:01:48 +00:00
l.Move(nNewline)
2018-12-17 13:41:24 +00:00
line++
offset -= l.Pos()
l.Skip()
}
}
}
func positionContext(l *buffer.Lexer, line, col int) (context string) {
for {
c := l.Peek(0)
if c == 0 && l.Err() != nil || c == '\n' || c == '\r' {
break
}
l.Move(1)
}
2019-06-09 00:01:48 +00:00
// cut off front or rear of context to stay between 60 characters
2018-12-17 13:41:24 +00:00
b := l.Lexeme()
2019-06-09 00:01:48 +00:00
limit := 60
offset := 20
ellipsisFront := ""
ellipsisRear := ""
if limit < len(b) {
if col <= limit-offset {
ellipsisRear = "..."
b = b[:limit-3]
} else if col >= len(b)-offset-3 {
ellipsisFront = "..."
col -= len(b) - offset - offset - 7
b = b[len(b)-offset-offset-4:]
} else {
ellipsisFront = "..."
ellipsisRear = "..."
b = b[col-offset-1 : col+offset]
col = offset + 4
}
}
// replace unprintable characters by a space
2018-12-17 13:41:24 +00:00
for i, c := range b {
if c < 0x20 || c == 0x7F {
b[i] = ' '
}
}
2019-06-09 00:01:48 +00:00
context += fmt.Sprintf("%5d: %s%s%s\n", line, ellipsisFront, string(b), ellipsisRear)
2018-12-17 13:41:24 +00:00
context += fmt.Sprintf("%s^", strings.Repeat(" ", col+6))
return
}