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