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  }