@fc-components/monaco-editor 0.1.26 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/monaco-editor.cjs.development.js +87 -21
- package/dist/monaco-editor.cjs.development.js.map +1 -1
- package/dist/monaco-editor.cjs.production.min.js +1 -1
- package/dist/monaco-editor.cjs.production.min.js.map +1 -1
- package/dist/monaco-editor.esm.js +88 -22
- package/dist/monaco-editor.esm.js.map +1 -1
- package/dist/promql/completion/situation.d.ts +3 -0
- package/dist/promql/index.d.ts +0 -1
- package/package.json +2 -2
- package/src/promql/__tests__/completions.test.ts +72 -0
- package/src/promql/__tests__/situation.test.ts +85 -0
- package/src/promql/completion/completions.ts +11 -2
- package/src/promql/completion/getCompletionProvider.ts +15 -1
- package/src/promql/completion/situation.ts +73 -1
- package/src/promql/index.tsx +0 -18
- package/src/promql/promql.ts +3 -1
|
@@ -3,7 +3,7 @@ import MonacoEditor from 'react-monaco-editor';
|
|
|
3
3
|
import * as monaco from 'monaco-editor';
|
|
4
4
|
import { languages, Range, editor, Selection, KeyMod, KeyCode, MarkerSeverity } from 'monaco-editor';
|
|
5
5
|
import { promLanguageDefinition } from 'monaco-promql';
|
|
6
|
-
import { parser, LabelName, UnquotedLabelMatcher, LabelMatchers, VectorSelector, PromQL, Identifier, FunctionCallBody, StringLiteral, BinaryExpr, NumberDurationLiteralInDurationContext, MatrixSelector, GroupingLabels, AggregateModifier, AggregateExpr, MatchOp, EqlSingle, EqlRegex, Neq, NeqRegex } from '@fc-components/lezer-metricsql';
|
|
6
|
+
import { parser, LabelName, UnquotedLabelMatcher, LabelMatchers, VectorSelector, PromQL, Identifier, FunctionCallBody, StringLiteral, BinaryExpr, NumberDurationLiteralInDurationContext, MatrixSelector, GroupingLabels, WithExpr, WithAssignment, AggregateModifier, AggregateExpr, MatchOp, EqlSingle, EqlRegex, Neq, NeqRegex } from '@fc-components/lezer-metricsql';
|
|
7
7
|
import { v4 } from 'uuid';
|
|
8
8
|
import { css } from '@emotion/css';
|
|
9
9
|
|
|
@@ -340,6 +340,7 @@ for (var _i = 0, aggregations_1 = aggregations; _i < aggregations_1.length; _i++
|
|
|
340
340
|
// PromQL vector matching + the by and without clauses
|
|
341
341
|
// (https://prometheus.io/docs/prometheus/latest/querying/operators/#vector-matching)
|
|
342
342
|
var vectorMatching = ['on', 'ignoring', 'group_right', 'group_left', 'by', 'without'];
|
|
343
|
+
var cteKeywords = ['with'];
|
|
343
344
|
// Produce a regex matching elements : (elt1|elt2|...)
|
|
344
345
|
var vectorMatchingRegex = '(' + /*#__PURE__*/vectorMatching.reduce(function (prev, curr) {
|
|
345
346
|
return prev + '|' + curr;
|
|
@@ -351,7 +352,7 @@ var operators = ['+', '-', '*', '/', '%', '^', '==', '!=', '>', '<', '>=', '<=',
|
|
|
351
352
|
// (https://prometheus.io/docs/prometheus/latest/querying/basics/#offset-modifier)
|
|
352
353
|
var offsetModifier = ['offset'];
|
|
353
354
|
// Merging all the keywords in one list
|
|
354
|
-
var keywords = /*#__PURE__*/aggregations.concat(functions).concat(aggregationsOverTime).concat(vectorMatching).concat(offsetModifier);
|
|
355
|
+
var keywords = /*#__PURE__*/aggregations.concat(functions).concat(aggregationsOverTime).concat(vectorMatching).concat(offsetModifier).concat(cteKeywords);
|
|
355
356
|
// noinspection JSUnusedGlobalSymbols
|
|
356
357
|
var language = {
|
|
357
358
|
ignoreCase: false,
|
|
@@ -1289,6 +1290,15 @@ var RESOLVERS = [{
|
|
|
1289
1290
|
}, {
|
|
1290
1291
|
path: [GroupingLabels],
|
|
1291
1292
|
fun: resolveLabelsForGrouping
|
|
1293
|
+
}, {
|
|
1294
|
+
path: [WithExpr],
|
|
1295
|
+
fun: resolveWithExpr
|
|
1296
|
+
}, {
|
|
1297
|
+
path: [WithExpr, PromQL],
|
|
1298
|
+
fun: resolveWithExpr
|
|
1299
|
+
}, {
|
|
1300
|
+
path: [WithAssignment, WithExpr],
|
|
1301
|
+
fun: resolveWithExpr
|
|
1292
1302
|
}];
|
|
1293
1303
|
var LABEL_OP_MAP = /*#__PURE__*/new Map([[EqlSingle, '='], [EqlRegex, '=~'], [Neq, '!='], [NeqRegex, '!~']]);
|
|
1294
1304
|
function getLabelOp(opNode) {
|
|
@@ -1386,6 +1396,10 @@ function resolveLabelMatcher(node, text, _pos) {
|
|
|
1386
1396
|
// - a StringNode (like in `{job="^"}`)
|
|
1387
1397
|
// - or an error node (like in `{job=^}`)
|
|
1388
1398
|
var inStringNode = !node.type.isError;
|
|
1399
|
+
// calculate where the value starts
|
|
1400
|
+
// for string nodes, it's after the opening quote
|
|
1401
|
+
// for error nodes, it's at the node start
|
|
1402
|
+
var valueStartPos = inStringNode ? node.from + 1 : node.from;
|
|
1389
1403
|
var parent = walk(node, [['parent', UnquotedLabelMatcher]]);
|
|
1390
1404
|
if (parent === null) {
|
|
1391
1405
|
return null;
|
|
@@ -1412,7 +1426,8 @@ function resolveLabelMatcher(node, text, _pos) {
|
|
|
1412
1426
|
type: 'IN_LABEL_SELECTOR_WITH_LABEL_NAME',
|
|
1413
1427
|
labelName: labelName,
|
|
1414
1428
|
betweenQuotes: inStringNode,
|
|
1415
|
-
otherLabels: otherLabels
|
|
1429
|
+
otherLabels: otherLabels,
|
|
1430
|
+
valueStartPos: valueStartPos
|
|
1416
1431
|
};
|
|
1417
1432
|
}
|
|
1418
1433
|
var metricName = getNodeText(metricNameNode, text);
|
|
@@ -1421,7 +1436,8 @@ function resolveLabelMatcher(node, text, _pos) {
|
|
|
1421
1436
|
metricName: metricName,
|
|
1422
1437
|
labelName: labelName,
|
|
1423
1438
|
betweenQuotes: inStringNode,
|
|
1424
|
-
otherLabels: otherLabels
|
|
1439
|
+
otherLabels: otherLabels,
|
|
1440
|
+
valueStartPos: valueStartPos
|
|
1425
1441
|
};
|
|
1426
1442
|
}
|
|
1427
1443
|
function resolveErrorInLabelMatcher(node, text, pos) {
|
|
@@ -1455,6 +1471,25 @@ function resolveInFunction() {
|
|
|
1455
1471
|
type: 'IN_FUNCTION'
|
|
1456
1472
|
};
|
|
1457
1473
|
}
|
|
1474
|
+
function resolveWithExpr(node, text, pos) {
|
|
1475
|
+
// Find the containing WithExpr node (node may be a WithAssignment child)
|
|
1476
|
+
var withExprNode = node.type.id === WithExpr ? node : node.parent;
|
|
1477
|
+
while (withExprNode && withExprNode.type.id !== WithExpr) {
|
|
1478
|
+
withExprNode = withExprNode.parent;
|
|
1479
|
+
}
|
|
1480
|
+
if (!withExprNode) return null;
|
|
1481
|
+
// Only return IN_WITH_BODY when cursor is in the body expression (after the closing ')')
|
|
1482
|
+
var children = getNodeChildren(withExprNode);
|
|
1483
|
+
var closeParen = children.find(function (c) {
|
|
1484
|
+
return getNodeText(c, text) === ')';
|
|
1485
|
+
});
|
|
1486
|
+
if (closeParen && pos > closeParen.to) {
|
|
1487
|
+
return {
|
|
1488
|
+
type: 'IN_WITH_BODY'
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
return null;
|
|
1492
|
+
}
|
|
1458
1493
|
function resolveDurations() {
|
|
1459
1494
|
return {
|
|
1460
1495
|
type: 'IN_DURATION'
|
|
@@ -1564,6 +1599,25 @@ function getErrorNode(tree, pos) {
|
|
|
1564
1599
|
}
|
|
1565
1600
|
return null;
|
|
1566
1601
|
}
|
|
1602
|
+
function findMatchingParen(text, openPos) {
|
|
1603
|
+
var depth = 0;
|
|
1604
|
+
for (var i = openPos; i < text.length; i++) {
|
|
1605
|
+
if (text[i] === '(') depth++;
|
|
1606
|
+
if (text[i] === ')') {
|
|
1607
|
+
depth--;
|
|
1608
|
+
if (depth === 0) return i;
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
return -1;
|
|
1612
|
+
}
|
|
1613
|
+
function isInWithBody(text, pos) {
|
|
1614
|
+
var withMatch = text.match(/^with\s*\(/i);
|
|
1615
|
+
if (!withMatch) return false;
|
|
1616
|
+
var openParenPos = withMatch[0].length - 1;
|
|
1617
|
+
var closeParenPos = findMatchingParen(text, openParenPos);
|
|
1618
|
+
if (closeParenPos === -1) return false;
|
|
1619
|
+
return pos > closeParenPos;
|
|
1620
|
+
}
|
|
1567
1621
|
function getSituation(text, pos) {
|
|
1568
1622
|
// there is a special-case when we are at the start of writing text,
|
|
1569
1623
|
// so we handle that case first
|
|
@@ -1572,6 +1626,12 @@ function getSituation(text, pos) {
|
|
|
1572
1626
|
type: 'EMPTY'
|
|
1573
1627
|
};
|
|
1574
1628
|
}
|
|
1629
|
+
// text-based check for with body (fallback until grammar supports WithExpr)
|
|
1630
|
+
if (isInWithBody(text, pos)) {
|
|
1631
|
+
return {
|
|
1632
|
+
type: 'IN_WITH_BODY'
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1575
1635
|
/**
|
|
1576
1636
|
PromQL
|
|
1577
1637
|
Expr
|
|
@@ -1654,12 +1714,19 @@ function _getAllFunctionsAndMetricNamesCompletions() {
|
|
|
1654
1714
|
while (1) switch (_context.n) {
|
|
1655
1715
|
case 0:
|
|
1656
1716
|
metricNames = getAllMetricNamesCompletions(dataProvider);
|
|
1657
|
-
return _context.a(2, [].concat(FUNCTION_COMPLETIONS, metricNames));
|
|
1717
|
+
return _context.a(2, [CTE_KEYWORD_COMPLETION].concat(FUNCTION_COMPLETIONS, metricNames));
|
|
1658
1718
|
}
|
|
1659
1719
|
}, _callee);
|
|
1660
1720
|
}));
|
|
1661
1721
|
return _getAllFunctionsAndMetricNamesCompletions.apply(this, arguments);
|
|
1662
1722
|
}
|
|
1723
|
+
var CTE_KEYWORD_COMPLETION = {
|
|
1724
|
+
type: 'FUNCTION',
|
|
1725
|
+
label: 'with',
|
|
1726
|
+
insertText: 'with (',
|
|
1727
|
+
detail: 'with (cte_name = expr, ...) expr',
|
|
1728
|
+
documentation: 'Define Common Table Expressions (CTEs) for use in the query. MetricsQL extension.'
|
|
1729
|
+
};
|
|
1663
1730
|
var DURATION_COMPLETIONS = /*#__PURE__*/['1m', '5m', '10m', '30m', '1h', '1d'].map(function (text) {
|
|
1664
1731
|
return {
|
|
1665
1732
|
type: 'DURATION',
|
|
@@ -1901,6 +1968,7 @@ function getCompletions(situation, dataProvider) {
|
|
|
1901
1968
|
return Promise.resolve(DURATION_COMPLETIONS);
|
|
1902
1969
|
case 'IN_FUNCTION':
|
|
1903
1970
|
return getAllFunctionsAndMetricNamesCompletions(dataProvider);
|
|
1971
|
+
case 'IN_WITH_BODY':
|
|
1904
1972
|
case 'AT_ROOT':
|
|
1905
1973
|
{
|
|
1906
1974
|
return getAllFunctionsAndMetricNamesCompletions(dataProvider);
|
|
@@ -1909,7 +1977,7 @@ function getCompletions(situation, dataProvider) {
|
|
|
1909
1977
|
{
|
|
1910
1978
|
var metricNames = getAllMetricNamesCompletions(dataProvider);
|
|
1911
1979
|
var historyCompletions = getAllHistoryCompletions();
|
|
1912
|
-
return Promise.resolve([].concat(historyCompletions, FUNCTION_COMPLETIONS, metricNames));
|
|
1980
|
+
return Promise.resolve([].concat(historyCompletions, [CTE_KEYWORD_COMPLETION], FUNCTION_COMPLETIONS, metricNames));
|
|
1913
1981
|
}
|
|
1914
1982
|
case 'IN_LABEL_SELECTOR_NO_LABEL_NAME':
|
|
1915
1983
|
return getLabelNamesForSelectorCompletions(situation.metricName, situation.hasOperator, situation.otherLabels, dataProvider);
|
|
@@ -1973,6 +2041,18 @@ function getCompletionProvider(monaco, dataProvider) {
|
|
|
1973
2041
|
// to stop it, we use a number-as-string sortkey,
|
|
1974
2042
|
// so that monaco keeps the order we use
|
|
1975
2043
|
var maxIndexDigits = items.length.toString().length;
|
|
2044
|
+
// Determine the completion range based on situation type
|
|
2045
|
+
var completionRange = range;
|
|
2046
|
+
if (situation && situation.type === 'IN_LABEL_SELECTOR_WITH_LABEL_NAME' && situation.betweenQuotes) {
|
|
2047
|
+
// For label values within quotes, replace from the start of the value to the current position
|
|
2048
|
+
var valueStartPosition = model.getPositionAt(situation.valueStartPos);
|
|
2049
|
+
completionRange = monaco.Range.lift({
|
|
2050
|
+
startLineNumber: valueStartPosition.lineNumber,
|
|
2051
|
+
endLineNumber: position.lineNumber,
|
|
2052
|
+
startColumn: valueStartPosition.column,
|
|
2053
|
+
endColumn: position.column
|
|
2054
|
+
});
|
|
2055
|
+
}
|
|
1976
2056
|
var suggestions = items.map(function (item, index) {
|
|
1977
2057
|
return {
|
|
1978
2058
|
kind: getMonacoCompletionItemKind(item.type, monaco),
|
|
@@ -1981,7 +2061,7 @@ function getCompletionProvider(monaco, dataProvider) {
|
|
|
1981
2061
|
detail: item.detail,
|
|
1982
2062
|
documentation: item.documentation,
|
|
1983
2063
|
sortText: index.toString().padStart(maxIndexDigits, '0'),
|
|
1984
|
-
range:
|
|
2064
|
+
range: completionRange,
|
|
1985
2065
|
command: item.triggerOnInsert ? {
|
|
1986
2066
|
id: 'editor.action.triggerSuggest',
|
|
1987
2067
|
title: ''
|
|
@@ -2130,9 +2210,7 @@ function PromQLEditor(props) {
|
|
|
2130
2210
|
onChange = props.onChange,
|
|
2131
2211
|
onEnter = props.onEnter,
|
|
2132
2212
|
onBlur = props.onBlur,
|
|
2133
|
-
editorDidMount = props.editorDidMount
|
|
2134
|
-
_props$debugEscKey = props.debugEscKey,
|
|
2135
|
-
debugEscKey = _props$debugEscKey === void 0 ? false : _props$debugEscKey;
|
|
2213
|
+
editorDidMount = props.editorDidMount;
|
|
2136
2214
|
var autocompleteDisposeFun = useRef(null);
|
|
2137
2215
|
var containerRef = useRef(null);
|
|
2138
2216
|
var dataProviderRef = useRef(null);
|
|
@@ -2222,18 +2300,6 @@ function PromQLEditor(props) {
|
|
|
2222
2300
|
editor$1.addCommand(KeyCode.Enter, function () {
|
|
2223
2301
|
onEnter == null || onEnter(editor$1.getValue());
|
|
2224
2302
|
}, '!suggestWidgetVisible && isEditorFocused' + id);
|
|
2225
|
-
editor$1.addCommand(KeyCode.Escape, function () {
|
|
2226
|
-
if (debugEscKey) {
|
|
2227
|
-
var _editor$_contextKeySe;
|
|
2228
|
-
var suggestWidgetVisible = editor$1 == null || (_editor$_contextKeySe = editor$1._contextKeyService) == null || _editor$_contextKeySe.getContextKeyValue == null ? void 0 : _editor$_contextKeySe.getContextKeyValue('suggestWidgetVisible');
|
|
2229
|
-
// eslint-disable-next-line no-console
|
|
2230
|
-
console.log('[PromQLEditor][ESC]', {
|
|
2231
|
-
suggestWidgetVisible: suggestWidgetVisible,
|
|
2232
|
-
valueLength: editor$1.getValue().length
|
|
2233
|
-
});
|
|
2234
|
-
}
|
|
2235
|
-
editor$1.trigger('keyboard', 'hideSuggestWidget', {});
|
|
2236
|
-
}, 'suggestWidgetVisible && isEditorFocused' + id);
|
|
2237
2303
|
// Initialize previous content tracking
|
|
2238
2304
|
previousContentRef.current = editor$1.getValue();
|
|
2239
2305
|
lastDeletionTriggerTimeRef.current = 0;
|