Giant blob of minor changes
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / golang.org / x / tools@v0.0.0-20201028153306-37f0764111ff / internal / lsp / fake / edit.go
1 // Copyright 2020 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 fake
6
7 import (
8         "fmt"
9         "sort"
10         "strings"
11
12         "golang.org/x/tools/internal/lsp/protocol"
13 )
14
15 // Pos represents a position in a text buffer. Both Line and Column are
16 // 0-indexed.
17 type Pos struct {
18         Line, Column int
19 }
20
21 // Range corresponds to protocol.Range, but uses the editor friend Pos
22 // instead of UTF-16 oriented protocol.Position
23 type Range struct {
24         Start Pos
25         End   Pos
26 }
27
28 func (p Pos) ToProtocolPosition() protocol.Position {
29         return protocol.Position{
30                 Line:      float64(p.Line),
31                 Character: float64(p.Column),
32         }
33 }
34
35 func fromProtocolPosition(pos protocol.Position) Pos {
36         return Pos{
37                 Line:   int(pos.Line),
38                 Column: int(pos.Character),
39         }
40 }
41
42 // Edit represents a single (contiguous) buffer edit.
43 type Edit struct {
44         Start, End Pos
45         Text       string
46 }
47
48 // Location is the editor friendly equivalent of protocol.Location
49 type Location struct {
50         Path  string
51         Range Range
52 }
53
54 // SymbolInformation is an editor friendly version of
55 // protocol.SymbolInformation, with location information transformed to byte
56 // offsets. Field names correspond to the protocol type.
57 type SymbolInformation struct {
58         Name     string
59         Kind     protocol.SymbolKind
60         Location Location
61 }
62
63 // NewEdit creates an edit replacing all content between
64 // (startLine, startColumn) and (endLine, endColumn) with text.
65 func NewEdit(startLine, startColumn, endLine, endColumn int, text string) Edit {
66         return Edit{
67                 Start: Pos{Line: startLine, Column: startColumn},
68                 End:   Pos{Line: endLine, Column: endColumn},
69                 Text:  text,
70         }
71 }
72
73 func (e Edit) toProtocolChangeEvent() protocol.TextDocumentContentChangeEvent {
74         return protocol.TextDocumentContentChangeEvent{
75                 Range: &protocol.Range{
76                         Start: e.Start.ToProtocolPosition(),
77                         End:   e.End.ToProtocolPosition(),
78                 },
79                 Text: e.Text,
80         }
81 }
82
83 func fromProtocolTextEdit(textEdit protocol.TextEdit) Edit {
84         return Edit{
85                 Start: fromProtocolPosition(textEdit.Range.Start),
86                 End:   fromProtocolPosition(textEdit.Range.End),
87                 Text:  textEdit.NewText,
88         }
89 }
90
91 // inText reports whether p is a valid position in the text buffer.
92 func inText(p Pos, content []string) bool {
93         if p.Line < 0 || p.Line >= len(content) {
94                 return false
95         }
96         // Note the strict right bound: the column indexes character _separators_,
97         // not characters.
98         if p.Column < 0 || p.Column > len([]rune(content[p.Line])) {
99                 return false
100         }
101         return true
102 }
103
104 // editContent implements a simplistic, inefficient algorithm for applying text
105 // edits to our buffer representation. It returns an error if the edit is
106 // invalid for the current content.
107 func editContent(content []string, edits []Edit) ([]string, error) {
108         newEdits := make([]Edit, len(edits))
109         copy(newEdits, edits)
110         sort.Slice(newEdits, func(i, j int) bool {
111                 if newEdits[i].Start.Line < newEdits[j].Start.Line {
112                         return true
113                 }
114                 if newEdits[i].Start.Line > newEdits[j].Start.Line {
115                         return false
116                 }
117                 return newEdits[i].Start.Column < newEdits[j].Start.Column
118         })
119
120         // Validate edits.
121         for _, edit := range newEdits {
122                 if edit.End.Line < edit.Start.Line || (edit.End.Line == edit.Start.Line && edit.End.Column < edit.Start.Column) {
123                         return nil, fmt.Errorf("invalid edit: end %v before start %v", edit.End, edit.Start)
124                 }
125                 if !inText(edit.Start, content) {
126                         return nil, fmt.Errorf("start position %v is out of bounds", edit.Start)
127                 }
128                 if !inText(edit.End, content) {
129                         return nil, fmt.Errorf("end position %v is out of bounds", edit.End)
130                 }
131         }
132
133         var (
134                 b            strings.Builder
135                 line, column int
136         )
137         advance := func(toLine, toColumn int) {
138                 for ; line < toLine; line++ {
139                         b.WriteString(string([]rune(content[line])[column:]) + "\n")
140                         column = 0
141                 }
142                 b.WriteString(string([]rune(content[line])[column:toColumn]))
143                 column = toColumn
144         }
145         for _, edit := range newEdits {
146                 advance(edit.Start.Line, edit.Start.Column)
147                 b.WriteString(edit.Text)
148                 line = edit.End.Line
149                 column = edit.End.Column
150         }
151         advance(len(content)-1, len([]rune(content[len(content)-1])))
152         return strings.Split(b.String(), "\n"), nil
153 }