-
Notifications
You must be signed in to change notification settings - Fork 338
/
completion.go
143 lines (129 loc) · 3.98 KB
/
completion.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/*----------------------------------------------------------------
* Copyright (c) ThoughtWorks, Inc.
* Licensed under the Apache License, Version 2.0
* See LICENSE in the project root for license information.
*----------------------------------------------------------------*/
package lang
import (
"encoding/json"
"strings"
"github.com/sourcegraph/go-langserver/pkg/lsp"
"github.com/sourcegraph/jsonrpc2"
)
type insertTextFormat int
const (
text insertTextFormat = 1
snippet insertTextFormat = 2
concept = "Concept"
step = "Step"
tag = "Tag"
tagIdentifier = "tags:"
emptyString = ""
colon = ":"
comma = ","
)
type completionItem struct {
lsp.CompletionItem
InsertTextFormat insertTextFormat `json:"insertTextFormat,omitempty"`
}
type completionList struct {
IsIncomplete bool `json:"isIncomplete"`
Items []completionItem `json:"items"`
}
func completion(req *jsonrpc2.Request) (interface{}, error) {
var params lsp.TextDocumentPositionParams
if err := json.Unmarshal(*req.Params, ¶ms); err != nil {
return nil, err
}
line := getLine(params.TextDocument.URI, params.Position.Line)
pLine := line
if len(line) > params.Position.Character {
pLine = line[:params.Position.Character]
}
if isInTagsContext(params.Position.Line, params.TextDocument.URI) {
return tagsCompletion(line, pLine, params)
}
if !isStepCompletion(pLine, params.Position.Character) {
return completionList{IsIncomplete: false, Items: []completionItem{}}, nil
}
if inParameterContext(line, params.Position.Character) {
return paramCompletion(line, pLine, params)
}
v, err := stepCompletion(line, pLine, params)
if err != nil && v != nil {
// there were errors, but gauge will return completions on a best effort promise.
logError(req, err.Error())
return v, nil
}
return v, err
}
func isInTagsContext(line int, uri lsp.DocumentURI) bool {
if strings.HasPrefix(strings.ToLower(strings.Join(strings.Fields(getLine(uri, line)), "")), tagIdentifier) {
return true
} else if line != 0 && (endsWithComma(getLine(uri, line-1)) && isInTagsContext(line-1, uri)) {
return true
}
return false
}
func endsWithComma(line string) bool {
return strings.HasSuffix(strings.TrimSpace(line), comma)
}
func isStepCompletion(line string, character int) bool {
if character == 0 {
return false
}
if !strings.HasPrefix(strings.TrimSpace(line), "*") {
return false
}
return true
}
func inParameterContext(line string, charPos int) bool {
pl := line
if len(line) > charPos {
pl = line[:charPos]
}
lineAfterCharPos := strings.SplitAfter(pl, "*")
if len(lineAfterCharPos) == 1 {
return false
}
l := strings.TrimPrefix(strings.SplitAfter(pl, "*")[1], " ")
var stack string
for _, value := range l {
if string(value) == "<" {
stack = stack + string(value)
}
if string(value) == ">" && len(stack) != 0 && stack[len(stack)-1:] == "<" {
stack = stack[:len(stack)-1]
}
if string(value) == "\"" {
if len(stack) != 0 && stack[len(stack)-1:] == "\"" {
stack = stack[:len(stack)-1]
} else {
stack = stack + string(value)
}
}
}
return len(stack) != 0
}
func resolveCompletion(req *jsonrpc2.Request) (interface{}, error) {
var params completionItem
if err := json.Unmarshal(*req.Params, ¶ms); err != nil {
return nil, err
}
return params, nil
}
func getEditRange(index int, position lsp.Position, pLine, line string, endSeparator string) lsp.Range {
start := lsp.Position{Line: position.Line, Character: index + 1}
endIndex := start.Character
if len(line) >= position.Character {
lineAfterCursor := line[position.Character:]
endIndex = strings.Index(lineAfterCursor, endSeparator)
}
if endIndex == -1 {
endIndex = len(line)
} else {
endIndex = len(pLine) + endIndex + 1
}
end := lsp.Position{Line: position.Line, Character: endIndex}
return lsp.Range{Start: start, End: end}
}