@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.
@@ -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: 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;