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;
|
||||
|
||||
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 value = Plain.deserialize('');
|
||||
const result = await instance.provideCompletionItems({ text: '', prefix: '', value, wrapperClasses: [] });
|
||||
expect(result.context).toBeUndefined();
|
||||
expect(result.suggestions).toMatchObject([
|
||||
{
|
||||
label: 'Functions',
|
||||
},
|
||||
]);
|
||||
expect(result.suggestions).toMatchObject([]);
|
||||
});
|
||||
|
||||
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);
|
||||
instance.metrics = ['foo', 'bar'];
|
||||
const value = Plain.deserialize('');
|
||||
const result = await instance.provideCompletionItems({ text: '', prefix: '', value, wrapperClasses: [] });
|
||||
expect(result.context).toBeUndefined();
|
||||
expect(result.suggestions).toMatchObject([
|
||||
{
|
||||
label: 'Functions',
|
||||
},
|
||||
{
|
||||
label: 'Metrics',
|
||||
},
|
||||
]);
|
||||
expect(result.suggestions).toMatchObject([]);
|
||||
});
|
||||
|
||||
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 value = Plain.deserialize('');
|
||||
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', () => {
|
||||
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);
|
||||
instance.metrics = ['foo', 'bar'];
|
||||
const history: Array<HistoryItem<PromQuery>> = [
|
||||
{
|
||||
ts: 0,
|
||||
query: { refId: '1', expr: 'metric' },
|
||||
},
|
||||
];
|
||||
let value = Plain.deserialize('a');
|
||||
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.suggestions).toMatchObject([
|
||||
{
|
||||
label: 'History',
|
||||
items: [
|
||||
{
|
||||
label: 'metric',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
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);
|
||||
instance.metrics = ['foo', 'bar'];
|
||||
const value = Plain.deserialize('*');
|
||||
const result = await instance.provideCompletionItems({ text: '*', prefix: '', value, wrapperClasses: [] });
|
||||
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([
|
||||
{
|
||||
label: 'Functions',
|
||||
|
@ -72,7 +72,14 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
||||
}
|
||||
|
||||
// Strip syntax chars
|
||||
cleanText = (s: string) => s.replace(/[{}[\]="(),!~+\-*/^%]/g, '').trim();
|
||||
cleanText = (s: string) =>
|
||||
s
|
||||
.replace(/[{}[\]="(),!]/g, '')
|
||||
.replace(/^\s*[~+\-*/^%]/, '')
|
||||
.trim()
|
||||
.split(' ')
|
||||
.pop()
|
||||
.trim();
|
||||
|
||||
get syntax() {
|
||||
return PromqlSyntax;
|
||||
@ -126,7 +133,7 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
||||
const noSuffix = !nextCharacter || nextCharacter === ')';
|
||||
|
||||
// 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
|
||||
const operatorsPattern = /[+\-*/^%]/;
|
||||
@ -145,7 +152,10 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
||||
} else if (empty) {
|
||||
// Suggestions for empty query field
|
||||
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
|
||||
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 => {
|
||||
const { history } = context;
|
||||
const suggestions = [];
|
||||
@ -177,9 +193,6 @@ export default class PromQlLanguageProvider extends LanguageProvider {
|
||||
});
|
||||
}
|
||||
|
||||
const termCompletionItems = this.getTermCompletionItems();
|
||||
suggestions.push(...termCompletionItems.suggestions);
|
||||
|
||||
return { suggestions };
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user