@fc-components/monaco-editor 0.1.23 → 0.1.25

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.
@@ -11,6 +11,7 @@ export declare type Situation = {
11
11
  type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME';
12
12
  metricName?: string;
13
13
  otherLabels: Label[];
14
+ hasOperator: boolean;
14
15
  } | {
15
16
  type: 'IN_GROUPING';
16
17
  metricName: string;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.1.23",
6
+ "version": "0.1.25",
7
7
  "license": "MIT",
8
8
  "main": "dist/index.js",
9
9
  "module": "dist/monaco-editor.esm.js",
@@ -122,8 +122,8 @@ async function getLabelNamesForCompletions(
122
122
  }));
123
123
  }
124
124
 
125
- async function getLabelNamesForSelectorCompletions(metric: string | undefined, otherLabels: Label[], dataProvider: DataProvider): Promise<Completion[]> {
126
- return getLabelNamesForCompletions(metric, '=', true, otherLabels, dataProvider);
125
+ async function getLabelNamesForSelectorCompletions(metric: string | undefined, hasOperator: boolean, otherLabels: Label[], dataProvider: DataProvider): Promise<Completion[]> {
126
+ return getLabelNamesForCompletions(metric, hasOperator ? '' : '=', true, otherLabels, dataProvider);
127
127
  }
128
128
 
129
129
  async function getLabelNamesForByCompletions(metric: string | undefined, otherLabels: Label[], dataProvider: DataProvider): Promise<Completion[]> {
@@ -186,7 +186,7 @@ export function getCompletions(situation: Situation, dataProvider: DataProvider)
186
186
  return Promise.resolve([...historyCompletions, ...FUNCTION_COMPLETIONS, ...metricNames]);
187
187
  }
188
188
  case 'IN_LABEL_SELECTOR_NO_LABEL_NAME':
189
- return getLabelNamesForSelectorCompletions(situation.metricName, situation.otherLabels, dataProvider);
189
+ return getLabelNamesForSelectorCompletions(situation.metricName, situation.hasOperator, situation.otherLabels, dataProvider);
190
190
  case 'IN_GROUPING':
191
191
  return getLabelNamesForByCompletions(situation.metricName, situation.otherLabels, dataProvider);
192
192
  case 'IN_LABEL_SELECTOR_WITH_LABEL_NAME':
@@ -132,6 +132,7 @@ export type Situation =
132
132
  type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME';
133
133
  metricName?: string;
134
134
  otherLabels: Label[];
135
+ hasOperator: boolean;
135
136
  }
