mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
vmui: fix auto-completion triggers (#6566)
### Describe Your Changes
- Fixes auto-complete triggers according to [these
comments](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5866#issuecomment-2065273421).
- Fixes loading and displaying suggestions when there is no metric in
the expression.
Related issue: #6153
- Adds quotes when inserting label values.
Related issue: #6260
- [x] My change adheres [VictoriaMetrics contributing
guidelines](https://docs.victoriametrics.com/contributing/).
(cherry picked from commit 53919327b2
)
This commit is contained in:
parent
9b543a1394
commit
7d37ca3159
4 changed files with 37 additions and 16 deletions
|
@ -34,14 +34,25 @@ const QueryEditorAutocomplete: FC<QueryEditorAutocompleteProps> = ({
|
|||
}, [value, caretPosition]);
|
||||
|
||||
const exprLastPart = useMemo(() => {
|
||||
const parts = values.beforeCursor.split("}");
|
||||
const regexpSplit = /\s(or|and|unless|default|ifnot|if|group_left|group_right)\s|}|\+|\|-|\*|\/|\^/i;
|
||||
const parts = values.beforeCursor.split(regexpSplit);
|
||||
return parts[parts.length - 1];
|
||||
}, [values]);
|
||||
|
||||
const metric = useMemo(() => {
|
||||
const regexp = /\b[^{}(),\s]+(?={|$)/g;
|
||||
const match = exprLastPart.match(regexp);
|
||||
return match ? match[0] : "";
|
||||
const regex1 = /\w+\((?<metricName>[^)]+)\)\s+(by|without|on|ignoring)\s*\(\w*/gi;
|
||||
const matchAlt = [...exprLastPart.matchAll(regex1)];
|
||||
if (matchAlt.length > 0 && matchAlt[0].groups && matchAlt[0].groups.metricName) {
|
||||
return matchAlt[0].groups.metricName;
|
||||
}
|
||||
|
||||
const regex2 = /^\s*\b(?<metricName>[^{}(),\s]+)(?={|$)/g;
|
||||
const match = [...exprLastPart.matchAll(regex2)];
|
||||
if (match.length > 0 && match[0].groups && match[0].groups.metricName) {
|
||||
return match[0].groups.metricName;
|
||||
}
|
||||
|
||||
return "";
|
||||
}, [exprLastPart]);
|
||||
|
||||
const label = useMemo(() => {
|
||||
|
@ -51,7 +62,7 @@ const QueryEditorAutocomplete: FC<QueryEditorAutocompleteProps> = ({
|
|||
}, [exprLastPart]);
|
||||
|
||||
const shouldSuppressAutoSuggestion = (value: string) => {
|
||||
const pattern = /([{(),+\-*/^]|\b(?:or|and|unless|default|ifnot|if|group_left|group_right)\b)/;
|
||||
const pattern = /([{(),+\-*/^]|\b(?:or|and|unless|default|ifnot|if|group_left|group_right|by|without|on|ignoring)\b)/i;
|
||||
const parts = value.split(/\s+/);
|
||||
const partsCount = parts.length;
|
||||
const lastPart = parts[partsCount - 1];
|
||||
|
@ -63,12 +74,16 @@ const QueryEditorAutocomplete: FC<QueryEditorAutocompleteProps> = ({
|
|||
};
|
||||
|
||||
const context = useMemo(() => {
|
||||
if (!values.beforeCursor || values.beforeCursor.endsWith("}") || shouldSuppressAutoSuggestion(values.beforeCursor)) {
|
||||
const valueBeforeCursor = values.beforeCursor.trim();
|
||||
const endOfClosedBrackets = ["}", ")"].some(char => valueBeforeCursor.endsWith(char));
|
||||
const endOfClosedQuotes = !hasUnclosedQuotes(valueBeforeCursor) && ["`", "'", "\""].some(char => valueBeforeCursor.endsWith(char));
|
||||
if (!values.beforeCursor || endOfClosedBrackets || endOfClosedQuotes || shouldSuppressAutoSuggestion(values.beforeCursor)) {
|
||||
return QueryContextType.empty;
|
||||
}
|
||||
|
||||
const labelRegexp = /\{[^}]*$/;
|
||||
const labelValueRegexp = new RegExp(`(${escapeRegexp(metric)})?{?.+${escapeRegexp(label)}(=|!=|=~|!~)"?([^"]*)$`, "g");
|
||||
const labelRegexp = /(?:by|without|on|ignoring)\s*\(\s*[^)]*$|\{[^}]*$/i;
|
||||
const patternLabelValue = `(${escapeRegexp(metric)})?{?.+${escapeRegexp(label)}(=|!=|=~|!~)"?([^"]*)$`;
|
||||
const labelValueRegexp = new RegExp(patternLabelValue, "g");
|
||||
|
||||
switch (true) {
|
||||
case labelValueRegexp.test(values.beforeCursor):
|
||||
|
@ -81,7 +96,7 @@ const QueryEditorAutocomplete: FC<QueryEditorAutocompleteProps> = ({
|
|||
}, [values, metric, label]);
|
||||
|
||||
const valueByContext = useMemo(() => {
|
||||
const wordMatch = values.beforeCursor.match(/([\w_\-.:/]+(?![},]))$/);
|
||||
const wordMatch = values.beforeCursor.match(/([\w_.:]+(?![},]))$/);
|
||||
return wordMatch ? wordMatch[0] : "";
|
||||
}, [values.beforeCursor]);
|
||||
|
||||
|
@ -119,9 +134,10 @@ const QueryEditorAutocomplete: FC<QueryEditorAutocompleteProps> = ({
|
|||
// Add quotes around the value if the context is labelValue
|
||||
if (context === QueryContextType.labelValue) {
|
||||
const quote = "\"";
|
||||
const needsQuote = /(?:=|!=|=~|!~)$/.test(beforeValueByContext);
|
||||
valueAfterCursor = valueAfterCursor.replace(/^[^\s"|},]*/, "");
|
||||
insert = `${needsQuote ? quote : ""}${insert}`;
|
||||
const needsOpenQuote = /(?:=|!=|=~|!~)$/.test(beforeValueByContext);
|
||||
const needsCloseQuote = valueAfterCursor.trim()[0] !== "\"";
|
||||
insert = `${needsOpenQuote ? quote : ""}${insert}${needsCloseQuote ? quote : ""}`;
|
||||
}
|
||||
|
||||
if (context === QueryContextType.label) {
|
||||
|
|
|
@ -137,7 +137,7 @@ export const useFetchQueryOptions = ({ valueByContext, metric, label, context }:
|
|||
|
||||
// fetch labels
|
||||
useEffect(() => {
|
||||
if (!serverUrl || !metric || context !== QueryContextType.label) {
|
||||
if (!serverUrl || context !== QueryContextType.label) {
|
||||
return;
|
||||
}
|
||||
setLabels([]);
|
||||
|
@ -149,7 +149,7 @@ export const useFetchQueryOptions = ({ valueByContext, metric, label, context }:
|
|||
urlSuffix: "labels",
|
||||
setter: setLabels,
|
||||
type: TypeData.label,
|
||||
params: getQueryParams({ "match[]": `{__name__="${metricEscaped}"}` })
|
||||
params: getQueryParams(metric ? { "match[]": `{__name__="${metricEscaped}"}` } : undefined)
|
||||
});
|
||||
|
||||
return () => abortControllerRef.current?.abort();
|
||||
|
@ -157,20 +157,23 @@ export const useFetchQueryOptions = ({ valueByContext, metric, label, context }:
|
|||
|
||||
// fetch labelValues
|
||||
useEffect(() => {
|
||||
if (!serverUrl || !metric || !label || context !== QueryContextType.labelValue) {
|
||||
if (!serverUrl || !label || context !== QueryContextType.labelValue) {
|
||||
return;
|
||||
}
|
||||
setLabelValues([]);
|
||||
|
||||
const metricEscaped = escapeDoubleQuotes(metric);
|
||||
const valueReEscaped = escapeDoubleQuotes(escapeRegexp(value));
|
||||
const matchMetric = metric ? `__name__="${metricEscaped}"` : "";
|
||||
const matchLabel = `${label}=~".*${valueReEscaped}.*"`;
|
||||
const matchValue = [matchMetric, matchLabel].filter(Boolean).join(",");
|
||||
|
||||
fetchData({
|
||||
value,
|
||||
urlSuffix: `label/${label}/values`,
|
||||
setter: setLabelValues,
|
||||
type: TypeData.labelValue,
|
||||
params: getQueryParams({ "match[]": `{__name__="${metricEscaped}", ${label}=~".*${valueReEscaped}.*"}` })
|
||||
params: getQueryParams({ "match[]": `{${matchValue}}` })
|
||||
});
|
||||
|
||||
return () => abortControllerRef.current?.abort();
|
||||
|
|
|
@ -8,6 +8,6 @@ export const escapeDoubleQuotes = (s: string) => {
|
|||
};
|
||||
|
||||
export const hasUnclosedQuotes = (str: string) => {
|
||||
const matches = str.match(/"/g);
|
||||
const matches = str.match(/["`']/g);
|
||||
return matches ? matches.length % 2 !== 0 : false;
|
||||
};
|
||||
|
|
|
@ -40,6 +40,8 @@ See also [LTS releases](https://docs.victoriametrics.com/lts-releases/).
|
|||
* BUGFIX: all VictoriaMetrics components: validate files specified via `-tlsKeyFile` and `-tlsCertFile` cmd-line flags on the process start-up. Previously, validation happened on the first connection accepted by HTTP server. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6608) for the details. Thanks to @yincongcyincong for the [pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/6621).
|
||||
* BUGFIX: [vmauth](https://docs.victoriametrics.com/vmauth/): properly proxy requests to backend urls ending with `/` if the original request path equals to `/`. Previously the trailing `/` at the backend path was incorrectly removed. For example, if the request to `http://vmauth/` is configured to be proxied to `url_prefix=http://backend/foo/`, then it was proxied to `http://backend/foo`, while it should go to `http://backend/foo/`.
|
||||
* BUGFIX: [vmauth](https://docs.victoriametrics.com/vmauth/): fix `cannot read data after closing the reader` error when proxying HTTP requests without body (aka `GET` requests). The issue has been introduced in [v1.102.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.102.0) in [this commit](https://github.com/VictoriaMetrics/VictoriaMetrics/commit/7ee57974935a662896f2de40fdf613156630617d).
|
||||
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix auto-completion triggers for metric and label names, and disable it after specific characters. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6153) and [these comments](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5866#issuecomment-2065273421).
|
||||
* BUGFIX: [vmui](https://docs.victoriametrics.com/#vmui): fix automatic addition of quotes when inserting label values from autocomplete suggestions. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6260).
|
||||
|
||||
## [v1.102.0](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.102.0)
|
||||
|
||||
|
|
Loading…
Reference in a new issue