@fc-components/monaco-editor 0.1.24 → 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.
- package/dist/monaco-editor.cjs.development.js +122 -10
- 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 +123 -11
- package/dist/monaco-editor.esm.js.map +1 -1
- package/dist/promql/completion/situation.d.ts +1 -0
- package/package.json +1 -1
- package/src/promql/completion/completions.ts +3 -3
- package/src/promql/completion/situation.ts +116 -1
- package/src/promql/index.tsx +24 -0
package/package.json
CHANGED
|
@@ -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:
|
|
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) {
|
package/src/promql/index.tsx
CHANGED
|
@@ -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
|
|