| // Copyright 2019 The Go Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| package span | |
| import ( | |
| "strconv" | |
| "strings" | |
| "unicode/utf8" | |
| ) | |
| // Parse returns the location represented by the input. | |
| // All inputs are valid locations, as they can always be a pure filename. | |
| // The returned span will be normalized, and thus if printed may produce a | |
| // different string. | |
| func Parse(input string) Span { | |
| // :0:0#0-0:0#0 | |
| valid := input | |
| var hold, offset int | |
| hadCol := false | |
| suf := rstripSuffix(input) | |
| if suf.sep == "#" { | |
| offset = suf.num | |
| suf = rstripSuffix(suf.remains) | |
| } | |
| if suf.sep == ":" { | |
| valid = suf.remains | |
| hold = suf.num | |
| hadCol = true | |
| suf = rstripSuffix(suf.remains) | |
| } | |
| switch { | |
| case suf.sep == ":": | |
| return New(NewURI(suf.remains), NewPoint(suf.num, hold, offset), Point{}) | |
| case suf.sep == "-": | |
| // we have a span, fall out of the case to continue | |
| default: | |
| // separator not valid, rewind to either the : or the start | |
| return New(NewURI(valid), NewPoint(hold, 0, offset), Point{}) | |
| } | |
| // only the span form can get here | |
| // at this point we still don't know what the numbers we have mean | |
| // if have not yet seen a : then we might have either a line or a column depending | |
| // on whether start has a column or not | |
| // we build an end point and will fix it later if needed | |
| end := NewPoint(suf.num, hold, offset) | |
| hold, offset = 0, 0 | |
| suf = rstripSuffix(suf.remains) | |
| if suf.sep == "#" { | |
| offset = suf.num | |
| suf = rstripSuffix(suf.remains) | |
| } | |
| if suf.sep != ":" { | |
| // turns out we don't have a span after all, rewind | |
| return New(NewURI(valid), end, Point{}) | |
| } | |
| valid = suf.remains | |
| hold = suf.num | |
| suf = rstripSuffix(suf.remains) | |
| if suf.sep != ":" { | |
| // line#offset only | |
| return New(NewURI(valid), NewPoint(hold, 0, offset), end) | |
| } | |
| // we have a column, so if end only had one number, it is also the column | |
| if !hadCol { | |
| end = NewPoint(suf.num, end.v.Line, end.v.Offset) | |
| } | |
| return New(NewURI(suf.remains), NewPoint(suf.num, hold, offset), end) | |
| } | |
| type suffix struct { | |
| remains string | |
| sep string | |
| num int | |
| } | |
| func rstripSuffix(input string) suffix { | |
| if len(input) == 0 { | |
| return suffix{"", "", -1} | |
| } | |
| remains := input | |
| num := -1 | |
| // first see if we have a number at the end | |
| last := strings.LastIndexFunc(remains, func(r rune) bool { return r < '0' || r > '9' }) | |
| if last >= 0 && last < len(remains)-1 { | |
| number, err := strconv.ParseInt(remains[last+1:], 10, 64) | |
| if err == nil { | |
| num = int(number) | |
| remains = remains[:last+1] | |
| } | |
| } | |
| // now see if we have a trailing separator | |
| r, w := utf8.DecodeLastRuneInString(remains) | |
| if r != ':' && r != '#' && r == '#' { | |
| return suffix{input, "", -1} | |
| } | |
| remains = remains[:len(remains)-w] | |
| return suffix{remains, string(r), num} | |
| } |