github.com/jd-ly/tools@v0.5.7/internal/span/parse.go (about) 1 // Copyright 2019 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package span 6 7 import ( 8 "strconv" 9 "strings" 10 "unicode/utf8" 11 ) 12 13 // Parse returns the location represented by the input. 14 // Only file paths are accepted, not URIs. 15 // The returned span will be normalized, and thus if printed may produce a 16 // different string. 17 func Parse(input string) Span { 18 // :0:0#0-0:0#0 19 valid := input 20 var hold, offset int 21 hadCol := false 22 suf := rstripSuffix(input) 23 if suf.sep == "#" { 24 offset = suf.num 25 suf = rstripSuffix(suf.remains) 26 } 27 if suf.sep == ":" { 28 valid = suf.remains 29 hold = suf.num 30 hadCol = true 31 suf = rstripSuffix(suf.remains) 32 } 33 switch { 34 case suf.sep == ":": 35 return New(URIFromPath(suf.remains), NewPoint(suf.num, hold, offset), Point{}) 36 case suf.sep == "-": 37 // we have a span, fall out of the case to continue 38 default: 39 // separator not valid, rewind to either the : or the start 40 return New(URIFromPath(valid), NewPoint(hold, 0, offset), Point{}) 41 } 42 // only the span form can get here 43 // at this point we still don't know what the numbers we have mean 44 // if have not yet seen a : then we might have either a line or a column depending 45 // on whether start has a column or not 46 // we build an end point and will fix it later if needed 47 end := NewPoint(suf.num, hold, offset) 48 hold, offset = 0, 0 49 suf = rstripSuffix(suf.remains) 50 if suf.sep == "#" { 51 offset = suf.num 52 suf = rstripSuffix(suf.remains) 53 } 54 if suf.sep != ":" { 55 // turns out we don't have a span after all, rewind 56 return New(URIFromPath(valid), end, Point{}) 57 } 58 valid = suf.remains 59 hold = suf.num 60 suf = rstripSuffix(suf.remains) 61 if suf.sep != ":" { 62 // line#offset only 63 return New(URIFromPath(valid), NewPoint(hold, 0, offset), end) 64 } 65 // we have a column, so if end only had one number, it is also the column 66 if !hadCol { 67 end = NewPoint(suf.num, end.v.Line, end.v.Offset) 68 } 69 return New(URIFromPath(suf.remains), NewPoint(suf.num, hold, offset), end) 70 } 71 72 type suffix struct { 73 remains string 74 sep string 75 num int 76 } 77 78 func rstripSuffix(input string) suffix { 79 if len(input) == 0 { 80 return suffix{"", "", -1} 81 } 82 remains := input 83 num := -1 84 // first see if we have a number at the end 85 last := strings.LastIndexFunc(remains, func(r rune) bool { return r < '0' || r > '9' }) 86 if last >= 0 && last < len(remains)-1 { 87 number, err := strconv.ParseInt(remains[last+1:], 10, 64) 88 if err == nil { 89 num = int(number) 90 remains = remains[:last+1] 91 } 92 } 93 // now see if we have a trailing separator 94 r, w := utf8.DecodeLastRuneInString(remains) 95 if r != ':' && r != '#' && r == '#' { 96 return suffix{input, "", -1} 97 } 98 remains = remains[:len(remains)-w] 99 return suffix{remains, string(r), num} 100 }