github.com/markusbkk/elvish@v0.0.0-20231204143114-91dc52438621/pkg/parse/error.go (about) 1 package parse 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/markusbkk/elvish/pkg/diag" 8 ) 9 10 const parseErrorType = "parse error" 11 12 // Error stores multiple underlying parse errors, and can pretty print them. 13 type Error struct { 14 Entries []*diag.Error 15 } 16 17 var _ diag.Shower = &Error{} 18 19 // GetError returns an *Error if the given error has dynamic type *Error, i.e. 20 // is returned by one of the Parse functions. Otherwise it returns nil. 21 func GetError(e error) *Error { 22 if er, ok := e.(*Error); ok { 23 return er 24 } 25 return nil 26 } 27 28 func (er *Error) add(msg string, ctx *diag.Context) { 29 err := &diag.Error{Type: parseErrorType, Message: msg, Context: *ctx} 30 er.Entries = append(er.Entries, err) 31 } 32 33 // Error returns a string representation of the error. 34 func (er *Error) Error() string { 35 switch len(er.Entries) { 36 case 0: 37 return "no parse error" 38 case 1: 39 return er.Entries[0].Error() 40 default: 41 sb := new(strings.Builder) 42 // Contexts of parse error entries all have the same name 43 fmt.Fprintf(sb, "multiple parse errors in %s: ", er.Entries[0].Context.Name) 44 for i, e := range er.Entries { 45 if i > 0 { 46 fmt.Fprint(sb, "; ") 47 } 48 fmt.Fprintf(sb, "%d-%d: %s", e.Context.From, e.Context.To, e.Message) 49 } 50 return sb.String() 51 } 52 } 53 54 // Show shows the error. 55 func (er *Error) Show(indent string) string { 56 switch len(er.Entries) { 57 case 0: 58 return "no parse error" 59 case 1: 60 return er.Entries[0].Show(indent) 61 default: 62 sb := new(strings.Builder) 63 fmt.Fprint(sb, "Multiple parse errors:") 64 for _, e := range er.Entries { 65 sb.WriteString("\n" + indent + " ") 66 fmt.Fprintf(sb, "\033[31;1m%s\033[m\n", e.Message) 67 sb.WriteString(indent + " ") 68 sb.WriteString(e.Context.Show(indent + " ")) 69 } 70 return sb.String() 71 } 72 }