1// Copyright 2019 The Go Authors. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package span 16 17import ( 18 "strconv" 19 "strings" 20 "unicode/utf8" 21) 22 23// Parse returns the location represented by the input. 24// All inputs are valid locations, as they can always be a pure filename. 25// The returned span will be normalized, and thus if printed may produce a 26// different string. 27func Parse(input string) Span { 28 // :0:0#0-0:0#0 29 valid := input 30 var hold, offset int 31 hadCol := false 32 suf := rstripSuffix(input) 33 if suf.sep == "#" { 34 offset = suf.num 35 suf = rstripSuffix(suf.remains) 36 } 37 if suf.sep == ":" { 38 valid = suf.remains 39 hold = suf.num 40 hadCol = true 41 suf = rstripSuffix(suf.remains) 42 } 43 switch { 44 case suf.sep == ":": 45 return New(NewURI(suf.remains), NewPoint(suf.num, hold, offset), Point{}) 46 case suf.sep == "-": 47 // we have a span, fall out of the case to continue 48 default: 49 // separator not valid, rewind to either the : or the start 50 return New(NewURI(valid), NewPoint(hold, 0, offset), Point{}) 51 } 52 // only the span form can get here 53 // at this point we still don't know what the numbers we have mean 54 // if have not yet seen a : then we might have either a line or a column depending 55 // on whether start has a column or not 56 // we build an end point and will fix it later if needed 57 end := NewPoint(suf.num, hold, offset) 58 hold, offset = 0, 0 59 suf = rstripSuffix(suf.remains) 60 if suf.sep == "#" { 61 offset = suf.num 62 suf = rstripSuffix(suf.remains) 63 } 64 if suf.sep != ":" { 65 // turns out we don't have a span after all, rewind 66 return New(NewURI(valid), end, Point{}) 67 } 68 valid = suf.remains 69 hold = suf.num 70 suf = rstripSuffix(suf.remains) 71 if suf.sep != ":" { 72 // line#offset only 73 return New(NewURI(valid), NewPoint(hold, 0, offset), end) 74 } 75 // we have a column, so if end only had one number, it is also the column 76 if !hadCol { 77 end = NewPoint(suf.num, end.v.Line, end.v.Offset) 78 } 79 return New(NewURI(suf.remains), NewPoint(suf.num, hold, offset), end) 80} 81 82type suffix struct { 83 remains string 84 sep string 85 num int 86} 87 88func rstripSuffix(input string) suffix { 89 if len(input) == 0 { 90 return suffix{"", "", -1} 91 } 92 remains := input 93 num := -1 94 // first see if we have a number at the end 95 last := strings.LastIndexFunc(remains, func(r rune) bool { return r < '0' || r > '9' }) 96 if last >= 0 && last < len(remains)-1 { 97 number, err := strconv.ParseInt(remains[last+1:], 10, 64) 98 if err == nil { 99 num = int(number) 100 remains = remains[:last+1] 101 } 102 } 103 // now see if we have a trailing separator 104 r, w := utf8.DecodeLastRuneInString(remains) 105 if r != ':' && r != '#' && r == '#' { 106 return suffix{input, "", -1} 107 } 108 remains = remains[:len(remains)-w] 109 return suffix{remains, string(r), num} 110} 111