cuelang.org/go@v0.10.1/internal/golangorgx/gopls/cmd/parsespan.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 cmd 6 7 import ( 8 "strconv" 9 "strings" 10 "unicode/utf8" 11 12 "cuelang.org/go/internal/golangorgx/gopls/protocol" 13 ) 14 15 // parseSpan returns the location represented by the input. 16 // Only file paths are accepted, not URIs. 17 // The returned span will be normalized, and thus if printed may produce a 18 // different string. 19 func parseSpan(input string) span { 20 uri := protocol.URIFromPath 21 22 // :0:0#0-0:0#0 23 valid := input 24 var hold, offset int 25 hadCol := false 26 suf := rstripSuffix(input) 27 if suf.sep == "#" { 28 offset = suf.num 29 suf = rstripSuffix(suf.remains) 30 } 31 if suf.sep == ":" { 32 valid = suf.remains 33 hold = suf.num 34 hadCol = true 35 suf = rstripSuffix(suf.remains) 36 } 37 switch { 38 case suf.sep == ":": 39 return newSpan(uri(suf.remains), newPoint(suf.num, hold, offset), point{}) 40 case suf.sep == "-": 41 // we have a span, fall out of the case to continue 42 default: 43 // separator not valid, rewind to either the : or the start 44 return newSpan(uri(valid), newPoint(hold, 0, offset), point{}) 45 } 46 // only the span form can get here 47 // at this point we still don't know what the numbers we have mean 48 // if have not yet seen a : then we might have either a line or a column depending 49 // on whether start has a column or not 50 // we build an end point and will fix it later if needed 51 end := newPoint(suf.num, hold, offset) 52 hold, offset = 0, 0 53 suf = rstripSuffix(suf.remains) 54 if suf.sep == "#" { 55 offset = suf.num 56 suf = rstripSuffix(suf.remains) 57 } 58 if suf.sep != ":" { 59 // turns out we don't have a span after all, rewind 60 return newSpan(uri(valid), end, point{}) 61 } 62 valid = suf.remains 63 hold = suf.num 64 suf = rstripSuffix(suf.remains) 65 if suf.sep != ":" { 66 // line#offset only 67 return newSpan(uri(valid), newPoint(hold, 0, offset), end) 68 } 69 // we have a column, so if end only had one number, it is also the column 70 if !hadCol { 71 end = newPoint(suf.num, end.v.Line, end.v.Offset) 72 } 73 return newSpan(uri(suf.remains), newPoint(suf.num, hold, offset), end) 74 } 75 76 type suffix struct { 77 remains string 78 sep string 79 num int 80 } 81 82 func rstripSuffix(input string) suffix { 83 if len(input) == 0 { 84 return suffix{"", "", -1} 85 } 86 remains := input 87 88 // Remove optional trailing decimal number. 89 num := -1 90 last := strings.LastIndexFunc(remains, func(r rune) bool { return r < '0' || r > '9' }) 91 if last >= 0 && last < len(remains)-1 { 92 number, err := strconv.ParseInt(remains[last+1:], 10, 64) 93 if err == nil { 94 num = int(number) 95 remains = remains[:last+1] 96 } 97 } 98 // now see if we have a trailing separator 99 r, w := utf8.DecodeLastRuneInString(remains) 100 // TODO(adonovan): this condition is clearly wrong. Should the third byte be '-'? 101 if r != ':' && r != '#' && r == '#' { 102 return suffix{input, "", -1} 103 } 104 remains = remains[:len(remains)-w] 105 return suffix{remains, string(r), num} 106 }