Merge branch 'main' into shati-patel/run-query-context-menu-local
This commit is contained in:
@@ -503,7 +503,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "codeQLQueries.runLocalQueryFromQueriesPanel",
|
"command": "codeQLQueries.runLocalQueryFromQueriesPanel",
|
||||||
"title": "Run local query",
|
"title": "Run local query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"command": "codeQL.runLocalQueryFromFileTab",
|
||||||
|
"title": "CodeQL: Run local query",
|
||||||
"icon": "$(run)"
|
"icon": "$(run)"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -881,6 +885,13 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"menus": {
|
"menus": {
|
||||||
|
"editor/title": [
|
||||||
|
{
|
||||||
|
"command": "codeQL.runLocalQueryFromFileTab",
|
||||||
|
"group": "navigation",
|
||||||
|
"when": "config.codeQL.queriesPanel && resourceExtname == .ql && codeQL.currentDatabaseItem"
|
||||||
|
}
|
||||||
|
],
|
||||||
"view/title": [
|
"view/title": [
|
||||||
{
|
{
|
||||||
"command": "codeQLDatabases.sortByName",
|
"command": "codeQLDatabases.sortByName",
|
||||||
@@ -1177,6 +1188,10 @@
|
|||||||
"command": "codeQLQueries.runLocalQueryFromQueriesPanel",
|
"command": "codeQLQueries.runLocalQueryFromQueriesPanel",
|
||||||
"when": "false"
|
"when": "false"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"command": "codeQL.runLocalQueryFromFileTab",
|
||||||
|
"when": "false"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"command": "codeQL.runQueryContextEditor",
|
"command": "codeQL.runQueryContextEditor",
|
||||||
"when": "false"
|
"when": "false"
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ export type LocalQueryCommands = {
|
|||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
"codeQLQueries.runLocalQueryFromQueriesPanel": TreeViewContextSingleSelectionCommandFunction<QueryTreeViewItem>;
|
"codeQLQueries.runLocalQueryFromQueriesPanel": TreeViewContextSingleSelectionCommandFunction<QueryTreeViewItem>;
|
||||||
"codeQLQueries.runLocalQueryContextMenu": TreeViewContextSingleSelectionCommandFunction<QueryTreeViewItem>;
|
"codeQLQueries.runLocalQueryContextMenu": TreeViewContextSingleSelectionCommandFunction<QueryTreeViewItem>;
|
||||||
|
"codeQL.runLocalQueryFromFileTab": (uri: Uri) => Promise<void>;
|
||||||
"codeQL.runQueries": ExplorerSelectionCommandFunction<Uri>;
|
"codeQL.runQueries": ExplorerSelectionCommandFunction<Uri>;
|
||||||
"codeQL.quickEval": (uri: Uri) => Promise<void>;
|
"codeQL.quickEval": (uri: Uri) => Promise<void>;
|
||||||
"codeQL.quickEvalContextEditor": (uri: Uri) => Promise<void>;
|
"codeQL.quickEvalContextEditor": (uri: Uri) => Promise<void>;
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import {
|
|||||||
ModelRequest,
|
ModelRequest,
|
||||||
} from "./auto-model-api";
|
} from "./auto-model-api";
|
||||||
import type { UsageSnippetsBySignature } from "./auto-model-usages-query";
|
import type { UsageSnippetsBySignature } from "./auto-model-usages-query";
|
||||||
|
import { groupMethods, sortGroupNames, sortMethods } from "./shared/sorting";
|
||||||
|
import { Mode } from "./shared/mode";
|
||||||
|
|
||||||
// Soft limit on the number of candidates to send to the model.
|
// Soft limit on the number of candidates to send to the model.
|
||||||
// Note that the model may return fewer than this number of candidates.
|
// Note that the model may return fewer than this number of candidates.
|
||||||
@@ -19,6 +21,7 @@ export function createAutoModelRequest(
|
|||||||
externalApiUsages: ExternalApiUsage[],
|
externalApiUsages: ExternalApiUsage[],
|
||||||
modeledMethods: Record<string, ModeledMethod>,
|
modeledMethods: Record<string, ModeledMethod>,
|
||||||
usages: UsageSnippetsBySignature,
|
usages: UsageSnippetsBySignature,
|
||||||
|
mode: Mode,
|
||||||
): ModelRequest {
|
): ModelRequest {
|
||||||
const request: ModelRequest = {
|
const request: ModelRequest = {
|
||||||
language,
|
language,
|
||||||
@@ -26,11 +29,14 @@ export function createAutoModelRequest(
|
|||||||
candidates: [],
|
candidates: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Sort by number of usages so we always send the most used methods first
|
// Sort the same way as the UI so we send the first ones listed in the UI first
|
||||||
externalApiUsages = [...externalApiUsages];
|
const grouped = groupMethods(externalApiUsages, mode);
|
||||||
externalApiUsages.sort((a, b) => b.usages.length - a.usages.length);
|
const sortedGroupNames = sortGroupNames(grouped);
|
||||||
|
const sortedExternalApiUsages = sortedGroupNames.flatMap((name) =>
|
||||||
|
sortMethods(grouped[name]),
|
||||||
|
);
|
||||||
|
|
||||||
for (const externalApiUsage of externalApiUsages) {
|
for (const externalApiUsage of sortedExternalApiUsages) {
|
||||||
const modeledMethod: ModeledMethod = modeledMethods[
|
const modeledMethod: ModeledMethod = modeledMethods[
|
||||||
externalApiUsage.signature
|
externalApiUsage.signature
|
||||||
] ?? {
|
] ?? {
|
||||||
|
|||||||
@@ -457,6 +457,7 @@ export class DataExtensionsEditorView extends AbstractWebview<
|
|||||||
externalApiUsages,
|
externalApiUsages,
|
||||||
modeledMethods,
|
modeledMethods,
|
||||||
usages,
|
usages,
|
||||||
|
this.mode,
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.showProgress({
|
await this.showProgress({
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ExternalApiUsage } from "../../data-extensions-editor/external-api-usage";
|
import { ExternalApiUsage } from "../external-api-usage";
|
||||||
|
|
||||||
export function calculateModeledPercentage(
|
export function calculateModeledPercentage(
|
||||||
externalApiUsages: Array<Pick<ExternalApiUsage, "supported">>,
|
externalApiUsages: Array<Pick<ExternalApiUsage, "supported">>,
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import { ExternalApiUsage } from "../external-api-usage";
|
||||||
|
import { Mode } from "./mode";
|
||||||
|
import { calculateModeledPercentage } from "./modeled-percentage";
|
||||||
|
|
||||||
|
export function groupMethods(
|
||||||
|
externalApiUsages: ExternalApiUsage[],
|
||||||
|
mode: Mode,
|
||||||
|
): Record<string, ExternalApiUsage[]> {
|
||||||
|
const groupedByLibrary: Record<string, ExternalApiUsage[]> = {};
|
||||||
|
|
||||||
|
for (const externalApiUsage of externalApiUsages) {
|
||||||
|
// Group by package if using framework mode
|
||||||
|
const key =
|
||||||
|
mode === Mode.Framework
|
||||||
|
? externalApiUsage.packageName
|
||||||
|
: externalApiUsage.library;
|
||||||
|
|
||||||
|
groupedByLibrary[key] ??= [];
|
||||||
|
groupedByLibrary[key].push(externalApiUsage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return groupedByLibrary;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sortGroupNames(
|
||||||
|
methods: Record<string, ExternalApiUsage[]>,
|
||||||
|
): string[] {
|
||||||
|
return Object.keys(methods).sort((a, b) =>
|
||||||
|
compareGroups(methods[a], a, methods[b], b),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sortMethods(
|
||||||
|
externalApiUsages: ExternalApiUsage[],
|
||||||
|
): ExternalApiUsage[] {
|
||||||
|
const sortedExternalApiUsages = [...externalApiUsages];
|
||||||
|
sortedExternalApiUsages.sort((a, b) => compareMethod(a, b));
|
||||||
|
return sortedExternalApiUsages;
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareGroups(
|
||||||
|
a: ExternalApiUsage[],
|
||||||
|
aName: string,
|
||||||
|
b: ExternalApiUsage[],
|
||||||
|
bName: string,
|
||||||
|
): number {
|
||||||
|
const supportedPercentageA = calculateModeledPercentage(a);
|
||||||
|
const supportedPercentageB = calculateModeledPercentage(b);
|
||||||
|
|
||||||
|
// Sort first by supported percentage ascending
|
||||||
|
if (supportedPercentageA > supportedPercentageB) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (supportedPercentageA < supportedPercentageB) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const numberOfUsagesA = a.reduce((acc, curr) => acc + curr.usages.length, 0);
|
||||||
|
const numberOfUsagesB = b.reduce((acc, curr) => acc + curr.usages.length, 0);
|
||||||
|
|
||||||
|
// If the number of usages is equal, sort by number of methods descending
|
||||||
|
if (numberOfUsagesA === numberOfUsagesB) {
|
||||||
|
const numberOfMethodsA = a.length;
|
||||||
|
const numberOfMethodsB = b.length;
|
||||||
|
|
||||||
|
// If the number of methods is equal, sort by library name ascending
|
||||||
|
if (numberOfMethodsA === numberOfMethodsB) {
|
||||||
|
return aName.localeCompare(bName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return numberOfMethodsB - numberOfMethodsA;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then sort by number of usages descending
|
||||||
|
return numberOfUsagesB - numberOfUsagesA;
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareMethod(a: ExternalApiUsage, b: ExternalApiUsage): number {
|
||||||
|
// Sort first by supported, putting unmodeled methods first.
|
||||||
|
if (a.supported && !b.supported) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!a.supported && b.supported) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
// Then sort by number of usages descending
|
||||||
|
return b.usages.length - a.usages.length;
|
||||||
|
}
|
||||||
@@ -105,6 +105,7 @@ export class LocalQueries extends DisposableObject {
|
|||||||
this.runQueryFromQueriesPanel.bind(this),
|
this.runQueryFromQueriesPanel.bind(this),
|
||||||
"codeQLQueries.runLocalQueryContextMenu":
|
"codeQLQueries.runLocalQueryContextMenu":
|
||||||
this.runQueryFromQueriesPanel.bind(this),
|
this.runQueryFromQueriesPanel.bind(this),
|
||||||
|
"codeQL.runLocalQueryFromFileTab": this.runQuery.bind(this),
|
||||||
"codeQL.runQueries": createMultiSelectionCommand(
|
"codeQL.runQueries": createMultiSelectionCommand(
|
||||||
this.runQueries.bind(this),
|
this.runQueries.bind(this),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { ExternalApiUsage } from "../../data-extensions-editor/external-api-usag
|
|||||||
import { ModeledMethod } from "../../data-extensions-editor/modeled-method";
|
import { ModeledMethod } from "../../data-extensions-editor/modeled-method";
|
||||||
import { assertNever } from "../../common/helpers-pure";
|
import { assertNever } from "../../common/helpers-pure";
|
||||||
import { vscode } from "../vscode-api";
|
import { vscode } from "../vscode-api";
|
||||||
import { calculateModeledPercentage } from "./modeled";
|
import { calculateModeledPercentage } from "../../data-extensions-editor/shared/modeled-percentage";
|
||||||
import { LinkIconButton } from "../variant-analysis/LinkIconButton";
|
import { LinkIconButton } from "../variant-analysis/LinkIconButton";
|
||||||
import { ViewTitle } from "../common";
|
import { ViewTitle } from "../common";
|
||||||
import { DataExtensionEditorViewState } from "../../data-extensions-editor/shared/view-state";
|
import { DataExtensionEditorViewState } from "../../data-extensions-editor/shared/view-state";
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { ExternalApiUsage } from "../../data-extensions-editor/external-api-usag
|
|||||||
import { ModeledMethod } from "../../data-extensions-editor/modeled-method";
|
import { ModeledMethod } from "../../data-extensions-editor/modeled-method";
|
||||||
import { pluralize } from "../../common/word";
|
import { pluralize } from "../../common/word";
|
||||||
import { ModeledMethodDataGrid } from "./ModeledMethodDataGrid";
|
import { ModeledMethodDataGrid } from "./ModeledMethodDataGrid";
|
||||||
import { calculateModeledPercentage } from "./modeled";
|
import { calculateModeledPercentage } from "../../data-extensions-editor/shared/modeled-percentage";
|
||||||
import { decimalFormatter, percentFormatter } from "./formatters";
|
import { decimalFormatter, percentFormatter } from "./formatters";
|
||||||
import { Codicon } from "../common";
|
import { Codicon } from "../common";
|
||||||
import { Mode } from "../../data-extensions-editor/shared/mode";
|
import { Mode } from "../../data-extensions-editor/shared/mode";
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { ExternalApiUsage } from "../../data-extensions-editor/external-api-usag
|
|||||||
import { ModeledMethod } from "../../data-extensions-editor/modeled-method";
|
import { ModeledMethod } from "../../data-extensions-editor/modeled-method";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { Mode } from "../../data-extensions-editor/shared/mode";
|
import { Mode } from "../../data-extensions-editor/shared/mode";
|
||||||
|
import { sortMethods } from "../../data-extensions-editor/shared/sorting";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
externalApiUsages: ExternalApiUsage[];
|
externalApiUsages: ExternalApiUsage[];
|
||||||
@@ -26,21 +27,10 @@ export const ModeledMethodDataGrid = ({
|
|||||||
mode,
|
mode,
|
||||||
onChange,
|
onChange,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const sortedExternalApiUsages = useMemo(() => {
|
const sortedExternalApiUsages = useMemo(
|
||||||
const sortedExternalApiUsages = [...externalApiUsages];
|
() => sortMethods(externalApiUsages),
|
||||||
sortedExternalApiUsages.sort((a, b) => {
|
[externalApiUsages],
|
||||||
// Sort first by supported, putting unmodeled methods first.
|
);
|
||||||
if (a.supported && !b.supported) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (!a.supported && b.supported) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
// Then sort by number of usages descending
|
|
||||||
return b.usages.length - a.usages.length;
|
|
||||||
});
|
|
||||||
return sortedExternalApiUsages;
|
|
||||||
}, [externalApiUsages]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<VSCodeDataGrid>
|
<VSCodeDataGrid>
|
||||||
|
|||||||
@@ -2,9 +2,12 @@ import * as React from "react";
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { ExternalApiUsage } from "../../data-extensions-editor/external-api-usage";
|
import { ExternalApiUsage } from "../../data-extensions-editor/external-api-usage";
|
||||||
import { ModeledMethod } from "../../data-extensions-editor/modeled-method";
|
import { ModeledMethod } from "../../data-extensions-editor/modeled-method";
|
||||||
import { calculateModeledPercentage } from "./modeled";
|
|
||||||
import { LibraryRow } from "./LibraryRow";
|
import { LibraryRow } from "./LibraryRow";
|
||||||
import { Mode } from "../../data-extensions-editor/shared/mode";
|
import { Mode } from "../../data-extensions-editor/shared/mode";
|
||||||
|
import {
|
||||||
|
groupMethods,
|
||||||
|
sortGroupNames,
|
||||||
|
} from "../../data-extensions-editor/shared/sorting";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
externalApiUsages: ExternalApiUsage[];
|
externalApiUsages: ExternalApiUsage[];
|
||||||
@@ -22,62 +25,12 @@ export const ModeledMethodsList = ({
|
|||||||
mode,
|
mode,
|
||||||
onChange,
|
onChange,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const grouped = useMemo(() => {
|
const grouped = useMemo(
|
||||||
const groupedByLibrary: Record<string, ExternalApiUsage[]> = {};
|
() => groupMethods(externalApiUsages, mode),
|
||||||
|
[externalApiUsages, mode],
|
||||||
|
);
|
||||||
|
|
||||||
for (const externalApiUsage of externalApiUsages) {
|
const sortedGroupNames = useMemo(() => sortGroupNames(grouped), [grouped]);
|
||||||
// Group by package if using framework mode
|
|
||||||
const key =
|
|
||||||
mode === Mode.Framework
|
|
||||||
? externalApiUsage.packageName
|
|
||||||
: externalApiUsage.library;
|
|
||||||
|
|
||||||
groupedByLibrary[key] ??= [];
|
|
||||||
groupedByLibrary[key].push(externalApiUsage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return groupedByLibrary;
|
|
||||||
}, [externalApiUsages, mode]);
|
|
||||||
|
|
||||||
const sortedGroupNames = useMemo(() => {
|
|
||||||
return Object.keys(grouped).sort((a, b) => {
|
|
||||||
const supportedPercentageA = calculateModeledPercentage(grouped[a]);
|
|
||||||
const supportedPercentageB = calculateModeledPercentage(grouped[b]);
|
|
||||||
|
|
||||||
// Sort first by supported percentage ascending
|
|
||||||
if (supportedPercentageA > supportedPercentageB) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (supportedPercentageA < supportedPercentageB) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const numberOfUsagesA = grouped[a].reduce(
|
|
||||||
(acc, curr) => acc + curr.usages.length,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
const numberOfUsagesB = grouped[b].reduce(
|
|
||||||
(acc, curr) => acc + curr.usages.length,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
|
|
||||||
// If the number of usages is equal, sort by number of methods descending
|
|
||||||
if (numberOfUsagesA === numberOfUsagesB) {
|
|
||||||
const numberOfMethodsA = grouped[a].length;
|
|
||||||
const numberOfMethodsB = grouped[b].length;
|
|
||||||
|
|
||||||
// If the number of methods is equal, sort by library name ascending
|
|
||||||
if (numberOfMethodsA === numberOfMethodsB) {
|
|
||||||
return a.localeCompare(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
return numberOfMethodsB - numberOfMethodsA;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then sort by number of usages descending
|
|
||||||
return numberOfUsagesB - numberOfUsagesA;
|
|
||||||
});
|
|
||||||
}, [grouped]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
ResultTableProps,
|
|
||||||
className,
|
className,
|
||||||
emptyQueryResultsMessage,
|
emptyQueryResultsMessage,
|
||||||
jumpToLocation,
|
jumpToLocation,
|
||||||
@@ -19,158 +19,158 @@ import { onNavigation } from "./results";
|
|||||||
import { tryGetResolvableLocation } from "../../common/bqrs-utils";
|
import { tryGetResolvableLocation } from "../../common/bqrs-utils";
|
||||||
import { ScrollIntoViewHelper } from "./scroll-into-view-helper";
|
import { ScrollIntoViewHelper } from "./scroll-into-view-helper";
|
||||||
import { sendTelemetry } from "../common/telemetry";
|
import { sendTelemetry } from "../common/telemetry";
|
||||||
|
import { assertNever } from "../../common/helpers-pure";
|
||||||
|
|
||||||
export type RawTableProps = ResultTableProps & {
|
export type RawTableProps = {
|
||||||
|
databaseUri: string;
|
||||||
resultSet: RawTableResultSet;
|
resultSet: RawTableResultSet;
|
||||||
sortState?: RawResultsSortState;
|
sortState?: RawResultsSortState;
|
||||||
offset: number;
|
offset: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface RawTableState {
|
interface TableItem {
|
||||||
selectedItem?: { row: number; column: number };
|
readonly row: number;
|
||||||
|
readonly column: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class RawTable extends React.Component<RawTableProps, RawTableState> {
|
export function RawTable({
|
||||||
private scroller = new ScrollIntoViewHelper();
|
databaseUri,
|
||||||
|
resultSet,
|
||||||
|
sortState,
|
||||||
|
offset,
|
||||||
|
}: RawTableProps) {
|
||||||
|
const [selectedItem, setSelectedItem] = useState<TableItem | undefined>();
|
||||||
|
|
||||||
constructor(props: RawTableProps) {
|
const scroller = useRef<ScrollIntoViewHelper | undefined>(undefined);
|
||||||
super(props);
|
if (scroller.current === undefined) {
|
||||||
this.setSelection = this.setSelection.bind(this);
|
scroller.current = new ScrollIntoViewHelper();
|
||||||
this.handleNavigationEvent = this.handleNavigationEvent.bind(this);
|
|
||||||
this.state = {};
|
|
||||||
}
|
}
|
||||||
|
useEffect(() => scroller.current?.update());
|
||||||
|
|
||||||
private setSelection(row: number, column: number) {
|
const setSelection = useCallback((row: number, column: number): void => {
|
||||||
this.setState((prev) => ({
|
setSelectedItem({ row, column });
|
||||||
...prev,
|
|
||||||
selectedItem: { row, column },
|
|
||||||
}));
|
|
||||||
sendTelemetry("local-results-raw-results-table-selected");
|
sendTelemetry("local-results-raw-results-table-selected");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const navigateWithDelta = useCallback(
|
||||||
|
(rowDelta: number, columnDelta: number): void => {
|
||||||
|
setSelectedItem((prevSelectedItem) => {
|
||||||
|
const numberOfAlerts = resultSet.rows.length;
|
||||||
|
if (numberOfAlerts === 0) {
|
||||||
|
return prevSelectedItem;
|
||||||
|
}
|
||||||
|
const currentRow = prevSelectedItem?.row;
|
||||||
|
const nextRow = currentRow === undefined ? 0 : currentRow + rowDelta;
|
||||||
|
if (nextRow < 0 || nextRow >= numberOfAlerts) {
|
||||||
|
return prevSelectedItem;
|
||||||
|
}
|
||||||
|
const currentColumn = prevSelectedItem?.column;
|
||||||
|
const nextColumn =
|
||||||
|
currentColumn === undefined ? 0 : currentColumn + columnDelta;
|
||||||
|
// Jump to the location of the new cell
|
||||||
|
const rowData = resultSet.rows[nextRow];
|
||||||
|
if (nextColumn < 0 || nextColumn >= rowData.length) {
|
||||||
|
return prevSelectedItem;
|
||||||
|
}
|
||||||
|
const cellData = rowData[nextColumn];
|
||||||
|
if (cellData != null && typeof cellData === "object") {
|
||||||
|
const location = tryGetResolvableLocation(cellData.url);
|
||||||
|
if (location !== undefined) {
|
||||||
|
jumpToLocation(location, databaseUri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scroller.current?.scrollIntoViewOnNextUpdate();
|
||||||
|
return { row: nextRow, column: nextColumn };
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[databaseUri, resultSet, scroller],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleNavigationEvent = useCallback(
|
||||||
|
(event: NavigateMsg) => {
|
||||||
|
switch (event.direction) {
|
||||||
|
case NavigationDirection.up: {
|
||||||
|
navigateWithDelta(-1, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NavigationDirection.down: {
|
||||||
|
navigateWithDelta(1, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NavigationDirection.left: {
|
||||||
|
navigateWithDelta(0, -1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case NavigationDirection.right: {
|
||||||
|
navigateWithDelta(0, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
assertNever(event.direction);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[navigateWithDelta],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
onNavigation.addListener(handleNavigationEvent);
|
||||||
|
return () => {
|
||||||
|
onNavigation.removeListener(handleNavigationEvent);
|
||||||
|
};
|
||||||
|
}, [handleNavigationEvent]);
|
||||||
|
|
||||||
|
const [dataRows, numTruncatedResults] = useMemo(() => {
|
||||||
|
if (resultSet.rows.length <= RAW_RESULTS_LIMIT) {
|
||||||
|
return [resultSet.rows, 0];
|
||||||
|
}
|
||||||
|
return [
|
||||||
|
resultSet.rows.slice(0, RAW_RESULTS_LIMIT),
|
||||||
|
resultSet.rows.length - RAW_RESULTS_LIMIT,
|
||||||
|
];
|
||||||
|
}, [resultSet]);
|
||||||
|
|
||||||
|
if (dataRows.length === 0) {
|
||||||
|
return emptyQueryResultsMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
render(): React.ReactNode {
|
const tableRows = dataRows.map((row: ResultRow, rowIndex: number) => (
|
||||||
const { resultSet, databaseUri } = this.props;
|
<RawTableRow
|
||||||
|
key={rowIndex}
|
||||||
|
rowIndex={rowIndex + offset}
|
||||||
|
row={row}
|
||||||
|
databaseUri={databaseUri}
|
||||||
|
selectedColumn={
|
||||||
|
selectedItem?.row === rowIndex ? selectedItem?.column : undefined
|
||||||
|
}
|
||||||
|
onSelected={setSelection}
|
||||||
|
scroller={scroller.current}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
|
||||||
let dataRows = resultSet.rows;
|
if (numTruncatedResults > 0) {
|
||||||
if (dataRows.length === 0) {
|
const colSpan = dataRows[0].length + 1; // one row for each data column, plus index column
|
||||||
return emptyQueryResultsMessage();
|
tableRows.push(
|
||||||
}
|
<tr>
|
||||||
|
<td
|
||||||
let numTruncatedResults = 0;
|
key={"message"}
|
||||||
if (dataRows.length > RAW_RESULTS_LIMIT) {
|
colSpan={colSpan}
|
||||||
numTruncatedResults = dataRows.length - RAW_RESULTS_LIMIT;
|
style={{ textAlign: "center", fontStyle: "italic" }}
|
||||||
dataRows = dataRows.slice(0, RAW_RESULTS_LIMIT);
|
>
|
||||||
}
|
Too many results to show at once. {numTruncatedResults} result(s)
|
||||||
|
omitted.
|
||||||
const tableRows = dataRows.map((row: ResultRow, rowIndex: number) => (
|
</td>
|
||||||
<RawTableRow
|
</tr>,
|
||||||
key={rowIndex}
|
|
||||||
rowIndex={rowIndex + this.props.offset}
|
|
||||||
row={row}
|
|
||||||
databaseUri={databaseUri}
|
|
||||||
selectedColumn={
|
|
||||||
this.state.selectedItem?.row === rowIndex
|
|
||||||
? this.state.selectedItem?.column
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
onSelected={this.setSelection}
|
|
||||||
scroller={this.scroller}
|
|
||||||
/>
|
|
||||||
));
|
|
||||||
|
|
||||||
if (numTruncatedResults > 0) {
|
|
||||||
const colSpan = dataRows[0].length + 1; // one row for each data column, plus index column
|
|
||||||
tableRows.push(
|
|
||||||
<tr>
|
|
||||||
<td
|
|
||||||
key={"message"}
|
|
||||||
colSpan={colSpan}
|
|
||||||
style={{ textAlign: "center", fontStyle: "italic" }}
|
|
||||||
>
|
|
||||||
Too many results to show at once. {numTruncatedResults} result(s)
|
|
||||||
omitted.
|
|
||||||
</td>
|
|
||||||
</tr>,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<table className={className}>
|
|
||||||
<RawTableHeader
|
|
||||||
columns={resultSet.schema.columns}
|
|
||||||
schemaName={resultSet.schema.name}
|
|
||||||
sortState={this.props.sortState}
|
|
||||||
/>
|
|
||||||
<tbody>{tableRows}</tbody>
|
|
||||||
</table>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleNavigationEvent(event: NavigateMsg) {
|
return (
|
||||||
switch (event.direction) {
|
<table className={className}>
|
||||||
case NavigationDirection.up: {
|
<RawTableHeader
|
||||||
this.navigateWithDelta(-1, 0);
|
columns={resultSet.schema.columns}
|
||||||
break;
|
schemaName={resultSet.schema.name}
|
||||||
}
|
sortState={sortState}
|
||||||
case NavigationDirection.down: {
|
/>
|
||||||
this.navigateWithDelta(1, 0);
|
<tbody>{tableRows}</tbody>
|
||||||
break;
|
</table>
|
||||||
}
|
);
|
||||||
case NavigationDirection.left: {
|
|
||||||
this.navigateWithDelta(0, -1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case NavigationDirection.right: {
|
|
||||||
this.navigateWithDelta(0, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private navigateWithDelta(rowDelta: number, columnDelta: number) {
|
|
||||||
this.setState((prevState) => {
|
|
||||||
const numberOfAlerts = this.props.resultSet.rows.length;
|
|
||||||
if (numberOfAlerts === 0) {
|
|
||||||
return prevState;
|
|
||||||
}
|
|
||||||
const currentRow = prevState.selectedItem?.row;
|
|
||||||
const nextRow = currentRow === undefined ? 0 : currentRow + rowDelta;
|
|
||||||
if (nextRow < 0 || nextRow >= numberOfAlerts) {
|
|
||||||
return prevState;
|
|
||||||
}
|
|
||||||
const currentColumn = prevState.selectedItem?.column;
|
|
||||||
const nextColumn =
|
|
||||||
currentColumn === undefined ? 0 : currentColumn + columnDelta;
|
|
||||||
// Jump to the location of the new cell
|
|
||||||
const rowData = this.props.resultSet.rows[nextRow];
|
|
||||||
if (nextColumn < 0 || nextColumn >= rowData.length) {
|
|
||||||
return prevState;
|
|
||||||
}
|
|
||||||
const cellData = rowData[nextColumn];
|
|
||||||
if (cellData != null && typeof cellData === "object") {
|
|
||||||
const location = tryGetResolvableLocation(cellData.url);
|
|
||||||
if (location !== undefined) {
|
|
||||||
jumpToLocation(location, this.props.databaseUri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.scroller.scrollIntoViewOnNextUpdate();
|
|
||||||
return {
|
|
||||||
...prevState,
|
|
||||||
selectedItem: { row: nextRow, column: nextColumn },
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate() {
|
|
||||||
this.scroller.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.scroller.update();
|
|
||||||
onNavigation.addListener(this.handleNavigationEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
onNavigation.removeListener(this.handleNavigationEvent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,12 @@ describe("commands declared in package.json", () => {
|
|||||||
contribContextMenuCmds.add(command);
|
contribContextMenuCmds.add(command);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
menus["editor/title"].forEach((commandDecl: CmdDecl) => {
|
||||||
|
const { command } = commandDecl;
|
||||||
|
paletteCmds.delete(command);
|
||||||
|
contribContextMenuCmds.add(command);
|
||||||
|
});
|
||||||
|
|
||||||
debuggers.forEach((debuggerDecl: DebuggerDecl) => {
|
debuggers.forEach((debuggerDecl: DebuggerDecl) => {
|
||||||
if (debuggerDecl.variables !== undefined) {
|
if (debuggerDecl.variables !== undefined) {
|
||||||
for (const command of Object.values(debuggerDecl.variables)) {
|
for (const command of Object.values(debuggerDecl.variables)) {
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
ClassificationType,
|
ClassificationType,
|
||||||
Method,
|
Method,
|
||||||
} from "../../../src/data-extensions-editor/auto-model-api";
|
} from "../../../src/data-extensions-editor/auto-model-api";
|
||||||
|
import { Mode } from "../../../src/data-extensions-editor/shared/mode";
|
||||||
|
|
||||||
describe("createAutoModelRequest", () => {
|
describe("createAutoModelRequest", () => {
|
||||||
const externalApiUsages: ExternalApiUsage[] = [
|
const externalApiUsages: ExternalApiUsage[] = [
|
||||||
@@ -259,7 +260,13 @@ describe("createAutoModelRequest", () => {
|
|||||||
|
|
||||||
it("creates a matching request", () => {
|
it("creates a matching request", () => {
|
||||||
expect(
|
expect(
|
||||||
createAutoModelRequest("java", externalApiUsages, modeledMethods, usages),
|
createAutoModelRequest(
|
||||||
|
"java",
|
||||||
|
externalApiUsages,
|
||||||
|
modeledMethods,
|
||||||
|
usages,
|
||||||
|
Mode.Application,
|
||||||
|
),
|
||||||
).toEqual({
|
).toEqual({
|
||||||
language: "java",
|
language: "java",
|
||||||
samples: [
|
samples: [
|
||||||
@@ -340,60 +347,6 @@ describe("createAutoModelRequest", () => {
|
|||||||
input: "Argument[0]",
|
input: "Argument[0]",
|
||||||
classification: undefined,
|
classification: undefined,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
package: "org.springframework.boot",
|
|
||||||
type: "SpringApplication",
|
|
||||||
name: "run",
|
|
||||||
signature: "(Class,String[])",
|
|
||||||
usages:
|
|
||||||
usages[
|
|
||||||
"org.springframework.boot.SpringApplication#run(Class,String[])"
|
|
||||||
],
|
|
||||||
input: "Argument[this]",
|
|
||||||
classification: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
package: "org.springframework.boot",
|
|
||||||
type: "SpringApplication",
|
|
||||||
name: "run",
|
|
||||||
signature: "(Class,String[])",
|
|
||||||
usages:
|
|
||||||
usages[
|
|
||||||
"org.springframework.boot.SpringApplication#run(Class,String[])"
|
|
||||||
],
|
|
||||||
input: "Argument[0]",
|
|
||||||
classification: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
package: "org.springframework.boot",
|
|
||||||
type: "SpringApplication",
|
|
||||||
name: "run",
|
|
||||||
signature: "(Class,String[])",
|
|
||||||
usages:
|
|
||||||
usages[
|
|
||||||
"org.springframework.boot.SpringApplication#run(Class,String[])"
|
|
||||||
],
|
|
||||||
input: "Argument[1]",
|
|
||||||
classification: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
package: "java.io",
|
|
||||||
type: "PrintStream",
|
|
||||||
name: "println",
|
|
||||||
signature: "(String)",
|
|
||||||
usages: usages["java.io.PrintStream#println(String)"],
|
|
||||||
input: "Argument[this]",
|
|
||||||
classification: undefined,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
package: "java.io",
|
|
||||||
type: "PrintStream",
|
|
||||||
name: "println",
|
|
||||||
signature: "(String)",
|
|
||||||
usages: usages["java.io.PrintStream#println(String)"],
|
|
||||||
input: "Argument[0]",
|
|
||||||
classification: undefined,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
package: "org.sql2o",
|
package: "org.sql2o",
|
||||||
type: "Sql2o",
|
type: "Sql2o",
|
||||||
@@ -430,6 +383,60 @@ describe("createAutoModelRequest", () => {
|
|||||||
input: "Argument[2]",
|
input: "Argument[2]",
|
||||||
classification: undefined,
|
classification: undefined,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
package: "java.io",
|
||||||
|
type: "PrintStream",
|
||||||
|
name: "println",
|
||||||
|
signature: "(String)",
|
||||||
|
usages: usages["java.io.PrintStream#println(String)"],
|
||||||
|
input: "Argument[this]",
|
||||||
|
classification: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
package: "java.io",
|
||||||
|
type: "PrintStream",
|
||||||
|
name: "println",
|
||||||
|
signature: "(String)",
|
||||||
|
usages: usages["java.io.PrintStream#println(String)"],
|
||||||
|
input: "Argument[0]",
|
||||||
|
classification: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
package: "org.springframework.boot",
|
||||||
|
type: "SpringApplication",
|
||||||
|
name: "run",
|
||||||
|
signature: "(Class,String[])",
|
||||||
|
usages:
|
||||||
|
usages[
|
||||||
|
"org.springframework.boot.SpringApplication#run(Class,String[])"
|
||||||
|
],
|
||||||
|
input: "Argument[this]",
|
||||||
|
classification: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
package: "org.springframework.boot",
|
||||||
|
type: "SpringApplication",
|
||||||
|
name: "run",
|
||||||
|
signature: "(Class,String[])",
|
||||||
|
usages:
|
||||||
|
usages[
|
||||||
|
"org.springframework.boot.SpringApplication#run(Class,String[])"
|
||||||
|
],
|
||||||
|
input: "Argument[0]",
|
||||||
|
classification: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
package: "org.springframework.boot",
|
||||||
|
type: "SpringApplication",
|
||||||
|
name: "run",
|
||||||
|
signature: "(Class,String[])",
|
||||||
|
usages:
|
||||||
|
usages[
|
||||||
|
"org.springframework.boot.SpringApplication#run(Class,String[])"
|
||||||
|
],
|
||||||
|
input: "Argument[1]",
|
||||||
|
classification: undefined,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { calculateModeledPercentage } from "../modeled";
|
import { calculateModeledPercentage } from "../../../../src/data-extensions-editor/shared/modeled-percentage";
|
||||||
|
|
||||||
describe("calculateModeledPercentage", () => {
|
describe("calculateModeledPercentage", () => {
|
||||||
it("when there are no external API usages", () => {
|
it("when there are no external API usages", () => {
|
||||||
Reference in New Issue
Block a user