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  }