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