mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
@@ -272,7 +272,7 @@
|
|||||||
"@grafana/schema": "workspace:*",
|
"@grafana/schema": "workspace:*",
|
||||||
"@grafana/ui": "workspace:*",
|
"@grafana/ui": "workspace:*",
|
||||||
"@kusto/monaco-kusto": "5.3.6",
|
"@kusto/monaco-kusto": "5.3.6",
|
||||||
"@leeoniya/ufuzzy": "0.9.1",
|
"@leeoniya/ufuzzy": "1.0.2",
|
||||||
"@lezer/common": "1.0.1",
|
"@lezer/common": "1.0.1",
|
||||||
"@lezer/highlight": "1.1.2",
|
"@lezer/highlight": "1.1.2",
|
||||||
"@lezer/lr": "1.3.1",
|
"@lezer/lr": "1.3.1",
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
"@grafana/data": "9.4.0-pre",
|
"@grafana/data": "9.4.0-pre",
|
||||||
"@grafana/e2e-selectors": "9.4.0-pre",
|
"@grafana/e2e-selectors": "9.4.0-pre",
|
||||||
"@grafana/schema": "9.4.0-pre",
|
"@grafana/schema": "9.4.0-pre",
|
||||||
"@leeoniya/ufuzzy": "0.9.0",
|
"@leeoniya/ufuzzy": "1.0.2",
|
||||||
"@monaco-editor/react": "4.4.6",
|
"@monaco-editor/react": "4.4.6",
|
||||||
"@popperjs/core": "2.11.6",
|
"@popperjs/core": "2.11.6",
|
||||||
"@react-aria/button": "3.6.1",
|
"@react-aria/button": "3.6.1",
|
||||||
|
|||||||
@@ -186,8 +186,8 @@ function useInternalMatches(filtered: ActionImpl[], search: string): Match[] {
|
|||||||
return throttledFiltered.map((action) => ({ score: 0, action }));
|
return throttledFiltered.map((action) => ({ score: 0, action }));
|
||||||
}
|
}
|
||||||
|
|
||||||
const haystack = throttledFiltered.map((action) =>
|
const haystack = throttledFiltered.map(({ name, keywords, subtitle }) =>
|
||||||
[action.name, action.keywords, action.subtitle].join(' ').toLowerCase()
|
`${name} ${keywords ?? ''} ${subtitle ?? ''}`.toLowerCase()
|
||||||
);
|
);
|
||||||
|
|
||||||
const results: Match[] = [];
|
const results: Match[] = [];
|
||||||
@@ -201,35 +201,27 @@ function useInternalMatches(filtered: ActionImpl[], search: string): Match[] {
|
|||||||
const haystackItem = haystack[haystackIndex];
|
const haystackItem = haystack[haystackIndex];
|
||||||
|
|
||||||
// Use the position of the match as a stand-in for score
|
// Use the position of the match as a stand-in for score
|
||||||
const substringPosition = haystackItem.toLowerCase().indexOf(query);
|
const substringPosition = haystackItem.indexOf(query);
|
||||||
|
|
||||||
if (substringPosition > -1) {
|
if (substringPosition > -1) {
|
||||||
const score = haystack.length - substringPosition;
|
const score = substringPosition * -1; // lower position of the match should be a higher priority score
|
||||||
const action = throttledFiltered[haystackIndex];
|
const action = throttledFiltered[haystackIndex];
|
||||||
results.push({ score, action });
|
results.push({ score, action });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const allMatchedIndexes = new Set<number>();
|
const termCount = ufuzzy.split(throttledSearch).length;
|
||||||
|
const infoThresh = Infinity;
|
||||||
|
const oooSearch = termCount < 5;
|
||||||
|
|
||||||
const queryWords = ufuzzy.split(throttledSearch);
|
const [, info, order] = ufuzzy.search(haystack, throttledSearch, oooSearch, infoThresh);
|
||||||
const queryPermutations =
|
|
||||||
queryWords.length < 5 ? uFuzzy.permute(queryWords).map((terms) => terms.join(' ')) : [throttledSearch];
|
|
||||||
|
|
||||||
for (const permutedSearchTerm of queryPermutations) {
|
|
||||||
const indexes = ufuzzy.filter(haystack, permutedSearchTerm);
|
|
||||||
const info = ufuzzy.info(indexes, haystack, permutedSearchTerm);
|
|
||||||
const order = ufuzzy.sort(info, haystack, permutedSearchTerm);
|
|
||||||
|
|
||||||
|
if (info && order) {
|
||||||
for (let orderIndex = 0; orderIndex < order.length; orderIndex++) {
|
for (let orderIndex = 0; orderIndex < order.length; orderIndex++) {
|
||||||
const actionIndex = order[orderIndex];
|
const actionIndex = order[orderIndex];
|
||||||
|
const score = order.length - orderIndex;
|
||||||
if (!allMatchedIndexes.has(actionIndex)) {
|
const action = throttledFiltered[info.idx[actionIndex]];
|
||||||
allMatchedIndexes.add(actionIndex);
|
results.push({ score, action });
|
||||||
const score = order.length - orderIndex;
|
|
||||||
const action = throttledFiltered[info.idx[actionIndex]];
|
|
||||||
results.push({ score, action });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,28 +116,27 @@ class FullResultCache {
|
|||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const values = allFields.map((v) => [] as any[]); // empty value for each field
|
const values = allFields.map((v) => [] as any[]); // empty value for each field
|
||||||
|
|
||||||
// out-of-order terms
|
let [idxs, info, order] = this.ufuzzy.search(haystack, query, true);
|
||||||
const oooIdxs = new Set<number>();
|
|
||||||
const queryTerms = this.ufuzzy.split(query);
|
|
||||||
const oooNeedles = uFuzzy.permute(queryTerms).map((terms) => terms.join(' '));
|
|
||||||
|
|
||||||
oooNeedles.forEach((needle) => {
|
for (let c = 0; c < allFields.length; c++) {
|
||||||
let idxs = this.ufuzzy.filter(haystack, needle);
|
let src = allFields[c].values.toArray();
|
||||||
let info = this.ufuzzy.info(idxs, haystack, needle);
|
let dst = values[c];
|
||||||
let order = this.ufuzzy.sort(info, haystack, needle);
|
|
||||||
|
|
||||||
for (let i = 0; i < order.length; i++) {
|
// <= 1000 matches (ranked)
|
||||||
let haystackIdx = info.idx[order[i]];
|
if (info && order) {
|
||||||
|
for (let i = 0; i < order.length; i++) {
|
||||||
if (!oooIdxs.has(haystackIdx)) {
|
let haystackIdx = info.idx[order[i]];
|
||||||
oooIdxs.add(haystackIdx);
|
dst.push(src[haystackIdx]);
|
||||||
|
|
||||||
for (let c = 0; c < allFields.length; c++) {
|
|
||||||
values[c].push(allFields[c].values.get(haystackIdx));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
// > 1000 matches (unranked)
|
||||||
|
else {
|
||||||
|
for (let i = 0; i < idxs.length; i++) {
|
||||||
|
let haystackIdx = idxs[i];
|
||||||
|
dst.push(src[haystackIdx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// mutates the search object
|
// mutates the search object
|
||||||
this.empty.dataFrame.fields.forEach((f, idx) => {
|
this.empty.dataFrame.fields.forEach((f, idx) => {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
// THIS SOFTWARE.
|
// THIS SOFTWARE.
|
||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
import uFuzzy from '@leeoniya/ufuzzy';
|
import uFuzzy from '@leeoniya/ufuzzy';
|
||||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { useMeasure } from 'react-use';
|
import { useMeasure } from 'react-use';
|
||||||
|
|
||||||
import { CoreApp, createTheme, DataFrame, FieldType, getDisplayProcessor } from '@grafana/data';
|
import { CoreApp, createTheme, DataFrame, FieldType, getDisplayProcessor } from '@grafana/data';
|
||||||
@@ -90,6 +90,26 @@ const FlameGraph = ({
|
|||||||
[levels, totalTicks, rangeMin]
|
[levels, totalTicks, rangeMin]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const [ufuzzy] = useState(() => {
|
||||||
|
return new uFuzzy();
|
||||||
|
});
|
||||||
|
|
||||||
|
const uniqueLabels = useMemo(() => {
|
||||||
|
return [...new Set<string>(data.fields.find((f) => f.name === 'label')?.values.toArray())];
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
|
const foundLabels = useMemo(() => {
|
||||||
|
const foundLabels = new Set<string>();
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
for (let idx of ufuzzy.filter(uniqueLabels, search)) {
|
||||||
|
foundLabels.add(uniqueLabels[idx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundLabels;
|
||||||
|
}, [ufuzzy, search, uniqueLabels]);
|
||||||
|
|
||||||
const render = useCallback(
|
const render = useCallback(
|
||||||
(pixelsPerTick: number) => {
|
(pixelsPerTick: number) => {
|
||||||
if (!levels.length) {
|
if (!levels.length) {
|
||||||
@@ -113,11 +133,6 @@ const FlameGraph = ({
|
|||||||
theme: createTheme() /* theme does not matter for us here */,
|
theme: createTheme() /* theme does not matter for us here */,
|
||||||
});
|
});
|
||||||
|
|
||||||
const ufuzzy = new uFuzzy({
|
|
||||||
intraMode: 0,
|
|
||||||
intraIns: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
for (let levelIndex = 0; levelIndex < levels.length; levelIndex++) {
|
for (let levelIndex = 0; levelIndex < levels.length; levelIndex++) {
|
||||||
const level = levels[levelIndex];
|
const level = levels[levelIndex];
|
||||||
// Get all the dimensions of the rectangles for the level. We do this by level instead of per rectangle, because
|
// Get all the dimensions of the rectangles for the level. We do this by level instead of per rectangle, because
|
||||||
@@ -125,11 +140,11 @@ const FlameGraph = ({
|
|||||||
const dimensions = getRectDimensionsForLevel(level, levelIndex, totalTicks, rangeMin, pixelsPerTick, processor);
|
const dimensions = getRectDimensionsForLevel(level, levelIndex, totalTicks, rangeMin, pixelsPerTick, processor);
|
||||||
for (const rect of dimensions) {
|
for (const rect of dimensions) {
|
||||||
// Render each rectangle based on the computed dimensions
|
// Render each rectangle based on the computed dimensions
|
||||||
renderRect(ctx, rect, totalTicks, rangeMin, rangeMax, search, levelIndex, topLevelIndex, ufuzzy);
|
renderRect(ctx, rect, totalTicks, rangeMin, rangeMax, search, levelIndex, topLevelIndex, foundLabels);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[levels, wrapperWidth, valueField, totalTicks, rangeMin, rangeMax, search, topLevelIndex]
|
[levels, wrapperWidth, valueField, totalTicks, rangeMin, rangeMax, search, topLevelIndex, foundLabels]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ export function renderRect(
|
|||||||
query: string,
|
query: string,
|
||||||
levelIndex: number,
|
levelIndex: number,
|
||||||
topLevelIndex: number,
|
topLevelIndex: number,
|
||||||
ufuzzy: uFuzzy
|
foundNames: Set<string>
|
||||||
) {
|
) {
|
||||||
if (rect.width < HIDE_THRESHOLD) {
|
if (rect.width < HIDE_THRESHOLD) {
|
||||||
return;
|
return;
|
||||||
@@ -101,19 +101,17 @@ export function renderRect(
|
|||||||
const l = 65 + 7 * intensity;
|
const l = 65 + 7 * intensity;
|
||||||
|
|
||||||
const name = rect.label;
|
const name = rect.label;
|
||||||
const idxs = ufuzzy.filter([name], query);
|
|
||||||
const queryResult = query && idxs.length > 0;
|
|
||||||
|
|
||||||
if (!rect.collapsed) {
|
if (!rect.collapsed) {
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
|
||||||
if (query) {
|
if (query) {
|
||||||
ctx.fillStyle = queryResult ? getBarColor(h, l) : colors[55];
|
ctx.fillStyle = foundNames.has(name) ? getBarColor(h, l) : colors[55];
|
||||||
} else {
|
} else {
|
||||||
ctx.fillStyle = levelIndex > topLevelIndex - 1 ? getBarColor(h, l) : getBarColor(h, l + 15);
|
ctx.fillStyle = levelIndex > topLevelIndex - 1 ? getBarColor(h, l) : getBarColor(h, l + 15);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ctx.fillStyle = queryResult ? getBarColor(h, l) : colors[55];
|
ctx.fillStyle = foundNames.has(name) ? getBarColor(h, l) : colors[55];
|
||||||
}
|
}
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
|
||||||
|
|||||||
19
yarn.lock
19
yarn.lock
@@ -5302,7 +5302,7 @@ __metadata:
|
|||||||
"@grafana/e2e-selectors": 9.4.0-pre
|
"@grafana/e2e-selectors": 9.4.0-pre
|
||||||
"@grafana/schema": 9.4.0-pre
|
"@grafana/schema": 9.4.0-pre
|
||||||
"@grafana/tsconfig": ^1.2.0-rc1
|
"@grafana/tsconfig": ^1.2.0-rc1
|
||||||
"@leeoniya/ufuzzy": 0.9.0
|
"@leeoniya/ufuzzy": 1.0.2
|
||||||
"@mdx-js/react": 1.6.22
|
"@mdx-js/react": 1.6.22
|
||||||
"@monaco-editor/react": 4.4.6
|
"@monaco-editor/react": 4.4.6
|
||||||
"@popperjs/core": 2.11.6
|
"@popperjs/core": 2.11.6
|
||||||
@@ -6246,17 +6246,10 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@leeoniya/ufuzzy@npm:0.9.0":
|
"@leeoniya/ufuzzy@npm:1.0.2":
|
||||||
version: 0.9.0
|
version: 1.0.2
|
||||||
resolution: "@leeoniya/ufuzzy@npm:0.9.0"
|
resolution: "@leeoniya/ufuzzy@npm:1.0.2"
|
||||||
checksum: ee1b781530b3dbddd44eb0923f576028829209648c7b7283e5981f89527ac029c034d234ac2e98c9c25e99fe1d97e524d1e3ee0a79f5cbb92230d36e9cfa69d5
|
checksum: 5460378a8c32d121b0bc7c8e95cde995316516655528e248051b1bf360cdca0311ef3275de14b802587748231333cee6183c931b3abba26f9e4236ecc4959aa3
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@leeoniya/ufuzzy@npm:0.9.1":
|
|
||||||
version: 0.9.1
|
|
||||||
resolution: "@leeoniya/ufuzzy@npm:0.9.1"
|
|
||||||
checksum: 27750dff2e754ec3729937abce7c36b87917325e934cef7b1bb54a2310fd1e497dcb725397abee8c714f24a84da155120177a93da8f9706eefe32a8a1bb66945
|
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -22083,7 +22076,7 @@ __metadata:
|
|||||||
"@grafana/tsconfig": ^1.2.0-rc1
|
"@grafana/tsconfig": ^1.2.0-rc1
|
||||||
"@grafana/ui": "workspace:*"
|
"@grafana/ui": "workspace:*"
|
||||||
"@kusto/monaco-kusto": 5.3.6
|
"@kusto/monaco-kusto": 5.3.6
|
||||||
"@leeoniya/ufuzzy": 0.9.1
|
"@leeoniya/ufuzzy": 1.0.2
|
||||||
"@lezer/common": 1.0.1
|
"@lezer/common": 1.0.1
|
||||||
"@lezer/highlight": 1.1.2
|
"@lezer/highlight": 1.1.2
|
||||||
"@lezer/lr": 1.3.1
|
"@lezer/lr": 1.3.1
|
||||||
|
|||||||
Reference in New Issue
Block a user