From 504c5e67ea90f9b756610ceb5333fbe2e07c3331 Mon Sep 17 00:00:00 2001 From: Julien Pivotto <291750+roidelapluie@users.noreply.github.com> Date: Fri, 17 Apr 2026 15:49:45 +0200 Subject: [PATCH] promql/ui: highlight start()/end()/range()/step() as functions; start()/end() as modifiers only after @ Signed-off-by: Julien Pivotto <291750+roidelapluie@users.noreply.github.com> --- .../src/complete/promql.terms.ts | 24 +++++++++ .../codemirror-promql/src/types/function.ts | 28 ++++++++++ web/ui/module/lezer-promql/src/highlight.js | 4 +- web/ui/module/lezer-promql/src/promql.grammar | 14 +++-- web/ui/module/lezer-promql/src/tokens.js | 28 ++++++++-- .../module/lezer-promql/test/expression.txt | 54 +++++++++++++++++-- 6 files changed, 139 insertions(+), 13 deletions(-) diff --git a/web/ui/module/codemirror-promql/src/complete/promql.terms.ts b/web/ui/module/codemirror-promql/src/complete/promql.terms.ts index 7fb89bf062..9b0aab9f72 100644 --- a/web/ui/module/codemirror-promql/src/complete/promql.terms.ts +++ b/web/ui/module/codemirror-promql/src/complete/promql.terms.ts @@ -275,6 +275,12 @@ export const functionIdentifierTerms = [ info: 'Calculate smoothed value of input series', type: 'function', }, + { + label: 'end', + detail: 'function', + info: 'Return the query end timestamp in seconds', + type: 'function', + }, { label: 'hour', detail: 'function', @@ -433,6 +439,12 @@ export const functionIdentifierTerms = [ // Avoid ranking higher than `rate`. boost: -1, }, + { + label: 'range', + detail: 'function', + info: 'Return the query range in seconds', + type: 'function', + }, { label: 'rate', detail: 'function', @@ -505,6 +517,18 @@ export const functionIdentifierTerms = [ info: 'Return the square root for input series', type: 'function', }, + { + label: 'start', + detail: 'function', + info: 'Return the query start timestamp in seconds', + type: 'function', + }, + { + label: 'step', + detail: 'function', + info: 'Return the query step in seconds', + type: 'function', + }, { label: 'stddev_over_time', detail: 'function', diff --git a/web/ui/module/codemirror-promql/src/types/function.ts b/web/ui/module/codemirror-promql/src/types/function.ts index cc1c0524fb..6414e1a955 100644 --- a/web/ui/module/codemirror-promql/src/types/function.ts +++ b/web/ui/module/codemirror-promql/src/types/function.ts @@ -37,6 +37,7 @@ import { Deg, Delta, Deriv, + EndFn, Exp, FirstOverTime, Floor, @@ -74,6 +75,7 @@ import { PresentOverTime, QuantileOverTime, Rad, + Range, Rate, Resets, Round, @@ -86,6 +88,8 @@ import { SortByLabel, SortByLabelDesc, Sqrt, + StartFn, + Step, StddevOverTime, StdvarOverTime, SumOverTime, @@ -265,6 +269,12 @@ const promqlFunctions: { [key: number]: PromQLFunction } = { variadic: 0, returnType: ValueType.vector, }, + [EndFn]: { + name: 'end', + argTypes: [], + variadic: 0, + returnType: ValueType.scalar, + }, [Exp]: { name: 'exp', argTypes: [ValueType.vector], @@ -487,6 +497,12 @@ const promqlFunctions: { [key: number]: PromQLFunction } = { variadic: 0, returnType: ValueType.vector, }, + [Range]: { + name: 'range', + argTypes: [], + variadic: 0, + returnType: ValueType.scalar, + }, [Rate]: { name: 'rate', argTypes: [ValueType.matrix], @@ -559,6 +575,18 @@ const promqlFunctions: { [key: number]: PromQLFunction } = { variadic: 0, returnType: ValueType.vector, }, + [StartFn]: { + name: 'start', + argTypes: [], + variadic: 0, + returnType: ValueType.scalar, + }, + [Step]: { + name: 'step', + argTypes: [], + variadic: 0, + returnType: ValueType.scalar, + }, [StddevOverTime]: { name: 'stddev_over_time', argTypes: [ValueType.matrix], diff --git a/web/ui/module/lezer-promql/src/highlight.js b/web/ui/module/lezer-promql/src/highlight.js index b452373345..6e88ce67df 100644 --- a/web/ui/module/lezer-promql/src/highlight.js +++ b/web/ui/module/lezer-promql/src/highlight.js @@ -20,10 +20,10 @@ export const promQLHighLight = styleTags({ NumberDurationLiteral: tags.number, NumberDurationLiteralInDurationContext: tags.number, Identifier: tags.variableName, - 'Abs Absent AbsentOverTime Acos Acosh Asin Asinh Atan Atanh AvgOverTime Ceil Changes Clamp ClampMax ClampMin Cos Cosh CountOverTime DaysInMonth DayOfMonth DayOfWeek DayOfYear Deg Delta Deriv Exp Floor HistogramAvg HistogramCount HistogramFraction HistogramQuantile HistogramSum DoubleExponentialSmoothing Hour Idelta Increase Irate LabelReplace LabelJoin LastOverTime Ln Log10 Log2 MaxOverTime MinOverTime Minute Month Pi PredictLinear PresentOverTime QuantileOverTime Rad Rate Resets Round Scalar Sgn Sin Sinh Sort SortDesc SortByLabel SortByLabelDesc Sqrt StddevOverTime StdvarOverTime SumOverTime Tan Tanh Time Timestamp Vector Year': + 'Abs Absent AbsentOverTime Acos Acosh Asin Asinh Atan Atanh AvgOverTime Ceil Changes Clamp ClampMax ClampMin Cos Cosh CountOverTime DaysInMonth DayOfMonth DayOfWeek DayOfYear Deg Delta Deriv EndFn Exp Floor HistogramAvg HistogramCount HistogramFraction HistogramQuantile HistogramSum DoubleExponentialSmoothing Hour Idelta Increase Irate LabelReplace LabelJoin LastOverTime Ln Log10 Log2 MaxOverTime MinOverTime Minute Month Pi PredictLinear PresentOverTime QuantileOverTime Rad Range Rate Resets Round Scalar Sgn Sin Sinh Sort SortDesc SortByLabel SortByLabelDesc Sqrt StartFn Step StddevOverTime StdvarOverTime SumOverTime Tan Tanh Time Timestamp Vector Year': tags.function(tags.variableName), 'Avg Bottomk Count Count_values Group LimitK LimitRatio Max Min Quantile Stddev Stdvar Sum Topk': tags.operatorKeyword, - 'By Without Bool On Ignoring GroupLeft GroupRight Offset Start End Smoothed Anchored': tags.modifier, + 'AtModifierPreprocessors By Without Bool On Ignoring GroupLeft GroupRight Offset Smoothed Anchored': tags.modifier, 'And Unless Or': tags.logicOperator, 'Sub Add Mul Mod Div Atan2 Eql Neq Lte Lss Gte Gtr EqlRegex EqlSingle NeqRegex Pow At': tags.operator, UnaryOp: tags.arithmeticOperator, diff --git a/web/ui/module/lezer-promql/src/promql.grammar b/web/ui/module/lezer-promql/src/promql.grammar index 7303dbd2ad..fd10ea0253 100644 --- a/web/ui/module/lezer-promql/src/promql.grammar +++ b/web/ui/module/lezer-promql/src/promql.grammar @@ -211,6 +211,10 @@ FunctionIdentifier { SortDesc | SortByLabel | SortByLabelDesc | + StartFn | + EndFn | + Range | + Step | Sqrt | StddevOverTime | StdvarOverTime | @@ -287,7 +291,7 @@ StepInvariantExpr { } AtModifierPreprocessors { - Start | End + AtStart | AtEnd } NumberDurationLiteral { @@ -387,8 +391,10 @@ NumberDurationLiteralInDurationContext { And, Or, Unless, - Start, - End, + StartFn, + EndFn, + AtStart, + AtEnd, Smoothed, Anchored, Fill, @@ -473,6 +479,8 @@ NumberDurationLiteralInDurationContext { SortDesc { condFn<"sort_desc"> } SortByLabel { condFn<"sort_by_label"> } SortByLabelDesc { condFn<"sort_by_label_desc"> } + Range { condFn<"range"> } + Step { condFn<"step"> } Sqrt { condFn<"sqrt"> } StddevOverTime { condFn<"stddev_over_time"> } StdvarOverTime { condFn<"stdvar_over_time"> } diff --git a/web/ui/module/lezer-promql/src/tokens.js b/web/ui/module/lezer-promql/src/tokens.js index 6fd681f1f8..c25263a92e 100644 --- a/web/ui/module/lezer-promql/src/tokens.js +++ b/web/ui/module/lezer-promql/src/tokens.js @@ -20,7 +20,6 @@ import { By, Count, CountValues, - End, Group, GroupLeft, GroupRight, @@ -35,13 +34,16 @@ import { Quantile, LimitK, LimitRatio, - Start, + StartFn, + EndFn, Stddev, Stdvar, Sum, Topk, Unless, Without, + AtEnd, + AtStart, Smoothed, Anchored, Fill, @@ -85,8 +87,6 @@ const contextualKeywordTokens = { and: And, or: Or, unless: Unless, - start: Start, - end: End, smoothed: Smoothed, anchored: Anchored, fill: Fill, @@ -95,5 +95,23 @@ const contextualKeywordTokens = { }; export const extendIdentifier = (value, stack) => { - return contextualKeywordTokens[value.toLowerCase()] || -1; + if (value === "start" && stack.canShift(StartFn)) { + return StartFn; + } + if (value === "end" && stack.canShift(EndFn)) { + return EndFn; + } + if (value.toLowerCase() === "start" && stack.canShift(AtStart)) { + return AtStart; + } + if (value.toLowerCase() === "end" && stack.canShift(AtEnd)) { + return AtEnd; + } + + const token = contextualKeywordTokens[value.toLowerCase()]; + if (token !== undefined && stack.canShift(token)) { + return token; + } + + return -1; }; diff --git a/web/ui/module/lezer-promql/test/expression.txt b/web/ui/module/lezer-promql/test/expression.txt index 0fe5f3d918..5ba516b6ec 100644 --- a/web/ui/module/lezer-promql/test/expression.txt +++ b/web/ui/module/lezer-promql/test/expression.txt @@ -428,6 +428,54 @@ end ==> PromQL(VectorSelector(Identifier)) +# Lowercase start function. + +start() + +==> +PromQL( + FunctionCall( + FunctionIdentifier(StartFn), + FunctionCallBody + ) +) + +# Lowercase end function. + +end() + +==> +PromQL( + FunctionCall( + FunctionIdentifier(EndFn), + FunctionCallBody + ) +) + +# Lowercase range function. + +range() + +==> +PromQL( + FunctionCall( + FunctionIdentifier(Range), + FunctionCallBody + ) +) + +# Lowercase step function. + +step() + +==> +PromQL( + FunctionCall( + FunctionIdentifier(Step), + FunctionCallBody + ) +) + # Simple At start foo @ start() @@ -439,7 +487,7 @@ PromQL( Identifier ), At, - AtModifierPreprocessors(Start), + AtModifierPreprocessors(AtStart), ) ) @@ -454,7 +502,7 @@ PromQL( Identifier ), At, - AtModifierPreprocessors(End), + AtModifierPreprocessors(AtEnd), ) ) @@ -484,7 +532,7 @@ PromQL( Identifier ), At, - AtModifierPreprocessors(Start), + AtModifierPreprocessors(AtStart), ) )