github.com/ladydascalie/elvish@v0.0.0-20170703214355-2964dd3ece7f/util/source_context.go (about) 1 package util 2 3 import ( 4 "fmt" 5 "io" 6 "strings" 7 ) 8 9 type SourceContext struct { 10 Name string 11 Source string 12 Begin int 13 End int 14 Next *SourceContext 15 } 16 17 var CulpritStyle = "1;4" 18 19 func (sc *SourceContext) Pprint(w io.Writer, sourceIndent string) { 20 if sc.Begin == -1 { 21 fmt.Fprintf(w, "%s, unknown position", sc.Name) 22 return 23 } else if sc.Begin < 0 || sc.End > len(sc.Source) || sc.Begin > sc.End { 24 fmt.Fprintf(w, "%s, invalid position %d-%d", sc.Name, sc.Begin, sc.End) 25 return 26 } 27 28 before, culprit, after := bca(sc.Source, sc.Begin, sc.End) 29 // Find the part of "before" that is on the same line as the culprit. 30 lineBefore := lastLine(before) 31 // Find on which line the culprit begins. 32 beginLine := strings.Count(before, "\n") + 1 33 34 // If the culprit ends with a newline, stripe it. Otherwise stick the part 35 // of "after" that is on the same line of the last line of the culprit. 36 var lineAfter string 37 if strings.HasSuffix(culprit, "\n") { 38 culprit = culprit[:len(culprit)-1] 39 } else { 40 lineAfter = firstLine(after) 41 } 42 43 // Find on which line and column the culprit ends. 44 endLine := beginLine + strings.Count(culprit, "\n") 45 46 if beginLine == endLine { 47 fmt.Fprintf(w, "%s, line %d:\n", sc.Name, beginLine) 48 } else { 49 fmt.Fprintf(w, "%s, line %d-%d:\n", sc.Name, beginLine, endLine) 50 } 51 52 fmt.Fprintf(w, "%s%s", sourceIndent, lineBefore) 53 54 if culprit == "" { 55 culprit = "^" 56 } 57 for i, line := range strings.Split(culprit, "\n") { 58 if i > 0 { 59 fmt.Fprintf(w, "\n%s", sourceIndent) 60 } 61 fmt.Fprintf(w, "\033[%sm%s\033[m", CulpritStyle, line) 62 } 63 64 fmt.Fprintf(w, "%s", lineAfter) 65 } 66 67 func bca(s string, a, b int) (string, string, string) { 68 return s[:a], s[a:b], s[b:] 69 } 70 71 func countRunes(s string) int { 72 return strings.Count(s, "") - 1 73 } 74 75 func firstLine(s string) string { 76 i := strings.IndexByte(s, '\n') 77 if i == -1 { 78 return s 79 } 80 return s[:i] 81 } 82 83 func lastLine(s string) string { 84 // When s does not contain '\n', LastIndexByte returns -1, which happens to 85 // be what we want. 86 return s[strings.LastIndexByte(s, '\n')+1:] 87 }