Skip to content

Commit b5225df

Browse files
committed
Fuzzy completion matching for Function names,Label values, Label names and Aggregators.
Signed-off-by: akerele abraham <abrahamakerele38@gmail.com>
1 parent 8ea8e68 commit b5225df

File tree

1 file changed

+118
-98
lines changed

1 file changed

+118
-98
lines changed

langserver/completion.go

Lines changed: 118 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -103,17 +103,14 @@ func (s *server) completeMetricName(ctx context.Context, completions *[]protocol
103103
return err
104104
}
105105

106-
names := make([]string, len(allMetadata))
107-
i := 0
106+
names := make([]string, 0, len(allMetadata))
108107
for name := range allMetadata {
109-
names[i] = name
110-
i++
108+
names = append(names, name)
111109
}
112-
matches := fuzzy.Find(metricName, names)
113-
for _, match := range matches {
110+
for _, match := range getMatches(metricName, names) {
114111
item := protocol.CompletionItem{
115112
Label: match.Str,
116-
SortText: fmt.Sprintf("__3__%d", match.Score),
113+
SortText: fmt.Sprintf("__3__%09d", match.Score),
117114
Kind: 12, //Value
118115
Documentation: allMetadata[match.Str][0].Help,
119116
Detail: string(allMetadata[match.Str][0].Type),
@@ -130,20 +127,22 @@ func (s *server) completeMetricName(ctx context.Context, completions *[]protocol
130127
return err
131128
}
132129

130+
names = make([]string, 0, len(queries))
133131
for _, q := range queries {
134-
if rec := q.Record; rec != "" && strings.HasPrefix(rec, metricName) {
135-
item := protocol.CompletionItem{
136-
Label: rec,
137-
SortText: "__2__" + rec,
138-
Kind: 3, //Value
139-
InsertTextFormat: 2, //Snippet
140-
TextEdit: &protocol.TextEdit{
141-
Range: editRange,
142-
NewText: rec,
143-
},
144-
}
145-
*completions = append(*completions, item)
132+
names = append(names, q.Record)
133+
}
134+
for _, match := range getMatches(metricName, names) {
135+
item := protocol.CompletionItem{
136+
Label: match.Str,
137+
SortText: fmt.Sprintf("__2__%09d", match.Score),
138+
Kind: 3, //Value
139+
InsertTextFormat: 2, //Snippet
140+
TextEdit: &protocol.TextEdit{
141+
Range: editRange,
142+
NewText: match.Str,
143+
},
146144
}
145+
*completions = append(*completions, item)
147146
}
148147

149148
return nil
@@ -155,41 +154,45 @@ func (s *server) completeFunctionName(completions *[]protocol.CompletionItem, lo
155154
return err
156155
}
157156

157+
names := make([]string, 0, len(promql.Functions))
158158
for name := range promql.Functions {
159-
if strings.HasPrefix(strings.ToLower(name), metricName) {
160-
item := protocol.CompletionItem{
161-
Label: name,
162-
SortText: "__1__" + name,
163-
Kind: 3, //Function
164-
InsertTextFormat: 2, //Snippet
165-
TextEdit: &protocol.TextEdit{
166-
Range: editRange,
167-
NewText: name + "($1)",
168-
},
169-
Command: &protocol.Command{
170-
// This might create problems with non VS Code clients
171-
Command: "editor.action.triggerParameterHints",
172-
},
173-
}
174-
*completions = append(*completions, item)
159+
names = append(names, name)
160+
}
161+
for _, match := range getMatches(metricName, names) {
162+
item := protocol.CompletionItem{
163+
Label: match.Str,
164+
SortText: fmt.Sprintf("__1__%09d", match.Score),
165+
Kind: 3, //Function
166+
InsertTextFormat: 2, //Snippet
167+
TextEdit: &protocol.TextEdit{
168+
Range: editRange,
169+
NewText: match.Str + "($1)",
170+
},
171+
Command: &protocol.Command{
172+
// This might create problems with non VS Code clients
173+
Command: "editor.action.triggerParameterHints",
174+
},
175175
}
176+
*completions = append(*completions, item)
176177
}
177178

178-
for name, desc := range aggregators {
179-
if strings.HasPrefix(strings.ToLower(name), metricName) {
180-
item := protocol.CompletionItem{
181-
Label: name,
182-
SortText: "__1__" + name,
183-
Kind: 3, //Function
184-
InsertTextFormat: 2, //Snippet
185-
Detail: desc,
186-
TextEdit: &protocol.TextEdit{
187-
Range: editRange,
188-
NewText: name + "($1)",
189-
},
190-
}
191-
*completions = append(*completions, item)
179+
names = make([]string, 0, len(aggregators))
180+
for name := range aggregators {
181+
names = append(names, name)
182+
}
183+
for _, match := range getMatches(strings.ToLower(metricName), names) {
184+
item := protocol.CompletionItem{
185+
Label: match.Str,
186+
SortText: fmt.Sprintf("__1__%09d", match.Score),
187+
Kind: 3, //Function
188+
InsertTextFormat: 2, //Snippet
189+
Detail: aggregators[match.Str],
190+
TextEdit: &protocol.TextEdit{
191+
Range: editRange,
192+
NewText: match.Str + "($1)",
193+
},
192194
}
195+
*completions = append(*completions, item)
193196
}
194197

195198
return nil
@@ -318,41 +321,38 @@ func (s *server) completeLabel(ctx context.Context, completions *[]protocol.Comp
318321
return err
319322
}
320323

321-
sort.Strings(allNames)
322-
323324
editRange, err := getEditRange(location, "")
324325
if err != nil {
325326
return err
326327
}
327328

329+
labelName := location.Node.(*promql.Item).Val
330+
var prevMatched string
328331
OUTER:
329-
for i, name := range allNames {
332+
for _, match := range getMatches(labelName, allNames) {
330333
// Skip duplicates
331-
if i > 0 && allNames[i-1] == name {
334+
if prevMatched == match.Str {
332335
continue
333336
}
334-
335-
if strings.HasPrefix(name, location.Node.(*promql.Item).Val) {
336-
// Skip labels that already have matchers
337-
if vs != nil {
338-
for _, m := range vs.LabelMatchers {
339-
if m != nil && m.Name == name {
340-
continue OUTER
341-
}
337+
prevMatched = match.Str
338+
// Skip labels that already have matchers
339+
if vs != nil {
340+
for _, m := range vs.LabelMatchers {
341+
if m != nil && m.Name == match.Str {
342+
continue OUTER
342343
}
343344
}
345+
}
344346

345-
item := protocol.CompletionItem{
346-
Label: name,
347-
Kind: 12, //Value
348-
TextEdit: &protocol.TextEdit{
349-
Range: editRange,
350-
NewText: name,
351-
},
352-
}
353-
354-
*completions = append(*completions, item)
347+
item := protocol.CompletionItem{
348+
Label: match.Str,
349+
Kind: 12, //Value
350+
TextEdit: &protocol.TextEdit{
351+
Range: editRange,
352+
NewText: match.Str,
353+
},
355354
}
355+
*completions = append(*completions, item)
356356
}
357357

358358
return nil
@@ -392,40 +392,42 @@ func (s *server) completeLabelValue(ctx context.Context, completions *[]protocol
392392
quote = '"'
393393
}
394394

395-
for _, name := range labelValues {
396-
if strings.HasPrefix(string(name), unquoted) {
397-
var quoted string
395+
names := make([]string, 0, len(labelValues))
396+
for _, v := range labelValues {
397+
names = append(names, string(v))
398+
}
399+
for _, match := range getMatches(unquoted, names) {
400+
var quoted string
398401

399-
if quote == '`' {
400-
if strings.ContainsRune(string(name), '`') {
401-
quote = '"'
402-
} else {
403-
quoted = fmt.Sprint("`", name, "`")
404-
}
402+
if quote == '`' {
403+
if strings.ContainsRune(match.Str, '`') {
404+
quote = '"'
405+
} else {
406+
quoted = fmt.Sprint("`", match.Str, "`")
405407
}
408+
}
406409

407-
if quoted == "" {
408-
quoted = strconv.Quote(string(name))
409-
}
410+
if quoted == "" {
411+
quoted = strconv.Quote(match.Str)
412+
}
410413

411-
if quote == '\'' {
412-
quoted = quoted[1 : len(quoted)-1]
414+
if quote == '\'' {
415+
quoted = quoted[1 : len(quoted)-1]
413416

414-
quoted = strings.ReplaceAll(quoted, `\"`, `"`)
415-
quoted = strings.ReplaceAll(quoted, `'`, `\'`)
416-
quoted = fmt.Sprint("'", quoted, "'")
417-
}
417+
quoted = strings.ReplaceAll(quoted, `\"`, `"`)
418+
quoted = strings.ReplaceAll(quoted, `'`, `\'`)
419+
quoted = fmt.Sprint("'", quoted, "'")
420+
}
418421

419-
item := protocol.CompletionItem{
420-
Label: quoted,
421-
Kind: 12, //Value
422-
TextEdit: &protocol.TextEdit{
423-
Range: editRange,
424-
NewText: quoted,
425-
},
426-
}
427-
*completions = append(*completions, item)
422+
item := protocol.CompletionItem{
423+
Label: quoted,
424+
Kind: 12, //Value
425+
TextEdit: &protocol.TextEdit{
426+
Range: editRange,
427+
NewText: quoted,
428+
},
428429
}
430+
*completions = append(*completions, item)
429431
}
430432

431433
return nil
@@ -452,3 +454,21 @@ func getEditRange(location *cache.Location, oldname string) (editRange protocol.
452454

453455
return
454456
}
457+
458+
// getMatches returns fuzzy matches for a slice of string and a pattern.
459+
func getMatches(pattern string, names []string) fuzzy.Matches {
460+
if pattern == "" {
461+
var matches fuzzy.Matches
462+
sort.Strings(names)
463+
for i, name := range names {
464+
matches = append(matches,
465+
fuzzy.Match{
466+
Str: name,
467+
Index: i,
468+
Score: len(names) - i,
469+
})
470+
}
471+
return matches
472+
}
473+
return fuzzy.Find(pattern, names)
474+
}

0 commit comments

Comments
 (0)