Skip to content

Commit

Permalink
Add Position.Col (#410)
Browse files Browse the repository at this point in the history
Previously it wasn't easy for users to get the Column information; this
has to be calculated from the Start field, but this isn't convenient.

Also the start column in "column x-y" was off by one, so fix that too as
a bonus.
  • Loading branch information
arp242 committed May 26, 2024
1 parent 5bcf186 commit 3cb6f88
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 41 deletions.
5 changes: 3 additions & 2 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -540,12 +540,13 @@ func (md *MetaData) badtype(dst string, data any) error {

func (md *MetaData) parseErr(err error) error {
k := md.context.String()
d := string(md.data)
return ParseError{
LastKey: k,
Position: md.keyInfo[k].pos,
Position: md.keyInfo[k].pos.withCol(d),
Line: md.keyInfo[k].pos.Line,
err: err,
input: string(md.data),
input: d,
}
}

Expand Down
46 changes: 24 additions & 22 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,28 @@ type ParseError struct {
// Position of an error.
type Position struct {
Line int // Line number, starting at 1.
Col int // Error column, starting at 1.
Start int // Start of error, as byte offset starting at 0.
Len int // Lenght in bytes.
Len int // Lenght of the error in bytes.
}

func (p Position) withCol(tomlFile string) Position {
var (
pos int
lines = strings.Split(tomlFile, "\n")
)
for i := range lines {
ll := len(lines[i]) + 1 // +1 for the removed newline
if pos+ll >= p.Start {
p.Col = p.Start - pos + 1
if p.Col < 1 { // Should never happen, but just in case.
p.Col = 1
}
break
}
pos += ll
}
return p
}

func (pe ParseError) Error() string {
Expand All @@ -94,7 +114,6 @@ func (pe ParseError) ErrorWithPosition() string {

var (
lines = strings.Split(pe.input, "\n")
col = pe.column(lines)
b = new(strings.Builder)
)

Expand All @@ -108,10 +127,10 @@ func (pe ParseError) ErrorWithPosition() string {

if pe.Position.Len == 1 {
fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d:\n\n",
msg, pe.Position.Line, col+1)
msg, pe.Position.Line, pe.Position.Col)
} else {
fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d-%d:\n\n",
msg, pe.Position.Line, col, col+pe.Position.Len)
msg, pe.Position.Line, pe.Position.Col, pe.Position.Col+pe.Position.Len-1)
}
if pe.Position.Line > 2 {
fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-2, expandTab(lines[pe.Position.Line-3]))
Expand All @@ -129,7 +148,7 @@ func (pe ParseError) ErrorWithPosition() string {
diff := len(expanded) - len(lines[pe.Position.Line-1])

fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line, expanded)
fmt.Fprintf(b, "% 10s%s%s\n", "", strings.Repeat(" ", col+diff), strings.Repeat("^", pe.Position.Len))
fmt.Fprintf(b, "% 10s%s%s\n", "", strings.Repeat(" ", pe.Position.Col-1+diff), strings.Repeat("^", pe.Position.Len))
return b.String()
}

Expand All @@ -151,23 +170,6 @@ func (pe ParseError) ErrorWithUsage() string {
return m
}

func (pe ParseError) column(lines []string) int {
var pos, col int
for i := range lines {
ll := len(lines[i]) + 1 // +1 for the removed newline
if pos+ll >= pe.Position.Start {
col = pe.Position.Start - pos
if col < 0 { // Should never happen, but just in case.
col = 0
}
break
}
pos += ll
}

return col
}

func expandTab(s string) string {
var (
b strings.Builder
Expand Down
24 changes: 12 additions & 12 deletions error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ At line 1, column 23:
{"array/tables-2.toml", `
toml: error: Key 'fruit.variety' has already been defined.
At line 9, column 3-8:
At line 9, column 4-8:
7 |
8 | # This table conflicts with the previous table
Expand All @@ -46,7 +46,7 @@ At line 9, column 3-8:
{"local-date/trailing-t.toml", `
toml: error: invalid datetime: "2006-01-30T"
At line 2, column 4-15:
At line 2, column 5-15:
1 | # Date cannot end with trailing T
2 | d = 2006-01-30T
Expand Down Expand Up @@ -92,7 +92,7 @@ func TestParseError(t *testing.T) {
"Int = 200",
`| toml: error: 200 is out of range for int8
|
| At line 1, column 6-9:
| At line 1, column 7-9:
|
| 1 | Int = 200
| ^^^
Expand Down Expand Up @@ -122,7 +122,7 @@ func TestParseError(t *testing.T) {
fmt.Sprintf("Int = %d", uint64(math.MaxInt64+1)),
`| toml: error: 9223372036854775808 is out of range for int64
|
| At line 1, column 6-25:
| At line 1, column 7-25:
|
| 1 | Int = 9223372036854775808
| ^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -153,7 +153,7 @@ func TestParseError(t *testing.T) {
`
| toml: error: 1.1e+99 is out of range for float32
|
| At line 1, column 8-14:
| At line 1, column 9-14:
|
| 1 | Float = 1.1e99
| ^^^^^^
Expand Down Expand Up @@ -185,7 +185,7 @@ func TestParseError(t *testing.T) {
`
| toml: error: invalid duration: "99 bottles"
|
| At line 1, column 5-15:
| At line 1, column 6-15:
|
| 1 | D = "99 bottles"
| ^^^^^^^^^^
Expand All @@ -211,7 +211,7 @@ func TestParseError(t *testing.T) {
`
| toml: error: invalid datetime: "2006-01-99"
|
| At line 1, column 4-14:
| At line 1, column 5-14:
|
| 1 | D = 2006-01-99
| ^^^^^^^^^^
Expand Down Expand Up @@ -297,7 +297,7 @@ func TestUnmarshalTypeError(t *testing.T) {

want := `toml: error: invalid value: "invalid"
At line 3, column 6-13:
At line 3, column 7-13:
1 | k1 = 'asd'
2 | k2 = 'ok'
Expand Down Expand Up @@ -339,7 +339,7 @@ func TestMarhsalError(t *testing.T) {

want := `toml: error: invalid value: "invalid"
At line 3, column 6-13:
At line 3, column 7-13:
1 | k1 = 'asd'
2 | k2 = 'ok'
Expand Down Expand Up @@ -369,7 +369,7 @@ func TestErrorIndent(t *testing.T) {
err := getErr(t, "\tspaces = xxx")
want := `toml: error: expected value but found "xxx" instead
At line 1, column 10-13:
At line 1, column 11-13:
1 | spaces = xxx
^^^
Expand All @@ -382,7 +382,7 @@ At line 1, column 10-13:
err = getErr(t, "\tspaces\t=\txxx")
want = `toml: error: expected value but found "xxx" instead
At line 1, column 10-13:
At line 1, column 11-13:
1 | spaces = xxx
^^^
Expand All @@ -394,7 +394,7 @@ At line 1, column 10-13:
err = getErr(t, "\txxx \t = \t 1\n\tspaces\t=\txxx")
want = `toml: error: expected value but found "xxx" instead
At line 2, column 10-13:
At line 2, column 11-13:
1 | xxx = 1
2 | spaces = xxx
Expand Down
10 changes: 5 additions & 5 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func parse(data string) (p *parser, err error) {
if i := strings.IndexRune(data[:ex], 0); i > -1 {
return nil, ParseError{
Message: "files cannot contain NULL bytes; probably using UTF-16; TOML files must be UTF-8",
Position: Position{Line: 1, Start: i, Len: 1},
Position: Position{Line: 1, Col: 1, Start: i, Len: 1},
Line: 1,
input: data,
}
Expand Down Expand Up @@ -93,7 +93,7 @@ func parse(data string) (p *parser, err error) {
func (p *parser) panicErr(it item, err error) {
panic(ParseError{
err: err,
Position: it.pos,
Position: it.pos.withCol(p.lx.input),
Line: it.pos.Len,
LastKey: p.current(),
})
Expand All @@ -102,7 +102,7 @@ func (p *parser) panicErr(it item, err error) {
func (p *parser) panicItemf(it item, format string, v ...any) {
panic(ParseError{
Message: fmt.Sprintf(format, v...),
Position: it.pos,
Position: it.pos.withCol(p.lx.input),
Line: it.pos.Len,
LastKey: p.current(),
})
Expand All @@ -111,7 +111,7 @@ func (p *parser) panicItemf(it item, format string, v ...any) {
func (p *parser) panicf(format string, v ...any) {
panic(ParseError{
Message: fmt.Sprintf(format, v...),
Position: p.pos,
Position: p.pos.withCol(p.lx.input),
Line: p.pos.Line,
LastKey: p.current(),
})
Expand All @@ -123,7 +123,7 @@ func (p *parser) next() item {
if it.typ == itemError {
if it.err != nil {
panic(ParseError{
Position: it.pos,
Position: it.pos.withCol(p.lx.input),
Line: it.pos.Line,
LastKey: p.current(),
err: it.err,
Expand Down

0 comments on commit 3cb6f88

Please sign in to comment.