From 8ffa18bbe9a0e580b5ae867af02abf33b9e63f13 Mon Sep 17 00:00:00 2001 From: Jan Heuermann Date: Sat, 2 Mar 2024 11:56:34 +0100 Subject: [PATCH] Preserve file name for syntax errors Resolves https://github.com/jotaen/klog/issues/278. Screenshot 2024-03-02 at 11 56 03 --- klog/app/cli/util/prettifier.go | 11 +++++++-- klog/app/cli/util/prettifier_test.go | 10 ++++---- klog/app/context.go | 11 +++++++-- klog/app/main/cli_test.go | 3 ++- klog/parser/txt/error.go | 34 +++++++++++++--------------- 5 files changed, 41 insertions(+), 28 deletions(-) diff --git a/klog/app/cli/util/prettifier.go b/klog/app/cli/util/prettifier.go index 4962c48..81cbb69 100644 --- a/klog/app/cli/util/prettifier.go +++ b/klog/app/cli/util/prettifier.go @@ -31,9 +31,16 @@ func PrettifyParsingError(err app.ParserErrors, isDebug bool, styler terminalfor styler.Props(terminalformat.StyleProps{Background: terminalformat.RED, Color: terminalformat.RED}).Format("[")+ styler.Props(terminalformat.StyleProps{Background: terminalformat.RED, Color: terminalformat.TEXT_INVERSE}).Format("SYNTAX ERROR")+ styler.Props(terminalformat.StyleProps{Background: terminalformat.RED, Color: terminalformat.RED}).Format("]")+ - styler.Props(terminalformat.StyleProps{Color: terminalformat.RED}).Format(" in line %d: "), + styler.Props(terminalformat.StyleProps{Color: terminalformat.RED}).Format(" in line %d"), e.LineNumber(), - ) + "\n" + ) + if e.Origin() != "" { + message += fmt.Sprintf( + styler.Props(terminalformat.StyleProps{Color: terminalformat.RED}).Format(" of file %s"), + e.Origin(), + ) + } + message += "\n" message += fmt.Sprintf( styler.Props(terminalformat.StyleProps{Color: terminalformat.SUBDUED}).Format(INDENT+"%s"), // Replace all tabs with one space each, otherwise the carets might diff --git a/klog/app/cli/util/prettifier_test.go b/klog/app/cli/util/prettifier_test.go index 583da1c..d66d42a 100644 --- a/klog/app/cli/util/prettifier_test.go +++ b/klog/app/cli/util/prettifier_test.go @@ -16,16 +16,16 @@ func TestFormatParserError(t *testing.T) { block2, _ := txt.ParseBlock("Another issue!", 133) err := app.NewParserErrors([]txt.Error{ txt.NewError(block1, 1, 0, 4, "CODE", "Error", "Short explanation."), - txt.NewError(block2, 0, 8, 5, "CODE", "Problem", "More info."), + txt.NewError(block2, 0, 8, 5, "CODE", "Problem", "More info.").SetOrigin("some-file.klg"), }) text := PrettifyParsingError(err, false, styler).Error() assert.Equal(t, ` -[SYNTAX ERROR] in line 39: +[SYNTAX ERROR] in line 39 Some malformed text ^^^^ Error: Short explanation. -[SYNTAX ERROR] in line 134: +[SYNTAX ERROR] in line 134 of file some-file.klg Another issue! ^^^^^ Problem: More info. @@ -39,7 +39,7 @@ func TestReflowsLongMessages(t *testing.T) { }) text := PrettifyParsingError(err, false, styler).Error() assert.Equal(t, ` -[SYNTAX ERROR] in line 2: +[SYNTAX ERROR] in line 2 Foo bar ^^^ Some Title: A verbose description with details, potentially @@ -56,7 +56,7 @@ func TestConvertsTabToSpaces(t *testing.T) { }) text := PrettifyParsingError(err, false, styler).Error() assert.Equal(t, ` -[SYNTAX ERROR] in line 14: +[SYNTAX ERROR] in line 14 Foo bar ^^^^^^^^ Error title: Error details diff --git a/klog/app/context.go b/klog/app/context.go index 72e8c69..7d3c4eb 100644 --- a/klog/app/context.go +++ b/klog/app/context.go @@ -177,13 +177,17 @@ func (ctx *context) ReadInputs(fileArgs ...FileOrBookmarkName) ([]klog.Record, E ) } var allRecords []klog.Record + var allErrors []txt.Error for _, f := range files { records, _, errs := ctx.parser.Parse(f.Contents()) - if errs != nil { - return nil, NewParserErrors(errs) + for _, e := range errs { + allErrors = append(allErrors, e.SetOrigin(f.Path())) } allRecords = append(allRecords, records...) } + if len(allErrors) > 0 { + return nil, NewParserErrors(allErrors) + } return allRecords, nil } @@ -213,6 +217,9 @@ func (ctx *context) ReconcileFile(fileArg FileOrBookmarkName, creators []reconci return nil, err } records, blocks, errs := ctx.parser.Parse(target.Contents()) + for i, e := range errs { + errs[i] = e.SetOrigin(target.Path()) + } if errs != nil { return nil, NewParserErrors(errs) } diff --git a/klog/app/main/cli_test.go b/klog/app/main/cli_test.go index ec49c0a..eb2bcfc 100644 --- a/klog/app/main/cli_test.go +++ b/klog/app/main/cli_test.go @@ -35,7 +35,8 @@ func TestPrintAppErrors(t *testing.T) { []string{"start", "valid.klg"}, ) // Out 0 should contain pretty-printed parsing errors. - assert.True(t, strings.Contains(out[0], "[SYNTAX ERROR] in line 1:"), out) + assert.True(t, strings.Contains(out[0], "[SYNTAX ERROR] in line 1 of file"), out) + assert.True(t, strings.Contains(out[0], "invalid.klg"), out) assert.True(t, strings.Contains(out[0], "2020-01-01asdf"), out) assert.True(t, strings.Contains(out[0], "^^^^^^^^^^^^^^"), out) assert.True(t, strings.Contains(out[0], "Invalid date"), out) diff --git a/klog/parser/txt/error.go b/klog/parser/txt/error.go index 5a1eb6f..5c378c0 100644 --- a/klog/parser/txt/error.go +++ b/klog/parser/txt/error.go @@ -32,12 +32,14 @@ type Error interface { // Message is a combination of Title and Details. Message() string - // Set fills in Code, Title and Message (in this order). - Set(string, string, string) Error + // Origin returns the origin of the error, such as the file name. + Origin() string + SetOrigin(string) Error } type err struct { context Block + origin string line int position int length int @@ -46,22 +48,18 @@ type err struct { details string } -func (e *err) Error() string { return e.Message() } -func (e *err) LineNumber() int { return e.context.OverallLineIndex(e.line) + 1 } -func (e *err) LineText() string { return e.context.Lines()[e.line].Text } -func (e *err) Position() int { return e.position } -func (e *err) Column() int { return e.position + 1 } -func (e *err) Length() int { return e.length } -func (e *err) Code() string { return e.code } -func (e *err) Title() string { return e.title } -func (e *err) Details() string { return e.details } -func (e *err) Message() string { return e.title + ": " + e.details } -func (e *err) Set(code string, title string, details string) Error { - e.code = code - e.title = title - e.details = details - return e -} +func (e *err) Error() string { return e.Message() } +func (e *err) LineNumber() int { return e.context.OverallLineIndex(e.line) + 1 } +func (e *err) LineText() string { return e.context.Lines()[e.line].Text } +func (e *err) Position() int { return e.position } +func (e *err) Column() int { return e.position + 1 } +func (e *err) Length() int { return e.length } +func (e *err) Code() string { return e.code } +func (e *err) Title() string { return e.title } +func (e *err) Details() string { return e.details } +func (e *err) Message() string { return e.title + ": " + e.details } +func (e *err) Origin() string { return e.origin } +func (e *err) SetOrigin(origin string) Error { e.origin = origin; return e } func NewError(b Block, line int, start int, length int, code string, title string, details string) Error { return &err{