gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

position.go (2069B)


      1 package parse
      2 
      3 import (
      4 	"fmt"
      5 	"io"
      6 	"strings"
      7 	"unicode"
      8 )
      9 
     10 // Position returns the line and column number for a certain position in a file. It is useful for recovering the position in a file that caused an error.
     11 // It only treates \n, \r, and \r\n as newlines, which might be different from some languages also recognizing \f, \u2028, and \u2029 to be newlines.
     12 func Position(r io.Reader, offset int) (line, col int, context string) {
     13 	l := NewInput(r)
     14 	line = 1
     15 	for l.Pos() < offset {
     16 		c := l.Peek(0)
     17 		n := 1
     18 		newline := false
     19 		if c == '\n' {
     20 			newline = true
     21 		} else if c == '\r' {
     22 			if l.Peek(1) == '\n' {
     23 				newline = true
     24 				n = 2
     25 			} else {
     26 				newline = true
     27 			}
     28 		} else if c >= 0xC0 {
     29 			var r rune
     30 			if r, n = l.PeekRune(0); r == '\u2028' || r == '\u2029' {
     31 				newline = true
     32 			}
     33 		} else if c == 0 && l.Err() != nil {
     34 			break
     35 		}
     36 
     37 		if 1 < n && offset < l.Pos()+n {
     38 			break
     39 		}
     40 		l.Move(n)
     41 
     42 		if newline {
     43 			line++
     44 			offset -= l.Pos()
     45 			l.Skip()
     46 		}
     47 	}
     48 
     49 	col = len([]rune(string(l.Lexeme()))) + 1
     50 	context = positionContext(l, line, col)
     51 	return
     52 }
     53 
     54 func positionContext(l *Input, line, col int) (context string) {
     55 	for {
     56 		c := l.Peek(0)
     57 		if c == 0 && l.Err() != nil || c == '\n' || c == '\r' {
     58 			break
     59 		}
     60 		l.Move(1)
     61 	}
     62 	rs := []rune(string(l.Lexeme()))
     63 
     64 	// cut off front or rear of context to stay between 60 characters
     65 	limit := 60
     66 	offset := 20
     67 	ellipsisFront := ""
     68 	ellipsisRear := ""
     69 	if limit < len(rs) {
     70 		if col <= limit-offset {
     71 			ellipsisRear = "..."
     72 			rs = rs[:limit-3]
     73 		} else if col >= len(rs)-offset-3 {
     74 			ellipsisFront = "..."
     75 			col -= len(rs) - offset - offset - 7
     76 			rs = rs[len(rs)-offset-offset-4:]
     77 		} else {
     78 			ellipsisFront = "..."
     79 			ellipsisRear = "..."
     80 			rs = rs[col-offset-1 : col+offset]
     81 			col = offset + 4
     82 		}
     83 	}
     84 
     85 	// replace unprintable characters by a space
     86 	for i, r := range rs {
     87 		if !unicode.IsGraphic(r) {
     88 			rs[i] = 'ยท'
     89 		}
     90 	}
     91 
     92 	context += fmt.Sprintf("%5d: %s%s%s\n", line, ellipsisFront, string(rs), ellipsisRear)
     93 	context += fmt.Sprintf("%s^", strings.Repeat(" ", 6+col))
     94 	return
     95 }