Prometheus: Fix label value suggestion (#21294)

* Prometheus: Fix label value suggestion

- remove quotes from typeahead input to suggest correct label values
- fix acceptance of partial label values

* Disable mid-word suggestions

* Fix test
This commit is contained in:
David
2019-12-31 08:56:57 +01:00
committed by GitHub
parent 649fa6789e
commit 334b89f3ee
4 changed files with 29 additions and 7 deletions

View File

@ -301,7 +301,7 @@ describe('Language completion provider', () => {
instance.lookupsDisabled = false; instance.lookupsDisabled = false;
const value = Plain.deserialize('{job!=}'); const value = Plain.deserialize('{job!=}');
const ed = new SlateEditor({ value }); const ed = new SlateEditor({ value });
const valueWithSelection = ed.moveForward(8).value; const valueWithSelection = ed.moveForward(6).value;
const result = await instance.provideCompletionItems({ const result = await instance.provideCompletionItems({
text: '!=', text: '!=',
prefix: '', prefix: '',

View File

@ -88,7 +88,10 @@ export default class PromQlLanguageProvider extends LanguageProvider {
cleanText(s: string) { cleanText(s: string) {
const parts = s.split(PREFIX_DELIMITER_REGEX); const parts = s.split(PREFIX_DELIMITER_REGEX);
const last = parts.pop(); const last = parts.pop();
return last.trimLeft().replace(/"$/, ''); return last
.trimLeft()
.replace(/"$/, '')
.replace(/^"/, '');
} }
get syntax() { get syntax() {
@ -297,8 +300,15 @@ export default class PromQlLanguageProvider extends LanguageProvider {
labelKey, labelKey,
value, value,
}: TypeaheadInput): Promise<TypeaheadOutput> => { }: TypeaheadInput): Promise<TypeaheadOutput> => {
const suggestions: CompletionItemGroup[] = [];
const line = value.anchorBlock.getText(); const line = value.anchorBlock.getText();
const cursorOffset = value.selection.anchor.offset; const cursorOffset = value.selection.anchor.offset;
const nextChar = line[cursorOffset];
const isValueContext = wrapperClasses.includes('attr-value');
if (!nextChar.match(/["}]/)) {
// Don't suggest anything inside a value
return { suggestions };
}
// Get normalized selector // Get normalized selector
let selector; let selector;
@ -313,7 +323,6 @@ export default class PromQlLanguageProvider extends LanguageProvider {
const containsMetric = selector.includes('__name__='); const containsMetric = selector.includes('__name__=');
const existingKeys = parsedSelector ? parsedSelector.labelKeys : []; const existingKeys = parsedSelector ? parsedSelector.labelKeys : [];
const suggestions: CompletionItemGroup[] = [];
let labelValues; let labelValues;
// Query labels for selector // Query labels for selector
if (selector) { if (selector) {
@ -326,7 +335,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
} }
let context: string; let context: string;
if ((text && text.match(/^!?=~?/)) || wrapperClasses.includes('attr-value')) { if ((text && text.match(/^!?=~?/)) || isValueContext) {
// Label values // Label values
if (labelKey && labelValues[labelKey]) { if (labelKey && labelValues[labelKey]) {
context = 'context-label-values'; context = 'context-label-values';

View File

@ -31,6 +31,13 @@ describe('parseSelector()', () => {
parsed = parseSelector('{foo="bar",baz="}'); parsed = parseSelector('{foo="bar",baz="}');
expect(parsed.selector).toBe('{foo="bar"}'); expect(parsed.selector).toBe('{foo="bar"}');
// Cursor in value area counts as incomplete
parsed = parseSelector('{foo="bar",baz=""}', 16);
expect(parsed.selector).toBe('{foo="bar"}');
parsed = parseSelector('{foo="bar",baz="4"}', 17);
expect(parsed.selector).toBe('{foo="bar"}');
}); });
it('throws if not inside a selector', () => { it('throws if not inside a selector', () => {
@ -55,7 +62,7 @@ describe('parseSelector()', () => {
parsed = parseSelector('bar{foo}', 4); parsed = parseSelector('bar{foo}', 4);
expect(parsed.selector).toBe('{__name__="bar"}'); expect(parsed.selector).toBe('{__name__="bar"}');
parsed = parseSelector('baz{foo="bar"}', 12); parsed = parseSelector('baz{foo="bar"}', 13);
expect(parsed.selector).toBe('{__name__="baz",foo="bar"}'); expect(parsed.selector).toBe('{__name__="baz",foo="bar"}');
parsed = parseSelector('bar:metric:1m{}', 14); parsed = parseSelector('bar:metric:1m{}', 14);

View File

@ -79,8 +79,14 @@ export function parseSelector(query: string, cursorOffset = 1): { labelKeys: any
// Extract clean labels to form clean selector, incomplete labels are dropped // Extract clean labels to form clean selector, incomplete labels are dropped
const selector = query.slice(prefixOpen, suffixClose); const selector = query.slice(prefixOpen, suffixClose);
const labels: { [key: string]: { value: string; operator: string } } = {}; const labels: { [key: string]: { value: string; operator: string } } = {};
selector.replace(labelRegexp, (_, key, operator, value) => { selector.replace(labelRegexp, (label, key, operator, value) => {
labels[key] = { value, operator }; const labelOffset = query.indexOf(label);
const valueStart = labelOffset + key.length + operator.length + 1;
const valueEnd = labelOffset + key.length + operator.length + value.length - 1;
// Skip label if cursor is in value
if (cursorOffset < valueStart || cursorOffset > valueEnd) {
labels[key] = { value, operator };
}
return ''; return '';
}); });