Commit 75923c43 by David Committed by GitHub

Promtheus: Improve tab completion (#20938)

Change in behavior:

- no longer suggest everything in empty field, only history
- term suggestions need at least one character
parent 67d83d1f
......@@ -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,15 +87,32 @@ 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 };
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment