/usr/share/grafana/public/app/plugins/datasource/loki/querybuilder
import { SyntaxNode, TreeCursor } from '@lezer/common'; import { QueryBuilderOperation, QueryBuilderOperationParamValue } from '@grafana/plugin-ui'; // Although 0 isn't explicitly provided in the @grafana/lezer-logql library as the error node ID, it does appear to be the ID of error nodes within lezer. export const ErrorId = 0; export function getLeftMostChild(cur: SyntaxNode): SyntaxNode { return cur.firstChild ? getLeftMostChild(cur.firstChild) : cur; } export function makeError(expr: string, node: SyntaxNode) { return { text: getString(expr, node), // TODO: this are positions in the string with the replaced variables. Means it cannot be used to show exact // placement of the error for the user. We need some translation table to positions before the variable // replace. from: node.from, to: node.to, parentType: node.parent?.name, }; } // Taken from template_srv, but copied so to not mess with the regex.index which is manipulated in the service /* * This regex matches 3 types of variable reference with an optional format specifier * \$(\w+) $var1 * \[\[([\s\S]+?)(?::(\w+))?\]\] [[var2]] or [[var2:fmt2]] * \${(\w+)(?::(\w+))?} ${var3} or ${var3:fmt3} */ export const variableRegex = /\$(\w+)|\[\[([\s\S]+?)(?::(\w+))?\]\]|\${(\w+)(?:\.([^:^\}]+))?(?::([^\}]+))?}/g; /** * As variables with $ are creating parsing errors, we first replace them with magic string that is parsable and at * the same time we can get the variable and its format back from it. * @param expr */ export function replaceVariables(expr: string) { return expr.replace(variableRegex, (match, var1, var2, fmt2, var3, fieldPath, fmt3) => { const fmt = fmt2 || fmt3; let variable = var1; let varType = '0'; if (var2) { variable = var2; varType = '1'; } if (var3) { variable = var3; varType = '2'; } return `__V_${varType}__` + variable + '__V__' + (fmt ? '__F__' + fmt + '__F__' : ''); }); } const varTypeFunc = [ (v: string, f?: string) => `\$${v}`, (v: string, f?: string) => `[[${v}${f ? `:${f}` : ''}]]`, (v: string, f?: string) => `\$\{${v}${f ? `:${f}` : ''}\}`, ]; /** * Get back the text with variables in their original format. * @param expr */ export function returnVariables(expr: string) { return expr.replace(/__V_(\d)__(.+?)__V__(?:__F__(\w+)__F__)?/g, (match, type, v, f) => { return varTypeFunc[parseInt(type, 10)](v, f); }); } /** * Get the actual string of the expression. That is not stored in the tree so we have to get the indexes from the node * and then based on that get it from the expression. * @param expr * @param node */ export function getString(expr: string, node: SyntaxNode | TreeCursor | null | undefined) { if (!node) { return ''; } return returnVariables(expr.substring(node.from, node.to)); } /** * Create simple scalar binary op object. * @param opDef - definition of the op to be created * @param expr * @param numberNode - the node for the scalar * @param hasBool - whether operation has a bool modifier. Is used only for ops for which it makes sense. */ export function makeBinOp( opDef: { id: string; comparison?: boolean }, expr: string, numberNode: SyntaxNode, hasBool: boolean ): QueryBuilderOperation { const params: QueryBuilderOperationParamValue[] = [parseFloat(getString(expr, numberNode))]; if (opDef.comparison) { params.push(hasBool); } return { id: opDef.id, params, }; } /** * Get all nodes with type in the tree. This traverses the tree so it is safe only when you know there shouldn't be * too much nesting but you just want to skip some of the wrappers. For example getting function args this way would * not be safe is it would also find arguments of nested functions. * @param expr * @param cur * @param type - can be string or number, some data-sources (loki) haven't migrated over to using numeric constants defined in the lezer parsing library (e.g. lezer-promql). * @todo Remove string type definition when all data-sources have migrated to numeric constants */ export function getAllByType(expr: string, cur: SyntaxNode, type: number | string): string[] { if (cur.type.id === type || cur.name === type) { return [getString(expr, cur)]; } const values: string[] = []; let pos = 0; let child = cur.childAfter(pos); while (child) { values.push(...getAllByType(expr, child, type)); pos = child.to; child = cur.childAfter(pos); } return values; } /** * There aren't any spaces in the metric names, so let's introduce a wildcard into the regex for each space to better facilitate a fuzzy search */ export const regexifyLabelValuesQueryString = (query: string) => { const queryArray = query.split(' '); return queryArray.map((query) => `${query}.*`).join(''); };
.
Edit
..
Edit
LokiQueryModeller.test.ts
Edit
LokiQueryModeller.ts
Edit
binaryScalarOperations.ts
Edit
components
Edit
operationUtils.test.ts
Edit
operationUtils.ts
Edit
operations.test.ts
Edit
operations.ts
Edit
parsing.test.ts
Edit
parsing.ts
Edit
parsingUtils.test.ts
Edit
parsingUtils.ts
Edit
state.test.ts
Edit
state.ts
Edit
types.ts
Edit