136
137
  | {
137
138
  type: 'IN_GROUPING';
@@ -158,14 +159,38 @@ function isPathMatch(resolverPath: NodeTypeId[], cursorPath: number[]): boolean
158
159
  const ERROR_NODE_NAME: NodeTypeId = 0; // this is used as error-id
159
160
 
160
161
  const RESOLVERS: Resolver[] = [
162
+ {
163
+ path: [LabelName, UnquotedLabelMatcher],
164
+ fun: resolveLabelName,
165
+ },
166
+ {
167
+ path: [UnquotedLabelMatcher, LabelMatchers],
168
+ fun: resolveLabelName,
169
+ },
161
170
  {
162
171
  path: [LabelMatchers, VectorSelector],
163
172
  fun: resolveLabelKeysWithEquals,
164
173
  },
174
+ {
175
+ path: [LabelMatchers, VectorSelector, PromQL],
176
+ fun: resolveLabelKeysWithEquals,
177
+ },
165
178
  {
166
179
  path: [PromQL],
167
180
  fun: resolveTopLevel,
168
181
  },
182
+ {
183
+ path: [VectorSelector, PromQL],
184
+ fun: resolveTopLevel,
185
+ },
186
+ {
187
+ path: [Identifier, VectorSelector],
188
+ fun: resolveTopLevel,
189
+ },
190
+ {
191
+ path: [Identifier, VectorSelector, PromQL],
192
+ fun: resolveTopLevel,
193
+ },
169
194
  {
170
195
  path: [FunctionCallBody],
171
196
  fun: resolveInFunction,
@@ -180,7 +205,7 @@ const RESOLVERS: Resolver[] = [
180
205
  },
181
206
  {
182
207
  path: [ERROR_NODE_NAME, UnquotedLabelMatcher],
183
- fun: resolveLabelMatcher,
208
+ fun: resolveErrorInLabelMatcher,
184
209
  },
185
210
  {
186
211
  path: [ERROR_NODE_NAME, NumberDurationLiteralInDurationContext, MatrixSelector],
@@ -359,6 +384,32 @@ function resolveLabelMatcher(node: SyntaxNode, text: string, _pos: number): Situ
359
384
  };
360
385
  }
361
386
 
387
+ function resolveErrorInLabelMatcher(node: SyntaxNode, text: string, pos: number): Situation | null {
388
+ // 处理错误节点在 UnquotedLabelMatcher 中的情况
389
+ // 需要判断是删除值还是删除名称
390
+
391
+ const parent = walk(node, [['parent', UnquotedLabelMatcher]]);
392
+ if (parent === null) {
393
+ return null;
394
+ }
395
+
396
+ const labelNameNode = walk(parent, [['firstChild', LabelName]]);
397
+
398
+ // 情况1: 有完整的标签名 -> 提示 label value
399
+ if (labelNameNode !== null) {
400
+ // 检查是否有 MatchOp(=, =~, !=, !~)
401
+ const hasMatchOp = walk(labelNameNode, [['nextSibling', MatchOp]]) !== null;
402
+ if (hasMatchOp) {
403
+ // 这是值场景,使用 resolveLabelMatcher 的逻辑
404
+ return resolveLabelMatcher(node, text, pos);
405
+ }
406
+ }
407
+
408
+ // 情况2: 没有完整的标签名或没有 MatchOp -> 提示 label name
409
+ // 使用 resolveLabelName 的逻辑
410
+ return resolveLabelName(parent, text, pos);
411
+ }
412
+
362
413
  function resolveTopLevel(): Situation {
363
414
  return {
364
415
  type: 'AT_ROOT',
@@ -377,6 +428,66 @@ function resolveDurations(): Situation {
377
428
  };
378
429
  }
379
430
 
431
+ function resolveLabelName(node: SyntaxNode, text: string, pos: number): Situation | null {
432
+ let labelMatcherNode: SyntaxNode | null = node;
433
+
434
+ // 如果节点是错误节点,尝试找到其父节点 UnquotedLabelMatcher
435
+ if (node.type.isError) {
436
+ const parent = node.parent;
437
+ if (parent !== null && parent.type.id === UnquotedLabelMatcher) {
438
+ labelMatcherNode = parent;
439
+ } else {
440
+ return null;
441
+ }
442
+ } else if (node.type.id === LabelName) {
443
+ labelMatcherNode = walk(node, [['parent', UnquotedLabelMatcher]]);
444
+ }
445
+
446
+ if (labelMatcherNode === null || labelMatcherNode.type.id !== UnquotedLabelMatcher) {
447
+ return null;
448
+ }
449
+
450
+ const labelNameNode = walk(labelMatcherNode, [['firstChild', LabelName]]);
451
+ if (labelNameNode === null) {
452
+ return null;
453
+ }
454
+
455
+ if (pos > labelNameNode.to) {
456
+ return null;
457
+ }
458
+
459
+ const labelMatchersNode = walk(labelMatcherNode, [['parent', LabelMatchers]]);
460
+ if (labelMatchersNode === null) {
461
+ return null;
462
+ }
463
+
464
+ const currentLabelName = getNodeText(labelNameNode, text);
465
+ const allLabels = getLabels(labelMatchersNode, text);
466
+ const otherLabels = allLabels.filter((label) => label.name !== currentLabelName);
467
+ const hasOperator = walk(labelNameNode, [['nextSibling', MatchOp]]) !== null;
468
+
469
+ const metricNameNode = walk(labelMatchersNode, [
470
+ ['parent', VectorSelector],
471
+ ['firstChild', Identifier],
472
+ ]);
473
+
474
+ if (metricNameNode === null) {
475
+ return {
476
+ type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME',
477
+ otherLabels,
478
+ hasOperator,
479
+ };
480
+ }
481
+
482
+ const metricName = getNodeText(metricNameNode, text);
483
+ return {
484
+ type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME',
485
+ metricName,
486
+ otherLabels,
487
+ hasOperator,
488
+ };
489
+ }
490
+
380
491
  function resolveLabelKeysWithEquals(node: SyntaxNode, text: string, pos: number): Situation | null {
381
492
  // next false positive:
382
493
  // `something{a="1"^}`
@@ -407,6 +518,7 @@ function resolveLabelKeysWithEquals(node: SyntaxNode, text: string, pos: number)
407
518
  return {
408
519
  type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME',
409
520
  otherLabels,
521
+ hasOperator: false,
410
522
  };
411
523
  }
412
524
 
@@ -416,6 +528,7 @@ function resolveLabelKeysWithEquals(node: SyntaxNode, text: string, pos: number)
416
528
  type: 'IN_LABEL_SELECTOR_NO_LABEL_NAME',
417
529
  metricName,
418
530
  otherLabels,
531
+ hasOperator: false,
419
532
  };
420
533
  }
421
534
 
@@ -471,8 +584,10 @@ export function getSituation(text: string, pos: number): Situation | null {
471
584
  const currentNode = cur.node;
472
585
 
473
586
  const ids = [cur.type.id];
587
+ // const names = [cur.type.name];
474
588
  while (cur.parent()) {
475
589
  ids.push(cur.type.id);
590
+ // names.push(cur.type.name);
476
591
  }
477
592
 
478
593
  for (let resolver of RESOLVERS) {
@@ -108,6 +108,8 @@ export default function PromQLEditor(props: PromQLEditorProps & DataProviderPara
108
108
  const containerRef = useRef<HTMLDivElement>(null);
109
109
  const dataProviderRef = useRef<DataProvider | null>(null);
110
110
  const editorRef = useRef<monacoTypes.editor.IStandaloneCodeEditor | null>(null);
111
+ const previousContentRef = useRef<string>('');
112
+ const lastDeletionTriggerTimeRef = useRef<number>(0);
111
113
  const styles = getStyles(placeholder);
112
114
  const handleEditorDidMount = (editor: monacoTypes.editor.IStandaloneCodeEditor) => {
113
115
  editorRef.current = editor;
@@ -209,6 +211,11 @@ export default function PromQLEditor(props: PromQLEditorProps & DataProviderPara
209
211
  '!suggestWidgetVisible && isEditorFocused' + id,
210
212
  );
211
213
 
214
+ // Initialize previous content tracking
215
+ previousContentRef.current = editor.getValue();
216
+ lastDeletionTriggerTimeRef.current = 0;
217
+ const DELETION_TRIGGER_DEBOUNCE_MS = 1000;
218
+
212
219
  editor.onDidChangeModelContent(() => {
213
220
  const model = editor.getModel();
214
221
  if (model) {
@@ -221,6 +228,23 @@ export default function PromQLEditor(props: PromQLEditorProps & DataProviderPara
221
228
  }));
222
229
 
223
230
  monaco.editor.setModelMarkers(model, 'owner', markers);
231
+
232
+ // Detect deletion and trigger completion
233
+ const previousContent = previousContentRef.current;
234
+ const isDeletion = query.length < previousContent.length;
235
+
236
+ if (isDeletion && enableAutocomplete) {
237
+ const now = Date.now();
238
+ if (now - lastDeletionTriggerTimeRef.current >= DELETION_TRIGGER_DEBOUNCE_MS) {
239
+ lastDeletionTriggerTimeRef.current = now;
240
+ // Trigger completion after deletion
241
+ setTimeout(() => {
242
+ editor.trigger('keyboard', 'editor.action.triggerSuggest', {});
243
+ }, 0);
244
+ }
245
+ }
246
+
247
+ previousContentRef.current = query;
224
248
  }
225
249
  });
226
250
 
package/src/sql/index.tsx CHANGED
@@ -267,7 +267,6 @@ export default function SqlEditor(props: SqlEditorProps) {
267
267
  scrollBeyondLastLine: false,
268
268
  smoothScrolling: true,
269
269
  fontSize,
270
- lineHeight: 24,
271
270
  tabSize: 2,
272
271
  wordWrap: 'on',
273
272
  automaticLayout: true,