mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Promtheus: Improve tab completion (#20938)
Change in behavior: - no longer suggest everything in empty field, only history - term suggestions need at least one character
This commit is contained in:
parent
67d83d1ffc
commit
75923c43d1
@ -13,35 +13,24 @@ describe('Language completion provider', () => {
|
|||||||
} as any) as PrometheusDatasource;
|
} as any) as PrometheusDatasource;
|
||||||
|
|
||||||
describe('empty query suggestions', () => {
|
describe('empty query suggestions', () => {
|
||||||
it('returns default suggestions on empty context', async () => {
|
it('returns no suggestions on empty context', async () => {
|
||||||
const instance = new LanguageProvider(datasource);
|
const instance = new LanguageProvider(datasource);
|
||||||
const value = Plain.deserialize('');
|
const value = Plain.deserialize('');
|
||||||
const result = await instance.provideCompletionItems({ text: '', prefix: '', value, wrapperClasses: [] });
|
const result = await instance.provideCompletionItems({ text: '', prefix: '', value, wrapperClasses: [] });
|
||||||
expect(result.context).toBeUndefined();
|
expect(result.context).toBeUndefined();
|
||||||
expect(result.suggestions).toMatchObject([
|
expect(result.suggestions).toMatchObject([]);
|
||||||
{
|
|
||||||
label: 'Functions',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns default suggestions with metrics on empty context when metrics were provided', async () => {
|
it('returns no suggestions with metrics on empty context even when metrics were provided', async () => {
|
||||||
const instance = new LanguageProvider(datasource);
|
const instance = new LanguageProvider(datasource);
|
||||||
instance.metrics = ['foo', 'bar'];
|
instance.metrics = ['foo', 'bar'];
|
||||||
const value = Plain.deserialize('');
|
const value = Plain.deserialize('');
|
||||||
const result = await instance.provideCompletionItems({ text: '', prefix: '', value, wrapperClasses: [] });
|
const result = await instance.provideCompletionItems({ text: '', prefix: '', value, wrapperClasses: [] });
|
||||||
expect(result.context).toBeUndefined();
|
expect(result.context).toBeUndefined();
|
||||||
expect(result.suggestions).toMatchObject([
|
expect(result.suggestions).toMatchObject([]);
|
||||||
{
|
|
||||||
label: 'Functions',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Metrics',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns default suggestions with history on empty context when history was provided', async () => {
|
it('returns history on empty context when history was provided', async () => {
|
||||||
const instance = new LanguageProvider(datasource);
|
const instance = new LanguageProvider(datasource);
|
||||||
const value = Plain.deserialize('');
|
const value = Plain.deserialize('');
|
||||||
const history: Array<HistoryItem<PromQuery>> = [
|
const history: Array<HistoryItem<PromQuery>> = [
|
||||||
@ -65,9 +54,6 @@ describe('Language completion provider', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: 'Functions',
|
|
||||||
},
|
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -101,14 +87,31 @@ describe('Language completion provider', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('metric suggestions', () => {
|
describe('metric suggestions', () => {
|
||||||
it('returns metrics and function suggestions in an unknown context', async () => {
|
it('returns history, metrics and function suggestions in an uknown context ', async () => {
|
||||||
const instance = new LanguageProvider(datasource);
|
const instance = new LanguageProvider(datasource);
|
||||||
instance.metrics = ['foo', 'bar'];
|
instance.metrics = ['foo', 'bar'];
|
||||||
|
const history: Array<HistoryItem<PromQuery>> = [
|
||||||
|
{
|
||||||
|
ts: 0,
|
||||||
|
query: { refId: '1', expr: 'metric' },
|
||||||
|
},
|
||||||
|
];
|
||||||
let value = Plain.deserialize('a');
|
let value = Plain.deserialize('a');
|
||||||
value = value.setSelection({ anchor: { offset: 1 }, focus: { offset: 1 } });
|
value = value.setSelection({ anchor: { offset: 1 }, focus: { offset: 1 } });
|
||||||
const result = await instance.provideCompletionItems({ text: 'a', prefix: 'a', value, wrapperClasses: [] });
|
const result = await instance.provideCompletionItems(
|
||||||
|
{ text: 'm', prefix: 'm', value, wrapperClasses: [] },
|
||||||
|
{ history }
|
||||||
|
);
|
||||||
expect(result.context).toBeUndefined();
|
expect(result.context).toBeUndefined();
|
||||||
expect(result.suggestions).toMatchObject([
|
expect(result.suggestions).toMatchObject([
|
||||||
|
{
|
||||||
|
label: 'History',
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
label: 'metric',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Functions',
|
label: 'Functions',
|
||||||
},
|
},
|
||||||
@ -118,12 +121,28 @@ describe('Language completion provider', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns metrics and function suggestions after a binary operator', async () => {
|
it('returns no suggestions directly after a binary operator', async () => {
|
||||||
const instance = new LanguageProvider(datasource);
|
const instance = new LanguageProvider(datasource);
|
||||||
instance.metrics = ['foo', 'bar'];
|
instance.metrics = ['foo', 'bar'];
|
||||||
const value = Plain.deserialize('*');
|
const value = Plain.deserialize('*');
|
||||||
const result = await instance.provideCompletionItems({ text: '*', prefix: '', value, wrapperClasses: [] });
|
const result = await instance.provideCompletionItems({ text: '*', prefix: '', value, wrapperClasses: [] });
|
||||||
expect(result.context).toBeUndefined();
|
expect(result.context).toBeUndefined();
|
||||||
|
expect(result.suggestions).toMatchObject([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns metric suggestions with prefix after a binary operator', async () => {
|
||||||
|
const instance = new LanguageProvider(datasource);
|
||||||
|
instance.metrics = ['foo', 'bar'];
|
||||||
|
const value = Plain.deserialize('foo + b');
|
||||||
|
const ed = new SlateEditor({ value });
|
||||||
|
const valueWithSelection = ed.moveForward(7).value;
|
||||||
|
const result = await instance.provideCompletionItems({
|
||||||
|
text: 'foo + b',
|
||||||
|
prefix: 'b',
|
||||||
|
value: valueWithSelection,
|
||||||
|
wrapperClasses: [],
|
||||||
|
});
|
||||||
|
expect(result.context).toBeUndefined();
|
||||||
expect(result.suggestions).toMatchObject([
|
expect(result.suggestions).toMatchObject([
|
||||||
{
|
{
|
||||||
label: 'Functions',
|
label: 'Functions',
|
||||||
|
@ -72,7 +72,14 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Strip syntax chars
|
// Strip syntax chars
|
||||||
cleanText = (s: string) => s.replace(/[{}[\]="(),!~+\-*/^%]/g, '').trim();
|
cleanText = (s: string) =>
|
||||||
|
s
|
||||||
|
.replace(/[{}[\]="(),!]/g, '')
|
||||||
|
.replace(/^\s*[~+\-*/^%]/, '')
|
||||||
|
.trim()
|
||||||
|
.split(' ')
|
||||||
|
.pop()
|
||||||
|
.trim();
|
||||||
|
|
||||||
get syntax() {
|
get syntax() {
|
||||||
return PromqlSyntax;
|
return PromqlSyntax;
|
||||||
@ -126,7 +133,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
|||||||
const noSuffix = !nextCharacter || nextCharacter === ')';
|
const noSuffix = !nextCharacter || nextCharacter === ')';
|
||||||
|
|
||||||
// Empty prefix is safe if it does not immediately follow a complete expression and has no text after it
|
// Empty prefix is safe if it does not immediately follow a complete expression and has no text after it
|
||||||
const safeEmptyPrefix = prefix === '' && !text.match(/^[\]})\s]+$/) && noSuffix;
|
const safePrefix = prefix && !text.match(/^[\]})\s]+$/) && noSuffix;
|
||||||
|
|
||||||
// About to type next operand if preceded by binary operator
|
// About to type next operand if preceded by binary operator
|
||||||
const operatorsPattern = /[+\-*/^%]/;
|
const operatorsPattern = /[+\-*/^%]/;
|
||||||
@ -145,7 +152,10 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
|||||||
} else if (empty) {
|
} else if (empty) {
|
||||||
// Suggestions for empty query field
|
// Suggestions for empty query field
|
||||||
return this.getEmptyCompletionItems(context);
|
return this.getEmptyCompletionItems(context);
|
||||||
} else if ((prefixUnrecognized && noSuffix) || safeEmptyPrefix || isNextOperand) {
|
} else if (prefixUnrecognized && noSuffix && !isNextOperand) {
|
||||||
|
// Show term suggestions in a couple of scenarios
|
||||||
|
return this.getBeginningCompletionItems(context);
|
||||||
|
} else if (prefixUnrecognized && safePrefix) {
|
||||||
// Show term suggestions in a couple of scenarios
|
// Show term suggestions in a couple of scenarios
|
||||||
return this.getTermCompletionItems();
|
return this.getTermCompletionItems();
|
||||||
}
|
}
|
||||||
@ -155,6 +165,12 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
getBeginningCompletionItems = (context: { history: Array<HistoryItem<PromQuery>> }): TypeaheadOutput => {
|
||||||
|
return {
|
||||||
|
suggestions: [...this.getEmptyCompletionItems(context).suggestions, ...this.getTermCompletionItems().suggestions],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
getEmptyCompletionItems = (context: { history: Array<HistoryItem<PromQuery>> }): TypeaheadOutput => {
|
getEmptyCompletionItems = (context: { history: Array<HistoryItem<PromQuery>> }): TypeaheadOutput => {
|
||||||
const { history } = context;
|
const { history } = context;
|
||||||
const suggestions = [];
|
const suggestions = [];
|
||||||
@ -177,9 +193,6 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const termCompletionItems = this.getTermCompletionItems();
|
|
||||||
suggestions.push(...termCompletionItems.suggestions);
|
|
||||||
|
|
||||||
return { suggestions };
|
return { suggestions };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user