Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201105173854-bc9fc8d8c4bc / internal / span / token.go
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         "fmt"
9         "go/token"
10 )
11
12 // Range represents a source code range in token.Pos form.
13 // It also carries the FileSet that produced the positions, so that it is
14 // self contained.
15 type Range struct {
16         FileSet   *token.FileSet
17         Start     token.Pos
18         End       token.Pos
19         Converter Converter
20 }
21
22 // TokenConverter is a Converter backed by a token file set and file.
23 // It uses the file set methods to work out the conversions, which
24 // makes it fast and does not require the file contents.
25 type TokenConverter struct {
26         fset *token.FileSet
27         file *token.File
28 }
29
30 // NewRange creates a new Range from a FileSet and two positions.
31 // To represent a point pass a 0 as the end pos.
32 func NewRange(fset *token.FileSet, start, end token.Pos) Range {
33         return Range{
34                 FileSet: fset,
35                 Start:   start,
36                 End:     end,
37         }
38 }
39
40 // NewTokenConverter returns an implementation of Converter backed by a
41 // token.File.
42 func NewTokenConverter(fset *token.FileSet, f *token.File) *TokenConverter {
43         return &TokenConverter{fset: fset, file: f}
44 }
45
46 // NewContentConverter returns an implementation of Converter for the
47 // given file content.
48 func NewContentConverter(filename string, content []byte) *TokenConverter {
49         fset := token.NewFileSet()
50         f := fset.AddFile(filename, -1, len(content))
51         f.SetLinesForContent(content)
52         return &TokenConverter{fset: fset, file: f}
53 }
54
55 // IsPoint returns true if the range represents a single point.
56 func (r Range) IsPoint() bool {
57         return r.Start == r.End
58 }
59
60 // Span converts a Range to a Span that represents the Range.
61 // It will fill in all the members of the Span, calculating the line and column
62 // information.
63 func (r Range) Span() (Span, error) {
64         if !r.Start.IsValid() {
65                 return Span{}, fmt.Errorf("start pos is not valid")
66         }
67         f := r.FileSet.File(r.Start)
68         if f == nil {
69                 return Span{}, fmt.Errorf("file not found in FileSet")
70         }
71         var s Span
72         var err error
73         var startFilename string
74         startFilename, s.v.Start.Line, s.v.Start.Column, err = position(f, r.Start)
75         if err != nil {
76                 return Span{}, err
77         }
78         s.v.URI = URIFromPath(startFilename)
79         if r.End.IsValid() {
80                 var endFilename string
81                 endFilename, s.v.End.Line, s.v.End.Column, err = position(f, r.End)
82                 if err != nil {
83                         return Span{}, err
84                 }
85                 // In the presence of line directives, a single File can have sections from
86                 // multiple file names.
87                 if endFilename != startFilename {
88                         return Span{}, fmt.Errorf("span begins in file %q but ends in %q", startFilename, endFilename)
89                 }
90         }
91         s.v.Start.clean()
92         s.v.End.clean()
93         s.v.clean()
94         if r.Converter != nil {
95                 return s.WithOffset(r.Converter)
96         }
97         if startFilename != f.Name() {
98                 return Span{}, fmt.Errorf("must supply Converter for file %q containing lines from %q", f.Name(), startFilename)
99         }
100         return s.WithOffset(NewTokenConverter(r.FileSet, f))
101 }
102
103 func position(f *token.File, pos token.Pos) (string, int, int, error) {
104         off, err := offset(f, pos)
105         if err != nil {
106                 return "", 0, 0, err
107         }
108         return positionFromOffset(f, off)
109 }
110
111 func positionFromOffset(f *token.File, offset int) (string, int, int, error) {
112         if offset > f.Size() {
113                 return "", 0, 0, fmt.Errorf("offset %v is past the end of the file %v", offset, f.Size())
114         }
115         pos := f.Pos(offset)
116         p := f.Position(pos)
117         // TODO(golang/go#41029): Consider returning line, column instead of line+1, 1 if
118         // the file's last character is not a newline.
119         if offset == f.Size() {
120                 return p.Filename, p.Line + 1, 1, nil
121         }
122         return p.Filename, p.Line, p.Column, nil
123 }
124
125 // offset is a copy of the Offset function in go/token, but with the adjustment
126 // that it does not panic on invalid positions.
127 func offset(f *token.File, pos token.Pos) (int, error) {
128         if int(pos) < f.Base() || int(pos) > f.Base()+f.Size() {
129                 return 0, fmt.Errorf("invalid pos")
130         }
131         return int(pos) - f.Base(), nil
132 }
133
134 // Range converts a Span to a Range that represents the Span for the supplied
135 // File.
136 func (s Span) Range(converter *TokenConverter) (Range, error) {
137         s, err := s.WithOffset(converter)
138         if err != nil {
139                 return Range{}, err
140         }
141         // go/token will panic if the offset is larger than the file's size,
142         // so check here to avoid panicking.
143         if s.Start().Offset() > converter.file.Size() {
144                 return Range{}, fmt.Errorf("start offset %v is past the end of the file %v", s.Start(), converter.file.Size())
145         }
146         if s.End().Offset() > converter.file.Size() {
147                 return Range{}, fmt.Errorf("end offset %v is past the end of the file %v", s.End(), converter.file.Size())
148         }
149         return Range{
150                 FileSet:   converter.fset,
151                 Start:     converter.file.Pos(s.Start().Offset()),
152                 End:       converter.file.Pos(s.End().Offset()),
153                 Converter: converter,
154         }, nil
155 }
156
157 func (l *TokenConverter) ToPosition(offset int) (int, int, error) {
158         _, line, col, err := positionFromOffset(l.file, offset)
159         return line, col, err
160 }
161
162 func (l *TokenConverter) ToOffset(line, col int) (int, error) {
163         if line < 0 {
164                 return -1, fmt.Errorf("line is not valid")
165         }
166         lineMax := l.file.LineCount() + 1
167         if line > lineMax {
168                 return -1, fmt.Errorf("line is beyond end of file %v", lineMax)
169         } else if line == lineMax {
170                 if col > 1 {
171                         return -1, fmt.Errorf("column is beyond end of file")
172                 }
173                 // at the end of the file, allowing for a trailing eol
174                 return l.file.Size(), nil
175         }
176         pos := lineStart(l.file, line)
177         if !pos.IsValid() {
178                 return -1, fmt.Errorf("line is not in file")
179         }
180         // we assume that column is in bytes here, and that the first byte of a
181         // line is at column 1
182         pos += token.Pos(col - 1)
183         return offset(l.file, pos)
184 }