Skip to content

Commit

Permalink
Fix plugin loading issue in query editor (#245)
Browse files Browse the repository at this point in the history
* fix query editor formating

* update CHANGELOG.md

* update monaco languages

---------

Co-authored-by: Roman Khavronenko <[email protected]>
  • Loading branch information
Loori-R and hagen1778 authored Jan 23, 2025
1 parent cf408d3 commit 214f68e
Show file tree
Hide file tree
Showing 4 changed files with 265 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## tip

* BUGFIX: fix issue with including the lezer-metricsql package to the build and fix public folder. See [this PR](https://github.com/VictoriaMetrics/victoriametrics-datasource/pull/256).
* BUGFIX: fix plugin loading for query formatting in the editor. See [this issue](https://github.com/VictoriaMetrics/victoriametrics-datasource/issues/234).
* BUGFIX: fix issue with "Prettify query" functionality corrupting dashboard JSON model. See [this issue](https://github.com/VictoriaMetrics/victoriametrics-datasource/issues/242).

## v0.12.0
Expand Down
11 changes: 6 additions & 5 deletions src/components/monaco-query-field/MonacoQueryField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { useTheme2, ReactMonacoEditor, Monaco, monacoTypes } from '@grafana/ui';
import { Props } from './MonacoQueryFieldProps';
import { getOverrideServices } from './getOverrideServices';
import { getCompletionProvider, getSuggestOptions } from './monaco-completion-provider';
import { language, languageConfiguration } from "./promql";

const options: monacoTypes.editor.IStandaloneEditorConstructionOptions = {
codeLens: false,
Expand Down Expand Up @@ -78,13 +79,13 @@ let PROMQL_SETUP_STARTED = false;
function ensurePromQL(monaco: Monaco) {
if (!PROMQL_SETUP_STARTED) {
PROMQL_SETUP_STARTED = true;
const { aliases, extensions, mimetypes, loader } = promLanguageDefinition;
const { aliases, extensions, mimetypes } = promLanguageDefinition;
monaco.languages.register({ id: PROMQL_LANG_ID, aliases, extensions, mimetypes });

loader().then((mod) => {
monaco.languages.setMonarchTokensProvider(PROMQL_LANG_ID, mod.language);
monaco.languages.setLanguageConfiguration(PROMQL_LANG_ID, mod.languageConfiguration);
});
// @ts-ignore
monaco.languages.setMonarchTokensProvider(PROMQL_LANG_ID, language);
// @ts-ignore
monaco.languages.setLanguageConfiguration(PROMQL_LANG_ID, languageConfiguration);
}
}

Expand Down
256 changes: 256 additions & 0 deletions src/components/monaco-query-field/promql.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
// The MIT License (MIT)
//
// Copyright (c) Celian Garcia and Augustin Husson @ Amadeus IT Group
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
'use strict';
// import { languages } from "monaco-editor";
// noinspection JSUnusedGlobalSymbols
export const languageConfiguration = {
// the default separators except `@$`
wordPattern: /(-?\d*\.\d\w*)|([^`~!#%^&*()\-=+\[{\]}\\|;:'",.<>\/?\s]+)/g,
// Not possible to make comments in PromQL syntax
comments: {
lineComment: '#',
},
brackets: [
['{', '}'],
['[', ']'],
['(', ')'],
],
autoClosingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '"', close: '"' },
{ open: "'", close: "'" },
],
surroundingPairs: [
{ open: '{', close: '}' },
{ open: '[', close: ']' },
{ open: '(', close: ')' },
{ open: '"', close: '"' },
{ open: "'", close: "'" },
{ open: '<', close: '>' },
],
folding: {},
};
// PromQL Aggregation Operators
// (https://prometheus.io/docs/prometheus/latest/querying/operators/#aggregation-operators)
const aggregations = [
'sum',
'min',
'max',
'avg',
'group',
'stddev',
'stdvar',
'count',
'count_values',
'bottomk',
'topk',
'quantile',
];
// PromQL functions
// (https://prometheus.io/docs/prometheus/latest/querying/functions/)
const functions = [
'abs',
'absent',
'ceil',
'changes',
'clamp_max',
'clamp_min',
'day_of_month',
'day_of_week',
'days_in_month',
'delta',
'deriv',
'double_exponential_smoothing',
'exp',
'floor',
'histogram_quantile',
'histogram_avg',
'histogram_count',
'histogram_sum',
'histogram_fraction',
'histogram_stddev',
'histogram_stdvar',
// Renamed as DoubleExponentialSmoothing with Prometheus v3.x
// https://github.com/prometheus/prometheus/pull/14930
'holt_winters',
'hour',
'idelta',
'increase',
'irate',
'label_join',
'label_replace',
'ln',
'log2',
'log10',
'minute',
'month',
'predict_linear',
'rate',
'resets',
'round',
'scalar',
'sort',
'sort_desc',
'sqrt',
'time',
'timestamp',
'vector',
'year',
];
// PromQL specific functions: Aggregations over time
// (https://prometheus.io/docs/prometheus/latest/querying/functions/#aggregation_over_time)
const aggregationsOverTime = [];
for (let _i = 0, aggregations_1 = aggregations; _i < aggregations_1.length; _i++) {
let agg = aggregations_1[_i];
aggregationsOverTime.push(agg + '_over_time');
}
// PromQL vector matching + the by and without clauses
// (https://prometheus.io/docs/prometheus/latest/querying/operators/#vector-matching)
const vectorMatching = ['on', 'ignoring', 'group_right', 'group_left', 'by', 'without'];
// Produce a regex matching elements : (elt1|elt2|...)
const vectorMatchingRegex =
'(' +
vectorMatching.reduce(function (prev, curr) {
return prev + '|' + curr;
}) +
')';
// PromQL Operators
// (https://prometheus.io/docs/prometheus/latest/querying/operators/)
const operators = ['+', '-', '*', '/', '%', '^', '==', '!=', '>', '<', '>=', '<=', 'and', 'or', 'unless'];
// PromQL offset modifier
// (https://prometheus.io/docs/prometheus/latest/querying/basics/#offset-modifier)
const offsetModifier = ['offset'];
// Merging all the keywords in one list
const keywords = aggregations
.concat(functions)
.concat(aggregationsOverTime)
.concat(vectorMatching)
.concat(offsetModifier);
// noinspection JSUnusedGlobalSymbols
export const language = {
ignoreCase: false,
defaultToken: '',
tokenPostfix: '.promql',
keywords: keywords,
operators: operators,
vectorMatching: vectorMatchingRegex,
// we include these common regular expressions
symbols: /[=><!~?:&|+\-*\/^%]+/,
escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
digits: /\d+(_+\d+)*/,
octaldigits: /[0-7]+(_+[0-7]+)*/,
binarydigits: /[0-1]+(_+[0-1]+)*/,
hexdigits: /[[0-9a-fA-F]+(_+[0-9a-fA-F]+)*/,
integersuffix: /(ll|LL|u|U|l|L)?(ll|LL|u|U|l|L)?/,
floatsuffix: /[fFlL]?/,
// The main tokenizer for our languages
tokenizer: {
root: [
// 'by', 'without' and vector matching
[/@vectorMatching\s*(?=\()/, 'type', '@clauses'],
// labels
[/[a-z_]\w*(?=\s*(=|!=|=~|!~))/, 'tag'],
// comments
[/(^#.*$)/, 'comment'],
// all keywords have the same color
[
/[a-zA-Z_]\w*/,
{
cases: {
'@keywords': 'type',
'@default': 'identifier',
},
},
],
// strings
[/"([^"\\]|\\.)*$/, 'string.invalid'],
[/'([^'\\]|\\.)*$/, 'string.invalid'],
[/"/, 'string', '@string_double'],
[/'/, 'string', '@string_single'],
[/`/, 'string', '@string_backtick'],
// whitespace
{ include: '@whitespace' },
// delimiters and operators
[/[{}()\[\]]/, '@brackets'],
[/[<>](?!@symbols)/, '@brackets'],
[
/@symbols/,
{
cases: {
'@operators': 'delimiter',
'@default': '',
},
},
],
// numbers
[/\d+[smhdwy]/, 'number'],
[/\d*\d+[eE]([\-+]?\d+)?(@floatsuffix)/, 'number.float'],
[/\d*\.\d+([eE][\-+]?\d+)?(@floatsuffix)/, 'number.float'],
[/0[xX][0-9a-fA-F']*[0-9a-fA-F](@integersuffix)/, 'number.hex'],
[/0[0-7']*[0-7](@integersuffix)/, 'number.octal'],
[/0[bB][0-1']*[0-1](@integersuffix)/, 'number.binary'],
[/\d[\d']*\d(@integersuffix)/, 'number'],
[/\d(@integersuffix)/, 'number'],
],
string_double: [
[/[^\\"]+/, 'string'],
[/@escapes/, 'string.escape'],
[/\\./, 'string.escape.invalid'],
[/"/, 'string', '@pop'],
],
string_single: [
[/[^\\']+/, 'string'],
[/@escapes/, 'string.escape'],
[/\\./, 'string.escape.invalid'],
[/'/, 'string', '@pop'],
],
string_backtick: [
[/[^\\`$]+/, 'string'],
[/@escapes/, 'string.escape'],
[/\\./, 'string.escape.invalid'],
[/`/, 'string', '@pop'],
],
clauses: [
[/[^(,)]/, 'tag'],
[/\)/, 'identifier', '@pop'],
],
whitespace: [[/[ \t\r\n]+/, 'white']],
},
};
// noinspection JSUnusedGlobalSymbols
// export const completionItemProvider = {
// provideCompletionItems: function () {
// // To simplify, we made the choice to never create automatically the parenthesis behind keywords
// // It is because in PromQL, some keywords need parenthesis behind, some don't, some can have but it's optional.
// const suggestions = keywords.map(function (value) {
// return {
// label: value,
// kind: languages.CompletionItemKind.Keyword,
// insertText: value,
// insertTextRules: languages.CompletionItemInsertTextRule.InsertAsSnippet
// };
// });
// return { suggestions: suggestions };
// }
// };
4 changes: 2 additions & 2 deletions src/metric_find_query.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import { DataSourceInstanceSettings, TimeRange, toUtc } from '@grafana/data';
import { BackendDataSourceResponse, BackendSrvRequest, FetchResponse, TemplateSrv } from '@grafana/runtime';

import { PrometheusDatasource } from './datasource';
import {PromOptions} from "./types";
import PrometheusMetricFindQuery from "./metric_find_query";
import { PromOptions } from "./types";

const fetchMock = jest.fn((options: BackendSrvRequest): Observable<FetchResponse<BackendDataSourceResponse>> => {
const fetchMock = jest.fn((_options: BackendSrvRequest): Observable<FetchResponse<BackendDataSourceResponse>> => {
return of({} as unknown as FetchResponse);
});

Expand Down

0 comments on commit 214f68e

Please sign in to comment.