feat(loki-monaco-editor): improve line filter suggestions (#57103)

This commit is contained in:
Matias Chomicki 2022-10-18 12:40:12 +02:00 committed by GitHub
parent 4d0dd0647e
commit d2abcbe4a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 68 additions and 41 deletions

View File

@ -69,37 +69,37 @@ const afterSelectorCompletions = [
type: 'OPPOSITE_PARSER_PLACEHOLDER', type: 'OPPOSITE_PARSER_PLACEHOLDER',
}, },
{ {
insertText: 'pattern', insertText: '| pattern',
label: 'pattern', label: 'pattern',
type: 'PARSER', type: 'PARSER',
}, },
{ {
insertText: 'regexp', insertText: '| regexp',
label: 'regexp', label: 'regexp',
type: 'PARSER', type: 'PARSER',
}, },
{ {
insertText: 'unpack', insertText: '| unpack',
label: 'unpack', label: 'unpack',
type: 'PARSER', type: 'PARSER',
}, },
{ {
insertText: 'unwrap extracted', insertText: '| unwrap extracted',
label: 'unwrap extracted (detected)', label: 'unwrap extracted (detected)',
type: 'LINE_FILTER', type: 'LINE_FILTER',
}, },
{ {
insertText: 'unwrap label', insertText: '| unwrap label',
label: 'unwrap label (detected)', label: 'unwrap label (detected)',
type: 'LINE_FILTER', type: 'LINE_FILTER',
}, },
{ {
insertText: 'unwrap', insertText: '| unwrap',
label: 'unwrap', label: 'unwrap',
type: 'LINE_FILTER', type: 'LINE_FILTER',
}, },
{ {
insertText: 'line_format "{{.$0}}"', insertText: '| line_format "{{.$0}}"',
isSnippet: true, isSnippet: true,
label: 'line_format', label: 'line_format',
type: 'LINE_FORMAT', type: 'LINE_FORMAT',
@ -110,27 +110,43 @@ function buildAfterSelectorCompletions(
detectedParser: string, detectedParser: string,
detectedParserType: string, detectedParserType: string,
otherParser: string, otherParser: string,
explanation = '(detected)' afterPipe: boolean
) { ) {
return afterSelectorCompletions.map((completion) => { const explanation = '(detected)';
const expectedCompletions = afterSelectorCompletions.map((completion) => {
if (completion.type === 'DETECTED_PARSER_PLACEHOLDER') { if (completion.type === 'DETECTED_PARSER_PLACEHOLDER') {
return { return {
...completion, ...completion,
type: detectedParserType, type: detectedParserType,
label: `${detectedParser} ${explanation}`, label: `${detectedParser} ${explanation}`,
insertText: detectedParser, insertText: `| ${detectedParser}`,
}; };
} else if (completion.type === 'OPPOSITE_PARSER_PLACEHOLDER') { } else if (completion.type === 'OPPOSITE_PARSER_PLACEHOLDER') {
return { return {
...completion, ...completion,
type: 'PARSER', type: 'PARSER',
label: otherParser, label: otherParser,
insertText: otherParser, insertText: `| ${otherParser}`,
}; };
} }
return { ...completion }; return { ...completion };
}); });
if (afterPipe) {
// Remove pipe
return (
expectedCompletions
.map((completion) => {
completion.insertText = completion.insertText.replace('|', '');
return completion;
})
// Remove != and !~
.filter((completion) => !completion.insertText.startsWith('!'))
);
}
return expectedCompletions;
} }
describe('getCompletions', () => { describe('getCompletions', () => {
@ -272,31 +288,37 @@ describe('getCompletions', () => {
]); ]);
}); });
test('Returns completion options when the situation is AFTER_SELECTOR and JSON parser', async () => { test.each([true, false])(
'Returns completion options when the situation is AFTER_SELECTOR, JSON parser, and afterPipe %s',
async (afterPipe: boolean) => {
jest.spyOn(completionProvider, 'getParserAndLabelKeys').mockResolvedValue({ jest.spyOn(completionProvider, 'getParserAndLabelKeys').mockResolvedValue({
extractedLabelKeys, extractedLabelKeys,
hasJSON: true, hasJSON: true,
hasLogfmt: false, hasLogfmt: false,
}); });
const situation: Situation = { type: 'AFTER_SELECTOR', labels: [], afterPipe: true }; const situation: Situation = { type: 'AFTER_SELECTOR', labels: [], afterPipe };
const completions = await getCompletions(situation, completionProvider); const completions = await getCompletions(situation, completionProvider);
const expected = buildAfterSelectorCompletions('json', 'PARSER', 'logfmt'); const expected = buildAfterSelectorCompletions('json', 'PARSER', 'logfmt', afterPipe);
expect(completions).toEqual(expected); expect(completions).toEqual(expected);
}); }
);
test('Returns completion options when the situation is AFTER_SELECTOR and Logfmt parser', async () => { test.each([true, false])(
'Returns completion options when the situation is AFTER_SELECTOR, Logfmt parser, and afterPipe %s',
async (afterPipe: boolean) => {
jest.spyOn(completionProvider, 'getParserAndLabelKeys').mockResolvedValue({ jest.spyOn(completionProvider, 'getParserAndLabelKeys').mockResolvedValue({
extractedLabelKeys, extractedLabelKeys,
hasJSON: false, hasJSON: false,
hasLogfmt: true, hasLogfmt: true,
}); });
const situation: Situation = { type: 'AFTER_SELECTOR', labels: [], afterPipe: true }; const situation: Situation = { type: 'AFTER_SELECTOR', labels: [], afterPipe };
const completions = await getCompletions(situation, completionProvider); const completions = await getCompletions(situation, completionProvider);
const expected = buildAfterSelectorCompletions('logfmt', 'DURATION', 'json'); const expected = buildAfterSelectorCompletions('logfmt', 'DURATION', 'json', afterPipe);
expect(completions).toEqual(expected); expect(completions).toEqual(expected);
}); }
);
test('Returns completion options when the situation is IN_AGGREGATION', async () => { test('Returns completion options when the situation is IN_AGGREGATION', async () => {
const situation: Situation = { type: 'IN_AGGREGATION' }; const situation: Situation = { type: 'IN_AGGREGATION' };

View File

@ -64,12 +64,17 @@ const DURATION_COMPLETIONS: Completion[] = ['$__interval', '$__range', '1m', '5m
}) })
); );
const LINE_FILTER_COMPLETIONS: Completion[] = ['|=', '!=', '|~', '!~'].map((item) => ({ function getLineFilterCompletions(afterPipe: boolean): Completion[] {
const lineFilters = afterPipe ? ['=', '~'] : ['|=', '!=', '|~', '!~'];
const prefix = afterPipe ? '|' : '';
return lineFilters.map((operator) => ({
type: 'LINE_FILTER', type: 'LINE_FILTER',
label: `${item} ""`, label: `${prefix}${operator} ""`,
insertText: `${item} "$0"`, insertText: `${operator} "$0"`,
isSnippet: true, isSnippet: true,
})); }));
}
async function getAllHistoryCompletions(dataProvider: CompletionDataProvider): Promise<Completion[]> { async function getAllHistoryCompletions(dataProvider: CompletionDataProvider): Promise<Completion[]> {
const history = await dataProvider.getHistory(); const history = await dataProvider.getHistory();
@ -133,7 +138,7 @@ async function getAfterSelectorCompletions(
const { extractedLabelKeys, hasJSON, hasLogfmt } = await dataProvider.getParserAndLabelKeys(labels); const { extractedLabelKeys, hasJSON, hasLogfmt } = await dataProvider.getParserAndLabelKeys(labels);
const allParsers = new Set(['json', 'logfmt', 'pattern', 'regexp', 'unpack']); const allParsers = new Set(['json', 'logfmt', 'pattern', 'regexp', 'unpack']);
const completions: Completion[] = []; const completions: Completion[] = [];
const prefix = afterPipe ? '' : '| '; const prefix = afterPipe ? ' ' : '| ';
const hasLevelInExtractedLabels = extractedLabelKeys.some((key) => key === 'level'); const hasLevelInExtractedLabels = extractedLabelKeys.some((key) => key === 'level');
if (hasJSON) { if (hasJSON) {
allParsers.delete('json'); allParsers.delete('json');
@ -185,7 +190,7 @@ async function getAfterSelectorCompletions(
isSnippet: true, isSnippet: true,
}); });
return [...LINE_FILTER_COMPLETIONS, ...completions]; return [...getLineFilterCompletions(afterPipe), ...completions];
} }
async function getLabelValuesForMetricCompletions( async function getLabelValuesForMetricCompletions(