Skip to content

Commit

Permalink
Correct error position with tab indentation
Browse files Browse the repository at this point in the history
Previously the ^^^s would be misplaced, e.g.:

      2 |               k1 = "asd"
      3 |               k2 = "ok"
      4 |               k3 = "invalid"
                  ^^^^^^^
  • Loading branch information
arp242 committed Oct 1, 2023
1 parent b6fe702 commit 1905bd7
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 4 deletions.
44 changes: 40 additions & 4 deletions error.go
Expand Up @@ -114,13 +114,22 @@ func (pe ParseError) ErrorWithPosition() string {
msg, pe.Position.Line, col, col+pe.Position.Len)
}
if pe.Position.Line > 2 {
fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-2, lines[pe.Position.Line-3])
fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-2, expandTab(lines[pe.Position.Line-3]))
}
if pe.Position.Line > 1 {
fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-1, lines[pe.Position.Line-2])
fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-1, expandTab(lines[pe.Position.Line-2]))
}
fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line, lines[pe.Position.Line-1])
fmt.Fprintf(b, "% 10s%s%s\n", "", strings.Repeat(" ", col), strings.Repeat("^", pe.Position.Len))

/// Expand tabs, so that the ^^^s are at the correct position, but leave
/// "column 10-13" intact. Adjusting this to the visual column would be
/// better, but we don't know the tabsize of the user in their editor, which
/// can be 8, 4, 2, or something else. We can't know. So leaving it as the
/// character index is probably the "most correct".
expanded := expandTab(lines[pe.Position.Line-1])
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))
return b.String()
}

Expand Down Expand Up @@ -159,6 +168,33 @@ func (pe ParseError) column(lines []string) int {
return col
}

func expandTab(s string) string {
var (
b strings.Builder
l int
fill = func(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = ' '
}
return string(b)
}
)
b.Grow(len(s))
for _, r := range s {
switch r {
case '\t':
tw := 8 - l%8
b.WriteString(fill(tw))
l += tw
default:
b.WriteRune(r)
l += 1
}
}
return b.String()
}

type (
errLexControl struct{ r rune }
errLexEscape struct{ r rune }
Expand Down
53 changes: 53 additions & 0 deletions error_test.go
Expand Up @@ -351,3 +351,56 @@ At line 3, column 6-13:
t.Errorf("\nwant:\n%s\nhave:\n%s", want, have)
}
}

func TestErrorIndent(t *testing.T) {
getErr := func(t *testing.T, tml string) toml.ParseError {
var m map[string]any
_, err := toml.Decode(tml, &m)
if err == nil {
t.Fatal(err)
}
var pErr toml.ParseError
if !errors.As(err, &pErr) {
t.Fatalf("not a ParseError: %#v", err)
}
return pErr
}

err := getErr(t, "\tspaces = xxx")
want := `toml: error: expected value but found "xxx" instead
At line 1, column 10-13:
1 | spaces = xxx
^^^
`

if have := err.ErrorWithUsage(); have != want {
t.Errorf("\nwant:\n%s\nhave:\n%s", want, have)
}

err = getErr(t, "\tspaces\t=\txxx")
want = `toml: error: expected value but found "xxx" instead
At line 1, column 10-13:
1 | spaces = xxx
^^^
`
if have := err.ErrorWithUsage(); have != want {
t.Errorf("\nwant:\n%s\nhave:\n%s", want, have)
}

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:
1 | xxx = 1
2 | spaces = xxx
^^^
`
if have := err.ErrorWithUsage(); have != want {
t.Errorf("\nwant:\n%s\nhave:\n%s", want, have)
}
}

0 comments on commit 1905bd7

Please sign in to comment.