mirror of
https://github.com/prometheus/prometheus.git
synced 2026-06-13 18:40:16 -04:00
Merge pull request #18894 from MichaHoffmann/mhoffmann/codemirror-fix-autocomplete-for-functions
Some checks are pending
buf.build / lint and publish (push) Waiting to run
CI / Go tests (push) Waiting to run
CI / More Go tests (push) Waiting to run
CI / Go tests for 32-bit x86 (push) Waiting to run
CI / Go tests for Prometheus upgrades and downgrades (push) Waiting to run
CI / Go tests with previous Go version (push) Waiting to run
CI / UI tests (push) Waiting to run
CI / Go tests on Windows (push) Waiting to run
CI / Mixins tests (push) Waiting to run
CI / Compliance testing (push) Waiting to run
CI / Build Prometheus for common architectures (push) Waiting to run
CI / Build Prometheus for all architectures (push) Waiting to run
CI / Report status of build Prometheus for all architectures (push) Blocked by required conditions
CI / Check generated parser (push) Waiting to run
CI / golangci-lint (push) Waiting to run
CI / fuzzing (push) Waiting to run
CI / codeql (push) Waiting to run
CI / Publish main branch artifacts (push) Blocked by required conditions
CI / Publish release artefacts (push) Blocked by required conditions
CI / Publish UI on npm Registry (push) Blocked by required conditions
govulncheck / Run govulncheck (push) Waiting to run
Scorecards supply-chain security / Scorecards analysis (push) Waiting to run
Some checks are pending
buf.build / lint and publish (push) Waiting to run
CI / Go tests (push) Waiting to run
CI / More Go tests (push) Waiting to run
CI / Go tests for 32-bit x86 (push) Waiting to run
CI / Go tests for Prometheus upgrades and downgrades (push) Waiting to run
CI / Go tests with previous Go version (push) Waiting to run
CI / UI tests (push) Waiting to run
CI / Go tests on Windows (push) Waiting to run
CI / Mixins tests (push) Waiting to run
CI / Compliance testing (push) Waiting to run
CI / Build Prometheus for common architectures (push) Waiting to run
CI / Build Prometheus for all architectures (push) Waiting to run
CI / Report status of build Prometheus for all architectures (push) Blocked by required conditions
CI / Check generated parser (push) Waiting to run
CI / golangci-lint (push) Waiting to run
CI / fuzzing (push) Waiting to run
CI / codeql (push) Waiting to run
CI / Publish main branch artifacts (push) Blocked by required conditions
CI / Publish release artefacts (push) Blocked by required conditions
CI / Publish UI on npm Registry (push) Blocked by required conditions
govulncheck / Run govulncheck (push) Waiting to run
Scorecards supply-chain security / Scorecards analysis (push) Waiting to run
codemirror: fix autocomplete edgecase for functions
This commit is contained in:
commit
a9564ef219
2 changed files with 116 additions and 4 deletions
|
|
@ -74,6 +74,31 @@ describe('analyzeCompletion test', () => {
|
|||
{ kind: ContextKind.Aggregation },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'autocomplete AggregateOpModifier or BinOp after closing aggregation',
|
||||
expr: 'sum()',
|
||||
pos: 5, // cursor is after the closing bracket
|
||||
expectedContext: [{ kind: ContextKind.AggregateOpModifier }, { kind: ContextKind.BinOp }],
|
||||
},
|
||||
{
|
||||
title: 'metric/function/aggregation autocompletion in incomplete function',
|
||||
expr: 'sum(',
|
||||
pos: 4,
|
||||
expectedContext: [
|
||||
{
|
||||
kind: ContextKind.MetricName,
|
||||
metricName: '',
|
||||
},
|
||||
{ kind: ContextKind.Function },
|
||||
{ kind: ContextKind.Aggregation },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'autocomplete binOp after closing function',
|
||||
expr: 'rate(foo[5m])',
|
||||
pos: 13, // cursor is after the closing bracket
|
||||
expectedContext: [{ kind: ContextKind.BinOp }],
|
||||
},
|
||||
{
|
||||
title: 'metric/function/aggregation autocompletion 2',
|
||||
expr: 'sum(rat)',
|
||||
|
|
@ -100,6 +125,18 @@ describe('analyzeCompletion test', () => {
|
|||
{ kind: ContextKind.Aggregation },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'autocomplete AggregateOpModifier or BinOp after closing nested aggregation',
|
||||
expr: 'sum(rate(foo[5m]))',
|
||||
pos: 18, // cursor is after the closing bracket
|
||||
expectedContext: [{ kind: ContextKind.AggregateOpModifier }, { kind: ContextKind.BinOp }],
|
||||
},
|
||||
{
|
||||
title: 'autocomplete binOp after closing aggregation with existing modifier',
|
||||
expr: 'sum by(job)(rate(foo[5m]))',
|
||||
pos: 26, // cursor is after the closing bracket
|
||||
expectedContext: [{ kind: ContextKind.BinOp }],
|
||||
},
|
||||
{
|
||||
title: 'metric/function/aggregation autocompletion 4',
|
||||
expr: 'sum(rate(my_))',
|
||||
|
|
@ -717,6 +754,18 @@ describe('computeStartCompletePosition test', () => {
|
|||
pos: 9, // cursor is between the bracket
|
||||
expectedStart: 9,
|
||||
},
|
||||
{
|
||||
title: 'start should be equal to the pos after closing function',
|
||||
expr: 'rate(foo[5m])',
|
||||
pos: 13, // cursor is after the closing bracket
|
||||
expectedStart: 13,
|
||||
},
|
||||
{
|
||||
title: 'start should be equal to the pos after closing aggregation',
|
||||
expr: 'sum(rate(foo[5m]))',
|
||||
pos: 18, // cursor is after the closing bracket
|
||||
expectedStart: 18,
|
||||
},
|
||||
{
|
||||
title: 'bracket containing a substring',
|
||||
expr: '{myL}',
|
||||
|
|
@ -994,12 +1043,24 @@ describe('computeEndCompletePosition test', () => {
|
|||
pos: 13, // cursor at '!' (error node)
|
||||
expectedEnd: 13, // error node returns pos
|
||||
},
|
||||
{
|
||||
title: 'end should be equal to the pos after closing function',
|
||||
expr: 'rate(foo[5m])',
|
||||
pos: 13, // cursor is after the closing bracket
|
||||
expectedEnd: 13,
|
||||
},
|
||||
{
|
||||
title: 'end should be equal to the pos after closing aggregation',
|
||||
expr: 'sum(rate(foo[5m]))',
|
||||
pos: 18, // cursor is after the closing bracket
|
||||
expectedEnd: 18,
|
||||
},
|
||||
];
|
||||
testCases.forEach((value) => {
|
||||
it(value.title, () => {
|
||||
const state = createEditorState(value.expr);
|
||||
const node = syntaxTree(state).resolve(value.pos, -1);
|
||||
const result = computeEndCompletePosition(node, value.pos);
|
||||
const result = computeEndCompletePosition(state, node, value.pos);
|
||||
expect(result).toEqual(value.expectedEnd);
|
||||
});
|
||||
});
|
||||
|
|
@ -1043,6 +1104,28 @@ describe('autocomplete promQL test', () => {
|
|||
validFor: /^[a-zA-Z0-9_:]+$/,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'offline autocomplete aggregate operation modifier or binary operator after closing aggregation',
|
||||
expr: 'sum()',
|
||||
pos: 5, // cursor is after the closing bracket
|
||||
expectedResult: {
|
||||
options: ([] as Completion[]).concat(aggregateOpModifierTerms, binOpTerms),
|
||||
from: 5,
|
||||
to: 5,
|
||||
validFor: /^[a-zA-Z0-9_:]+$/,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'offline autocomplete binary operator after closing function',
|
||||
expr: 'rate(foo[5m])',
|
||||
pos: 13, // cursor is after the closing bracket
|
||||
expectedResult: {
|
||||
options: binOpTerms,
|
||||
from: 13,
|
||||
to: 13,
|
||||
validFor: /^[a-zA-Z0-9_:]+$/,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'offline function/aggregation autocompletion in aggregation 2',
|
||||
expr: 'sum(ra)',
|
||||
|
|
@ -1087,6 +1170,17 @@ describe('autocomplete promQL test', () => {
|
|||
validFor: /^[a-zA-Z0-9_:]+$/,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'offline autocomplete aggregate operation modifier or binary operator after closing nested aggregation',
|
||||
expr: 'sum(rate(foo[5m]))',
|
||||
pos: 18, // cursor is after the closing bracket
|
||||
expectedResult: {
|
||||
options: ([] as Completion[]).concat(aggregateOpModifierTerms, binOpTerms),
|
||||
from: 18,
|
||||
to: 18,
|
||||
validFor: /^[a-zA-Z0-9_:]+$/,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'offline function/aggregation autocompletion in aggregation 4',
|
||||
expr: 'sum by (instance, job) ( sum_over(scrape_series_added[1h])) / sum by (instance, job) (sum_over_time(scrape_samples_scraped[1h])) > 0.1 and sum by(instance, job) (scrape_samples_scraped{) > 100',
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { PrometheusClient } from '../client';
|
|||
import {
|
||||
Add,
|
||||
AggregateExpr,
|
||||
AggregateModifier,
|
||||
And,
|
||||
BinaryExpr,
|
||||
BoolModifier,
|
||||
|
|
@ -175,16 +176,24 @@ function escapePromQLString(str: string): string {
|
|||
return str.replace(/([\\"])/g, '\\$1');
|
||||
}
|
||||
|
||||
function isAfterClosedFunctionCallBody(state: EditorState, node: SyntaxNode, pos: number): boolean {
|
||||
return node.type.id === FunctionCallBody && pos >= node.to && node.from < node.to && state.sliceDoc(node.to - 1, node.to) === ')';
|
||||
}
|
||||
|
||||
// computeEndCompletePosition calculates the end position for autocompletion replacement.
|
||||
// When the cursor is in the middle of a token, this ensures the entire token is replaced,
|
||||
// not just the portion before the cursor. This fixes issue #15839.
|
||||
// Note: this method is exported only for testing purpose.
|
||||
export function computeEndCompletePosition(node: SyntaxNode, pos: number): number {
|
||||
export function computeEndCompletePosition(state: EditorState, node: SyntaxNode, pos: number): number {
|
||||
// For error nodes, use the cursor position as the end position
|
||||
if (node.type.id === 0) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
if (isAfterClosedFunctionCallBody(state, node, pos)) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
if (
|
||||
node.type.id === LabelMatchers ||
|
||||
node.type.id === GroupingLabels ||
|
||||
|
|
@ -240,7 +249,9 @@ function computeStartCompleteLabelPositionInLabelMatcherOrInGroupingLabel(node:
|
|||
export function computeStartCompletePosition(state: EditorState, node: SyntaxNode, pos: number): number {
|
||||
const currentText = state.doc.slice(node.from, pos).toString();
|
||||
let start = node.from;
|
||||
if (node.type.id === LabelMatchers || node.type.id === GroupingLabels) {
|
||||
if (isAfterClosedFunctionCallBody(state, node, pos)) {
|
||||
start = pos;
|
||||
} else if (node.type.id === LabelMatchers || node.type.id === GroupingLabels) {
|
||||
start = computeStartCompleteLabelPositionInLabelMatcherOrInGroupingLabel(node, pos);
|
||||
} else if (
|
||||
(node.type.id === FunctionCallBody && node.firstChild === null) ||
|
||||
|
|
@ -545,6 +556,13 @@ export function analyzeCompletion(state: EditorState, node: SyntaxNode, pos: num
|
|||
result.push({ kind: ContextKind.Duration });
|
||||
break;
|
||||
case FunctionCallBody:
|
||||
if (isAfterClosedFunctionCallBody(state, node, pos)) {
|
||||
if (node.parent?.type.id === AggregateExpr && !containsAtLeastOneChild(node.parent, AggregateModifier)) {
|
||||
result.push({ kind: ContextKind.AggregateOpModifier });
|
||||
}
|
||||
result.push({ kind: ContextKind.BinOp });
|
||||
break;
|
||||
}
|
||||
// For aggregation function such as Topk, the first parameter is a number.
|
||||
// The second one is an expression.
|
||||
// When moving to the second parameter, the node is an error node.
|
||||
|
|
@ -711,7 +729,7 @@ export class HybridComplete implements CompleteStrategy {
|
|||
return arrayToCompletionResult(
|
||||
result,
|
||||
computeStartCompletePosition(state, tree, pos),
|
||||
computeEndCompletePosition(tree, pos),
|
||||
computeEndCompletePosition(state, tree, pos),
|
||||
completeSnippet,
|
||||
span
|
||||
);
|
||||
|
|
|
|||
Loading…
Reference in a new issue