diff options
Diffstat (limited to 'common/herrors')
-rw-r--r-- | common/herrors/error_locator.go | 10 | ||||
-rw-r--r-- | common/herrors/file_error.go | 78 | ||||
-rw-r--r-- | common/herrors/file_error_test.go | 4 |
3 files changed, 86 insertions, 6 deletions
diff --git a/common/herrors/error_locator.go b/common/herrors/error_locator.go index 0f22f545f..18c21e51b 100644 --- a/common/herrors/error_locator.go +++ b/common/herrors/error_locator.go @@ -52,6 +52,16 @@ var NopLineMatcher = func(m LineMatcher) int { return 1 } +// OffsetMatcher is a line matcher that matches by offset. +var OffsetMatcher = func(m LineMatcher) int { + if m.Offset+len(m.Line) >= m.Position.Offset { + // We found the line, but return 0 to signal that we want to determine + // the column from the error. + return 0 + } + return -1 +} + // ErrorContext contains contextual information about an error. This will // typically be the lines surrounding some problem in a file. type ErrorContext struct { diff --git a/common/herrors/file_error.go b/common/herrors/file_error.go index 85007a057..e6baaf6e3 100644 --- a/common/herrors/file_error.go +++ b/common/herrors/file_error.go @@ -19,6 +19,8 @@ import ( "io" "path/filepath" + "github.com/bep/godartsass" + "github.com/bep/golibsass/libsass/libsasserrors" "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/common/text" "github.com/pelletier/go-toml/v2" @@ -132,7 +134,22 @@ func (e fileError) Position() text.Position { } func (e *fileError) Error() string { - return fmt.Sprintf("%s: %s", e.position, e.cause) + return fmt.Sprintf("%s: %s", e.position, e.causeString()) +} + +func (e *fileError) causeString() string { + if e.cause == nil { + return "" + } + switch v := e.cause.(type) { + // Avoid repeating the file info in the error message. + case godartsass.SassError: + return v.Message + case libsasserrors.Error: + return v.Message + default: + return v.Error() + } } func (e *fileError) Unwrap() error { @@ -140,9 +157,17 @@ func (e *fileError) Unwrap() error { } // NewFileError creates a new FileError that wraps err. +// It will try to extract the filename and line number from err. +func NewFileError(err error) FileError { + // Filetype is used to determine the Chroma lexer to use. + fileType, pos := extractFileTypePos(err) + return &fileError{cause: err, fileType: fileType, position: pos} +} + +// NewFileErrorFromName creates a new FileError that wraps err. // The value for name should identify the file, the best // being the full filename to the file on disk. -func NewFileError(err error, name string) FileError { +func NewFileErrorFromName(err error, name string) FileError { // Filetype is used to determine the Chroma lexer to use. fileType, pos := extractFileTypePos(err) pos.Filename = name @@ -165,6 +190,23 @@ func NewFileErrorFromPos(err error, pos text.Position) FileError { } +func NewFileErrorFromFileInErr(err error, fs afero.Fs, linematcher LineMatcherFn) FileError { + fe := NewFileError(err) + pos := fe.Position() + if pos.Filename == "" { + return fe + } + + f, realFilename, err2 := openFile(pos.Filename, fs) + if err2 != nil { + return fe + } + + pos.Filename = realFilename + defer f.Close() + return fe.UpdateContent(f, linematcher) +} + func NewFileErrorFromFileInPos(err error, pos text.Position, fs afero.Fs, linematcher LineMatcherFn) FileError { if err == nil { panic("err is nil") @@ -185,10 +227,10 @@ func NewFileErrorFromFile(err error, filename string, fs afero.Fs, linematcher L } f, realFilename, err2 := openFile(filename, fs) if err2 != nil { - return NewFileError(err, realFilename) + return NewFileErrorFromName(err, realFilename) } defer f.Close() - return NewFileError(err, realFilename).UpdateContent(f, linematcher) + return NewFileErrorFromName(err, realFilename).UpdateContent(f, linematcher) } func openFile(filename string, fs afero.Fs) (afero.File, string, error) { @@ -223,8 +265,15 @@ func Cause(err error) error { func extractFileTypePos(err error) (string, text.Position) { err = Cause(err) + var fileType string + // LibSass, DartSass + if pos := extractPosition(err); pos.LineNumber > 0 || pos.Offset > 0 { + _, fileType = paths.FileAndExtNoDelimiter(pos.Filename) + return fileType, pos + } + // Default to line 1 col 1 if we don't find any better. pos := text.Position{ Offset: -1, @@ -259,6 +308,10 @@ func extractFileTypePos(err error) (string, text.Position) { } } + if fileType == "" && pos.Filename != "" { + _, fileType = paths.FileAndExtNoDelimiter(pos.Filename) + } + return fileType, pos } @@ -322,3 +375,20 @@ func exctractLineNumberAndColumnNumber(e error) (int, int) { return -1, -1 } + +func extractPosition(e error) (pos text.Position) { + switch v := e.(type) { + case godartsass.SassError: + span := v.Span + start := span.Start + filename, _ := paths.UrlToFilename(span.Url) + pos.Filename = filename + pos.Offset = start.Offset + pos.ColumnNumber = start.Column + case libsasserrors.Error: + pos.Filename = v.File + pos.LineNumber = v.Line + pos.ColumnNumber = v.Column + } + return +} diff --git a/common/herrors/file_error_test.go b/common/herrors/file_error_test.go index 04baadf16..0b260a255 100644 --- a/common/herrors/file_error_test.go +++ b/common/herrors/file_error_test.go @@ -30,7 +30,7 @@ func TestNewFileError(t *testing.T) { c := qt.New(t) - fe := NewFileError(errors.New("bar"), "foo.html") + fe := NewFileErrorFromName(errors.New("bar"), "foo.html") c.Assert(fe.Error(), qt.Equals, `"foo.html:1:1": bar`) lines := "" @@ -70,7 +70,7 @@ func TestNewFileErrorExtractFromMessage(t *testing.T) { {errors.New(`execute of template failed: template: index.html:2:5: executing "index.html" at <partial "foo.html" .>: error calling partial: "/layouts/partials/foo.html:3:6": execute of template failed: template: partials/foo.html:3:6: executing "partials/foo.html" at <.ThisDoesNotExist>: can't evaluate field ThisDoesNotExist in type *hugolib.pageStat`), 0, 2, 5}, } { - got := NewFileError(test.in, "test.txt") + got := NewFileErrorFromName(test.in, "test.txt") errMsg := qt.Commentf("[%d][%T]", i, got) |