Compare commits
149 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4736cfe069 | ||
|
|
9136c789bb | ||
|
|
e2264435f3 | ||
|
|
181e28cbf9 | ||
|
|
12e06b79fc | ||
|
|
515132fe94 | ||
|
|
972da52147 | ||
|
|
99222b0265 | ||
|
|
abbe5976fe | ||
|
|
510ccbdd0f | ||
|
|
b6b707e7fe | ||
|
|
4b3ee3f368 | ||
|
|
238592a9bf | ||
|
|
dc79c53259 | ||
|
|
15a3f36644 | ||
|
|
59f3205286 | ||
|
|
507ddaf8b0 | ||
|
|
7f19aa8037 | ||
|
|
8dcfce550d | ||
|
|
cbf4d5376d | ||
|
|
aec8028e3e | ||
|
|
5d59ffd138 | ||
|
|
7571e4457d | ||
|
|
7b0458eacf | ||
|
|
5ae1d01168 | ||
|
|
4456f5b080 | ||
|
|
11e3aa24dd | ||
|
|
0a90b45c7a | ||
|
|
f4895986de | ||
|
|
7907d8de5d | ||
|
|
2b4fa0d2a5 | ||
|
|
5f4522d7ca | ||
|
|
8fa40be8a9 | ||
|
|
386679a6b2 | ||
|
|
c5fc526b0f | ||
|
|
7fe8a39364 | ||
|
|
df1c397a7e | ||
|
|
d62ade89c5 | ||
|
|
6b4cb5baaa | ||
|
|
30ff64d371 | ||
|
|
7eb7ea263f | ||
|
|
f2a31135f9 | ||
|
|
9aa2b7f6d6 | ||
|
|
f0471ef6e1 | ||
|
|
b9a0f841fb | ||
|
|
1782fd0294 | ||
|
|
7412ea95f5 | ||
|
|
fba62a9dbc | ||
|
|
f14b8cdf44 | ||
|
|
a3363209e1 | ||
|
|
345bc128e8 | ||
|
|
8aec438307 | ||
|
|
fcba347a33 | ||
|
|
282bd221fe | ||
|
|
1c146b97ae | ||
|
|
50329a8bf1 | ||
|
|
5eb1008546 | ||
|
|
d0c9849ff0 | ||
|
|
d8680dcb1e | ||
|
|
0065596964 | ||
|
|
91bc7fc5f9 | ||
|
|
f16331ba36 | ||
|
|
b5dbd52440 | ||
|
|
8315da4b10 | ||
|
|
dffbcc28bb | ||
|
|
97c497bbd6 | ||
|
|
0a2ade3c9d | ||
|
|
a94e5fe9f4 | ||
|
|
28d07a8e6e | ||
|
|
1f175a723b | ||
|
|
bbbb045945 | ||
|
|
cbf6f730d8 | ||
|
|
a123c03c4e | ||
|
|
e1b5135c8e | ||
|
|
63f6e91732 | ||
|
|
885dfa08e2 | ||
|
|
b63c8ea8e4 | ||
|
|
067c2f45c8 | ||
|
|
ede25887f2 | ||
|
|
14c974e9a0 | ||
|
|
cddf078dd0 | ||
|
|
bfa7ea1be6 | ||
|
|
8326ef3c5d | ||
|
|
756200b03d | ||
|
|
268fd9fcd9 | ||
|
|
dc6d5c14ea | ||
|
|
b501a04074 | ||
|
|
96c33a14b8 | ||
|
|
1e3d41b532 | ||
|
|
c90f8056e4 | ||
|
|
0a731e2a14 | ||
|
|
7b9fe1e22a | ||
|
|
babf3f12ac | ||
|
|
4546616871 | ||
|
|
b201c45e2d | ||
|
|
3364af5305 | ||
|
|
d6af6e7c4c | ||
|
|
f9f454509b | ||
|
|
9f0453a80a | ||
|
|
af2a592e3d | ||
|
|
a4fcbd093c | ||
|
|
f02c007bdb | ||
|
|
66874823aa | ||
|
|
1a59467707 | ||
|
|
78383e376c | ||
|
|
32cf05cb4b | ||
|
|
1ec47a4eef | ||
|
|
4d2f84d599 | ||
|
|
a858b39f26 | ||
|
|
5d71ce41a8 | ||
|
|
76354c4518 | ||
|
|
4e310ce652 | ||
|
|
68bfa00707 | ||
|
|
51ef674cc5 | ||
|
|
d2612e2dca | ||
|
|
1db3dda533 | ||
|
|
9c3b0b24c9 | ||
|
|
df06c75070 | ||
|
|
b3c44ea317 | ||
|
|
d6673c4ede | ||
|
|
d68e270e90 | ||
|
|
97b9c43ae1 | ||
|
|
7e00e9cd2f | ||
|
|
459d606751 | ||
|
|
a069ae45fb | ||
|
|
a6225c5edf | ||
|
|
64d4439815 | ||
|
|
b11a9e2c88 | ||
|
|
75ab23bbd8 | ||
|
|
5bab2421ea | ||
|
|
d5edbca4e4 | ||
|
|
1ee349d2ef | ||
|
|
79f89c9e4b | ||
|
|
2fbdefe4f0 | ||
|
|
01403aeee7 | ||
|
|
9f7f34a87c | ||
|
|
987b92ab1e | ||
|
|
6fd2579c9c | ||
|
|
c9e1a64d3a | ||
|
|
03f330da52 | ||
|
|
4f6d72a9ce | ||
|
|
b5d3a612d8 | ||
|
|
48209e7732 | ||
|
|
9934449ae9 | ||
|
|
fdc4d97f93 | ||
|
|
b1ca9418b4 | ||
|
|
7d3a350a28 | ||
|
|
ca4c511227 | ||
|
|
8b4f2d2009 |
4
.github/workflows/e2e-tests.yml
vendored
4
.github/workflows/e2e-tests.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Start containers
|
- name: Start containers
|
||||||
working-directory: extensions/ql-vscode/test/e2e
|
working-directory: extensions/ql-vscode/test/e2e
|
||||||
run: docker-compose -f "docker-compose.yml" up -d --build
|
run: docker compose -f "docker-compose.yml" up -d --build
|
||||||
|
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
working-directory: extensions/ql-vscode
|
working-directory: extensions/ql-vscode
|
||||||
@@ -43,4 +43,4 @@ jobs:
|
|||||||
- name: Stop containers
|
- name: Stop containers
|
||||||
working-directory: extensions/ql-vscode/test/e2e
|
working-directory: extensions/ql-vscode/test/e2e
|
||||||
if: always()
|
if: always()
|
||||||
run: docker-compose -f "docker-compose.yml" down -v
|
run: docker compose -f "docker-compose.yml" down -v
|
||||||
|
|||||||
22
.github/workflows/main.yml
vendored
22
.github/workflows/main.yml
vendored
@@ -79,13 +79,6 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
npm run check-types
|
npm run check-types
|
||||||
|
|
||||||
- name: Lint
|
|
||||||
working-directory: extensions/ql-vscode
|
|
||||||
env:
|
|
||||||
NODE_OPTIONS: '--max-old-space-size=4096'
|
|
||||||
run: |
|
|
||||||
npm run lint
|
|
||||||
|
|
||||||
- name: Lint Markdown
|
- name: Lint Markdown
|
||||||
working-directory: extensions/ql-vscode
|
working-directory: extensions/ql-vscode
|
||||||
run: |
|
run: |
|
||||||
@@ -101,6 +94,21 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
npm run find-deadcode
|
npm run find-deadcode
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
if: "${{ !cancelled() }}"
|
||||||
|
working-directory: extensions/ql-vscode
|
||||||
|
env:
|
||||||
|
NODE_OPTIONS: '--max-old-space-size=4096'
|
||||||
|
run: |
|
||||||
|
npm run lint-ci
|
||||||
|
|
||||||
|
- name: Upload ESLint results to Code Scanning
|
||||||
|
if: "${{ !cancelled() && !startsWith(github.head_ref, 'dependabot/')}}"
|
||||||
|
uses: github/codeql-action/upload-sarif@main
|
||||||
|
with:
|
||||||
|
sarif_file: extensions/ql-vscode/build/eslint.sarif
|
||||||
|
category: eslint
|
||||||
|
|
||||||
generated:
|
generated:
|
||||||
name: Check generated code
|
name: Check generated code
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -1,4 +1 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
. "$(dirname -- "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
cd extensions/ql-vscode && npm run format-staged
|
cd extensions/ql-vscode && npm run format-staged
|
||||||
|
|||||||
@@ -1,4 +1 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
. "$(dirname -- "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
cd extensions/ql-vscode && ./scripts/forbid-test-only
|
cd extensions/ql-vscode && ./scripts/forbid-test-only
|
||||||
|
|||||||
@@ -3,5 +3,14 @@ node_modules/
|
|||||||
out/
|
out/
|
||||||
build/
|
build/
|
||||||
|
|
||||||
|
# Ignore js files
|
||||||
|
.eslintrc.js
|
||||||
|
jest.config.js
|
||||||
|
test/vscode-tests/activated-extension/jest-runner-vscode.config.js
|
||||||
|
test/vscode-tests/cli-integration/jest-runner-vscode.config.js
|
||||||
|
test/vscode-tests/jest-runner-vscode.config.base.js
|
||||||
|
test/vscode-tests/minimal-workspace/jest-runner-vscode.config.js
|
||||||
|
test/vscode-tests/no-workspace/jest-runner-vscode.config.js
|
||||||
|
|
||||||
# Include the Storybook config
|
# Include the Storybook config
|
||||||
!.storybook
|
!.storybook
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ const baseConfig = {
|
|||||||
"@typescript-eslint/no-invalid-this": "off",
|
"@typescript-eslint/no-invalid-this": "off",
|
||||||
"@typescript-eslint/no-shadow": "off",
|
"@typescript-eslint/no-shadow": "off",
|
||||||
"prefer-const": ["warn", { destructuring: "all" }],
|
"prefer-const": ["warn", { destructuring: "all" }],
|
||||||
"@typescript-eslint/no-throw-literal": "error",
|
"@typescript-eslint/only-throw-error": "error",
|
||||||
"@typescript-eslint/consistent-type-imports": "error",
|
"@typescript-eslint/consistent-type-imports": "error",
|
||||||
"import/consistent-type-specifier-style": ["error", "prefer-top-level"],
|
"import/consistent-type-specifier-style": ["error", "prefer-top-level"],
|
||||||
curly: ["error", "all"],
|
curly: ["error", "all"],
|
||||||
@@ -133,18 +133,7 @@ module.exports = {
|
|||||||
...baseConfig.rules,
|
...baseConfig.rules,
|
||||||
// We want to allow mocking of functions in modules, so we need to allow namespace imports.
|
// We want to allow mocking of functions in modules, so we need to allow namespace imports.
|
||||||
"import/no-namespace": "off",
|
"import/no-namespace": "off",
|
||||||
"@typescript-eslint/ban-types": [
|
"@typescript-eslint/no-unsafe-function-type": "off",
|
||||||
"error",
|
|
||||||
{
|
|
||||||
// For a full list of the default banned types, see:
|
|
||||||
// https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/ban-types.md
|
|
||||||
extendDefaults: true,
|
|
||||||
types: {
|
|
||||||
// Don't complain about the `Function` type in test files. (Default is `true`.)
|
|
||||||
Function: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v20.9.0
|
v20.15.1
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"target": "es6",
|
"target": "es2021",
|
||||||
"outDir": "out",
|
"outDir": "out",
|
||||||
"lib": ["ES2021", "dom"],
|
"lib": ["ES2021", "dom"],
|
||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { addons } from "@storybook/manager-api";
|
import { addons } from "@storybook/manager-api";
|
||||||
import { Addon_TypesEnum } from "@storybook/types";
|
import { Addon_TypesEnum } from "storybook/internal/types";
|
||||||
import { ThemeSelector } from "./ThemeSelector";
|
import { ThemeSelector } from "./ThemeSelector";
|
||||||
|
|
||||||
const ADDON_ID = "vscode-theme-addon";
|
const ADDON_ID = "vscode-theme-addon";
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
# CodeQL for Visual Studio Code: Changelog
|
# CodeQL for Visual Studio Code: Changelog
|
||||||
|
|
||||||
## [UNRELEASED]
|
## 1.15.0 - 26 September 2024
|
||||||
|
|
||||||
|
- Update results view to display the length of the shortest path for path queries. [#3687](https://github.com/github/vscode-codeql/pull/3687)
|
||||||
|
- Remove support for CodeQL CLI versions older than 2.16.6. [#3728](https://github.com/github/vscode-codeql/pull/3728)
|
||||||
|
|
||||||
|
## 1.14.0 - 7 August 2024
|
||||||
|
|
||||||
|
- Add Python support to the CodeQL Model Editor. [#3676](https://github.com/github/vscode-codeql/pull/3676)
|
||||||
|
- Update variant analysis view to display the length of the shortest path for path queries. [#3671](https://github.com/github/vscode-codeql/pull/3671)
|
||||||
|
- Remove support for CodeQL CLI versions older than 2.15.5. [#3681](https://github.com/github/vscode-codeql/pull/3681)
|
||||||
|
|
||||||
## 1.13.1 - 29 May 2024
|
## 1.13.1 - 29 May 2024
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { src, dest } from "gulp";
|
import { src, dest } from "gulp";
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires,import/no-commonjs
|
// eslint-disable-next-line @typescript-eslint/no-require-imports,import/no-commonjs
|
||||||
const replace = require("gulp-replace");
|
const replace = require("gulp-replace");
|
||||||
|
|
||||||
/** Inject the application insights key into the telemetry file */
|
/** Inject the application insights key into the telemetry file */
|
||||||
|
|||||||
@@ -77,5 +77,8 @@ export function copyWasmFiles() {
|
|||||||
// to configure the path to the WASM file. So, source-map will always load the file from `__dirname/mappings.wasm`.
|
// to configure the path to the WASM file. So, source-map will always load the file from `__dirname/mappings.wasm`.
|
||||||
// In version 0.8.0, it may be possible to do this properly by calling SourceMapConsumer.initialize by
|
// In version 0.8.0, it may be possible to do this properly by calling SourceMapConsumer.initialize by
|
||||||
// using the "browser" field in source-map's package.json to load the WASM file from a given file path.
|
// using the "browser" field in source-map's package.json to load the WASM file from a given file path.
|
||||||
return src("node_modules/source-map/lib/mappings.wasm").pipe(dest("out"));
|
return src("node_modules/source-map/lib/mappings.wasm", {
|
||||||
|
// WASM is a binary format, so don't try to re-encode it as text.
|
||||||
|
encoding: false,
|
||||||
|
}).pipe(dest("out"));
|
||||||
}
|
}
|
||||||
|
|||||||
9577
extensions/ql-vscode/package-lock.json
generated
9577
extensions/ql-vscode/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
|||||||
"description": "CodeQL for Visual Studio Code",
|
"description": "CodeQL for Visual Studio Code",
|
||||||
"author": "GitHub",
|
"author": "GitHub",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.13.2",
|
"version": "1.15.0",
|
||||||
"publisher": "GitHub",
|
"publisher": "GitHub",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"icon": "media/VS-marketplace-CodeQL-icon.png",
|
"icon": "media/VS-marketplace-CodeQL-icon.png",
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.82.0",
|
"vscode": "^1.82.0",
|
||||||
"node": "^20.9.0",
|
"node": "^20.15.1",
|
||||||
"npm": ">=7.20.6"
|
"npm": ">=7.20.6"
|
||||||
},
|
},
|
||||||
"categories": [
|
"categories": [
|
||||||
@@ -1790,8 +1790,7 @@
|
|||||||
"when": "false"
|
"when": "false"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"command": "codeQL.trimCache",
|
"command": "codeQL.trimCache"
|
||||||
"when": "codeql.supportsTrimCache"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"editor/context": [
|
"editor/context": [
|
||||||
@@ -1954,6 +1953,7 @@
|
|||||||
"update-vscode": "node ./node_modules/vscode/bin/install",
|
"update-vscode": "node ./node_modules/vscode/bin/install",
|
||||||
"format": "prettier --write **/*.{ts,tsx} && eslint . --ext .ts,.tsx --fix",
|
"format": "prettier --write **/*.{ts,tsx} && eslint . --ext .ts,.tsx --fix",
|
||||||
"lint": "eslint . --ext .js,.ts,.tsx --max-warnings=0",
|
"lint": "eslint . --ext .js,.ts,.tsx --max-warnings=0",
|
||||||
|
"lint-ci": "SARIF_ESLINT_IGNORE_SUPPRESSED=true eslint . --ext .js,.ts,.tsx --max-warnings=0 --format @microsoft/eslint-formatter-sarif --output-file=build/eslint.sarif",
|
||||||
"lint:markdown": "markdownlint-cli2 \"../../**/*.{md,mdx}\" \"!**/node_modules/**\" \"!**/.vscode-test/**\" \"!**/build/cli/v*/**\"",
|
"lint:markdown": "markdownlint-cli2 \"../../**/*.{md,mdx}\" \"!**/node_modules/**\" \"!**/.vscode-test/**\" \"!**/build/cli/v*/**\"",
|
||||||
"find-deadcode": "vite-node scripts/find-deadcode.ts",
|
"find-deadcode": "vite-node scripts/find-deadcode.ts",
|
||||||
"format-staged": "lint-staged",
|
"format-staged": "lint-staged",
|
||||||
@@ -1969,10 +1969,10 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@floating-ui/react": "^0.26.12",
|
"@floating-ui/react": "^0.26.12",
|
||||||
"@octokit/plugin-retry": "^6.0.1",
|
"@octokit/plugin-retry": "^7.1.2",
|
||||||
"@octokit/plugin-throttling": "^8.0.0",
|
"@octokit/plugin-throttling": "^9.3.1",
|
||||||
"@octokit/rest": "^20.0.2",
|
"@octokit/rest": "^21.0.2",
|
||||||
"@vscode/codicons": "^0.0.35",
|
"@vscode/codicons": "^0.0.36",
|
||||||
"@vscode/debugadapter": "^1.59.0",
|
"@vscode/debugadapter": "^1.59.0",
|
||||||
"@vscode/debugprotocol": "^1.65.0",
|
"@vscode/debugprotocol": "^1.65.0",
|
||||||
"@vscode/webview-ui-toolkit": "^1.0.1",
|
"@vscode/webview-ui-toolkit": "^1.0.1",
|
||||||
@@ -1985,7 +1985,7 @@
|
|||||||
"js-yaml": "^4.1.0",
|
"js-yaml": "^4.1.0",
|
||||||
"msw": "^2.2.13",
|
"msw": "^2.2.13",
|
||||||
"nanoid": "^5.0.7",
|
"nanoid": "^5.0.7",
|
||||||
"node-fetch": "^2.6.7",
|
"node-fetch": "^3.3.2",
|
||||||
"p-queue": "^8.0.1",
|
"p-queue": "^8.0.1",
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1",
|
"react-dom": "^18.3.1",
|
||||||
@@ -1998,36 +1998,37 @@
|
|||||||
"tmp-promise": "^3.0.2",
|
"tmp-promise": "^3.0.2",
|
||||||
"tree-kill": "^1.2.2",
|
"tree-kill": "^1.2.2",
|
||||||
"vscode-extension-telemetry": "^0.1.6",
|
"vscode-extension-telemetry": "^0.1.6",
|
||||||
"vscode-jsonrpc": "^8.0.2",
|
"vscode-jsonrpc": "^8.2.1",
|
||||||
"vscode-languageclient": "^8.0.2",
|
"vscode-languageclient": "^8.0.2",
|
||||||
"yauzl": "^2.10.0",
|
"yauzl": "^2.10.0",
|
||||||
"zip-a-folder": "^3.1.6"
|
"zip-a-folder": "^3.1.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.24.6",
|
"@babel/core": "^7.24.6",
|
||||||
"@babel/plugin-transform-modules-commonjs": "^7.18.6",
|
"@babel/plugin-transform-modules-commonjs": "^7.24.7",
|
||||||
"@babel/preset-env": "^7.24.4",
|
"@babel/preset-env": "^7.24.4",
|
||||||
"@babel/preset-react": "^7.24.1",
|
"@babel/preset-react": "^7.24.1",
|
||||||
"@babel/preset-typescript": "^7.21.4",
|
"@babel/preset-typescript": "^7.21.4",
|
||||||
"@faker-js/faker": "^8.4.1",
|
"@faker-js/faker": "^8.4.1",
|
||||||
"@github/markdownlint-github": "^0.6.2",
|
"@github/markdownlint-github": "^0.6.2",
|
||||||
|
"@microsoft/eslint-formatter-sarif": "^3.1.0",
|
||||||
"@playwright/test": "^1.40.1",
|
"@playwright/test": "^1.40.1",
|
||||||
"@storybook/addon-a11y": "^8.1.10",
|
"@storybook/addon-a11y": "^8.3.1",
|
||||||
"@storybook/addon-actions": "^8.1.10",
|
"@storybook/addon-actions": "^8.3.1",
|
||||||
"@storybook/addon-essentials": "^8.1.10",
|
"@storybook/addon-essentials": "^8.3.1",
|
||||||
"@storybook/addon-interactions": "^8.1.10",
|
"@storybook/addon-interactions": "^8.3.1",
|
||||||
"@storybook/addon-links": "^8.1.10",
|
"@storybook/addon-links": "^8.3.1",
|
||||||
"@storybook/blocks": "^8.0.2",
|
"@storybook/blocks": "^8.0.2",
|
||||||
"@storybook/components": "^8.0.2",
|
"@storybook/components": "^8.3.1",
|
||||||
"@storybook/csf": "^0.1.8",
|
"@storybook/csf": "^0.1.11",
|
||||||
"@storybook/icons": "^1.2.9",
|
"@storybook/icons": "^1.2.12",
|
||||||
"@storybook/manager-api": "^8.1.10",
|
"@storybook/manager-api": "^8.3.1",
|
||||||
"@storybook/react": "^8.1.10",
|
"@storybook/react": "^8.3.1",
|
||||||
"@storybook/react-vite": "^8.1.10",
|
"@storybook/react-vite": "^8.3.1",
|
||||||
"@storybook/theming": "^8.1.10",
|
"@storybook/theming": "^8.2.4",
|
||||||
"@testing-library/dom": "^10.1.0",
|
"@testing-library/dom": "^10.4.0",
|
||||||
"@testing-library/jest-dom": "^6.4.6",
|
"@testing-library/jest-dom": "^6.5.0",
|
||||||
"@testing-library/react": "^16.0.0",
|
"@testing-library/react": "^16.0.1",
|
||||||
"@testing-library/user-event": "^14.5.2",
|
"@testing-library/user-event": "^14.5.2",
|
||||||
"@types/child-process-promise": "^2.2.1",
|
"@types/child-process-promise": "^2.2.1",
|
||||||
"@types/d3": "^7.4.0",
|
"@types/d3": "^7.4.0",
|
||||||
@@ -2039,8 +2040,7 @@
|
|||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
"@types/js-yaml": "^4.0.6",
|
"@types/js-yaml": "^4.0.6",
|
||||||
"@types/nanoid": "^3.0.0",
|
"@types/nanoid": "^3.0.0",
|
||||||
"@types/node": "20.9.*",
|
"@types/node": "20.15.*",
|
||||||
"@types/node-fetch": "^2.5.2",
|
|
||||||
"@types/react": "^18.3.1",
|
"@types/react": "^18.3.1",
|
||||||
"@types/react-dom": "^18.3.0",
|
"@types/react-dom": "^18.3.0",
|
||||||
"@types/sarif": "^2.1.2",
|
"@types/sarif": "^2.1.2",
|
||||||
@@ -2052,8 +2052,8 @@
|
|||||||
"@types/tmp": "^0.2.6",
|
"@types/tmp": "^0.2.6",
|
||||||
"@types/vscode": "^1.82.0",
|
"@types/vscode": "^1.82.0",
|
||||||
"@types/yauzl": "^2.10.3",
|
"@types/yauzl": "^2.10.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.5.0",
|
"@typescript-eslint/eslint-plugin": "^8.6.0",
|
||||||
"@typescript-eslint/parser": "^7.5.0",
|
"@typescript-eslint/parser": "^8.6.0",
|
||||||
"@vscode/test-electron": "^2.3.9",
|
"@vscode/test-electron": "^2.3.9",
|
||||||
"@vscode/vsce": "^2.24.0",
|
"@vscode/vsce": "^2.24.0",
|
||||||
"ansi-colors": "^4.1.1",
|
"ansi-colors": "^4.1.1",
|
||||||
@@ -2064,21 +2064,21 @@
|
|||||||
"eslint": "^8.56.0",
|
"eslint": "^8.56.0",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-import-resolver-typescript": "^3.6.1",
|
"eslint-import-resolver-typescript": "^3.6.1",
|
||||||
"eslint-plugin-deprecation": "^2.0.0",
|
"eslint-plugin-deprecation": "^3.0.0",
|
||||||
"eslint-plugin-etc": "^2.0.2",
|
"eslint-plugin-etc": "^2.0.2",
|
||||||
"eslint-plugin-github": "^4.10.2",
|
"eslint-plugin-github": "^5.0.1",
|
||||||
"eslint-plugin-import": "^2.29.1",
|
"eslint-plugin-import": "^2.29.1",
|
||||||
"eslint-plugin-jest-dom": "^5.2.0",
|
"eslint-plugin-jest-dom": "^5.2.0",
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-react": "^7.34.1",
|
"eslint-plugin-react": "^7.34.1",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.2",
|
||||||
"eslint-plugin-storybook": "^0.8.0",
|
"eslint-plugin-storybook": "^0.8.0",
|
||||||
"glob": "^10.0.0",
|
"glob": "^10.0.0",
|
||||||
"gulp": "^4.0.2",
|
"gulp": "^5.0.0",
|
||||||
"gulp-esbuild": "^0.12.0",
|
"gulp-esbuild": "^0.12.1",
|
||||||
"gulp-replace": "^1.1.3",
|
"gulp-replace": "^1.1.3",
|
||||||
"gulp-typescript": "^5.0.1",
|
"gulp-typescript": "^5.0.1",
|
||||||
"husky": "^9.0.11",
|
"husky": "^9.1.5",
|
||||||
"jest": "^29.0.3",
|
"jest": "^29.0.3",
|
||||||
"jest-environment-jsdom": "^29.0.3",
|
"jest-environment-jsdom": "^29.0.3",
|
||||||
"jest-runner-vscode": "^3.0.1",
|
"jest-runner-vscode": "^3.0.1",
|
||||||
@@ -2088,16 +2088,16 @@
|
|||||||
"npm-run-all": "^4.1.5",
|
"npm-run-all": "^4.1.5",
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
"prettier": "^3.2.5",
|
"prettier": "^3.2.5",
|
||||||
"storybook": "^8.1.10",
|
"storybook": "^8.3.1",
|
||||||
"tar-stream": "^3.1.7",
|
"tar-stream": "^3.1.7",
|
||||||
"through2": "^4.0.2",
|
"through2": "^4.0.2",
|
||||||
"ts-jest": "^29.1.4",
|
"ts-jest": "^29.1.4",
|
||||||
"ts-json-schema-generator": "^2.1.1",
|
"ts-json-schema-generator": "^2.1.1",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
"ts-unused-exports": "^10.1.0",
|
"ts-unused-exports": "^10.1.0",
|
||||||
"typescript": "^5.0.2",
|
"typescript": "^5.6.2",
|
||||||
"vite": "^5.2.11",
|
"vite": "^5.4.6",
|
||||||
"vite-node": "^1.5.3"
|
"vite-node": "^2.0.5"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"lint-staged": {
|
||||||
"./**/*.{json,css,scss}": [
|
"./**/*.{json,css,scss}": [
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ interface VersionResult {
|
|||||||
export interface CliFeatures {
|
export interface CliFeatures {
|
||||||
featuresInVersionResult?: boolean;
|
featuresInVersionResult?: boolean;
|
||||||
mrvaPackCreate?: boolean;
|
mrvaPackCreate?: boolean;
|
||||||
|
generateSummarySymbolMap?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VersionAndFeatures {
|
export interface VersionAndFeatures {
|
||||||
|
|||||||
@@ -943,7 +943,7 @@ export class CodeQLCliServer implements Disposable {
|
|||||||
if (line.startsWith("Enter value for --github-auth-stdin")) {
|
if (line.startsWith("Enter value for --github-auth-stdin")) {
|
||||||
try {
|
try {
|
||||||
return await this.app.credentials.getAccessToken();
|
return await this.app.credentials.getAccessToken();
|
||||||
} catch (e) {
|
} catch {
|
||||||
// If the user cancels the authentication prompt, we still need to give a value to the CLI.
|
// If the user cancels the authentication prompt, we still need to give a value to the CLI.
|
||||||
// By giving a potentially invalid value, the user will just get a 401/403 when they try to access a
|
// By giving a potentially invalid value, the user will just get a 401/403 when they try to access a
|
||||||
// private package and the access token is invalid.
|
// private package and the access token is invalid.
|
||||||
@@ -1211,10 +1211,15 @@ export class CodeQLCliServer implements Disposable {
|
|||||||
outputPath: string,
|
outputPath: string,
|
||||||
endSummaryPath: string,
|
endSummaryPath: string,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
|
const supportsGenerateSummarySymbolMap =
|
||||||
|
await this.cliConstraints.supportsGenerateSummarySymbolMap();
|
||||||
const subcommandArgs = [
|
const subcommandArgs = [
|
||||||
"--format=text",
|
"--format=text",
|
||||||
`--end-summary=${endSummaryPath}`,
|
`--end-summary=${endSummaryPath}`,
|
||||||
"--sourcemap",
|
"--sourcemap",
|
||||||
|
...(supportsGenerateSummarySymbolMap
|
||||||
|
? ["--summary-symbol-map", "--minify-output"]
|
||||||
|
: []),
|
||||||
inputPath,
|
inputPath,
|
||||||
outputPath,
|
outputPath,
|
||||||
];
|
];
|
||||||
@@ -1750,14 +1755,6 @@ export class CodeQLCliServer implements Disposable {
|
|||||||
this._versionChangedListeners.forEach((listener) =>
|
this._versionChangedListeners.forEach((listener) =>
|
||||||
listener(newVersionAndFeatures),
|
listener(newVersionAndFeatures),
|
||||||
);
|
);
|
||||||
// this._version is only undefined upon config change, so we reset CLI-based context key only when necessary.
|
|
||||||
await this.app.commands.execute(
|
|
||||||
"setContext",
|
|
||||||
"codeql.supportsTrimCache",
|
|
||||||
newVersionAndFeatures.version.compare(
|
|
||||||
CliVersionConstraint.CLI_VERSION_WITH_TRIM_CACHE,
|
|
||||||
) >= 0,
|
|
||||||
);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._versionChangedListeners.forEach((listener) =>
|
this._versionChangedListeners.forEach((listener) =>
|
||||||
listener(undefined),
|
listener(undefined),
|
||||||
@@ -1912,45 +1909,17 @@ function shouldDebugCliServer() {
|
|||||||
export class CliVersionConstraint {
|
export class CliVersionConstraint {
|
||||||
// The oldest version of the CLI that we support. This is used to determine
|
// The oldest version of the CLI that we support. This is used to determine
|
||||||
// whether to show a warning about the CLI being too old on startup.
|
// whether to show a warning about the CLI being too old on startup.
|
||||||
public static OLDEST_SUPPORTED_CLI_VERSION = new SemVer("2.14.6");
|
public static OLDEST_SUPPORTED_CLI_VERSION = new SemVer("2.16.6");
|
||||||
|
|
||||||
/**
|
|
||||||
* CLI version where the query server supports the `evaluation/trimCache` method
|
|
||||||
* with `codeql database cleanup --mode=trim` semantics.
|
|
||||||
*/
|
|
||||||
public static CLI_VERSION_WITH_TRIM_CACHE = new SemVer("2.15.1");
|
|
||||||
|
|
||||||
public static CLI_VERSION_WITHOUT_MRVA_EXTENSIBLE_PREDICATE_HACK = new SemVer(
|
|
||||||
"2.16.1",
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CLI version where there is support for multiple queries on the pack create command.
|
|
||||||
*/
|
|
||||||
public static CLI_VERSION_WITH_MULTI_QUERY_PACK_CREATE = new SemVer("2.16.1");
|
|
||||||
|
|
||||||
constructor(private readonly cli: CodeQLCliServer) {
|
constructor(private readonly cli: CodeQLCliServer) {
|
||||||
/**/
|
/**/
|
||||||
}
|
}
|
||||||
|
|
||||||
private async isVersionAtLeast(v: SemVer) {
|
|
||||||
return (await this.cli.getVersion()).compare(v) >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
async preservesExtensiblePredicatesInMrvaPack() {
|
|
||||||
// Negated, because we _stopped_ preserving these in 2.16.1.
|
|
||||||
return !(await this.isVersionAtLeast(
|
|
||||||
CliVersionConstraint.CLI_VERSION_WITHOUT_MRVA_EXTENSIBLE_PREDICATE_HACK,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
async supportsPackCreateWithMultipleQueries() {
|
|
||||||
return this.isVersionAtLeast(
|
|
||||||
CliVersionConstraint.CLI_VERSION_WITH_MULTI_QUERY_PACK_CREATE,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async supportsMrvaPackCreate(): Promise<boolean> {
|
async supportsMrvaPackCreate(): Promise<boolean> {
|
||||||
return (await this.cli.getFeatures()).mrvaPackCreate === true;
|
return (await this.cli.getFeatures()).mrvaPackCreate === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async supportsGenerateSummarySymbolMap(): Promise<boolean> {
|
||||||
|
return (await this.cli.getFeatures()).generateSummarySymbolMap === true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -404,6 +404,11 @@ class ExtensionSpecificDistributionManager {
|
|||||||
signal,
|
signal,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const body = assetStream.body;
|
||||||
|
if (!body) {
|
||||||
|
throw new Error("No body in asset stream");
|
||||||
|
}
|
||||||
|
|
||||||
const archivePath = join(tmpDirectory, "distributionDownload.zip");
|
const archivePath = join(tmpDirectory, "distributionDownload.zip");
|
||||||
archiveFile = createWriteStream(archivePath);
|
archiveFile = createWriteStream(archivePath);
|
||||||
|
|
||||||
@@ -412,26 +417,23 @@ class ExtensionSpecificDistributionManager {
|
|||||||
? parseInt(contentLength, 10)
|
? parseInt(contentLength, 10)
|
||||||
: undefined;
|
: undefined;
|
||||||
reportStreamProgress(
|
reportStreamProgress(
|
||||||
assetStream.body,
|
body,
|
||||||
`Downloading CodeQL CLI ${release.name}…`,
|
`Downloading CodeQL CLI ${release.name}…`,
|
||||||
totalNumBytes,
|
totalNumBytes,
|
||||||
progressCallback,
|
progressCallback,
|
||||||
);
|
);
|
||||||
|
|
||||||
assetStream.body.on("data", onData);
|
body.on("data", onData);
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
if (!archiveFile) {
|
if (!archiveFile) {
|
||||||
throw new Error("Invariant violation: archiveFile not set");
|
throw new Error("Invariant violation: archiveFile not set");
|
||||||
}
|
}
|
||||||
|
|
||||||
assetStream.body
|
body.pipe(archiveFile).on("finish", resolve).on("error", reject);
|
||||||
.pipe(archiveFile)
|
|
||||||
.on("finish", resolve)
|
|
||||||
.on("error", reject);
|
|
||||||
|
|
||||||
// If an error occurs on the body, we also want to reject the promise (e.g. during a timeout error).
|
// If an error occurs on the body, we also want to reject the promise (e.g. during a timeout error).
|
||||||
assetStream.body.on("error", reject);
|
body.on("error", reject);
|
||||||
});
|
});
|
||||||
|
|
||||||
disposeTimeout();
|
disposeTimeout();
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ export class ReleasesApiConsumer {
|
|||||||
additionalCompatibilityCheck?: (release: GithubRelease) => boolean,
|
additionalCompatibilityCheck?: (release: GithubRelease) => boolean,
|
||||||
): Promise<Release> {
|
): Promise<Release> {
|
||||||
const apiPath = `/repos/${this.repositoryNwo}/releases`;
|
const apiPath = `/repos/${this.repositoryNwo}/releases`;
|
||||||
const allReleases: GithubRelease[] = await (
|
const allReleases = (await (
|
||||||
await this.makeApiCall(apiPath)
|
await this.makeApiCall(apiPath)
|
||||||
).json();
|
).json()) as GithubRelease[];
|
||||||
const compatibleReleases = allReleases.filter((release) => {
|
const compatibleReleases = allReleases.filter((release) => {
|
||||||
if (release.prerelease && !includePrerelease) {
|
if (release.prerelease && !includePrerelease) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export async function findLanguage(
|
|||||||
void extLogger.log(
|
void extLogger.log(
|
||||||
"Query language is unsupported. Select language manually.",
|
"Query language is unsupported. Select language manually.",
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch {
|
||||||
void extLogger.log(
|
void extLogger.log(
|
||||||
"Could not autodetect query language. Select language manually.",
|
"Could not autodetect query language. Select language manually.",
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ function getNwoOrOwnerFromGitHubUrl(
|
|||||||
}
|
}
|
||||||
const nwo = `${paths[0]}/${paths[1]}`;
|
const nwo = `${paths[0]}/${paths[1]}`;
|
||||||
return paths[1] ? nwo : undefined;
|
return paths[1] ? nwo : undefined;
|
||||||
} catch (e) {
|
} catch {
|
||||||
// Ignore the error here, since we catch failures at a higher level.
|
// Ignore the error here, since we catch failures at a higher level.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,6 +147,21 @@ interface SetStateMsg {
|
|||||||
parsedResultSets: ParsedResultSets;
|
parsedResultSets: ParsedResultSets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface UserSettings {
|
||||||
|
/** Whether to display links to the dataflow models that generated particular nodes in a flow path. */
|
||||||
|
shouldShowProvenance: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DEFAULT_USER_SETTINGS: UserSettings = {
|
||||||
|
shouldShowProvenance: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Message indicating that the user's configuration settings have changed. */
|
||||||
|
interface SetUserSettingsMsg {
|
||||||
|
t: "setUserSettings";
|
||||||
|
userSettings: UserSettings;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message indicating that the results view should display interpreted
|
* Message indicating that the results view should display interpreted
|
||||||
* results.
|
* results.
|
||||||
@@ -191,6 +206,7 @@ interface UntoggleShowProblemsMsg {
|
|||||||
export type IntoResultsViewMsg =
|
export type IntoResultsViewMsg =
|
||||||
| ResultsUpdatingMsg
|
| ResultsUpdatingMsg
|
||||||
| SetStateMsg
|
| SetStateMsg
|
||||||
|
| SetUserSettingsMsg
|
||||||
| ShowInterpretedPageMsg
|
| ShowInterpretedPageMsg
|
||||||
| NavigateMsg
|
| NavigateMsg
|
||||||
| UntoggleShowProblemsMsg;
|
| UntoggleShowProblemsMsg;
|
||||||
@@ -208,13 +224,15 @@ export type FromResultsViewMsg =
|
|||||||
| OpenFileMsg;
|
| OpenFileMsg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message from the results view to open a database source
|
* Message from the results view to open a source
|
||||||
* file at the provided location.
|
* file at the provided location.
|
||||||
*/
|
*/
|
||||||
interface ViewSourceFileMsg {
|
interface ViewSourceFileMsg {
|
||||||
t: "viewSourceFile";
|
t: "viewSourceFile";
|
||||||
loc: UrlValueResolvable;
|
loc: UrlValueResolvable;
|
||||||
databaseUri: string;
|
/** URI of the database whose source archive contains the file, or `undefined` to open a file from
|
||||||
|
* the local disk. The latter case is used for opening links to data extension model files. */
|
||||||
|
databaseUri: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -341,7 +359,8 @@ interface ChangeCompareMessage {
|
|||||||
|
|
||||||
export type ToCompareViewMessage =
|
export type ToCompareViewMessage =
|
||||||
| SetComparisonQueryInfoMessage
|
| SetComparisonQueryInfoMessage
|
||||||
| SetComparisonsMessage;
|
| SetComparisonsMessage
|
||||||
|
| SetUserSettingsMsg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Message to the compare view that sets the metadata of the compared queries.
|
* Message to the compare view that sets the metadata of the compared queries.
|
||||||
@@ -541,16 +560,6 @@ interface SetModifiedMethodsMessage {
|
|||||||
methodSignatures: string[];
|
methodSignatures: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SetInProgressMethodsMessage {
|
|
||||||
t: "setInProgressMethods";
|
|
||||||
methods: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SetProcessedByAutoModelMethodsMessage {
|
|
||||||
t: "setProcessedByAutoModelMethods";
|
|
||||||
methods: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SwitchModeMessage {
|
interface SwitchModeMessage {
|
||||||
t: "switchMode";
|
t: "switchMode";
|
||||||
mode: Mode;
|
mode: Mode;
|
||||||
@@ -582,17 +591,6 @@ interface GenerateMethodMessage {
|
|||||||
t: "generateMethod";
|
t: "generateMethod";
|
||||||
}
|
}
|
||||||
|
|
||||||
interface GenerateMethodsFromLlmMessage {
|
|
||||||
t: "generateMethodsFromLlm";
|
|
||||||
packageName: string;
|
|
||||||
methodSignatures: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StopGeneratingMethodsFromLlmMessage {
|
|
||||||
t: "stopGeneratingMethodsFromLlm";
|
|
||||||
packageName: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StartModelEvaluationMessage {
|
interface StartModelEvaluationMessage {
|
||||||
t: "startModelEvaluation";
|
t: "startModelEvaluation";
|
||||||
}
|
}
|
||||||
@@ -630,16 +628,6 @@ interface SetInModelingModeMessage {
|
|||||||
inModelingMode: boolean;
|
inModelingMode: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SetInProgressMessage {
|
|
||||||
t: "setInProgress";
|
|
||||||
inProgress: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SetProcessedByAutoModelMessage {
|
|
||||||
t: "setProcessedByAutoModel";
|
|
||||||
processedByAutoModel: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RevealMethodMessage {
|
interface RevealMethodMessage {
|
||||||
t: "revealMethod";
|
t: "revealMethod";
|
||||||
methodSignature: string;
|
methodSignature: string;
|
||||||
@@ -660,8 +648,6 @@ export type ToModelEditorMessage =
|
|||||||
| SetMethodsMessage
|
| SetMethodsMessage
|
||||||
| SetModeledAndModifiedMethodsMessage
|
| SetModeledAndModifiedMethodsMessage
|
||||||
| SetModifiedMethodsMessage
|
| SetModifiedMethodsMessage
|
||||||
| SetInProgressMethodsMessage
|
|
||||||
| SetProcessedByAutoModelMethodsMessage
|
|
||||||
| RevealMethodMessage
|
| RevealMethodMessage
|
||||||
| SetAccessPathSuggestionsMessage
|
| SetAccessPathSuggestionsMessage
|
||||||
| SetModelEvaluationRunMessage;
|
| SetModelEvaluationRunMessage;
|
||||||
@@ -675,8 +661,6 @@ export type FromModelEditorMessage =
|
|||||||
| JumpToMethodMessage
|
| JumpToMethodMessage
|
||||||
| SaveModeledMethods
|
| SaveModeledMethods
|
||||||
| GenerateMethodMessage
|
| GenerateMethodMessage
|
||||||
| GenerateMethodsFromLlmMessage
|
|
||||||
| StopGeneratingMethodsFromLlmMessage
|
|
||||||
| ModelDependencyMessage
|
| ModelDependencyMessage
|
||||||
| HideModeledMethodsMessage
|
| HideModeledMethodsMessage
|
||||||
| SetMultipleModeledMethodsMessage
|
| SetMultipleModeledMethodsMessage
|
||||||
@@ -719,8 +703,6 @@ interface SetSelectedMethodMessage {
|
|||||||
method: Method;
|
method: Method;
|
||||||
modeledMethods: ModeledMethod[];
|
modeledMethods: ModeledMethod[];
|
||||||
isModified: boolean;
|
isModified: boolean;
|
||||||
isInProgress: boolean;
|
|
||||||
processedByAutoModel: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ToMethodModelingMessage =
|
export type ToMethodModelingMessage =
|
||||||
@@ -729,9 +711,7 @@ export type ToMethodModelingMessage =
|
|||||||
| SetMethodModifiedMessage
|
| SetMethodModifiedMessage
|
||||||
| SetNoMethodSelectedMessage
|
| SetNoMethodSelectedMessage
|
||||||
| SetSelectedMethodMessage
|
| SetSelectedMethodMessage
|
||||||
| SetInModelingModeMessage
|
| SetInModelingModeMessage;
|
||||||
| SetInProgressMessage
|
|
||||||
| SetProcessedByAutoModelMessage;
|
|
||||||
|
|
||||||
interface SetModelAlertsViewStateMessage {
|
interface SetModelAlertsViewStateMessage {
|
||||||
t: "setModelAlertsViewState";
|
t: "setModelAlertsViewState";
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ export enum RequestKind {
|
|||||||
GetVariantAnalysisRepo = "getVariantAnalysisRepo",
|
GetVariantAnalysisRepo = "getVariantAnalysisRepo",
|
||||||
GetVariantAnalysisRepoResult = "getVariantAnalysisRepoResult",
|
GetVariantAnalysisRepoResult = "getVariantAnalysisRepoResult",
|
||||||
CodeSearch = "codeSearch",
|
CodeSearch = "codeSearch",
|
||||||
AutoModel = "autoModel",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BasicErrorResponse {
|
export interface BasicErrorResponse {
|
||||||
@@ -69,7 +68,7 @@ export interface GetVariantAnalysisRepoResultRequest {
|
|||||||
};
|
};
|
||||||
response: {
|
response: {
|
||||||
status: number;
|
status: number;
|
||||||
body?: Buffer | string;
|
body?: ArrayBuffer | string;
|
||||||
contentType: string;
|
contentType: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -92,31 +91,13 @@ interface CodeSearchRequest {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AutoModelResponse {
|
|
||||||
models: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AutoModelRequest {
|
|
||||||
request: {
|
|
||||||
kind: RequestKind.AutoModel;
|
|
||||||
body?: {
|
|
||||||
candidates: string;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
response: {
|
|
||||||
status: number;
|
|
||||||
body?: AutoModelResponse | BasicErrorResponse;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export type GitHubApiRequest =
|
export type GitHubApiRequest =
|
||||||
| GetRepoRequest
|
| GetRepoRequest
|
||||||
| SubmitVariantAnalysisRequest
|
| SubmitVariantAnalysisRequest
|
||||||
| GetVariantAnalysisRequest
|
| GetVariantAnalysisRequest
|
||||||
| GetVariantAnalysisRepoRequest
|
| GetVariantAnalysisRepoRequest
|
||||||
| GetVariantAnalysisRepoResultRequest
|
| GetVariantAnalysisRepoResultRequest
|
||||||
| CodeSearchRequest
|
| CodeSearchRequest;
|
||||||
| AutoModelRequest;
|
|
||||||
|
|
||||||
export const isGetRepoRequest = (
|
export const isGetRepoRequest = (
|
||||||
request: GitHubApiRequest,
|
request: GitHubApiRequest,
|
||||||
@@ -146,8 +127,3 @@ export const isCodeSearchRequest = (
|
|||||||
request: GitHubApiRequest,
|
request: GitHubApiRequest,
|
||||||
): request is CodeSearchRequest =>
|
): request is CodeSearchRequest =>
|
||||||
request.request.kind === RequestKind.CodeSearch;
|
request.request.kind === RequestKind.CodeSearch;
|
||||||
|
|
||||||
export const isAutoModelRequest = (
|
|
||||||
request: GitHubApiRequest,
|
|
||||||
): request is AutoModelRequest =>
|
|
||||||
request.request.kind === RequestKind.AutoModel;
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import { DisposableObject } from "../disposable-object";
|
|||||||
import { gzipDecode } from "../zlib";
|
import { gzipDecode } from "../zlib";
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
AutoModelResponse,
|
|
||||||
BasicErrorResponse,
|
BasicErrorResponse,
|
||||||
CodeSearchResponse,
|
CodeSearchResponse,
|
||||||
GetVariantAnalysisRepoResultRequest,
|
GetVariantAnalysisRepoResultRequest,
|
||||||
@@ -91,7 +90,14 @@ export class Recorder extends DisposableObject {
|
|||||||
|
|
||||||
let bodyFileLink = undefined;
|
let bodyFileLink = undefined;
|
||||||
if (writtenRequest.response.body) {
|
if (writtenRequest.response.body) {
|
||||||
await writeFile(bodyFilePath, writtenRequest.response.body);
|
if (typeof writtenRequest.response.body === "string") {
|
||||||
|
await writeFile(bodyFilePath, writtenRequest.response.body);
|
||||||
|
} else {
|
||||||
|
await writeFile(
|
||||||
|
bodyFilePath,
|
||||||
|
Buffer.from(writtenRequest.response.body),
|
||||||
|
);
|
||||||
|
}
|
||||||
bodyFileLink = `file:${bodyFileName}`;
|
bodyFileLink = `file:${bodyFileName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -226,7 +232,7 @@ async function createGitHubApiRequest(
|
|||||||
"x-vscode-codeql-msw-bypass": "true",
|
"x-vscode-codeql-msw-bypass": "true",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const responseBuffer = await response.buffer();
|
const responseBuffer = await response.arrayBuffer();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
request: {
|
request: {
|
||||||
@@ -258,23 +264,6 @@ async function createGitHubApiRequest(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const autoModelMatch = url.match(
|
|
||||||
/\/repos\/github\/codeql\/code-scanning\/codeql\/auto-model/,
|
|
||||||
);
|
|
||||||
if (autoModelMatch) {
|
|
||||||
return {
|
|
||||||
request: {
|
|
||||||
kind: RequestKind.AutoModel,
|
|
||||||
},
|
|
||||||
response: {
|
|
||||||
status,
|
|
||||||
body: await jsonResponseBody<
|
|
||||||
BasicErrorResponse | AutoModelResponse | undefined
|
|
||||||
>(response),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import type { RequestHandler } from "msw";
|
|||||||
import { http } from "msw";
|
import { http } from "msw";
|
||||||
import type { GitHubApiRequest } from "./gh-api-request";
|
import type { GitHubApiRequest } from "./gh-api-request";
|
||||||
import {
|
import {
|
||||||
isAutoModelRequest,
|
|
||||||
isCodeSearchRequest,
|
isCodeSearchRequest,
|
||||||
isGetRepoRequest,
|
isGetRepoRequest,
|
||||||
isGetVariantAnalysisRepoRequest,
|
isGetVariantAnalysisRepoRequest,
|
||||||
@@ -41,7 +40,6 @@ export async function createRequestHandlers(
|
|||||||
createGetVariantAnalysisRepoRequestHandler(requests),
|
createGetVariantAnalysisRepoRequestHandler(requests),
|
||||||
createGetVariantAnalysisRepoResultRequestHandler(requests),
|
createGetVariantAnalysisRepoResultRequestHandler(requests),
|
||||||
createCodeSearchRequestHandler(requests),
|
createCodeSearchRequestHandler(requests),
|
||||||
createAutoModelRequestHandler(requests),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
return handlers;
|
return handlers;
|
||||||
@@ -230,29 +228,3 @@ function createCodeSearchRequestHandler(
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAutoModelRequestHandler(
|
|
||||||
requests: GitHubApiRequest[],
|
|
||||||
): RequestHandler {
|
|
||||||
const autoModelRequests = requests.filter(isAutoModelRequest);
|
|
||||||
let requestIndex = 0;
|
|
||||||
|
|
||||||
// During automodeling there can be multiple API requests for each batch
|
|
||||||
// of candidates we want to model. We need to return different responses for each request,
|
|
||||||
// so keep an index of the request and return the appropriate response.
|
|
||||||
return http.post(
|
|
||||||
`${baseUrl}/repos/github/codeql/code-scanning/codeql/auto-model`,
|
|
||||||
() => {
|
|
||||||
const request = autoModelRequests[requestIndex];
|
|
||||||
|
|
||||||
if (requestIndex < autoModelRequests.length - 1) {
|
|
||||||
// If there are more requests to come, increment the index.
|
|
||||||
requestIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return jsonResponse(request.response.body, {
|
|
||||||
status: request.response.status,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"request": {
|
|
||||||
"kind": "autoModel"
|
|
||||||
},
|
|
||||||
"response": {
|
|
||||||
"status": 200,
|
|
||||||
"body": {
|
|
||||||
"models": "extensions:\n- addsTo: {extensible: sinkModel, pack: codeql/java-all}\n data:\n - [javax.servlet.http, HttpServletResponse, true, sendRedirect, (String), '', 'Argument[this]',\n request-forgery, ai-generated]\n - [javax.servlet.http, HttpServletResponse, true, sendRedirect, (String), '', 'Argument[0]',\n request-forgery, ai-generated]\n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"request": {
|
|
||||||
"kind": "autoModel"
|
|
||||||
},
|
|
||||||
"response": {
|
|
||||||
"status": 200,
|
|
||||||
"body": {
|
|
||||||
"models": "extensions:\n- addsTo: {extensible: sinkModel, pack: codeql/java-all}\n data:\n - [javax.servlet, MultipartConfigElement, true, MultipartConfigElement, (String),\n '', 'Argument[0]', request-forgery, ai-generated]\n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
This scenario is best when modeling the `javax.servlet-api` package.
|
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
export type DeepReadonly<T> =
|
export type DeepReadonly<T> =
|
||||||
T extends Array<infer R>
|
T extends Array<infer R>
|
||||||
? DeepReadonlyArray<R>
|
? DeepReadonlyArray<R>
|
||||||
: // eslint-disable-next-line @typescript-eslint/ban-types
|
: // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
||||||
T extends Function
|
T extends Function
|
||||||
? T
|
? T
|
||||||
: T extends object
|
: T extends object
|
||||||
? DeepReadonlyObject<T>
|
? DeepReadonlyObject<T>
|
||||||
: T;
|
: T;
|
||||||
|
|
||||||
interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}
|
type DeepReadonlyArray<T> = ReadonlyArray<DeepReadonly<T>>;
|
||||||
|
|
||||||
type DeepReadonlyObject<T> = {
|
type DeepReadonlyObject<T> = {
|
||||||
readonly [P in keyof T]: DeepReadonly<T[P]>;
|
readonly [P in keyof T]: DeepReadonly<T[P]>;
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
import type { Log, Tool } from "sarif";
|
import type { Log } from "sarif";
|
||||||
import { createReadStream } from "fs-extra";
|
import { createReadStream } from "fs-extra";
|
||||||
import { connectTo } from "stream-json/Assembler";
|
import { connectTo } from "stream-json/Assembler";
|
||||||
import { getErrorMessage } from "./helpers-pure";
|
import { getErrorMessage } from "./helpers-pure";
|
||||||
import { withParser } from "stream-json/filters/Pick";
|
import { withParser } from "stream-json/filters/Ignore";
|
||||||
|
|
||||||
const DUMMY_TOOL: Tool = { driver: { name: "" } };
|
|
||||||
|
|
||||||
export async function sarifParser(
|
export async function sarifParser(
|
||||||
interpretedResultsPath: string,
|
interpretedResultsPath: string,
|
||||||
): Promise<Log> {
|
): Promise<Log> {
|
||||||
try {
|
try {
|
||||||
// Parse the SARIF file into token streams, filtering out only the results array.
|
// Parse the SARIF file into token streams, filtering out some of the larger subtrees that we
|
||||||
|
// don't need.
|
||||||
const pipeline = createReadStream(interpretedResultsPath).pipe(
|
const pipeline = createReadStream(interpretedResultsPath).pipe(
|
||||||
withParser({ filter: "runs.0.results" }),
|
withParser({
|
||||||
|
// We don't need to run's `artifacts` property, nor the driver's `notifications` property.
|
||||||
|
filter: /^runs\.\d+\.(artifacts|tool\.driver\.notifications)/,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Creates JavaScript objects from the token stream
|
// Creates JavaScript objects from the token stream
|
||||||
@@ -38,15 +40,17 @@ export async function sarifParser(
|
|||||||
});
|
});
|
||||||
|
|
||||||
asm.on("done", (asm) => {
|
asm.on("done", (asm) => {
|
||||||
const log: Log = {
|
const log = asm.current;
|
||||||
version: "2.1.0",
|
|
||||||
runs: [
|
// Do some trivial validation. This isn't a full validation of the SARIF file, but it's at
|
||||||
{
|
// least enough to ensure that we're not trying to parse complete garbage later.
|
||||||
tool: DUMMY_TOOL,
|
if (log.runs === undefined || log.runs.length < 1) {
|
||||||
results: asm.current ?? [],
|
reject(
|
||||||
},
|
new Error(
|
||||||
],
|
"Invalid SARIF file: expecting at least one run with result.",
|
||||||
};
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
resolve(log);
|
resolve(log);
|
||||||
alreadyDone = true;
|
alreadyDone = true;
|
||||||
|
|||||||
@@ -1,10 +1,5 @@
|
|||||||
import { promisify } from "util";
|
import { promisify } from "util";
|
||||||
import { gzip, gunzip } from "zlib";
|
import { gunzip } from "zlib";
|
||||||
|
|
||||||
/**
|
|
||||||
* Promisified version of zlib.gzip
|
|
||||||
*/
|
|
||||||
export const gzipEncode = promisify(gzip);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Promisified version of zlib.gunzip
|
* Promisified version of zlib.gunzip
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import {
|
|||||||
getResultSetNames,
|
getResultSetNames,
|
||||||
} from "./result-set-names";
|
} from "./result-set-names";
|
||||||
import { compareInterpretedResults } from "./interpreted-results";
|
import { compareInterpretedResults } from "./interpreted-results";
|
||||||
|
import { isCanary } from "../config";
|
||||||
|
|
||||||
interface ComparePair {
|
interface ComparePair {
|
||||||
from: CompletedLocalQueryInfo;
|
from: CompletedLocalQueryInfo;
|
||||||
@@ -116,6 +117,13 @@ export class CompareView extends AbstractWebview<
|
|||||||
panel.reveal(undefined, true);
|
panel.reveal(undefined, true);
|
||||||
await this.waitForPanelLoaded();
|
await this.waitForPanelLoaded();
|
||||||
|
|
||||||
|
await this.postMessage({
|
||||||
|
t: "setUserSettings",
|
||||||
|
userSettings: {
|
||||||
|
shouldShowProvenance: isCanary(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
await this.postMessage({
|
await this.postMessage({
|
||||||
t: "setComparisonQueryInfo",
|
t: "setComparisonQueryInfo",
|
||||||
stats: {
|
stats: {
|
||||||
|
|||||||
@@ -828,19 +828,9 @@ export async function setAutogenerateQlPacks(choice: AutogenerateQLPacks) {
|
|||||||
|
|
||||||
const MODEL_SETTING = new Setting("model", ROOT_SETTING);
|
const MODEL_SETTING = new Setting("model", ROOT_SETTING);
|
||||||
const FLOW_GENERATION = new Setting("flowGeneration", MODEL_SETTING);
|
const FLOW_GENERATION = new Setting("flowGeneration", MODEL_SETTING);
|
||||||
const LLM_GENERATION = new Setting("llmGeneration", MODEL_SETTING);
|
|
||||||
const LLM_GENERATION_BATCH_SIZE = new Setting(
|
|
||||||
"llmGenerationBatchSize",
|
|
||||||
MODEL_SETTING,
|
|
||||||
);
|
|
||||||
const LLM_GENERATION_DEV_ENDPOINT = new Setting(
|
|
||||||
"llmGenerationDevEndpoint",
|
|
||||||
MODEL_SETTING,
|
|
||||||
);
|
|
||||||
const MODEL_EVALUATION = new Setting("evaluation", MODEL_SETTING);
|
const MODEL_EVALUATION = new Setting("evaluation", MODEL_SETTING);
|
||||||
const MODEL_PACK_LOCATION = new Setting("packLocation", MODEL_SETTING);
|
const MODEL_PACK_LOCATION = new Setting("packLocation", MODEL_SETTING);
|
||||||
const MODEL_PACK_NAME = new Setting("packName", MODEL_SETTING);
|
const MODEL_PACK_NAME = new Setting("packName", MODEL_SETTING);
|
||||||
const ENABLE_PYTHON = new Setting("enablePython", MODEL_SETTING);
|
|
||||||
|
|
||||||
export type ModelConfigPackVariables = {
|
export type ModelConfigPackVariables = {
|
||||||
database: string;
|
database: string;
|
||||||
@@ -851,13 +841,11 @@ export type ModelConfigPackVariables = {
|
|||||||
|
|
||||||
export interface ModelConfig {
|
export interface ModelConfig {
|
||||||
flowGeneration: boolean;
|
flowGeneration: boolean;
|
||||||
llmGeneration: boolean;
|
|
||||||
getPackLocation(
|
getPackLocation(
|
||||||
languageId: string,
|
languageId: string,
|
||||||
variables: ModelConfigPackVariables,
|
variables: ModelConfigPackVariables,
|
||||||
): string;
|
): string;
|
||||||
getPackName(languageId: string, variables: ModelConfigPackVariables): string;
|
getPackName(languageId: string, variables: ModelConfigPackVariables): string;
|
||||||
enablePython: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ModelConfigListener extends ConfigListener implements ModelConfig {
|
export class ModelConfigListener extends ConfigListener implements ModelConfig {
|
||||||
@@ -872,26 +860,6 @@ export class ModelConfigListener extends ConfigListener implements ModelConfig {
|
|||||||
return !!FLOW_GENERATION.getValue<boolean>();
|
return !!FLOW_GENERATION.getValue<boolean>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public get llmGeneration(): boolean {
|
|
||||||
return !!LLM_GENERATION.getValue<boolean>() && !hasEnterpriseUri();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Limits the number of candidates we send to the model in each request to avoid long requests.
|
|
||||||
* Note that the model may return fewer than this number of candidates.
|
|
||||||
*/
|
|
||||||
public get llmGenerationBatchSize(): number {
|
|
||||||
return LLM_GENERATION_BATCH_SIZE.getValue<number | null>() || 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The URL of the endpoint to use for LLM generation. This should only be set
|
|
||||||
* if you want to test against a dev server.
|
|
||||||
*/
|
|
||||||
public get llmGenerationDevEndpoint(): string | undefined {
|
|
||||||
return LLM_GENERATION_DEV_ENDPOINT.getValue<string | undefined>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public get modelEvaluation(): boolean {
|
public get modelEvaluation(): boolean {
|
||||||
return !!MODEL_EVALUATION.getValue<boolean>();
|
return !!MODEL_EVALUATION.getValue<boolean>();
|
||||||
}
|
}
|
||||||
@@ -919,10 +887,6 @@ export class ModelConfigListener extends ConfigListener implements ModelConfig {
|
|||||||
variables,
|
variables,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public get enablePython(): boolean {
|
|
||||||
return !!ENABLE_PYTHON.getValue<boolean>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const GITHUB_DATABASE_SETTING = new Setting("githubDatabase", ROOT_SETTING);
|
const GITHUB_DATABASE_SETTING = new Setting("githubDatabase", ROOT_SETTING);
|
||||||
|
|||||||
@@ -298,7 +298,7 @@ export class DbConfigStore extends DisposableObject {
|
|||||||
let newConfig: DbConfig | undefined = undefined;
|
let newConfig: DbConfig | undefined = undefined;
|
||||||
try {
|
try {
|
||||||
newConfig = await readJSON(this.configPath);
|
newConfig = await readJSON(this.configPath);
|
||||||
} catch (e) {
|
} catch {
|
||||||
this.configErrors = [
|
this.configErrors = [
|
||||||
{
|
{
|
||||||
kind: DbConfigValidationErrorKind.InvalidJson,
|
kind: DbConfigValidationErrorKind.InvalidJson,
|
||||||
@@ -332,7 +332,7 @@ export class DbConfigStore extends DisposableObject {
|
|||||||
let newConfig: DbConfig | undefined = undefined;
|
let newConfig: DbConfig | undefined = undefined;
|
||||||
try {
|
try {
|
||||||
newConfig = readJSONSync(this.configPath);
|
newConfig = readJSONSync(this.configPath);
|
||||||
} catch (e) {
|
} catch {
|
||||||
this.configErrors = [
|
this.configErrors = [
|
||||||
{
|
{
|
||||||
kind: DbConfigValidationErrorKind.InvalidJson,
|
kind: DbConfigValidationErrorKind.InvalidJson,
|
||||||
|
|||||||
@@ -454,7 +454,7 @@ export class DatabaseFetcher {
|
|||||||
let uri;
|
let uri;
|
||||||
try {
|
try {
|
||||||
uri = Uri.parse(databaseUrl, true);
|
uri = Uri.parse(databaseUrl, true);
|
||||||
} catch (e) {
|
} catch {
|
||||||
throw new Error(`Invalid url: ${databaseUrl}`);
|
throw new Error(`Invalid url: ${databaseUrl}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,30 +545,27 @@ export class DatabaseFetcher {
|
|||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const body = response.body;
|
||||||
|
if (!body) {
|
||||||
|
throw new Error("No response body found");
|
||||||
|
}
|
||||||
|
|
||||||
const archiveFileStream = createWriteStream(archivePath);
|
const archiveFileStream = createWriteStream(archivePath);
|
||||||
|
|
||||||
const contentLength = response.headers.get("content-length");
|
const contentLength = response.headers.get("content-length");
|
||||||
const totalNumBytes = contentLength
|
const totalNumBytes = contentLength
|
||||||
? parseInt(contentLength, 10)
|
? parseInt(contentLength, 10)
|
||||||
: undefined;
|
: undefined;
|
||||||
reportStreamProgress(
|
reportStreamProgress(body, "Downloading database", totalNumBytes, progress);
|
||||||
response.body,
|
|
||||||
"Downloading database",
|
|
||||||
totalNumBytes,
|
|
||||||
progress,
|
|
||||||
);
|
|
||||||
|
|
||||||
response.body.on("data", onData);
|
body.on("data", onData);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await new Promise((resolve, reject) => {
|
await new Promise((resolve, reject) => {
|
||||||
response.body
|
body.pipe(archiveFileStream).on("finish", resolve).on("error", reject);
|
||||||
.pipe(archiveFileStream)
|
|
||||||
.on("finish", resolve)
|
|
||||||
.on("error", reject);
|
|
||||||
|
|
||||||
// If an error occurs on the body, we also want to reject the promise (e.g. during a timeout error).
|
// If an error occurs on the body, we also want to reject the promise (e.g. during a timeout error).
|
||||||
response.body.on("error", reject);
|
body.on("error", reject);
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Close and remove the file if an error occurs
|
// Close and remove the file if an error occurs
|
||||||
@@ -612,7 +609,7 @@ export class DatabaseFetcher {
|
|||||||
const obj = JSON.parse(text);
|
const obj = JSON.parse(text);
|
||||||
msg =
|
msg =
|
||||||
obj.error || obj.message || obj.reason || JSON.stringify(obj, null, 2);
|
obj.error || obj.message || obj.reason || JSON.stringify(obj, null, 2);
|
||||||
} catch (e) {
|
} catch {
|
||||||
msg = text;
|
msg = text;
|
||||||
}
|
}
|
||||||
throw new Error(`${errorMessage}.\n\nReason: ${msg}`);
|
throw new Error(`${errorMessage}.\n\nReason: ${msg}`);
|
||||||
|
|||||||
@@ -231,7 +231,7 @@ export class DatabaseManager extends DisposableObject {
|
|||||||
let originStat;
|
let originStat;
|
||||||
try {
|
try {
|
||||||
originStat = await stat(originDbYml);
|
originStat = await stat(originDbYml);
|
||||||
} catch (e) {
|
} catch {
|
||||||
// if there is an error here, assume that the origin database
|
// if there is an error here, assume that the origin database
|
||||||
// is no longer available. Safely ignore and do not try to re-import.
|
// is no longer available. Safely ignore and do not try to re-import.
|
||||||
return false;
|
return false;
|
||||||
@@ -240,7 +240,7 @@ export class DatabaseManager extends DisposableObject {
|
|||||||
try {
|
try {
|
||||||
const importedStat = await stat(importedDbYml);
|
const importedStat = await stat(importedDbYml);
|
||||||
return originStat.mtimeMs > importedStat.mtimeMs;
|
return originStat.mtimeMs > importedStat.mtimeMs;
|
||||||
} catch (e) {
|
} catch {
|
||||||
// If either of the files does not exist, we assume the origin is newer.
|
// If either of the files does not exist, we assume the origin is newer.
|
||||||
// This shouldn't happen unless the user manually deleted one of the files.
|
// This shouldn't happen unless the user manually deleted one of the files.
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -37,11 +37,12 @@ export const shownLocationLineDecoration =
|
|||||||
/**
|
/**
|
||||||
* Resolves the specified CodeQL location to a URI into the source archive.
|
* Resolves the specified CodeQL location to a URI into the source archive.
|
||||||
* @param loc CodeQL location to resolve. Must have a non-empty value for `loc.file`.
|
* @param loc CodeQL location to resolve. Must have a non-empty value for `loc.file`.
|
||||||
* @param databaseItem Database in which to resolve the file location.
|
* @param databaseItem Database in which to resolve the file location, or `undefined` to resolve
|
||||||
|
* from the local file system.
|
||||||
*/
|
*/
|
||||||
function resolveFivePartLocation(
|
function resolveFivePartLocation(
|
||||||
loc: UrlValueLineColumnLocation,
|
loc: UrlValueLineColumnLocation,
|
||||||
databaseItem: DatabaseItem,
|
databaseItem: DatabaseItem | undefined,
|
||||||
): Location {
|
): Location {
|
||||||
// `Range` is a half-open interval, and is zero-based. CodeQL locations are closed intervals, and
|
// `Range` is a half-open interval, and is zero-based. CodeQL locations are closed intervals, and
|
||||||
// are one-based. Adjust accordingly.
|
// are one-based. Adjust accordingly.
|
||||||
@@ -52,7 +53,10 @@ function resolveFivePartLocation(
|
|||||||
Math.max(1, loc.endColumn),
|
Math.max(1, loc.endColumn),
|
||||||
);
|
);
|
||||||
|
|
||||||
return new Location(databaseItem.resolveSourceFile(loc.uri), range);
|
return new Location(
|
||||||
|
databaseItem?.resolveSourceFile(loc.uri) ?? Uri.parse(loc.uri),
|
||||||
|
range,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,22 +66,26 @@ function resolveFivePartLocation(
|
|||||||
*/
|
*/
|
||||||
function resolveWholeFileLocation(
|
function resolveWholeFileLocation(
|
||||||
loc: UrlValueWholeFileLocation,
|
loc: UrlValueWholeFileLocation,
|
||||||
databaseItem: DatabaseItem,
|
databaseItem: DatabaseItem | undefined,
|
||||||
): Location {
|
): Location {
|
||||||
// A location corresponding to the start of the file.
|
// A location corresponding to the start of the file.
|
||||||
const range = new Range(0, 0, 0, 0);
|
const range = new Range(0, 0, 0, 0);
|
||||||
return new Location(databaseItem.resolveSourceFile(loc.uri), range);
|
return new Location(
|
||||||
|
databaseItem?.resolveSourceFile(loc.uri) ?? Uri.parse(loc.uri),
|
||||||
|
range,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try to resolve the specified CodeQL location to a URI into the source archive. If no exact location
|
* Try to resolve the specified CodeQL location to a URI into the source archive. If no exact location
|
||||||
* can be resolved, returns `undefined`.
|
* can be resolved, returns `undefined`.
|
||||||
* @param loc CodeQL location to resolve
|
* @param loc CodeQL location to resolve
|
||||||
* @param databaseItem Database in which to resolve the file location.
|
* @param databaseItem Database in which to resolve the file location, or `undefined` to resolve
|
||||||
|
* from the local file system.
|
||||||
*/
|
*/
|
||||||
export function tryResolveLocation(
|
export function tryResolveLocation(
|
||||||
loc: UrlValueResolvable | undefined,
|
loc: UrlValueResolvable | undefined,
|
||||||
databaseItem: DatabaseItem,
|
databaseItem: DatabaseItem | undefined,
|
||||||
): Location | undefined {
|
): Location | undefined {
|
||||||
if (!loc) {
|
if (!loc) {
|
||||||
return;
|
return;
|
||||||
@@ -95,7 +103,7 @@ export function tryResolveLocation(
|
|||||||
|
|
||||||
export async function showResolvableLocation(
|
export async function showResolvableLocation(
|
||||||
loc: UrlValueResolvable,
|
loc: UrlValueResolvable,
|
||||||
databaseItem: DatabaseItem,
|
databaseItem: DatabaseItem | undefined,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
@@ -151,13 +159,14 @@ export async function showLocation(location?: Location) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function jumpToLocation(
|
export async function jumpToLocation(
|
||||||
databaseUri: string,
|
databaseUri: string | undefined,
|
||||||
loc: UrlValueResolvable,
|
loc: UrlValueResolvable,
|
||||||
databaseManager: DatabaseManager,
|
databaseManager: DatabaseManager,
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
) {
|
) {
|
||||||
const databaseItem = databaseManager.findDatabaseItem(Uri.parse(databaseUri));
|
const databaseItem =
|
||||||
if (databaseItem !== undefined) {
|
databaseUri !== undefined
|
||||||
await showResolvableLocation(loc, databaseItem, logger);
|
? databaseManager.findDatabaseItem(Uri.parse(databaseUri))
|
||||||
}
|
: undefined;
|
||||||
|
await showResolvableLocation(loc, databaseItem, logger);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ export type Response = DebugProtocol.Response & { type: "response" };
|
|||||||
export type InitializeResponse = DebugProtocol.InitializeResponse &
|
export type InitializeResponse = DebugProtocol.InitializeResponse &
|
||||||
Response & { command: "initialize" };
|
Response & { command: "initialize" };
|
||||||
|
|
||||||
export interface QuickEvalResponse extends Response {}
|
export type QuickEvalResponse = Response;
|
||||||
|
|
||||||
export type AnyResponse = InitializeResponse | QuickEvalResponse;
|
export type AnyResponse = InitializeResponse | QuickEvalResponse;
|
||||||
|
|
||||||
|
|||||||
@@ -300,12 +300,12 @@ const shouldUpdateOnNextActivationKey = "shouldUpdateOnNextActivation";
|
|||||||
|
|
||||||
const codeQlVersionRange = DEFAULT_DISTRIBUTION_VERSION_RANGE;
|
const codeQlVersionRange = DEFAULT_DISTRIBUTION_VERSION_RANGE;
|
||||||
|
|
||||||
// This is the minimum version of vscode that we _want_ to support. We want to update to Node 18, but that
|
// This is the minimum version of vscode that we _want_ to support. We want to update to Node 20, but that
|
||||||
// requires 1.82 or later. If we change the minimum version in the package.json, then anyone on an older version of vscode will
|
// requires 1.90 or later. If we change the minimum version in the package.json, then anyone on an older version of vscode will
|
||||||
// silently be unable to upgrade. So, the solution is to first bump the minimum version here and release. Then
|
// silently be unable to upgrade. So, the solution is to first bump the minimum version here and release. Then
|
||||||
// bump the version in the package.json and release again. This way, anyone on an older version of vscode will get a warning
|
// bump the version in the package.json and release again. This way, anyone on an older version of vscode will get a warning
|
||||||
// before silently being refused to upgrade.
|
// before silently being refused to upgrade.
|
||||||
const MIN_VERSION = "1.82.0";
|
const MIN_VERSION = "1.90.0";
|
||||||
|
|
||||||
function sendConfigTelemetryData() {
|
function sendConfigTelemetryData() {
|
||||||
const config: Record<string, string> = {};
|
const config: Record<string, string> = {};
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export function fileRangeFromURI(
|
|||||||
return new Location(db.resolveSourceFile(uri.uri), range);
|
return new Location(db.resolveSourceFile(uri.uri), range);
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
} catch (e) {
|
} catch {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export async function resolveContextualQlPacksForDatabase(
|
|||||||
): Promise<QlPacksForLanguage> {
|
): Promise<QlPacksForLanguage> {
|
||||||
try {
|
try {
|
||||||
return await qlpackOfDatabase(cli, databaseItem);
|
return await qlpackOfDatabase(cli, databaseItem);
|
||||||
} catch (e) {
|
} catch {
|
||||||
// If we can't find the qlpacks for the database, use the defaults instead
|
// If we can't find the qlpacks for the database, use the defaults instead
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { languages, IndentAction } from "vscode";
|
|||||||
* See https://github.com/microsoft/vscode/blob/master/src/vs/editor/test/common/modes/supports/javascriptOnEnterRules.ts
|
* See https://github.com/microsoft/vscode/blob/master/src/vs/editor/test/common/modes/supports/javascriptOnEnterRules.ts
|
||||||
*/
|
*/
|
||||||
export function install() {
|
export function install() {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||||
const langConfig = require("../../language-configuration.json");
|
const langConfig = require("../../language-configuration.json");
|
||||||
// setLanguageConfiguration requires a regexp for the wordpattern, not a string
|
// setLanguageConfiguration requires a regexp for the wordpattern, not a string
|
||||||
langConfig.wordPattern = new RegExp(langConfig.wordPattern);
|
langConfig.wordPattern = new RegExp(langConfig.wordPattern);
|
||||||
|
|||||||
@@ -537,6 +537,14 @@ export class ResultsView extends AbstractWebview<
|
|||||||
resultSetNames,
|
resultSetNames,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
await this.postMessage({
|
||||||
|
t: "setUserSettings",
|
||||||
|
userSettings: {
|
||||||
|
// Only show provenance info in canary mode for now.
|
||||||
|
shouldShowProvenance: isCanary(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
await this.postMessage({
|
await this.postMessage({
|
||||||
t: "setState",
|
t: "setState",
|
||||||
interpretation: interpretationPage,
|
interpretation: interpretationPage,
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
import type { Credentials } from "../common/authentication";
|
|
||||||
import type { OctokitResponse } from "@octokit/types";
|
|
||||||
import fetch from "node-fetch";
|
|
||||||
import type { ModelConfigListener } from "../config";
|
|
||||||
|
|
||||||
export enum AutomodelMode {
|
|
||||||
Unspecified = "AUTOMODEL_MODE_UNSPECIFIED",
|
|
||||||
Framework = "AUTOMODEL_MODE_FRAMEWORK",
|
|
||||||
Application = "AUTOMODEL_MODE_APPLICATION",
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ModelRequest {
|
|
||||||
mode: AutomodelMode;
|
|
||||||
// Base64-encoded GZIP-compressed SARIF log
|
|
||||||
candidates: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ModelResponse {
|
|
||||||
models: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function autoModel(
|
|
||||||
credentials: Credentials,
|
|
||||||
request: ModelRequest,
|
|
||||||
modelingConfig: ModelConfigListener,
|
|
||||||
): Promise<ModelResponse> {
|
|
||||||
const devEndpoint = modelingConfig.llmGenerationDevEndpoint;
|
|
||||||
if (devEndpoint) {
|
|
||||||
return callAutoModelDevEndpoint(devEndpoint, request);
|
|
||||||
} else {
|
|
||||||
const octokit = await credentials.getOctokit();
|
|
||||||
|
|
||||||
const response: OctokitResponse<ModelResponse> = await octokit.request(
|
|
||||||
"POST /repos/github/codeql/code-scanning/codeql/auto-model",
|
|
||||||
{
|
|
||||||
data: request,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
return response.data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function callAutoModelDevEndpoint(
|
|
||||||
endpoint: string,
|
|
||||||
request: ModelRequest,
|
|
||||||
): Promise<ModelResponse> {
|
|
||||||
const json = JSON.stringify(request);
|
|
||||||
const response = await fetch(endpoint, {
|
|
||||||
method: "POST",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
body: json,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(
|
|
||||||
`Error calling auto-model API: ${response.status} ${response.statusText}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
return data as ModelResponse;
|
|
||||||
}
|
|
||||||
@@ -1,233 +0,0 @@
|
|||||||
import type { CodeQLCliServer, SourceInfo } from "../codeql-cli/cli";
|
|
||||||
import type { CoreCompletedQuery, QueryRunner } from "../query-server";
|
|
||||||
import type { DatabaseItem } from "../databases/local-databases";
|
|
||||||
import type { ProgressCallback } from "../common/vscode/progress";
|
|
||||||
import type { Log } from "sarif";
|
|
||||||
import type { Mode } from "./shared/mode";
|
|
||||||
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
|
|
||||||
import { interpretResultsSarif } from "../query-results";
|
|
||||||
import { join } from "path";
|
|
||||||
import { dir } from "tmp-promise";
|
|
||||||
import { writeFile, outputFile } from "fs-extra";
|
|
||||||
import { dump as dumpYaml } from "js-yaml";
|
|
||||||
import type { MethodSignature } from "./method";
|
|
||||||
import { runQuery } from "../local-queries/run-query";
|
|
||||||
import type { QueryMetadata } from "../common/interface-types";
|
|
||||||
import type { CancellationTokenSource } from "vscode";
|
|
||||||
import { resolveQueries } from "../local-queries";
|
|
||||||
import { modeTag } from "./mode-tag";
|
|
||||||
|
|
||||||
type AutoModelQueriesOptions = {
|
|
||||||
mode: Mode;
|
|
||||||
candidateMethods: MethodSignature[];
|
|
||||||
cliServer: CodeQLCliServer;
|
|
||||||
queryRunner: QueryRunner;
|
|
||||||
databaseItem: DatabaseItem;
|
|
||||||
queryStorageDir: string;
|
|
||||||
|
|
||||||
progress: ProgressCallback;
|
|
||||||
cancellationTokenSource: CancellationTokenSource;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AutoModelQueriesResult = {
|
|
||||||
candidates: Log;
|
|
||||||
};
|
|
||||||
|
|
||||||
export async function runAutoModelQueries({
|
|
||||||
mode,
|
|
||||||
candidateMethods,
|
|
||||||
cliServer,
|
|
||||||
queryRunner,
|
|
||||||
databaseItem,
|
|
||||||
queryStorageDir,
|
|
||||||
progress,
|
|
||||||
cancellationTokenSource,
|
|
||||||
}: AutoModelQueriesOptions): Promise<AutoModelQueriesResult | undefined> {
|
|
||||||
// First, resolve the query that we want to run.
|
|
||||||
const queryPath = await resolveAutomodelQuery(
|
|
||||||
cliServer,
|
|
||||||
databaseItem,
|
|
||||||
"candidates",
|
|
||||||
mode,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Generate a pack containing the candidate filters
|
|
||||||
const { packDir: filterPackDir, cleanup: cleanupFilterPack } =
|
|
||||||
await generateCandidateFilterPack(databaseItem.language, candidateMethods);
|
|
||||||
|
|
||||||
const additionalPacks = [...getOnDiskWorkspaceFolders(), filterPackDir];
|
|
||||||
const extensionPacks = Object.keys(
|
|
||||||
await cliServer.resolveQlpacks(additionalPacks, true),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Run the actual query
|
|
||||||
const completedQuery = await runQuery({
|
|
||||||
queryRunner,
|
|
||||||
databaseItem,
|
|
||||||
queryPath,
|
|
||||||
queryStorageDir,
|
|
||||||
additionalPacks,
|
|
||||||
extensionPacks,
|
|
||||||
progress,
|
|
||||||
token: cancellationTokenSource.token,
|
|
||||||
});
|
|
||||||
|
|
||||||
await cleanupFilterPack();
|
|
||||||
|
|
||||||
if (!completedQuery) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get metadata for the query. This is required to interpret the results. We already know the kind is problem
|
|
||||||
// (because of the constraint in resolveQueries), so we don't need any more checks on the metadata.
|
|
||||||
const metadata = await cliServer.resolveMetadata(queryPath);
|
|
||||||
|
|
||||||
// CodeQL needs to have access to the database to be able to retrieve the
|
|
||||||
// snippets from it. The source location prefix is used to determine the
|
|
||||||
// base path of the database.
|
|
||||||
const sourceLocationPrefix =
|
|
||||||
await databaseItem.getSourceLocationPrefix(cliServer);
|
|
||||||
const sourceArchiveUri = databaseItem.sourceArchive;
|
|
||||||
const sourceInfo =
|
|
||||||
sourceArchiveUri === undefined
|
|
||||||
? undefined
|
|
||||||
: {
|
|
||||||
sourceArchive: sourceArchiveUri.fsPath,
|
|
||||||
sourceLocationPrefix,
|
|
||||||
};
|
|
||||||
|
|
||||||
const candidates = await interpretAutomodelResults(
|
|
||||||
cliServer,
|
|
||||||
completedQuery,
|
|
||||||
metadata,
|
|
||||||
sourceInfo,
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
candidates,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function resolveAutomodelQuery(
|
|
||||||
cliServer: CodeQLCliServer,
|
|
||||||
databaseItem: DatabaseItem,
|
|
||||||
queryTag: string,
|
|
||||||
mode: Mode,
|
|
||||||
): Promise<string> {
|
|
||||||
const packsToSearch = [`codeql/${databaseItem.language}-automodel-queries`];
|
|
||||||
|
|
||||||
// First, resolve the query that we want to run.
|
|
||||||
// All queries are tagged like this:
|
|
||||||
// internal extract automodel <mode> <queryTag>
|
|
||||||
// Example: internal extract automodel framework-mode candidates
|
|
||||||
const queries = await resolveQueries(
|
|
||||||
cliServer,
|
|
||||||
packsToSearch,
|
|
||||||
`Extract automodel ${queryTag}`,
|
|
||||||
{
|
|
||||||
kind: "problem",
|
|
||||||
"tags contain all": ["automodel", modeTag(mode), ...queryTag.split(" ")],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
if (queries.length > 1) {
|
|
||||||
throw new Error(
|
|
||||||
`Found multiple auto model queries for ${mode} ${queryTag}. Can't continue`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (queries.length === 0) {
|
|
||||||
throw new Error(
|
|
||||||
`Did not found any auto model queries for ${mode} ${queryTag}. Can't continue`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return queries[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
type CandidateFilterPackResult = {
|
|
||||||
packDir: string;
|
|
||||||
cleanup: () => Promise<void>;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* generateCandidateFilterPack will create a temporary extension pack.
|
|
||||||
* This pack will contain a filter that will restrict the automodel queries
|
|
||||||
* to the specified candidate methods only.
|
|
||||||
* This is done using the `extensible` predicate "automodelCandidateFilter".
|
|
||||||
* @param language
|
|
||||||
* @param candidateMethods
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export async function generateCandidateFilterPack(
|
|
||||||
language: string,
|
|
||||||
candidateMethods: MethodSignature[],
|
|
||||||
): Promise<CandidateFilterPackResult> {
|
|
||||||
// Pack resides in a temporary directory, to not pollute the workspace.
|
|
||||||
const { path: packDir, cleanup } = await dir({ unsafeCleanup: true });
|
|
||||||
|
|
||||||
const syntheticConfigPack = {
|
|
||||||
name: "codeql/automodel-filter",
|
|
||||||
version: "0.0.0",
|
|
||||||
library: true,
|
|
||||||
extensionTargets: {
|
|
||||||
[`codeql/${language}-automodel-queries`]: "*",
|
|
||||||
},
|
|
||||||
dataExtensions: ["filter.yml"],
|
|
||||||
};
|
|
||||||
|
|
||||||
const qlpackFile = join(packDir, "codeql-pack.yml");
|
|
||||||
await outputFile(qlpackFile, dumpYaml(syntheticConfigPack), "utf8");
|
|
||||||
|
|
||||||
// The predicate has the following defintion:
|
|
||||||
// extensible predicate automodelCandidateFilter(string package, string type, string name, string signature)
|
|
||||||
const dataRows = candidateMethods.map((method) => [
|
|
||||||
method.packageName,
|
|
||||||
method.typeName,
|
|
||||||
method.methodName,
|
|
||||||
method.methodParameters,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const filter = {
|
|
||||||
extensions: [
|
|
||||||
{
|
|
||||||
addsTo: {
|
|
||||||
pack: `codeql/${language}-automodel-queries`,
|
|
||||||
extensible: "automodelCandidateFilter",
|
|
||||||
},
|
|
||||||
data: dataRows,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
const filterFile = join(packDir, "filter.yml");
|
|
||||||
await writeFile(filterFile, dumpYaml(filter), "utf8");
|
|
||||||
|
|
||||||
return {
|
|
||||||
packDir,
|
|
||||||
cleanup,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async function interpretAutomodelResults(
|
|
||||||
cliServer: CodeQLCliServer,
|
|
||||||
completedQuery: CoreCompletedQuery,
|
|
||||||
metadata: QueryMetadata,
|
|
||||||
sourceInfo: SourceInfo | undefined,
|
|
||||||
): Promise<Log> {
|
|
||||||
const interpretedResultsPath = join(
|
|
||||||
completedQuery.outputDir.querySaveDir,
|
|
||||||
"results.sarif",
|
|
||||||
);
|
|
||||||
|
|
||||||
const { ...sarif } = await interpretResultsSarif(
|
|
||||||
cliServer,
|
|
||||||
metadata,
|
|
||||||
{
|
|
||||||
resultsPath: completedQuery.outputDir.bqrsPath,
|
|
||||||
interpretedResultsPath,
|
|
||||||
},
|
|
||||||
sourceInfo,
|
|
||||||
["--sarif-add-snippets"],
|
|
||||||
);
|
|
||||||
|
|
||||||
return sarif;
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
import type { ModelRequest } from "./auto-model-api";
|
|
||||||
import { AutomodelMode } from "./auto-model-api";
|
|
||||||
import { Mode } from "./shared/mode";
|
|
||||||
import type { AutoModelQueriesResult } from "./auto-model-codeml-queries";
|
|
||||||
import { assertNever } from "../common/helpers-pure";
|
|
||||||
import type { Log } from "sarif";
|
|
||||||
import { gzipEncode } from "../common/zlib";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encode a SARIF log to the format expected by the server: JSON, GZIP-compressed, base64-encoded
|
|
||||||
* @param log SARIF log to encode
|
|
||||||
* @returns base64-encoded GZIP-compressed SARIF log
|
|
||||||
*/
|
|
||||||
export async function encodeSarif(log: Log): Promise<string> {
|
|
||||||
const json = JSON.stringify(log);
|
|
||||||
const buffer = Buffer.from(json, "utf-8");
|
|
||||||
const compressed = await gzipEncode(buffer);
|
|
||||||
return compressed.toString("base64");
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function createAutoModelRequest(
|
|
||||||
mode: Mode,
|
|
||||||
result: AutoModelQueriesResult,
|
|
||||||
): Promise<ModelRequest> {
|
|
||||||
let requestMode: AutomodelMode;
|
|
||||||
switch (mode) {
|
|
||||||
case Mode.Application:
|
|
||||||
requestMode = AutomodelMode.Application;
|
|
||||||
break;
|
|
||||||
case Mode.Framework:
|
|
||||||
requestMode = AutomodelMode.Framework;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assertNever(mode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
mode: requestMode,
|
|
||||||
candidates: await encodeSarif(result.candidates),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,249 +0,0 @@
|
|||||||
import type { Method, MethodSignature } from "./method";
|
|
||||||
import type { ModeledMethod } from "./modeled-method";
|
|
||||||
import { load as loadYaml } from "js-yaml";
|
|
||||||
import type { ProgressCallback } from "../common/vscode/progress";
|
|
||||||
import { withProgress } from "../common/vscode/progress";
|
|
||||||
import { createAutoModelRequest } from "./auto-model";
|
|
||||||
import { getCandidates } from "./shared/auto-model-candidates";
|
|
||||||
import { runAutoModelQueries } from "./auto-model-codeml-queries";
|
|
||||||
import { loadDataExtensionYaml } from "./yaml";
|
|
||||||
import type { ModelRequest, ModelResponse } from "./auto-model-api";
|
|
||||||
import { autoModel } from "./auto-model-api";
|
|
||||||
import { RequestError } from "@octokit/request-error";
|
|
||||||
import { showAndLogExceptionWithTelemetry } from "../common/logging";
|
|
||||||
import { redactableError } from "../common/errors";
|
|
||||||
import type { App } from "../common/app";
|
|
||||||
import type { CodeQLCliServer } from "../codeql-cli/cli";
|
|
||||||
import type { QueryRunner } from "../query-server";
|
|
||||||
import type { DatabaseItem } from "../databases/local-databases";
|
|
||||||
import type { Mode } from "./shared/mode";
|
|
||||||
import { CancellationTokenSource } from "vscode";
|
|
||||||
import type { ModelingStore } from "./modeling-store";
|
|
||||||
import type { ModelConfigListener } from "../config";
|
|
||||||
import type { QueryLanguage } from "../common/query-language";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The auto-modeler holds state around auto-modeling jobs and allows
|
|
||||||
* starting and stopping them.
|
|
||||||
*/
|
|
||||||
export class AutoModeler {
|
|
||||||
// Keep track of auto-modeling jobs that are in progress
|
|
||||||
// so that we can stop them.
|
|
||||||
private readonly jobs: Map<string, CancellationTokenSource>;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly app: App,
|
|
||||||
private readonly cliServer: CodeQLCliServer,
|
|
||||||
private readonly queryRunner: QueryRunner,
|
|
||||||
private readonly modelConfig: ModelConfigListener,
|
|
||||||
private readonly modelingStore: ModelingStore,
|
|
||||||
private readonly queryStorageDir: string,
|
|
||||||
private readonly databaseItem: DatabaseItem,
|
|
||||||
private readonly language: QueryLanguage,
|
|
||||||
private readonly addModeledMethods: (
|
|
||||||
modeledMethods: Record<string, ModeledMethod[]>,
|
|
||||||
) => Promise<void>,
|
|
||||||
) {
|
|
||||||
this.jobs = new Map<string, CancellationTokenSource>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Models the given package's external API usages, except
|
|
||||||
* the ones that are already modeled.
|
|
||||||
* @param packageName The name of the package to model.
|
|
||||||
* @param methods The methods.
|
|
||||||
* @param modeledMethods The currently modeled methods.
|
|
||||||
* @param mode The mode we are modeling in.
|
|
||||||
*/
|
|
||||||
public async startModeling(
|
|
||||||
packageName: string,
|
|
||||||
methods: readonly Method[],
|
|
||||||
modeledMethods: Record<string, readonly ModeledMethod[]>,
|
|
||||||
processedByAutoModelMethods: Set<string>,
|
|
||||||
mode: Mode,
|
|
||||||
): Promise<void> {
|
|
||||||
if (this.jobs.has(packageName)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
this.jobs.set(packageName, cancellationTokenSource);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await this.modelPackage(
|
|
||||||
packageName,
|
|
||||||
methods,
|
|
||||||
modeledMethods,
|
|
||||||
processedByAutoModelMethods,
|
|
||||||
mode,
|
|
||||||
cancellationTokenSource,
|
|
||||||
);
|
|
||||||
} finally {
|
|
||||||
this.jobs.delete(packageName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops modeling the given package.
|
|
||||||
* @param packageName The name of the package to stop modeling.
|
|
||||||
*/
|
|
||||||
public async stopModeling(packageName: string): Promise<void> {
|
|
||||||
void this.app.logger.log(`Stopping modeling for package ${packageName}`);
|
|
||||||
const cancellationTokenSource = this.jobs.get(packageName);
|
|
||||||
if (cancellationTokenSource) {
|
|
||||||
cancellationTokenSource.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops all in-progress modeling jobs.
|
|
||||||
*/
|
|
||||||
public async stopAllModeling(): Promise<void> {
|
|
||||||
for (const cancellationTokenSource of this.jobs.values()) {
|
|
||||||
cancellationTokenSource.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async modelPackage(
|
|
||||||
packageName: string,
|
|
||||||
methods: readonly Method[],
|
|
||||||
modeledMethods: Record<string, readonly ModeledMethod[]>,
|
|
||||||
processedByAutoModelMethods: Set<string>,
|
|
||||||
mode: Mode,
|
|
||||||
cancellationTokenSource: CancellationTokenSource,
|
|
||||||
): Promise<void> {
|
|
||||||
void this.app.logger.log(`Modeling package ${packageName}`);
|
|
||||||
|
|
||||||
const candidateBatchSize = this.modelConfig.llmGenerationBatchSize;
|
|
||||||
|
|
||||||
await withProgress(async (progress) => {
|
|
||||||
// Fetch the candidates to send to the model
|
|
||||||
const allCandidateMethods = getCandidates(
|
|
||||||
mode,
|
|
||||||
methods,
|
|
||||||
modeledMethods,
|
|
||||||
processedByAutoModelMethods,
|
|
||||||
);
|
|
||||||
|
|
||||||
// If there are no candidates, there is nothing to model and we just return
|
|
||||||
if (allCandidateMethods.length === 0) {
|
|
||||||
void this.app.logger.log("No candidates to model. Stopping.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find number of slices to make
|
|
||||||
const batchNumber = Math.ceil(
|
|
||||||
allCandidateMethods.length / candidateBatchSize,
|
|
||||||
);
|
|
||||||
try {
|
|
||||||
for (let i = 0; i < batchNumber; i++) {
|
|
||||||
// Check if we should stop
|
|
||||||
if (cancellationTokenSource.token.isCancellationRequested) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const start = i * candidateBatchSize;
|
|
||||||
const end = start + candidateBatchSize;
|
|
||||||
const candidatesToProcess = allCandidateMethods.slice(start, end);
|
|
||||||
const candidateSignatures = candidatesToProcess.map(
|
|
||||||
(c) => c.signature,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Let the UI know which candidates we are modeling
|
|
||||||
this.modelingStore.addInProgressMethods(
|
|
||||||
this.databaseItem,
|
|
||||||
candidateSignatures,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Kick off the process to model the slice of candidates
|
|
||||||
await this.modelCandidates(
|
|
||||||
candidatesToProcess,
|
|
||||||
mode,
|
|
||||||
progress,
|
|
||||||
cancellationTokenSource,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Let the UI know which candidates we are done modeling
|
|
||||||
this.modelingStore.removeInProgressMethods(
|
|
||||||
this.databaseItem,
|
|
||||||
candidateSignatures,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Let the UI know which methods have been sent to the LLM
|
|
||||||
this.modelingStore.addProcessedByAutoModelMethods(
|
|
||||||
this.databaseItem,
|
|
||||||
candidateSignatures,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
// Clear out in progress methods in case anything went wrong
|
|
||||||
this.modelingStore.removeInProgressMethods(
|
|
||||||
this.databaseItem,
|
|
||||||
allCandidateMethods.map((c) => c.signature),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async modelCandidates(
|
|
||||||
candidateMethods: MethodSignature[],
|
|
||||||
mode: Mode,
|
|
||||||
progress: ProgressCallback,
|
|
||||||
cancellationTokenSource: CancellationTokenSource,
|
|
||||||
): Promise<void> {
|
|
||||||
void this.app.logger.log("Executing auto-model queries");
|
|
||||||
|
|
||||||
const usages = await runAutoModelQueries({
|
|
||||||
mode,
|
|
||||||
candidateMethods,
|
|
||||||
cliServer: this.cliServer,
|
|
||||||
queryRunner: this.queryRunner,
|
|
||||||
queryStorageDir: this.queryStorageDir,
|
|
||||||
databaseItem: this.databaseItem,
|
|
||||||
progress: (update) => progress({ ...update }),
|
|
||||||
cancellationTokenSource,
|
|
||||||
});
|
|
||||||
if (!usages) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = await createAutoModelRequest(mode, usages);
|
|
||||||
|
|
||||||
void this.app.logger.log("Calling auto-model API");
|
|
||||||
|
|
||||||
const response = await this.callAutoModelApi(request);
|
|
||||||
if (!response) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const models = loadYaml(response.models, {
|
|
||||||
filename: "auto-model.yml",
|
|
||||||
});
|
|
||||||
|
|
||||||
const loadedMethods = loadDataExtensionYaml(models, this.language);
|
|
||||||
if (!loadedMethods) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.addModeledMethods(loadedMethods);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async callAutoModelApi(
|
|
||||||
request: ModelRequest,
|
|
||||||
): Promise<ModelResponse | null> {
|
|
||||||
try {
|
|
||||||
return await autoModel(this.app.credentials, request, this.modelConfig);
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof RequestError && e.status === 429) {
|
|
||||||
void showAndLogExceptionWithTelemetry(
|
|
||||||
this.app.logger,
|
|
||||||
this.app.telemetry,
|
|
||||||
redactableError`Rate limit hit, please try again soon.`,
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -33,6 +33,7 @@ export function decodeBqrsToMethods(
|
|||||||
let libraryVersion: string | undefined;
|
let libraryVersion: string | undefined;
|
||||||
let type: ModeledMethodType;
|
let type: ModeledMethodType;
|
||||||
let classification: CallClassification;
|
let classification: CallClassification;
|
||||||
|
let endpointKindColumn: string | BqrsEntityValue | undefined;
|
||||||
let endpointType: EndpointType | undefined = undefined;
|
let endpointType: EndpointType | undefined = undefined;
|
||||||
|
|
||||||
if (mode === Mode.Application) {
|
if (mode === Mode.Application) {
|
||||||
@@ -47,6 +48,7 @@ export function decodeBqrsToMethods(
|
|||||||
libraryVersion,
|
libraryVersion,
|
||||||
type,
|
type,
|
||||||
classification,
|
classification,
|
||||||
|
endpointKindColumn,
|
||||||
] = tuple as ApplicationModeTuple;
|
] = tuple as ApplicationModeTuple;
|
||||||
} else {
|
} else {
|
||||||
[
|
[
|
||||||
@@ -58,6 +60,7 @@ export function decodeBqrsToMethods(
|
|||||||
supported,
|
supported,
|
||||||
library,
|
library,
|
||||||
type,
|
type,
|
||||||
|
endpointKindColumn,
|
||||||
] = tuple as FrameworkModeTuple;
|
] = tuple as FrameworkModeTuple;
|
||||||
|
|
||||||
classification = CallClassification.Unknown;
|
classification = CallClassification.Unknown;
|
||||||
@@ -68,13 +71,18 @@ export function decodeBqrsToMethods(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (definition.endpointTypeForEndpoint) {
|
if (definition.endpointTypeForEndpoint) {
|
||||||
endpointType = definition.endpointTypeForEndpoint({
|
endpointType = definition.endpointTypeForEndpoint(
|
||||||
endpointType,
|
{
|
||||||
packageName,
|
endpointType,
|
||||||
typeName,
|
packageName,
|
||||||
methodName,
|
typeName,
|
||||||
methodParameters,
|
methodName,
|
||||||
});
|
methodParameters,
|
||||||
|
},
|
||||||
|
typeof endpointKindColumn === "object"
|
||||||
|
? endpointKindColumn.label
|
||||||
|
: endpointKindColumn,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (endpointType === undefined) {
|
if (endpointType === undefined) {
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ async function findGitFolder(
|
|||||||
const stat = await workspace.fs.stat(gitFolder);
|
const stat = await workspace.fs.stat(gitFolder);
|
||||||
// Check whether it's a directory
|
// Check whether it's a directory
|
||||||
return (stat.type & FileType.Directory) !== 0;
|
return (stat.type & FileType.Directory) !== 0;
|
||||||
} catch (e) {
|
} catch {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -174,11 +174,14 @@ export type ModelsAsDataLanguage = {
|
|||||||
* be determined by heuristics.
|
* be determined by heuristics.
|
||||||
* @param method The method to get the endpoint type for. The endpoint type can be undefined if the
|
* @param method The method to get the endpoint type for. The endpoint type can be undefined if the
|
||||||
* query does not return an endpoint type.
|
* query does not return an endpoint type.
|
||||||
|
* @param endpointKind An optional column that may be provided by the query to help determine the
|
||||||
|
* endpoint type.
|
||||||
*/
|
*/
|
||||||
endpointTypeForEndpoint?: (
|
endpointTypeForEndpoint?: (
|
||||||
method: Omit<MethodDefinition, "endpointType"> & {
|
method: Omit<MethodDefinition, "endpointType"> & {
|
||||||
endpointType: EndpointType | undefined;
|
endpointType: EndpointType | undefined;
|
||||||
},
|
},
|
||||||
|
endpointKind: string | undefined,
|
||||||
) => EndpointType | undefined;
|
) => EndpointType | undefined;
|
||||||
predicates: ModelsAsDataLanguagePredicates;
|
predicates: ModelsAsDataLanguagePredicates;
|
||||||
modelGeneration?: ModelsAsDataLanguageModelGeneration;
|
modelGeneration?: ModelsAsDataLanguageModelGeneration;
|
||||||
|
|||||||
@@ -4,7 +4,26 @@ import { EndpointType } from "../../method";
|
|||||||
|
|
||||||
const memberTokenRegex = /^Member\[(.+)]$/;
|
const memberTokenRegex = /^Member\[(.+)]$/;
|
||||||
|
|
||||||
export function parsePythonAccessPath(path: string): {
|
// In Python, the type can contain both the package name and the type name.
|
||||||
|
export function parsePythonType(type: string) {
|
||||||
|
// The first part is always the package name. All remaining parts are the type
|
||||||
|
// name.
|
||||||
|
|
||||||
|
const parts = type.split(".");
|
||||||
|
const packageName = parts.shift() ?? "";
|
||||||
|
|
||||||
|
return {
|
||||||
|
packageName,
|
||||||
|
typeName: parts.join("."),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// The type name can also be specified in the type, so this will combine
|
||||||
|
// the already parsed type name and the type name from the access path.
|
||||||
|
export function parsePythonAccessPath(
|
||||||
|
path: string,
|
||||||
|
shortTypeName: string,
|
||||||
|
): {
|
||||||
typeName: string;
|
typeName: string;
|
||||||
methodName: string;
|
methodName: string;
|
||||||
endpointType: EndpointType;
|
endpointType: EndpointType;
|
||||||
@@ -13,8 +32,12 @@ export function parsePythonAccessPath(path: string): {
|
|||||||
const tokens = parseAccessPathTokens(path);
|
const tokens = parseAccessPathTokens(path);
|
||||||
|
|
||||||
if (tokens.length === 0) {
|
if (tokens.length === 0) {
|
||||||
|
const typeName = shortTypeName.endsWith("!")
|
||||||
|
? shortTypeName.slice(0, -1)
|
||||||
|
: shortTypeName;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
typeName: "",
|
typeName,
|
||||||
methodName: "",
|
methodName: "",
|
||||||
endpointType: EndpointType.Method,
|
endpointType: EndpointType.Method,
|
||||||
path: "",
|
path: "",
|
||||||
@@ -23,6 +46,10 @@ export function parsePythonAccessPath(path: string): {
|
|||||||
|
|
||||||
const typeParts = [];
|
const typeParts = [];
|
||||||
let endpointType = EndpointType.Function;
|
let endpointType = EndpointType.Function;
|
||||||
|
// If a short type name was given and it doesn't end in a `!`, then this refers to a method.
|
||||||
|
if (shortTypeName !== "" && !shortTypeName.endsWith("!")) {
|
||||||
|
endpointType = EndpointType.Method;
|
||||||
|
}
|
||||||
|
|
||||||
let remainingTokens: typeof tokens = [];
|
let remainingTokens: typeof tokens = [];
|
||||||
|
|
||||||
@@ -32,6 +59,7 @@ export function parsePythonAccessPath(path: string): {
|
|||||||
if (memberMatch) {
|
if (memberMatch) {
|
||||||
typeParts.push(memberMatch[1]);
|
typeParts.push(memberMatch[1]);
|
||||||
} else if (token.text === "Instance") {
|
} else if (token.text === "Instance") {
|
||||||
|
// Alternative way of specifying that this refers to a method.
|
||||||
endpointType = EndpointType.Method;
|
endpointType = EndpointType.Method;
|
||||||
} else {
|
} else {
|
||||||
remainingTokens = tokens.slice(i);
|
remainingTokens = tokens.slice(i);
|
||||||
@@ -40,9 +68,22 @@ export function parsePythonAccessPath(path: string): {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const methodName = typeParts.pop() ?? "";
|
const methodName = typeParts.pop() ?? "";
|
||||||
const typeName = typeParts.join(".");
|
let typeName = typeParts.join(".");
|
||||||
const remainingPath = remainingTokens.map((token) => token.text).join(".");
|
const remainingPath = remainingTokens.map((token) => token.text).join(".");
|
||||||
|
|
||||||
|
if (shortTypeName !== "") {
|
||||||
|
if (shortTypeName.endsWith("!")) {
|
||||||
|
// The actual type name is the name without the `!`.
|
||||||
|
shortTypeName = shortTypeName.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeName !== "") {
|
||||||
|
typeName = `${shortTypeName}.${typeName}`;
|
||||||
|
} else {
|
||||||
|
typeName = shortTypeName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
methodName,
|
methodName,
|
||||||
typeName,
|
typeName,
|
||||||
@@ -51,53 +92,59 @@ export function parsePythonAccessPath(path: string): {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function parsePythonTypeAndPath(
|
||||||
|
type: string,
|
||||||
|
path: string,
|
||||||
|
): {
|
||||||
|
packageName: string;
|
||||||
|
typeName: string;
|
||||||
|
methodName: string;
|
||||||
|
endpointType: EndpointType;
|
||||||
|
path: string;
|
||||||
|
} {
|
||||||
|
const { packageName, typeName: shortTypeName } = parsePythonType(type);
|
||||||
|
const {
|
||||||
|
typeName,
|
||||||
|
methodName,
|
||||||
|
endpointType,
|
||||||
|
path: remainingPath,
|
||||||
|
} = parsePythonAccessPath(path, shortTypeName);
|
||||||
|
|
||||||
|
return {
|
||||||
|
packageName,
|
||||||
|
typeName,
|
||||||
|
methodName,
|
||||||
|
endpointType,
|
||||||
|
path: remainingPath,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function pythonMethodSignature(typeName: string, methodName: string) {
|
export function pythonMethodSignature(typeName: string, methodName: string) {
|
||||||
return `${typeName}#${methodName}`;
|
return `${typeName}#${methodName}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function pythonTypePath(typeName: string) {
|
export function pythonType(
|
||||||
if (typeName === "") {
|
packageName: string,
|
||||||
|
typeName: string,
|
||||||
|
endpointType: EndpointType,
|
||||||
|
) {
|
||||||
|
if (typeName !== "" && packageName !== "") {
|
||||||
|
return `${packageName}.${typeName}${endpointType === EndpointType.Function ? "!" : ""}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${packageName}${typeName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function pythonMethodPath(methodName: string) {
|
||||||
|
if (methodName === "") {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
return typeName
|
return `Member[${methodName}]`;
|
||||||
.split(".")
|
|
||||||
.map((part) => `Member[${part}]`)
|
|
||||||
.join(".");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pythonMethodPath(
|
export function pythonPath(methodName: string, path: string) {
|
||||||
typeName: string,
|
const methodPath = pythonMethodPath(methodName);
|
||||||
methodName: string,
|
|
||||||
endpointType: EndpointType,
|
|
||||||
) {
|
|
||||||
if (methodName === "") {
|
|
||||||
return pythonTypePath(typeName);
|
|
||||||
}
|
|
||||||
|
|
||||||
const typePath = pythonTypePath(typeName);
|
|
||||||
|
|
||||||
let result = typePath;
|
|
||||||
if (typePath !== "" && endpointType === EndpointType.Method) {
|
|
||||||
result += ".Instance";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result !== "") {
|
|
||||||
result += ".";
|
|
||||||
}
|
|
||||||
|
|
||||||
result += `Member[${methodName}]`;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function pythonPath(
|
|
||||||
typeName: string,
|
|
||||||
methodName: string,
|
|
||||||
endpointType: EndpointType,
|
|
||||||
path: string,
|
|
||||||
) {
|
|
||||||
const methodPath = pythonMethodPath(typeName, methodName, endpointType);
|
|
||||||
if (methodPath === "") {
|
if (methodPath === "") {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
@@ -111,7 +158,24 @@ export function pythonPath(
|
|||||||
|
|
||||||
export function pythonEndpointType(
|
export function pythonEndpointType(
|
||||||
method: Omit<MethodDefinition, "endpointType">,
|
method: Omit<MethodDefinition, "endpointType">,
|
||||||
|
endpointKind: string | undefined,
|
||||||
): EndpointType {
|
): EndpointType {
|
||||||
|
switch (endpointKind) {
|
||||||
|
case "Function":
|
||||||
|
return EndpointType.Function;
|
||||||
|
case "InstanceMethod":
|
||||||
|
return EndpointType.Method;
|
||||||
|
case "ClassMethod":
|
||||||
|
return EndpointType.ClassMethod;
|
||||||
|
case "StaticMethod":
|
||||||
|
return EndpointType.StaticMethod;
|
||||||
|
case "InitMethod":
|
||||||
|
return EndpointType.Constructor;
|
||||||
|
case "Class":
|
||||||
|
return EndpointType.Class;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy behavior for when the kind column is missing.
|
||||||
if (
|
if (
|
||||||
method.methodParameters.startsWith("(self,") ||
|
method.methodParameters.startsWith("(self,") ||
|
||||||
method.methodParameters === "(self)"
|
method.methodParameters === "(self)"
|
||||||
@@ -120,3 +184,12 @@ export function pythonEndpointType(
|
|||||||
}
|
}
|
||||||
return EndpointType.Function;
|
return EndpointType.Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function hasPythonSelfArgument(endpointType: EndpointType): boolean {
|
||||||
|
// Instance methods and class methods both use `Argument[self]` for the first parameter. The first
|
||||||
|
// parameter after self is called `Argument[0]`.
|
||||||
|
return (
|
||||||
|
endpointType === EndpointType.Method ||
|
||||||
|
endpointType === EndpointType.ClassMethod
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,44 +4,48 @@ import { Mode } from "../../shared/mode";
|
|||||||
import type { MethodArgument } from "../../method";
|
import type { MethodArgument } from "../../method";
|
||||||
import { EndpointType, getArgumentsList } from "../../method";
|
import { EndpointType, getArgumentsList } from "../../method";
|
||||||
import {
|
import {
|
||||||
parsePythonAccessPath,
|
hasPythonSelfArgument,
|
||||||
|
parsePythonTypeAndPath,
|
||||||
pythonEndpointType,
|
pythonEndpointType,
|
||||||
pythonMethodPath,
|
pythonMethodPath,
|
||||||
pythonMethodSignature,
|
pythonMethodSignature,
|
||||||
pythonPath,
|
pythonPath,
|
||||||
|
pythonType,
|
||||||
} from "./access-paths";
|
} from "./access-paths";
|
||||||
|
|
||||||
export const python: ModelsAsDataLanguage = {
|
export const python: ModelsAsDataLanguage = {
|
||||||
availableModes: [Mode.Framework],
|
availableModes: [Mode.Framework],
|
||||||
createMethodSignature: ({ typeName, methodName }) =>
|
createMethodSignature: ({ typeName, methodName }) =>
|
||||||
`${typeName}#${methodName}`,
|
`${typeName}#${methodName}`,
|
||||||
endpointTypeForEndpoint: (method) => pythonEndpointType(method),
|
endpointTypeForEndpoint: (method, endpointKind) =>
|
||||||
|
pythonEndpointType(method, endpointKind),
|
||||||
predicates: {
|
predicates: {
|
||||||
source: {
|
source: {
|
||||||
extensiblePredicate: sharedExtensiblePredicates.source,
|
extensiblePredicate: sharedExtensiblePredicates.source,
|
||||||
supportedKinds: sharedKinds.source,
|
supportedKinds: sharedKinds.source,
|
||||||
supportedEndpointTypes: [EndpointType.Method, EndpointType.Function],
|
supportedEndpointTypes: [
|
||||||
|
EndpointType.Method,
|
||||||
|
EndpointType.Function,
|
||||||
|
EndpointType.Constructor,
|
||||||
|
EndpointType.ClassMethod,
|
||||||
|
EndpointType.StaticMethod,
|
||||||
|
],
|
||||||
// extensible predicate sourceModel(
|
// extensible predicate sourceModel(
|
||||||
// string type, string path, string kind
|
// string type, string path, string kind
|
||||||
// );
|
// );
|
||||||
generateMethodDefinition: (method) => [
|
generateMethodDefinition: (method) => [
|
||||||
method.packageName,
|
pythonType(method.packageName, method.typeName, method.endpointType),
|
||||||
pythonPath(
|
pythonPath(method.methodName, method.output),
|
||||||
method.typeName,
|
|
||||||
method.methodName,
|
|
||||||
method.endpointType,
|
|
||||||
method.output,
|
|
||||||
),
|
|
||||||
method.kind,
|
method.kind,
|
||||||
],
|
],
|
||||||
readModeledMethod: (row) => {
|
readModeledMethod: (row) => {
|
||||||
const packageName = row[0] as string;
|
|
||||||
const {
|
const {
|
||||||
|
packageName,
|
||||||
typeName,
|
typeName,
|
||||||
methodName,
|
methodName,
|
||||||
endpointType,
|
endpointType,
|
||||||
path: output,
|
path: output,
|
||||||
} = parsePythonAccessPath(row[1] as string);
|
} = parsePythonTypeAndPath(row[0] as string, row[1] as string);
|
||||||
return {
|
return {
|
||||||
type: "source",
|
type: "source",
|
||||||
output,
|
output,
|
||||||
@@ -59,30 +63,31 @@ export const python: ModelsAsDataLanguage = {
|
|||||||
sink: {
|
sink: {
|
||||||
extensiblePredicate: sharedExtensiblePredicates.sink,
|
extensiblePredicate: sharedExtensiblePredicates.sink,
|
||||||
supportedKinds: sharedKinds.sink,
|
supportedKinds: sharedKinds.sink,
|
||||||
supportedEndpointTypes: [EndpointType.Method, EndpointType.Function],
|
supportedEndpointTypes: [
|
||||||
|
EndpointType.Method,
|
||||||
|
EndpointType.Function,
|
||||||
|
EndpointType.Constructor,
|
||||||
|
EndpointType.ClassMethod,
|
||||||
|
EndpointType.StaticMethod,
|
||||||
|
],
|
||||||
// extensible predicate sinkModel(
|
// extensible predicate sinkModel(
|
||||||
// string type, string path, string kind
|
// string type, string path, string kind
|
||||||
// );
|
// );
|
||||||
generateMethodDefinition: (method) => {
|
generateMethodDefinition: (method) => {
|
||||||
return [
|
return [
|
||||||
method.packageName,
|
pythonType(method.packageName, method.typeName, method.endpointType),
|
||||||
pythonPath(
|
pythonPath(method.methodName, method.input),
|
||||||
method.typeName,
|
|
||||||
method.methodName,
|
|
||||||
method.endpointType,
|
|
||||||
method.input,
|
|
||||||
),
|
|
||||||
method.kind,
|
method.kind,
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
readModeledMethod: (row) => {
|
readModeledMethod: (row) => {
|
||||||
const packageName = row[0] as string;
|
|
||||||
const {
|
const {
|
||||||
|
packageName,
|
||||||
typeName,
|
typeName,
|
||||||
methodName,
|
methodName,
|
||||||
endpointType,
|
endpointType,
|
||||||
path: input,
|
path: input,
|
||||||
} = parsePythonAccessPath(row[1] as string);
|
} = parsePythonTypeAndPath(row[0] as string, row[1] as string);
|
||||||
return {
|
return {
|
||||||
type: "sink",
|
type: "sink",
|
||||||
input,
|
input,
|
||||||
@@ -100,25 +105,26 @@ export const python: ModelsAsDataLanguage = {
|
|||||||
summary: {
|
summary: {
|
||||||
extensiblePredicate: sharedExtensiblePredicates.summary,
|
extensiblePredicate: sharedExtensiblePredicates.summary,
|
||||||
supportedKinds: sharedKinds.summary,
|
supportedKinds: sharedKinds.summary,
|
||||||
supportedEndpointTypes: [EndpointType.Method, EndpointType.Function],
|
supportedEndpointTypes: [
|
||||||
|
EndpointType.Method,
|
||||||
|
EndpointType.Function,
|
||||||
|
EndpointType.Constructor,
|
||||||
|
EndpointType.ClassMethod,
|
||||||
|
EndpointType.StaticMethod,
|
||||||
|
],
|
||||||
// extensible predicate summaryModel(
|
// extensible predicate summaryModel(
|
||||||
// string type, string path, string input, string output, string kind
|
// string type, string path, string input, string output, string kind
|
||||||
// );
|
// );
|
||||||
generateMethodDefinition: (method) => [
|
generateMethodDefinition: (method) => [
|
||||||
method.packageName,
|
pythonType(method.packageName, method.typeName, method.endpointType),
|
||||||
pythonMethodPath(
|
pythonMethodPath(method.methodName),
|
||||||
method.typeName,
|
|
||||||
method.methodName,
|
|
||||||
method.endpointType,
|
|
||||||
),
|
|
||||||
method.input,
|
method.input,
|
||||||
method.output,
|
method.output,
|
||||||
method.kind,
|
method.kind,
|
||||||
],
|
],
|
||||||
readModeledMethod: (row) => {
|
readModeledMethod: (row) => {
|
||||||
const packageName = row[0] as string;
|
const { packageName, typeName, methodName, endpointType, path } =
|
||||||
const { typeName, methodName, endpointType, path } =
|
parsePythonTypeAndPath(row[0] as string, row[1] as string);
|
||||||
parsePythonAccessPath(row[1] as string);
|
|
||||||
if (path !== "") {
|
if (path !== "") {
|
||||||
throw new Error("Summary path must be a method");
|
throw new Error("Summary path must be a method");
|
||||||
}
|
}
|
||||||
@@ -144,18 +150,13 @@ export const python: ModelsAsDataLanguage = {
|
|||||||
// string type, string path, string kind
|
// string type, string path, string kind
|
||||||
// );
|
// );
|
||||||
generateMethodDefinition: (method) => [
|
generateMethodDefinition: (method) => [
|
||||||
method.packageName,
|
pythonType(method.packageName, method.typeName, method.endpointType),
|
||||||
pythonMethodPath(
|
pythonMethodPath(method.methodName),
|
||||||
method.typeName,
|
|
||||||
method.methodName,
|
|
||||||
method.endpointType,
|
|
||||||
),
|
|
||||||
method.kind,
|
method.kind,
|
||||||
],
|
],
|
||||||
readModeledMethod: (row) => {
|
readModeledMethod: (row) => {
|
||||||
const packageName = row[0] as string;
|
const { packageName, typeName, methodName, endpointType, path } =
|
||||||
const { typeName, methodName, endpointType, path } =
|
parsePythonTypeAndPath(row[0] as string, row[1] as string);
|
||||||
parsePythonAccessPath(row[1] as string);
|
|
||||||
if (path !== "") {
|
if (path !== "") {
|
||||||
throw new Error("Neutral path must be a method");
|
throw new Error("Neutral path must be a method");
|
||||||
}
|
}
|
||||||
@@ -172,25 +173,46 @@ export const python: ModelsAsDataLanguage = {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
type: {
|
||||||
|
extensiblePredicate: "typeModel",
|
||||||
|
// extensible predicate typeModel(string type1, string type2, string path);
|
||||||
|
generateMethodDefinition: (method) => [
|
||||||
|
method.relatedTypeName,
|
||||||
|
pythonType(method.packageName, method.typeName, method.endpointType),
|
||||||
|
pythonPath(method.methodName, method.path),
|
||||||
|
],
|
||||||
|
readModeledMethod: (row) => {
|
||||||
|
const { packageName, typeName, methodName, endpointType, path } =
|
||||||
|
parsePythonTypeAndPath(row[1] as string, row[2] as string);
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "type",
|
||||||
|
relatedTypeName: row[0] as string,
|
||||||
|
path,
|
||||||
|
signature: pythonMethodSignature(typeName, methodName),
|
||||||
|
endpointType,
|
||||||
|
packageName,
|
||||||
|
typeName,
|
||||||
|
methodName,
|
||||||
|
methodParameters: "",
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
getArgumentOptions: (method) => {
|
getArgumentOptions: (method) => {
|
||||||
// Argument and Parameter are equivalent in Python, but we'll use Argument in the model editor
|
// Argument and Parameter are equivalent in Python, but we'll use Argument in the model editor
|
||||||
const argumentsList = getArgumentsList(method.methodParameters).map(
|
const argumentsList = getArgumentsList(method.methodParameters).map(
|
||||||
(argument, index): MethodArgument => {
|
(argument, index): MethodArgument => {
|
||||||
if (
|
if (hasPythonSelfArgument(method.endpointType) && index === 0) {
|
||||||
method.endpointType === EndpointType.Method &&
|
|
||||||
argument === "self" &&
|
|
||||||
index === 0
|
|
||||||
) {
|
|
||||||
return {
|
return {
|
||||||
path: "Argument[self]",
|
path: "Argument[self]",
|
||||||
label: "Argument[self]: self",
|
label: `Argument[self]: ${argument}`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this is a method, self does not count as an argument index, so we
|
// If this endpoint has a self argument, self does not count as an argument index so we
|
||||||
// should start at 0 for the second argument
|
// should start at 0 for the second argument
|
||||||
if (method.endpointType === EndpointType.Method) {
|
if (hasPythonSelfArgument(method.endpointType)) {
|
||||||
index -= 1;
|
index -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,8 +70,6 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
|
|||||||
method: Method,
|
method: Method,
|
||||||
modeledMethods: readonly ModeledMethod[],
|
modeledMethods: readonly ModeledMethod[],
|
||||||
isModified: boolean,
|
isModified: boolean,
|
||||||
isInProgress: boolean,
|
|
||||||
processedByAutoModel: boolean,
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.databaseItem = databaseItem;
|
this.databaseItem = databaseItem;
|
||||||
@@ -82,8 +80,6 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
|
|||||||
method,
|
method,
|
||||||
modeledMethods,
|
modeledMethods,
|
||||||
isModified,
|
isModified,
|
||||||
isInProgress,
|
|
||||||
processedByAutoModel,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,8 +100,6 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
|
|||||||
selectedMethod.method,
|
selectedMethod.method,
|
||||||
selectedMethod.modeledMethods,
|
selectedMethod.modeledMethods,
|
||||||
selectedMethod.isModified,
|
selectedMethod.isModified,
|
||||||
selectedMethod.isInProgress,
|
|
||||||
selectedMethod.processedByAutoModel,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,8 +197,6 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
|
|||||||
e.method,
|
e.method,
|
||||||
e.modeledMethods,
|
e.modeledMethods,
|
||||||
e.isModified,
|
e.isModified,
|
||||||
e.isInProgress,
|
|
||||||
e.processedByAutoModel,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@@ -232,36 +224,6 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.push(
|
|
||||||
this.modelingEvents.onInProgressMethodsChanged(async (e) => {
|
|
||||||
if (this.method && this.databaseItem) {
|
|
||||||
const dbUri = this.databaseItem.databaseUri.toString();
|
|
||||||
if (e.dbUri === dbUri) {
|
|
||||||
const inProgress = e.methods.has(this.method.signature);
|
|
||||||
await this.postMessage({
|
|
||||||
t: "setInProgress",
|
|
||||||
inProgress,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.push(
|
|
||||||
this.modelingEvents.onProcessedByAutoModelMethodsChanged(async (e) => {
|
|
||||||
if (this.method && this.databaseItem) {
|
|
||||||
const dbUri = this.databaseItem.databaseUri.toString();
|
|
||||||
if (e.dbUri === dbUri) {
|
|
||||||
const processedByAutoModel = e.methods.has(this.method.signature);
|
|
||||||
await this.postMessage({
|
|
||||||
t: "setProcessedByAutoModel",
|
|
||||||
processedByAutoModel,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerToModelConfigEvents(): void {
|
private registerToModelConfigEvents(): void {
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ export enum EndpointType {
|
|||||||
Method = "method",
|
Method = "method",
|
||||||
Constructor = "constructor",
|
Constructor = "constructor",
|
||||||
Function = "function",
|
Function = "function",
|
||||||
|
StaticMethod = "staticMethod",
|
||||||
|
ClassMethod = "classMethod",
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MethodDefinition {
|
export interface MethodDefinition {
|
||||||
|
|||||||
@@ -204,7 +204,6 @@ export class ModelEditorModule extends DisposableObject {
|
|||||||
this.app.logger,
|
this.app.logger,
|
||||||
queryDir,
|
queryDir,
|
||||||
language,
|
language,
|
||||||
this.modelConfig,
|
|
||||||
initialMode,
|
initialMode,
|
||||||
);
|
);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
syntheticQueryPackName,
|
syntheticQueryPackName,
|
||||||
} from "./model-editor-queries";
|
} from "./model-editor-queries";
|
||||||
import type { CodeQLCliServer } from "../codeql-cli/cli";
|
import type { CodeQLCliServer } from "../codeql-cli/cli";
|
||||||
import type { ModelConfig } from "../config";
|
|
||||||
import type { Mode } from "./shared/mode";
|
import type { Mode } from "./shared/mode";
|
||||||
import type { NotificationLogger } from "../common/logging";
|
import type { NotificationLogger } from "../common/logging";
|
||||||
|
|
||||||
@@ -30,7 +29,6 @@ import type { NotificationLogger } from "../common/logging";
|
|||||||
* @param logger The logger to use.
|
* @param logger The logger to use.
|
||||||
* @param queryDir The directory to set up.
|
* @param queryDir The directory to set up.
|
||||||
* @param language The language to use for the queries.
|
* @param language The language to use for the queries.
|
||||||
* @param modelConfig The model config to use.
|
|
||||||
* @param initialMode The initial mode to use to check the existence of the queries.
|
* @param initialMode The initial mode to use to check the existence of the queries.
|
||||||
* @returns true if the setup was successful, false otherwise.
|
* @returns true if the setup was successful, false otherwise.
|
||||||
*/
|
*/
|
||||||
@@ -39,7 +37,6 @@ export async function setUpPack(
|
|||||||
logger: NotificationLogger,
|
logger: NotificationLogger,
|
||||||
queryDir: string,
|
queryDir: string,
|
||||||
language: QueryLanguage,
|
language: QueryLanguage,
|
||||||
modelConfig: ModelConfig,
|
|
||||||
initialMode: Mode,
|
initialMode: Mode,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
// Download the required query packs
|
// Download the required query packs
|
||||||
@@ -91,10 +88,5 @@ export async function setUpPack(
|
|||||||
await cliServer.packInstall(queryDir);
|
await cliServer.packInstall(queryDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Download any other required packs
|
|
||||||
if (language === "java" && modelConfig.llmGeneration) {
|
|
||||||
await cliServer.packDownload([`codeql/${language}-automodel-queries`]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ import {
|
|||||||
import { pickExtensionPack } from "./extension-pack-picker";
|
import { pickExtensionPack } from "./extension-pack-picker";
|
||||||
import type { QueryLanguage } from "../common/query-language";
|
import type { QueryLanguage } from "../common/query-language";
|
||||||
import { getLanguageDisplayName } from "../common/query-language";
|
import { getLanguageDisplayName } from "../common/query-language";
|
||||||
import { AutoModeler } from "./auto-modeler";
|
|
||||||
import { telemetryListener } from "../common/vscode/telemetry";
|
import { telemetryListener } from "../common/vscode/telemetry";
|
||||||
import type { ModelingStore } from "./modeling-store";
|
import type { ModelingStore } from "./modeling-store";
|
||||||
import type { ModelingEvents } from "./modeling-events";
|
import type { ModelingEvents } from "./modeling-events";
|
||||||
@@ -77,7 +76,6 @@ export class ModelEditorView extends AbstractWebview<
|
|||||||
ToModelEditorMessage,
|
ToModelEditorMessage,
|
||||||
FromModelEditorMessage
|
FromModelEditorMessage
|
||||||
> {
|
> {
|
||||||
private readonly autoModeler: AutoModeler;
|
|
||||||
private readonly modelEvaluator: ModelEvaluator;
|
private readonly modelEvaluator: ModelEvaluator;
|
||||||
private readonly languageDefinition: ModelsAsDataLanguage;
|
private readonly languageDefinition: ModelsAsDataLanguage;
|
||||||
// Cancellation token source that can be used for passing into long-running operations. Should only
|
// Cancellation token source that can be used for passing into long-running operations. Should only
|
||||||
@@ -114,19 +112,6 @@ export class ModelEditorView extends AbstractWebview<
|
|||||||
this.registerToModelingEvents();
|
this.registerToModelingEvents();
|
||||||
this.registerToModelConfigEvents();
|
this.registerToModelConfigEvents();
|
||||||
|
|
||||||
this.autoModeler = new AutoModeler(
|
|
||||||
app,
|
|
||||||
cliServer,
|
|
||||||
queryRunner,
|
|
||||||
this.modelConfig,
|
|
||||||
modelingStore,
|
|
||||||
queryStorageDir,
|
|
||||||
databaseItem,
|
|
||||||
language,
|
|
||||||
async (modeledMethods) => {
|
|
||||||
this.addModeledMethods(modeledMethods);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
this.languageDefinition = getModelsAsDataLanguage(language);
|
this.languageDefinition = getModelsAsDataLanguage(language);
|
||||||
|
|
||||||
this.modelEvaluator = new ModelEvaluator(
|
this.modelEvaluator = new ModelEvaluator(
|
||||||
@@ -317,21 +302,6 @@ export class ModelEditorView extends AbstractWebview<
|
|||||||
"model-editor-generate-modeled-methods",
|
"model-editor-generate-modeled-methods",
|
||||||
);
|
);
|
||||||
|
|
||||||
break;
|
|
||||||
case "generateMethodsFromLlm":
|
|
||||||
await this.generateModeledMethodsFromLlm(
|
|
||||||
msg.packageName,
|
|
||||||
msg.methodSignatures,
|
|
||||||
);
|
|
||||||
void telemetryListener?.sendUIInteraction(
|
|
||||||
"model-editor-generate-methods-from-llm",
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
case "stopGeneratingMethodsFromLlm":
|
|
||||||
await this.autoModeler.stopModeling(msg.packageName);
|
|
||||||
void telemetryListener?.sendUIInteraction(
|
|
||||||
"model-editor-stop-generating-methods-from-llm",
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case "modelDependency":
|
case "modelDependency":
|
||||||
await this.modelDependency();
|
await this.modelDependency();
|
||||||
@@ -438,9 +408,6 @@ export class ModelEditorView extends AbstractWebview<
|
|||||||
this.modelConfig.flowGeneration) &&
|
this.modelConfig.flowGeneration) &&
|
||||||
!!modelsAsDataLanguage.modelGeneration;
|
!!modelsAsDataLanguage.modelGeneration;
|
||||||
|
|
||||||
const showLlmButton =
|
|
||||||
this.databaseItem.language === "java" && this.modelConfig.llmGeneration;
|
|
||||||
|
|
||||||
const showEvaluationUi = this.modelConfig.modelEvaluation;
|
const showEvaluationUi = this.modelConfig.modelEvaluation;
|
||||||
|
|
||||||
const sourceArchiveAvailable =
|
const sourceArchiveAvailable =
|
||||||
@@ -456,7 +423,6 @@ export class ModelEditorView extends AbstractWebview<
|
|||||||
extensionPack: this.extensionPack,
|
extensionPack: this.extensionPack,
|
||||||
language: this.language,
|
language: this.language,
|
||||||
showGenerateButton,
|
showGenerateButton,
|
||||||
showLlmButton,
|
|
||||||
showEvaluationUi,
|
showEvaluationUi,
|
||||||
mode: this.modelingStore.getMode(this.databaseItem),
|
mode: this.modelingStore.getMode(this.databaseItem),
|
||||||
showModeSwitchButton,
|
showModeSwitchButton,
|
||||||
@@ -805,33 +771,6 @@ export class ModelEditorView extends AbstractWebview<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async generateModeledMethodsFromLlm(
|
|
||||||
packageName: string,
|
|
||||||
methodSignatures: string[],
|
|
||||||
): Promise<void> {
|
|
||||||
const methods = this.modelingStore.getMethods(
|
|
||||||
this.databaseItem,
|
|
||||||
methodSignatures,
|
|
||||||
);
|
|
||||||
const modeledMethods = this.modelingStore.getModeledMethods(
|
|
||||||
this.databaseItem,
|
|
||||||
methodSignatures,
|
|
||||||
);
|
|
||||||
const processedByAutoModelMethods =
|
|
||||||
this.modelingStore.getProcessedByAutoModelMethods(
|
|
||||||
this.databaseItem,
|
|
||||||
methodSignatures,
|
|
||||||
);
|
|
||||||
const mode = this.modelingStore.getMode(this.databaseItem);
|
|
||||||
await this.autoModeler.startModeling(
|
|
||||||
packageName,
|
|
||||||
methods,
|
|
||||||
modeledMethods,
|
|
||||||
processedByAutoModelMethods,
|
|
||||||
mode,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async modelDependency(): Promise<void> {
|
private async modelDependency(): Promise<void> {
|
||||||
return withProgress(
|
return withProgress(
|
||||||
async (progress, token) => {
|
async (progress, token) => {
|
||||||
@@ -983,30 +922,6 @@ export class ModelEditorView extends AbstractWebview<
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.push(
|
|
||||||
this.modelingEvents.onInProgressMethodsChanged(async (event) => {
|
|
||||||
if (event.dbUri === this.databaseItem.databaseUri.toString()) {
|
|
||||||
await this.postMessage({
|
|
||||||
t: "setInProgressMethods",
|
|
||||||
methods: Array.from(event.methods),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.push(
|
|
||||||
this.modelingEvents.onProcessedByAutoModelMethodsChanged(
|
|
||||||
async (event) => {
|
|
||||||
if (event.dbUri === this.databaseItem.databaseUri.toString()) {
|
|
||||||
await this.postMessage({
|
|
||||||
t: "setProcessedByAutoModelMethods",
|
|
||||||
methods: Array.from(event.methods),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
this.push(
|
this.push(
|
||||||
this.modelingEvents.onRevealInModelEditor(async (event) => {
|
this.modelingEvents.onRevealInModelEditor(async (event) => {
|
||||||
if (event.dbUri === this.databaseItem.databaseUri.toString()) {
|
if (event.dbUri === this.databaseItem.databaseUri.toString()) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { assertNever } from "../common/helpers-pure";
|
import { assertNever } from "../common/helpers-pure";
|
||||||
import type { MethodSignature } from "./method";
|
import type { MethodSignature } from "./method";
|
||||||
import type { ModelingStatus } from "./shared/modeling-status";
|
|
||||||
|
|
||||||
export type ModeledMethodType =
|
export type ModeledMethodType =
|
||||||
| "none"
|
| "none"
|
||||||
@@ -15,10 +14,6 @@ export type Provenance =
|
|||||||
| "df-generated"
|
| "df-generated"
|
||||||
// Generated by the dataflow model and manually edited
|
// Generated by the dataflow model and manually edited
|
||||||
| "df-manual"
|
| "df-manual"
|
||||||
// Generated by the auto-model
|
|
||||||
| "ai-generated"
|
|
||||||
// Generated by the auto-model and manually edited
|
|
||||||
| "ai-manual"
|
|
||||||
// Entered by the user in the editor manually
|
// Entered by the user in the editor manually
|
||||||
| "manual";
|
| "manual";
|
||||||
|
|
||||||
@@ -112,30 +107,6 @@ export function modeledMethodSupportsProvenance(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isModelPending(
|
|
||||||
modeledMethod: ModeledMethod | undefined,
|
|
||||||
modelingStatus: ModelingStatus,
|
|
||||||
processedByAutoModel: boolean,
|
|
||||||
): boolean {
|
|
||||||
if (
|
|
||||||
(!modeledMethod || modeledMethod.type === "none") &&
|
|
||||||
processedByAutoModel
|
|
||||||
) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!modeledMethod) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
modelingStatus === "unsaved" &&
|
|
||||||
modeledMethod.type !== "none" &&
|
|
||||||
modeledMethodSupportsProvenance(modeledMethod) &&
|
|
||||||
modeledMethod.provenance === "ai-generated"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the new provenance for a modeled method based on the current provenance.
|
* Calculates the new provenance for a modeled method based on the current provenance.
|
||||||
* @param modeledMethod The modeled method if there is one.
|
* @param modeledMethod The modeled method if there is one.
|
||||||
@@ -158,13 +129,6 @@ export function calculateNewProvenance(
|
|||||||
case "df-manual":
|
case "df-manual":
|
||||||
// If the method has had manual edits, we want the provenance to stay the same.
|
// If the method has had manual edits, we want the provenance to stay the same.
|
||||||
return "df-manual";
|
return "df-manual";
|
||||||
case "ai-generated":
|
|
||||||
// If the method has been generated and there has been a change, we assume
|
|
||||||
// that the user has manually edited it.
|
|
||||||
return "ai-manual";
|
|
||||||
case "ai-manual":
|
|
||||||
// If the method has had manual edits, we want the provenance to stay the same.
|
|
||||||
return "ai-manual";
|
|
||||||
default:
|
default:
|
||||||
// The method has been modeled manually.
|
// The method has been modeled manually.
|
||||||
return "manual";
|
return "manual";
|
||||||
|
|||||||
@@ -37,18 +37,6 @@ interface SelectedMethodChangedEvent {
|
|||||||
readonly usage: Usage;
|
readonly usage: Usage;
|
||||||
readonly modeledMethods: readonly ModeledMethod[];
|
readonly modeledMethods: readonly ModeledMethod[];
|
||||||
readonly isModified: boolean;
|
readonly isModified: boolean;
|
||||||
readonly isInProgress: boolean;
|
|
||||||
readonly processedByAutoModel: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface InProgressMethodsChangedEvent {
|
|
||||||
readonly dbUri: string;
|
|
||||||
readonly methods: ReadonlySet<string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ProcessedByAutoModelMethodsChangedEvent {
|
|
||||||
readonly dbUri: string;
|
|
||||||
readonly methods: ReadonlySet<string>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ModelEvaluationRunChangedEvent {
|
interface ModelEvaluationRunChangedEvent {
|
||||||
@@ -83,8 +71,6 @@ export class ModelingEvents extends DisposableObject {
|
|||||||
public readonly onModeChanged: AppEvent<ModeChangedEvent>;
|
public readonly onModeChanged: AppEvent<ModeChangedEvent>;
|
||||||
public readonly onModeledAndModifiedMethodsChanged: AppEvent<ModeledAndModifiedMethodsChangedEvent>;
|
public readonly onModeledAndModifiedMethodsChanged: AppEvent<ModeledAndModifiedMethodsChangedEvent>;
|
||||||
public readonly onSelectedMethodChanged: AppEvent<SelectedMethodChangedEvent>;
|
public readonly onSelectedMethodChanged: AppEvent<SelectedMethodChangedEvent>;
|
||||||
public readonly onInProgressMethodsChanged: AppEvent<InProgressMethodsChangedEvent>;
|
|
||||||
public readonly onProcessedByAutoModelMethodsChanged: AppEvent<ProcessedByAutoModelMethodsChangedEvent>;
|
|
||||||
public readonly onModelEvaluationRunChanged: AppEvent<ModelEvaluationRunChangedEvent>;
|
public readonly onModelEvaluationRunChanged: AppEvent<ModelEvaluationRunChangedEvent>;
|
||||||
public readonly onRevealInModelEditor: AppEvent<RevealInModelEditorEvent>;
|
public readonly onRevealInModelEditor: AppEvent<RevealInModelEditorEvent>;
|
||||||
public readonly onFocusModelEditor: AppEvent<FocusModelEditorEvent>;
|
public readonly onFocusModelEditor: AppEvent<FocusModelEditorEvent>;
|
||||||
@@ -99,8 +85,6 @@ export class ModelingEvents extends DisposableObject {
|
|||||||
private readonly onModeChangedEventEmitter: AppEventEmitter<ModeChangedEvent>;
|
private readonly onModeChangedEventEmitter: AppEventEmitter<ModeChangedEvent>;
|
||||||
private readonly onModeledAndModifiedMethodsChangedEventEmitter: AppEventEmitter<ModeledAndModifiedMethodsChangedEvent>;
|
private readonly onModeledAndModifiedMethodsChangedEventEmitter: AppEventEmitter<ModeledAndModifiedMethodsChangedEvent>;
|
||||||
private readonly onSelectedMethodChangedEventEmitter: AppEventEmitter<SelectedMethodChangedEvent>;
|
private readonly onSelectedMethodChangedEventEmitter: AppEventEmitter<SelectedMethodChangedEvent>;
|
||||||
private readonly onInProgressMethodsChangedEventEmitter: AppEventEmitter<InProgressMethodsChangedEvent>;
|
|
||||||
private readonly onProcessedByAutoModelMethodsChangedEventEmitter: AppEventEmitter<ProcessedByAutoModelMethodsChangedEvent>;
|
|
||||||
private readonly onModelEvaluationRunChangedEventEmitter: AppEventEmitter<ModelEvaluationRunChangedEvent>;
|
private readonly onModelEvaluationRunChangedEventEmitter: AppEventEmitter<ModelEvaluationRunChangedEvent>;
|
||||||
private readonly onRevealInModelEditorEventEmitter: AppEventEmitter<RevealInModelEditorEvent>;
|
private readonly onRevealInModelEditorEventEmitter: AppEventEmitter<RevealInModelEditorEvent>;
|
||||||
private readonly onFocusModelEditorEventEmitter: AppEventEmitter<FocusModelEditorEvent>;
|
private readonly onFocusModelEditorEventEmitter: AppEventEmitter<FocusModelEditorEvent>;
|
||||||
@@ -151,18 +135,6 @@ export class ModelingEvents extends DisposableObject {
|
|||||||
this.onSelectedMethodChanged =
|
this.onSelectedMethodChanged =
|
||||||
this.onSelectedMethodChangedEventEmitter.event;
|
this.onSelectedMethodChangedEventEmitter.event;
|
||||||
|
|
||||||
this.onInProgressMethodsChangedEventEmitter = this.push(
|
|
||||||
app.createEventEmitter<InProgressMethodsChangedEvent>(),
|
|
||||||
);
|
|
||||||
this.onInProgressMethodsChanged =
|
|
||||||
this.onInProgressMethodsChangedEventEmitter.event;
|
|
||||||
|
|
||||||
this.onProcessedByAutoModelMethodsChangedEventEmitter = this.push(
|
|
||||||
app.createEventEmitter<ProcessedByAutoModelMethodsChangedEvent>(),
|
|
||||||
);
|
|
||||||
this.onProcessedByAutoModelMethodsChanged =
|
|
||||||
this.onProcessedByAutoModelMethodsChangedEventEmitter.event;
|
|
||||||
|
|
||||||
this.onModelEvaluationRunChangedEventEmitter = this.push(
|
this.onModelEvaluationRunChangedEventEmitter = this.push(
|
||||||
app.createEventEmitter<ModelEvaluationRunChangedEvent>(),
|
app.createEventEmitter<ModelEvaluationRunChangedEvent>(),
|
||||||
);
|
);
|
||||||
@@ -254,8 +226,6 @@ export class ModelingEvents extends DisposableObject {
|
|||||||
usage: Usage,
|
usage: Usage,
|
||||||
modeledMethods: ModeledMethod[],
|
modeledMethods: ModeledMethod[],
|
||||||
isModified: boolean,
|
isModified: boolean,
|
||||||
isInProgress: boolean,
|
|
||||||
processedByAutoModel: boolean,
|
|
||||||
) {
|
) {
|
||||||
this.onSelectedMethodChangedEventEmitter.fire({
|
this.onSelectedMethodChangedEventEmitter.fire({
|
||||||
databaseItem,
|
databaseItem,
|
||||||
@@ -263,28 +233,6 @@ export class ModelingEvents extends DisposableObject {
|
|||||||
usage,
|
usage,
|
||||||
modeledMethods,
|
modeledMethods,
|
||||||
isModified,
|
isModified,
|
||||||
isInProgress,
|
|
||||||
processedByAutoModel,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public fireInProgressMethodsChangedEvent(
|
|
||||||
dbUri: string,
|
|
||||||
methods: ReadonlySet<string>,
|
|
||||||
) {
|
|
||||||
this.onInProgressMethodsChangedEventEmitter.fire({
|
|
||||||
dbUri,
|
|
||||||
methods,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public fireProcessedByAutoModelMethodsChangedEvent(
|
|
||||||
dbUri: string,
|
|
||||||
methods: ReadonlySet<string>,
|
|
||||||
) {
|
|
||||||
this.onProcessedByAutoModelMethodsChangedEventEmitter.fire({
|
|
||||||
dbUri,
|
|
||||||
methods,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ interface InternalDbModelingState {
|
|||||||
mode: Mode;
|
mode: Mode;
|
||||||
modeledMethods: Record<string, ModeledMethod[]>;
|
modeledMethods: Record<string, ModeledMethod[]>;
|
||||||
modifiedMethodSignatures: Set<string>;
|
modifiedMethodSignatures: Set<string>;
|
||||||
inProgressMethods: Set<string>;
|
|
||||||
processedByAutoModelMethods: Set<string>;
|
|
||||||
selectedMethod: Method | undefined;
|
selectedMethod: Method | undefined;
|
||||||
selectedUsage: Usage | undefined;
|
selectedUsage: Usage | undefined;
|
||||||
modelEvaluationRun: ModelEvaluationRun | undefined;
|
modelEvaluationRun: ModelEvaluationRun | undefined;
|
||||||
@@ -30,8 +28,6 @@ export interface DbModelingState {
|
|||||||
readonly mode: Mode;
|
readonly mode: Mode;
|
||||||
readonly modeledMethods: Readonly<Record<string, readonly ModeledMethod[]>>;
|
readonly modeledMethods: Readonly<Record<string, readonly ModeledMethod[]>>;
|
||||||
readonly modifiedMethodSignatures: ReadonlySet<string>;
|
readonly modifiedMethodSignatures: ReadonlySet<string>;
|
||||||
readonly inProgressMethods: ReadonlySet<string>;
|
|
||||||
readonly processedByAutoModelMethods: ReadonlySet<string>;
|
|
||||||
readonly selectedMethod: Method | undefined;
|
readonly selectedMethod: Method | undefined;
|
||||||
readonly selectedUsage: Usage | undefined;
|
readonly selectedUsage: Usage | undefined;
|
||||||
readonly modelEvaluationRun: ModelEvaluationRun | undefined;
|
readonly modelEvaluationRun: ModelEvaluationRun | undefined;
|
||||||
@@ -44,8 +40,6 @@ export interface SelectedMethodDetails {
|
|||||||
readonly usage: Usage | undefined;
|
readonly usage: Usage | undefined;
|
||||||
readonly modeledMethods: readonly ModeledMethod[];
|
readonly modeledMethods: readonly ModeledMethod[];
|
||||||
readonly isModified: boolean;
|
readonly isModified: boolean;
|
||||||
readonly isInProgress: boolean;
|
|
||||||
readonly processedByAutoModel: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ModelingStore extends DisposableObject {
|
export class ModelingStore extends DisposableObject {
|
||||||
@@ -68,10 +62,8 @@ export class ModelingStore extends DisposableObject {
|
|||||||
mode,
|
mode,
|
||||||
modeledMethods: {},
|
modeledMethods: {},
|
||||||
modifiedMethodSignatures: new Set(),
|
modifiedMethodSignatures: new Set(),
|
||||||
processedByAutoModelMethods: new Set(),
|
|
||||||
selectedMethod: undefined,
|
selectedMethod: undefined,
|
||||||
selectedUsage: undefined,
|
selectedUsage: undefined,
|
||||||
inProgressMethods: new Set(),
|
|
||||||
modelEvaluationRun: undefined,
|
modelEvaluationRun: undefined,
|
||||||
isModelAlertsViewOpen: false,
|
isModelAlertsViewOpen: false,
|
||||||
});
|
});
|
||||||
@@ -160,7 +152,6 @@ export class ModelingStore extends DisposableObject {
|
|||||||
methods,
|
methods,
|
||||||
state.modeledMethods,
|
state.modeledMethods,
|
||||||
state.modifiedMethodSignatures,
|
state.modifiedMethodSignatures,
|
||||||
state.processedByAutoModelMethods,
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -171,7 +162,6 @@ export class ModelingStore extends DisposableObject {
|
|||||||
state.methods,
|
state.methods,
|
||||||
state.modeledMethods,
|
state.modeledMethods,
|
||||||
state.modifiedMethodSignatures,
|
state.modifiedMethodSignatures,
|
||||||
state.processedByAutoModelMethods,
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -308,75 +298,15 @@ export class ModelingStore extends DisposableObject {
|
|||||||
|
|
||||||
const modeledMethods = dbState.modeledMethods[method.signature] ?? [];
|
const modeledMethods = dbState.modeledMethods[method.signature] ?? [];
|
||||||
const isModified = dbState.modifiedMethodSignatures.has(method.signature);
|
const isModified = dbState.modifiedMethodSignatures.has(method.signature);
|
||||||
const isInProgress = dbState.inProgressMethods.has(method.signature);
|
|
||||||
const processedByAutoModel = dbState.processedByAutoModelMethods.has(
|
|
||||||
method.signature,
|
|
||||||
);
|
|
||||||
this.modelingEvents.fireSelectedMethodChangedEvent(
|
this.modelingEvents.fireSelectedMethodChangedEvent(
|
||||||
dbItem,
|
dbItem,
|
||||||
method,
|
method,
|
||||||
usage,
|
usage,
|
||||||
modeledMethods,
|
modeledMethods,
|
||||||
isModified,
|
isModified,
|
||||||
isInProgress,
|
|
||||||
processedByAutoModel,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public addInProgressMethods(
|
|
||||||
dbItem: DatabaseItem,
|
|
||||||
inProgressMethods: string[],
|
|
||||||
) {
|
|
||||||
this.changeInProgressMethods(dbItem, (state) => {
|
|
||||||
state.inProgressMethods = new Set([
|
|
||||||
...state.inProgressMethods,
|
|
||||||
...inProgressMethods,
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public removeInProgressMethods(
|
|
||||||
dbItem: DatabaseItem,
|
|
||||||
methodSignatures: string[],
|
|
||||||
) {
|
|
||||||
this.changeInProgressMethods(dbItem, (state) => {
|
|
||||||
state.inProgressMethods = new Set(
|
|
||||||
Array.from(state.inProgressMethods).filter(
|
|
||||||
(s) => !methodSignatures.includes(s),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public getProcessedByAutoModelMethods(
|
|
||||||
dbItem: DatabaseItem,
|
|
||||||
methodSignatures?: string[],
|
|
||||||
): Set<string> {
|
|
||||||
const processedByAutoModelMethods =
|
|
||||||
this.getState(dbItem).processedByAutoModelMethods;
|
|
||||||
if (!methodSignatures) {
|
|
||||||
return processedByAutoModelMethods;
|
|
||||||
}
|
|
||||||
return new Set(
|
|
||||||
Array.from(processedByAutoModelMethods).filter((x) =>
|
|
||||||
methodSignatures.includes(x),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public addProcessedByAutoModelMethods(
|
|
||||||
dbItem: DatabaseItem,
|
|
||||||
processedByAutoModelMethods: string[],
|
|
||||||
) {
|
|
||||||
this.changeProcessedByAutoModelMethods(dbItem, (state) => {
|
|
||||||
state.processedByAutoModelMethods = new Set([
|
|
||||||
...state.processedByAutoModelMethods,
|
|
||||||
...processedByAutoModelMethods,
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
this.updateMethodSorting(dbItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
public updateModelEvaluationRun(
|
public updateModelEvaluationRun(
|
||||||
dbItem: DatabaseItem,
|
dbItem: DatabaseItem,
|
||||||
evaluationRun: ModelEvaluationRun | undefined,
|
evaluationRun: ModelEvaluationRun | undefined,
|
||||||
@@ -405,10 +335,6 @@ export class ModelingStore extends DisposableObject {
|
|||||||
isModified: dbState.modifiedMethodSignatures.has(
|
isModified: dbState.modifiedMethodSignatures.has(
|
||||||
selectedMethod.signature,
|
selectedMethod.signature,
|
||||||
),
|
),
|
||||||
isInProgress: dbState.inProgressMethods.has(selectedMethod.signature),
|
|
||||||
processedByAutoModel: dbState.processedByAutoModelMethods.has(
|
|
||||||
selectedMethod.signature,
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -460,34 +386,6 @@ export class ModelingStore extends DisposableObject {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private changeInProgressMethods(
|
|
||||||
dbItem: DatabaseItem,
|
|
||||||
updateState: (state: InternalDbModelingState) => void,
|
|
||||||
) {
|
|
||||||
const state = this.getState(dbItem);
|
|
||||||
|
|
||||||
updateState(state);
|
|
||||||
|
|
||||||
this.modelingEvents.fireInProgressMethodsChangedEvent(
|
|
||||||
dbItem.databaseUri.toString(),
|
|
||||||
state.inProgressMethods,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private changeProcessedByAutoModelMethods(
|
|
||||||
dbItem: DatabaseItem,
|
|
||||||
updateState: (state: InternalDbModelingState) => void,
|
|
||||||
) {
|
|
||||||
const state = this.getState(dbItem);
|
|
||||||
|
|
||||||
updateState(state);
|
|
||||||
|
|
||||||
this.modelingEvents.fireProcessedByAutoModelMethodsChangedEvent(
|
|
||||||
dbItem.databaseUri.toString(),
|
|
||||||
state.processedByAutoModelMethods,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private changeModelEvaluationRun(
|
private changeModelEvaluationRun(
|
||||||
dbItem: DatabaseItem,
|
dbItem: DatabaseItem,
|
||||||
updateState: (state: InternalDbModelingState) => void,
|
updateState: (state: InternalDbModelingState) => void,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export type Query = {
|
|||||||
* - libraryVersion: the version of the library that contains the external API. This is a string and can be empty if the version cannot be determined.
|
* - libraryVersion: the version of the library that contains the external API. This is a string and can be empty if the version cannot be determined.
|
||||||
* - type: the modeled kind of the method, either "sink", "source", "summary", or "neutral"
|
* - type: the modeled kind of the method, either "sink", "source", "summary", or "neutral"
|
||||||
* - classification: the classification of the use of the method, either "source", "test", "generated", or "unknown"
|
* - classification: the classification of the use of the method, either "source", "test", "generated", or "unknown"
|
||||||
|
* - kind: the kind of the endpoint, language-specific, e.g. "method" or "function"
|
||||||
*/
|
*/
|
||||||
applicationModeQuery: string;
|
applicationModeQuery: string;
|
||||||
/**
|
/**
|
||||||
@@ -32,6 +33,7 @@ export type Query = {
|
|||||||
* - supported: whether this method is modeled. This should be a string representation of a boolean to satify the result pattern for a problem query.
|
* - supported: whether this method is modeled. This should be a string representation of a boolean to satify the result pattern for a problem query.
|
||||||
* - libraryName: the name of the file or library that contains the method. This is a string and usually the basename of a file.
|
* - libraryName: the name of the file or library that contains the method. This is a string and usually the basename of a file.
|
||||||
* - type: the modeled kind of the method, either "sink", "source", "summary", or "neutral"
|
* - type: the modeled kind of the method, either "sink", "source", "summary", or "neutral"
|
||||||
|
* - kind: the kind of the endpoint, language-specific, e.g. "method" or "function"
|
||||||
*/
|
*/
|
||||||
frameworkModeQuery: string;
|
frameworkModeQuery: string;
|
||||||
dependencies?: {
|
dependencies?: {
|
||||||
@@ -50,6 +52,7 @@ export type ApplicationModeTuple = [
|
|||||||
string,
|
string,
|
||||||
ModeledMethodType,
|
ModeledMethodType,
|
||||||
CallClassification,
|
CallClassification,
|
||||||
|
string | BqrsEntityValue | undefined,
|
||||||
];
|
];
|
||||||
|
|
||||||
export type FrameworkModeTuple = [
|
export type FrameworkModeTuple = [
|
||||||
@@ -61,4 +64,5 @@ export type FrameworkModeTuple = [
|
|||||||
boolean,
|
boolean,
|
||||||
string,
|
string,
|
||||||
ModeledMethodType,
|
ModeledMethodType,
|
||||||
|
string | BqrsEntityValue | undefined,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,48 +0,0 @@
|
|||||||
import type { Method, MethodSignature } from "../method";
|
|
||||||
import type { ModeledMethod } from "../modeled-method";
|
|
||||||
import type { Mode } from "./mode";
|
|
||||||
import { groupMethods, sortGroupNames } from "./sorting";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the candidates that the model should be run on. This includes limiting the number of
|
|
||||||
* candidates to the candidate limit and filtering out anything that is already modeled and respecting
|
|
||||||
* the order in the UI.
|
|
||||||
* @param mode Whether it is application or framework mode.
|
|
||||||
* @param methods all methods.
|
|
||||||
* @param modeledMethodsBySignature the currently modeled methods.
|
|
||||||
* @returns list of modeled methods that are candidates for modeling.
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function getCandidates(
|
|
||||||
mode: Mode,
|
|
||||||
methods: readonly Method[],
|
|
||||||
modeledMethodsBySignature: Record<string, readonly ModeledMethod[]>,
|
|
||||||
processedByAutoModelMethods: Set<string>,
|
|
||||||
): MethodSignature[] {
|
|
||||||
const candidateMethods = methods.filter((method) => {
|
|
||||||
// Filter out any methods already processed by auto-model
|
|
||||||
if (processedByAutoModelMethods.has(method.signature)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const modeledMethods: ModeledMethod[] = [
|
|
||||||
...(modeledMethodsBySignature[method.signature] ?? []),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Anything that is modeled is not a candidate
|
|
||||||
if (modeledMethods.some((m) => m.type !== "none")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A method that is supported is modeled outside of the model file, so it is not a candidate.
|
|
||||||
if (method.supported) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
// Sort the same way as the UI so we send the first ones listed in the UI first
|
|
||||||
const grouped = groupMethods(candidateMethods, mode);
|
|
||||||
return sortGroupNames(grouped).flatMap((name) => grouped[name]);
|
|
||||||
}
|
|
||||||
@@ -47,7 +47,6 @@ export function sortMethods(
|
|||||||
methods: readonly Method[],
|
methods: readonly Method[],
|
||||||
modeledMethodsMap: Record<string, readonly ModeledMethod[]>,
|
modeledMethodsMap: Record<string, readonly ModeledMethod[]>,
|
||||||
modifiedSignatures: ReadonlySet<string>,
|
modifiedSignatures: ReadonlySet<string>,
|
||||||
processedByAutoModelMethods: ReadonlySet<string>,
|
|
||||||
): Method[] {
|
): Method[] {
|
||||||
const sortedMethods = [...methods];
|
const sortedMethods = [...methods];
|
||||||
sortedMethods.sort((a, b) => {
|
sortedMethods.sort((a, b) => {
|
||||||
@@ -56,13 +55,11 @@ export function sortMethods(
|
|||||||
a,
|
a,
|
||||||
modeledMethodsMap[a.signature] ?? [],
|
modeledMethodsMap[a.signature] ?? [],
|
||||||
modifiedSignatures.has(a.signature),
|
modifiedSignatures.has(a.signature),
|
||||||
processedByAutoModelMethods.has(a.signature),
|
|
||||||
);
|
);
|
||||||
const methodBPrimarySortOrdinal = getMethodPrimarySortOrdinal(
|
const methodBPrimarySortOrdinal = getMethodPrimarySortOrdinal(
|
||||||
b,
|
b,
|
||||||
modeledMethodsMap[b.signature] ?? [],
|
modeledMethodsMap[b.signature] ?? [],
|
||||||
modifiedSignatures.has(b.signature),
|
modifiedSignatures.has(b.signature),
|
||||||
processedByAutoModelMethods.has(b.signature),
|
|
||||||
);
|
);
|
||||||
if (methodAPrimarySortOrdinal !== methodBPrimarySortOrdinal) {
|
if (methodAPrimarySortOrdinal !== methodBPrimarySortOrdinal) {
|
||||||
return methodAPrimarySortOrdinal - methodBPrimarySortOrdinal;
|
return methodAPrimarySortOrdinal - methodBPrimarySortOrdinal;
|
||||||
@@ -82,32 +79,25 @@ export function sortMethods(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Assigns numbers to the following classes of methods:
|
* Assigns numbers to the following classes of methods:
|
||||||
* - Unsaved positive AutoModel predictions => 0
|
* - Unsaved manual models + unmodeled methods => 0
|
||||||
* - Negative AutoModel predictions => 1
|
* - Saved models from this model pack (AutoModel and manual) => 1
|
||||||
* - Unsaved manual models + unmodeled methods => 2
|
* - Methods not modelable in this model pack => 2
|
||||||
* - Saved models from this model pack (AutoModel and manual) => 3
|
|
||||||
* - Methods not modelable in this model pack => 4
|
|
||||||
*/
|
*/
|
||||||
function getMethodPrimarySortOrdinal(
|
function getMethodPrimarySortOrdinal(
|
||||||
method: Method,
|
method: Method,
|
||||||
modeledMethods: readonly ModeledMethod[],
|
modeledMethods: readonly ModeledMethod[],
|
||||||
isUnsaved: boolean,
|
isUnsaved: boolean,
|
||||||
isProcessedByAutoModel: boolean,
|
|
||||||
): number {
|
): number {
|
||||||
const canBeModeled = canMethodBeModeled(method, modeledMethods, isUnsaved);
|
const canBeModeled = canMethodBeModeled(method, modeledMethods, isUnsaved);
|
||||||
const isModeled = modeledMethods.length > 0;
|
const isModeled = modeledMethods.length > 0;
|
||||||
if (canBeModeled) {
|
if (canBeModeled) {
|
||||||
if (isModeled && isUnsaved && isProcessedByAutoModel) {
|
if ((isModeled && isUnsaved) || !isModeled) {
|
||||||
return 0;
|
return 0;
|
||||||
} else if (!isModeled && isProcessedByAutoModel) {
|
|
||||||
return 1;
|
|
||||||
} else if ((isModeled && isUnsaved) || !isModeled) {
|
|
||||||
return 2;
|
|
||||||
} else {
|
} else {
|
||||||
return 3;
|
return 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return 4;
|
return 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ export interface ModelEditorViewState {
|
|||||||
extensionPack: ExtensionPack;
|
extensionPack: ExtensionPack;
|
||||||
language: QueryLanguage;
|
language: QueryLanguage;
|
||||||
showGenerateButton: boolean;
|
showGenerateButton: boolean;
|
||||||
showLlmButton: boolean;
|
|
||||||
showEvaluationUi: boolean;
|
showEvaluationUi: boolean;
|
||||||
mode: Mode;
|
mode: Mode;
|
||||||
showModeSwitchButton: boolean;
|
showModeSwitchButton: boolean;
|
||||||
|
|||||||
@@ -9,20 +9,16 @@ export const SUPPORTED_LANGUAGES: QueryLanguage[] = [
|
|||||||
QueryLanguage.Java,
|
QueryLanguage.Java,
|
||||||
QueryLanguage.CSharp,
|
QueryLanguage.CSharp,
|
||||||
QueryLanguage.Ruby,
|
QueryLanguage.Ruby,
|
||||||
|
QueryLanguage.Python,
|
||||||
];
|
];
|
||||||
|
|
||||||
export function isSupportedLanguage(
|
export function isSupportedLanguage(
|
||||||
language: QueryLanguage,
|
language: QueryLanguage,
|
||||||
modelConfig: ModelConfig,
|
_modelConfig: ModelConfig,
|
||||||
) {
|
) {
|
||||||
if (SUPPORTED_LANGUAGES.includes(language)) {
|
if (SUPPORTED_LANGUAGES.includes(language)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (language === QueryLanguage.Python) {
|
|
||||||
// Python is only enabled when the config setting is set
|
|
||||||
return modelConfig.enablePython;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -845,7 +845,7 @@ export class QueryHistoryManager extends DisposableObject {
|
|||||||
evalLogData,
|
evalLogData,
|
||||||
);
|
);
|
||||||
this.evalLogViewer.updateRoots(await evalLogTreeBuilder.getRoots());
|
this.evalLogViewer.updateRoots(await evalLogTreeBuilder.getRoots());
|
||||||
} catch (e) {
|
} catch {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Could not read evaluator log summary JSON file to generate viewer data at ${item.jsonEvalLogSummaryLocation}.`,
|
`Could not read evaluator log summary JSON file to generate viewer data at ${item.jsonEvalLogSummaryLocation}.`,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export class TestRunner extends DisposableObject {
|
|||||||
})) {
|
})) {
|
||||||
await eventHandler(event);
|
await eventHandler(event);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch {
|
||||||
// CodeQL testing can throw exception even in normal scenarios. For example, if the test run
|
// CodeQL testing can throw exception even in normal scenarios. For example, if the test run
|
||||||
// produces no output (which is normal), the testing command would throw an exception on
|
// produces no output (which is normal), the testing command would throw an exception on
|
||||||
// unexpected EOF during json parsing. So nothing needs to be done here - all the relevant
|
// unexpected EOF during json parsing. So nothing needs to be done here - all the relevant
|
||||||
|
|||||||
@@ -544,9 +544,16 @@ export async function generateEvalLogSummaries(
|
|||||||
await cliServer.generateJsonLogSummary(log, jsonSummary);
|
await cliServer.generateJsonLogSummary(log, jsonSummary);
|
||||||
|
|
||||||
if (humanReadableSummary !== undefined) {
|
if (humanReadableSummary !== undefined) {
|
||||||
progress(progressUpdate(3, 3, "Generating summary symbols file"));
|
|
||||||
summarySymbols = outputDir.evalLogSummarySymbolsPath;
|
summarySymbols = outputDir.evalLogSummarySymbolsPath;
|
||||||
await generateSummarySymbolsFile(humanReadableSummary, summarySymbols);
|
if (
|
||||||
|
!(await cliServer.cliConstraints.supportsGenerateSummarySymbolMap())
|
||||||
|
) {
|
||||||
|
// We're using an old CLI that cannot generate the summary symbols file while generating the
|
||||||
|
// human-readable log summary. As a fallback, create it by parsing the human-readable
|
||||||
|
// summary.
|
||||||
|
progress(progressUpdate(3, 3, "Generating summary symbols file"));
|
||||||
|
await generateSummarySymbolsFile(humanReadableSummary, summarySymbols);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -600,7 +607,7 @@ export async function logEndSummary(
|
|||||||
const endSummaryContent = await readFile(endSummary, "utf-8");
|
const endSummaryContent = await readFile(endSummary, "utf-8");
|
||||||
void logger.log(" --- Evaluator Log Summary --- ");
|
void logger.log(" --- Evaluator Log Summary --- ");
|
||||||
void logger.log(endSummaryContent);
|
void logger.log(endSummaryContent);
|
||||||
} catch (e) {
|
} catch {
|
||||||
void showAndLogWarningMessage(
|
void showAndLogWarningMessage(
|
||||||
extLogger,
|
extLogger,
|
||||||
`Could not read structured evaluator log end of summary file at ${endSummary}.`,
|
`Could not read structured evaluator log end of summary file at ${endSummary}.`,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export default {
|
|||||||
component: ResponsiveContainerComponent,
|
component: ResponsiveContainerComponent,
|
||||||
} as Meta<typeof ResponsiveContainerComponent>;
|
} as Meta<typeof ResponsiveContainerComponent>;
|
||||||
|
|
||||||
const Template: StoryFn<typeof ResponsiveContainerComponent> = (args) => (
|
const Template: StoryFn<typeof ResponsiveContainerComponent> = () => (
|
||||||
<ResponsiveContainerComponent>
|
<ResponsiveContainerComponent>
|
||||||
<span>Hello</span>
|
<span>Hello</span>
|
||||||
</ResponsiveContainerComponent>
|
</ResponsiveContainerComponent>
|
||||||
|
|||||||
@@ -53,20 +53,3 @@ FullyModeledMethod.args = {
|
|||||||
method,
|
method,
|
||||||
modeledMethod,
|
modeledMethod,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ModelingInProgress = Template.bind({});
|
|
||||||
ModelingInProgress.args = {
|
|
||||||
method,
|
|
||||||
modeledMethod,
|
|
||||||
isModelingInProgress: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const generatedModeledMethod = createSinkModeledMethod({
|
|
||||||
provenance: "ai-generated",
|
|
||||||
});
|
|
||||||
export const ModelingNotAccepted = Template.bind({});
|
|
||||||
ModelingNotAccepted.args = {
|
|
||||||
method,
|
|
||||||
modeledMethod: generatedModeledMethod,
|
|
||||||
modelPending: true,
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
import type { Meta, StoryFn } from "@storybook/react";
|
|
||||||
|
|
||||||
import { InProgressDropdown as InProgressDropdownComponent } from "../../view/model-editor/InProgressDropdown";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: "CodeQL Model Editor/In Progress Dropdown",
|
|
||||||
component: InProgressDropdownComponent,
|
|
||||||
} as Meta<typeof InProgressDropdownComponent>;
|
|
||||||
|
|
||||||
const Template: StoryFn<typeof InProgressDropdownComponent> = (args) => (
|
|
||||||
<InProgressDropdownComponent />
|
|
||||||
);
|
|
||||||
|
|
||||||
export const InProgressDropdown = Template.bind({});
|
|
||||||
@@ -220,10 +220,8 @@ LibraryRow.args = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
modifiedSignatures: new Set(["org.sql2o.Sql2o#Sql2o(String)"]),
|
modifiedSignatures: new Set(["org.sql2o.Sql2o#Sql2o(String)"]),
|
||||||
inProgressMethods: new Set(),
|
|
||||||
viewState: createMockModelEditorViewState({
|
viewState: createMockModelEditorViewState({
|
||||||
showGenerateButton: true,
|
showGenerateButton: true,
|
||||||
showLlmButton: true,
|
|
||||||
}),
|
}),
|
||||||
hideModeledMethods: false,
|
hideModeledMethods: false,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ const modeledMethod: ModeledMethod = {
|
|||||||
|
|
||||||
const viewState = createMockModelEditorViewState({
|
const viewState = createMockModelEditorViewState({
|
||||||
showGenerateButton: true,
|
showGenerateButton: true,
|
||||||
showLlmButton: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const Unmodeled = Template.bind({});
|
export const Unmodeled = Template.bind({});
|
||||||
@@ -146,15 +145,6 @@ AlreadyModeled.args = {
|
|||||||
viewState,
|
viewState,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ModelingInProgress = Template.bind({});
|
|
||||||
ModelingInProgress.args = {
|
|
||||||
method,
|
|
||||||
modeledMethods: [modeledMethod],
|
|
||||||
modelingInProgress: true,
|
|
||||||
methodCanBeModeled: true,
|
|
||||||
viewState,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const MultipleModelings = Template.bind({});
|
export const MultipleModelings = Template.bind({});
|
||||||
MultipleModelings.args = {
|
MultipleModelings.args = {
|
||||||
method,
|
method,
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ ModelEditor.args = {
|
|||||||
dataExtensions: [],
|
dataExtensions: [],
|
||||||
},
|
},
|
||||||
showGenerateButton: true,
|
showGenerateButton: true,
|
||||||
showLlmButton: true,
|
|
||||||
}),
|
}),
|
||||||
initialMethods: [
|
initialMethods: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"target": "es6",
|
"target": "es2021",
|
||||||
"outDir": "out",
|
"outDir": "out",
|
||||||
"lib": ["ES2021", "dom"],
|
"lib": ["ES2021", "dom"],
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export async function readRepoStates(
|
|||||||
const repoStates = mapRepoStatesToDomainModel(repoStatesData);
|
const repoStates = mapRepoStatesToDomainModel(repoStatesData);
|
||||||
|
|
||||||
return repoStates;
|
return repoStates;
|
||||||
} catch (e) {
|
} catch {
|
||||||
// Ignore this error, we simply might not have downloaded anything yet
|
// Ignore this error, we simply might not have downloaded anything yet
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,15 +116,6 @@ async function generateQueryPack(
|
|||||||
|
|
||||||
let precompilationOpts: string[];
|
let precompilationOpts: string[];
|
||||||
if (cliSupportsMrvaPackCreate) {
|
if (cliSupportsMrvaPackCreate) {
|
||||||
if (
|
|
||||||
qlPackDetails.queryFiles.length > 1 &&
|
|
||||||
!(await cliServer.cliConstraints.supportsPackCreateWithMultipleQueries())
|
|
||||||
) {
|
|
||||||
throw new Error(
|
|
||||||
`Installed CLI version does not allow creating a MRVA pack with multiple queries`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const queryOpts = qlPackDetails.queryFiles.flatMap((q) => [
|
const queryOpts = qlPackDetails.queryFiles.flatMap((q) => [
|
||||||
"--query",
|
"--query",
|
||||||
join(targetPackPath, relative(qlPackDetails.qlPackRootPath, q)),
|
join(targetPackPath, relative(qlPackDetails.qlPackRootPath, q)),
|
||||||
|
|||||||
@@ -562,7 +562,7 @@ export class VariantAnalysisManager
|
|||||||
});
|
});
|
||||||
const doc = await workspace.openTextDocument(uri);
|
const doc = await workspace.openTextDocument(uri);
|
||||||
await Window.showTextDocument(doc, { preview: false });
|
await Window.showTextDocument(doc, { preview: false });
|
||||||
} catch (error) {
|
} catch {
|
||||||
void showAndLogWarningMessage(
|
void showAndLogWarningMessage(
|
||||||
this.app.logger,
|
this.app.logger,
|
||||||
"Could not open variant analysis query text. Failed to open text document.",
|
"Could not open variant analysis query text. Failed to open text document.",
|
||||||
@@ -586,7 +586,7 @@ export class VariantAnalysisManager
|
|||||||
variantAnalysis.query.filePath,
|
variantAnalysis.query.filePath,
|
||||||
);
|
);
|
||||||
await Window.showTextDocument(textDocument, ViewColumn.One);
|
await Window.showTextDocument(textDocument, ViewColumn.One);
|
||||||
} catch (error) {
|
} catch {
|
||||||
void showAndLogWarningMessage(
|
void showAndLogWarningMessage(
|
||||||
this.app.logger,
|
this.app.logger,
|
||||||
`Could not open file: ${variantAnalysis.query.filePath}`,
|
`Could not open file: ${variantAnalysis.query.filePath}`,
|
||||||
|
|||||||
@@ -99,6 +99,10 @@ export class VariantAnalysisResultsManager extends DisposableObject {
|
|||||||
responseSize = response.size;
|
responseSize = response.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!response.body) {
|
||||||
|
throw new Error("No response body found");
|
||||||
|
}
|
||||||
|
|
||||||
let amountDownloaded = 0;
|
let amountDownloaded = 0;
|
||||||
for await (const chunk of response.body) {
|
for await (const chunk of response.body) {
|
||||||
await appendFile(zipFilePath, Buffer.from(chunk));
|
await appendFile(zipFilePath, Buffer.from(chunk));
|
||||||
|
|||||||
@@ -12,6 +12,18 @@ const ShowPathsLink = styled(VSCodeLink)`
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const Label = styled.span`
|
||||||
|
color: var(--vscode-descriptionForeground);
|
||||||
|
margin-left: 10px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
function getShortestPathLength(codeFlows: CodeFlow[]): number {
|
||||||
|
const allPathLengths = codeFlows
|
||||||
|
.map((codeFlow) => codeFlow.threadFlows.length)
|
||||||
|
.flat();
|
||||||
|
return Math.min(...allPathLengths);
|
||||||
|
}
|
||||||
|
|
||||||
export type CodePathsProps = {
|
export type CodePathsProps = {
|
||||||
codeFlows: CodeFlow[];
|
codeFlows: CodeFlow[];
|
||||||
ruleDescription: string;
|
ruleDescription: string;
|
||||||
@@ -40,6 +52,7 @@ export const CodePaths = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ShowPathsLink onClick={onShowPathsClick}>Show paths</ShowPathsLink>
|
<ShowPathsLink onClick={onShowPathsClick}>Show paths</ShowPathsLink>
|
||||||
|
<Label>(Shortest: {getShortestPathLength(codeFlows)})</Label>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ describe(CodePaths.name, () => {
|
|||||||
expect(screen.getByText("Show paths")).toBeInTheDocument();
|
expect(screen.getByText("Show paths")).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders shortest path for code flows", () => {
|
||||||
|
render();
|
||||||
|
|
||||||
|
expect(screen.getByText("(Shortest: 1)")).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
it("posts extension message when 'show paths' link clicked", async () => {
|
it("posts extension message when 'show paths' link clicked", async () => {
|
||||||
render();
|
render();
|
||||||
|
|
||||||
|
|||||||
@@ -64,23 +64,35 @@ type Props = {
|
|||||||
export const SuggestBoxItem = forwardRef<
|
export const SuggestBoxItem = forwardRef<
|
||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
Props & HTMLProps<HTMLDivElement>
|
Props & HTMLProps<HTMLDivElement>
|
||||||
>(({ children, active, icon, labelText, details, ...props }, ref) => {
|
>(
|
||||||
const id = useId();
|
(
|
||||||
return (
|
{
|
||||||
<Container
|
children,
|
||||||
ref={ref}
|
active,
|
||||||
role="option"
|
icon,
|
||||||
id={id}
|
labelText,
|
||||||
aria-selected={active}
|
details,
|
||||||
$active={active}
|
...props
|
||||||
{...props}
|
}: Props & HTMLProps<HTMLDivElement>,
|
||||||
>
|
ref,
|
||||||
{icon}
|
) => {
|
||||||
<LabelContainer>
|
const id = useId();
|
||||||
<Label>{labelText}</Label>
|
return (
|
||||||
{details && <DetailsLabel>{details}</DetailsLabel>}
|
<Container
|
||||||
</LabelContainer>
|
ref={ref}
|
||||||
</Container>
|
role="option"
|
||||||
);
|
id={id}
|
||||||
});
|
aria-selected={active}
|
||||||
|
$active={active}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
|
<LabelContainer>
|
||||||
|
<Label>{labelText}</Label>
|
||||||
|
{details && <DetailsLabel>{details}</DetailsLabel>}
|
||||||
|
</LabelContainer>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
SuggestBoxItem.displayName = "SuggestBoxItem";
|
SuggestBoxItem.displayName = "SuggestBoxItem";
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import type {
|
|||||||
ToCompareViewMessage,
|
ToCompareViewMessage,
|
||||||
SetComparisonsMessage,
|
SetComparisonsMessage,
|
||||||
SetComparisonQueryInfoMessage,
|
SetComparisonQueryInfoMessage,
|
||||||
|
UserSettings,
|
||||||
} from "../../common/interface-types";
|
} from "../../common/interface-types";
|
||||||
|
import { DEFAULT_USER_SETTINGS } from "../../common/interface-types";
|
||||||
import CompareSelector from "./CompareSelector";
|
import CompareSelector from "./CompareSelector";
|
||||||
import { vscode } from "../vscode-api";
|
import { vscode } from "../vscode-api";
|
||||||
import CompareTable from "./CompareTable";
|
import CompareTable from "./CompareTable";
|
||||||
@@ -31,6 +33,9 @@ export function Compare(_: Record<string, never>): React.JSX.Element {
|
|||||||
const [comparison, setComparison] = useState<SetComparisonsMessage | null>(
|
const [comparison, setComparison] = useState<SetComparisonsMessage | null>(
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
const [userSettings, setUserSettings] = useState<UserSettings>(
|
||||||
|
DEFAULT_USER_SETTINGS,
|
||||||
|
);
|
||||||
|
|
||||||
const message = comparison?.message || "Empty comparison";
|
const message = comparison?.message || "Empty comparison";
|
||||||
const hasRows =
|
const hasRows =
|
||||||
@@ -48,6 +53,9 @@ export function Compare(_: Record<string, never>): React.JSX.Element {
|
|||||||
case "setComparisons":
|
case "setComparisons":
|
||||||
setComparison(msg);
|
setComparison(msg);
|
||||||
break;
|
break;
|
||||||
|
case "setUserSettings":
|
||||||
|
setUserSettings(msg.userSettings);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assertNever(msg);
|
assertNever(msg);
|
||||||
}
|
}
|
||||||
@@ -85,6 +93,7 @@ export function Compare(_: Record<string, never>): React.JSX.Element {
|
|||||||
<CompareTable
|
<CompareTable
|
||||||
queryInfo={queryInfo}
|
queryInfo={queryInfo}
|
||||||
comparison={comparison}
|
comparison={comparison}
|
||||||
|
userSettings={userSettings}
|
||||||
></CompareTable>
|
></CompareTable>
|
||||||
) : (
|
) : (
|
||||||
<Message>{message}</Message>
|
<Message>{message}</Message>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type {
|
import type {
|
||||||
SetComparisonQueryInfoMessage,
|
SetComparisonQueryInfoMessage,
|
||||||
SetComparisonsMessage,
|
SetComparisonsMessage,
|
||||||
|
UserSettings,
|
||||||
} from "../../common/interface-types";
|
} from "../../common/interface-types";
|
||||||
import { className } from "../results/result-table-utils";
|
import { className } from "../results/result-table-utils";
|
||||||
import { vscode } from "../vscode-api";
|
import { vscode } from "../vscode-api";
|
||||||
@@ -12,6 +13,7 @@ import { InterpretedCompareResultTable } from "./InterpretedCompareResultTable";
|
|||||||
interface Props {
|
interface Props {
|
||||||
queryInfo: SetComparisonQueryInfoMessage;
|
queryInfo: SetComparisonQueryInfoMessage;
|
||||||
comparison: SetComparisonsMessage;
|
comparison: SetComparisonsMessage;
|
||||||
|
userSettings: UserSettings;
|
||||||
}
|
}
|
||||||
|
|
||||||
const OpenButton = styled(TextButton)`
|
const OpenButton = styled(TextButton)`
|
||||||
@@ -29,7 +31,11 @@ const Table = styled.table`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default function CompareTable({ queryInfo, comparison }: Props) {
|
export default function CompareTable({
|
||||||
|
queryInfo,
|
||||||
|
comparison,
|
||||||
|
userSettings,
|
||||||
|
}: Props) {
|
||||||
const result = comparison.result!;
|
const result = comparison.result!;
|
||||||
|
|
||||||
async function openQuery(kind: "from" | "to") {
|
async function openQuery(kind: "from" | "to") {
|
||||||
@@ -78,6 +84,7 @@ export default function CompareTable({ queryInfo, comparison }: Props) {
|
|||||||
{result.kind === "interpreted" && (
|
{result.kind === "interpreted" && (
|
||||||
<InterpretedCompareResultTable
|
<InterpretedCompareResultTable
|
||||||
results={result.from}
|
results={result.from}
|
||||||
|
userSettings={userSettings}
|
||||||
databaseUri={queryInfo.databaseUri}
|
databaseUri={queryInfo.databaseUri}
|
||||||
sourceLocationPrefix={result.sourceLocationPrefix}
|
sourceLocationPrefix={result.sourceLocationPrefix}
|
||||||
/>
|
/>
|
||||||
@@ -96,6 +103,7 @@ export default function CompareTable({ queryInfo, comparison }: Props) {
|
|||||||
{result.kind === "interpreted" && (
|
{result.kind === "interpreted" && (
|
||||||
<InterpretedCompareResultTable
|
<InterpretedCompareResultTable
|
||||||
results={result.to}
|
results={result.to}
|
||||||
|
userSettings={userSettings}
|
||||||
databaseUri={queryInfo.databaseUri}
|
databaseUri={queryInfo.databaseUri}
|
||||||
sourceLocationPrefix={result.sourceLocationPrefix}
|
sourceLocationPrefix={result.sourceLocationPrefix}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,27 +1,32 @@
|
|||||||
import type { Result } from "sarif";
|
import type { Result, Run } from "sarif";
|
||||||
import { AlertTable } from "../results/AlertTable";
|
import { AlertTable } from "../results/AlertTable";
|
||||||
|
import type { UserSettings } from "../../common/interface-types";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
results: Result[];
|
results: Result[];
|
||||||
databaseUri: string;
|
databaseUri: string;
|
||||||
sourceLocationPrefix: string;
|
sourceLocationPrefix: string;
|
||||||
|
run?: Run;
|
||||||
|
userSettings: UserSettings;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const InterpretedCompareResultTable = ({
|
export const InterpretedCompareResultTable = ({
|
||||||
results,
|
results,
|
||||||
databaseUri,
|
databaseUri,
|
||||||
sourceLocationPrefix,
|
sourceLocationPrefix,
|
||||||
|
userSettings,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
return (
|
return (
|
||||||
<AlertTable
|
<AlertTable
|
||||||
results={results}
|
results={results}
|
||||||
|
userSettings={userSettings}
|
||||||
databaseUri={databaseUri}
|
databaseUri={databaseUri}
|
||||||
sourceLocationPrefix={sourceLocationPrefix}
|
sourceLocationPrefix={sourceLocationPrefix}
|
||||||
header={
|
header={
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th colSpan={2}></th>
|
<th colSpan={2}></th>
|
||||||
<th className={`vscode-codeql__alert-message-cell`} colSpan={3}>
|
<th className={`vscode-codeql__alert-message-cell`} colSpan={4}>
|
||||||
Message
|
Message
|
||||||
</th>
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { Config } from "jest";
|
import type { Config } from "jest";
|
||||||
|
import { transformIgnorePatterns } from "../../test/jest-config";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For a detailed explanation regarding each configuration property and type check, visit:
|
* For a detailed explanation regarding each configuration property and type check, visit:
|
||||||
@@ -184,10 +185,7 @@ const config: Config = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||||
transformIgnorePatterns: [
|
transformIgnorePatterns,
|
||||||
// These use ES modules, so need to be transformed
|
|
||||||
"node_modules/(?!(?:@vscode/webview-ui-toolkit|@microsoft/.+|exenv-es6|d3|d3-(.*)|internmap|delaunator|robust-predicates)/.*)",
|
|
||||||
],
|
|
||||||
|
|
||||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||||
// unmockedModulePathPatterns: undefined,
|
// unmockedModulePathPatterns: undefined,
|
||||||
|
|||||||
@@ -55,8 +55,6 @@ export type MethodModelingProps = {
|
|||||||
modelingStatus: ModelingStatus;
|
modelingStatus: ModelingStatus;
|
||||||
method: Method;
|
method: Method;
|
||||||
modeledMethods: ModeledMethod[];
|
modeledMethods: ModeledMethod[];
|
||||||
isModelingInProgress: boolean;
|
|
||||||
isProcessedByAutoModel: boolean;
|
|
||||||
onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void;
|
onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -66,8 +64,6 @@ export const MethodModeling = ({
|
|||||||
modelingStatus,
|
modelingStatus,
|
||||||
modeledMethods,
|
modeledMethods,
|
||||||
method,
|
method,
|
||||||
isModelingInProgress,
|
|
||||||
isProcessedByAutoModel,
|
|
||||||
onChange,
|
onChange,
|
||||||
}: MethodModelingProps): React.JSX.Element => {
|
}: MethodModelingProps): React.JSX.Element => {
|
||||||
return (
|
return (
|
||||||
@@ -86,9 +82,6 @@ export const MethodModeling = ({
|
|||||||
modelConfig={modelConfig}
|
modelConfig={modelConfig}
|
||||||
method={method}
|
method={method}
|
||||||
modeledMethods={modeledMethods}
|
modeledMethods={modeledMethods}
|
||||||
isModelingInProgress={isModelingInProgress}
|
|
||||||
isProcessedByAutoModel={isProcessedByAutoModel}
|
|
||||||
modelingStatus={modelingStatus}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
<ReviewInEditorButton method={method} />
|
<ReviewInEditorButton method={method} />
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { ModelTypeDropdown } from "../model-editor/ModelTypeDropdown";
|
|||||||
import { ModelInputDropdown } from "../model-editor/ModelInputDropdown";
|
import { ModelInputDropdown } from "../model-editor/ModelInputDropdown";
|
||||||
import { ModelOutputDropdown } from "../model-editor/ModelOutputDropdown";
|
import { ModelOutputDropdown } from "../model-editor/ModelOutputDropdown";
|
||||||
import { ModelKindDropdown } from "../model-editor/ModelKindDropdown";
|
import { ModelKindDropdown } from "../model-editor/ModelKindDropdown";
|
||||||
import { InProgressDropdown } from "../model-editor/InProgressDropdown";
|
|
||||||
import type { QueryLanguage } from "../../common/query-language";
|
import type { QueryLanguage } from "../../common/query-language";
|
||||||
import type { ModelConfig } from "../../model-editor/languages";
|
import type { ModelConfig } from "../../model-editor/languages";
|
||||||
|
|
||||||
@@ -28,8 +27,6 @@ export type MethodModelingInputsProps = {
|
|||||||
modelConfig: ModelConfig;
|
modelConfig: ModelConfig;
|
||||||
method: Method;
|
method: Method;
|
||||||
modeledMethod: ModeledMethod | undefined;
|
modeledMethod: ModeledMethod | undefined;
|
||||||
modelPending: boolean;
|
|
||||||
isModelingInProgress: boolean;
|
|
||||||
onChange: (modeledMethod: ModeledMethod) => void;
|
onChange: (modeledMethod: ModeledMethod) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -38,15 +35,12 @@ export const MethodModelingInputs = ({
|
|||||||
modelConfig,
|
modelConfig,
|
||||||
method,
|
method,
|
||||||
modeledMethod,
|
modeledMethod,
|
||||||
modelPending,
|
|
||||||
isModelingInProgress,
|
|
||||||
onChange,
|
onChange,
|
||||||
}: MethodModelingInputsProps): React.JSX.Element => {
|
}: MethodModelingInputsProps): React.JSX.Element => {
|
||||||
const inputProps = {
|
const inputProps = {
|
||||||
language,
|
language,
|
||||||
method,
|
method,
|
||||||
modeledMethod,
|
modeledMethod,
|
||||||
modelPending,
|
|
||||||
onChange,
|
onChange,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -55,41 +49,25 @@ export const MethodModelingInputs = ({
|
|||||||
<Container>
|
<Container>
|
||||||
<Input>
|
<Input>
|
||||||
<Name>Model Type</Name>
|
<Name>Model Type</Name>
|
||||||
{isModelingInProgress ? (
|
<ModelTypeDropdown modelConfig={modelConfig} {...inputProps} />
|
||||||
<InProgressDropdown />
|
|
||||||
) : (
|
|
||||||
<ModelTypeDropdown modelConfig={modelConfig} {...inputProps} />
|
|
||||||
)}
|
|
||||||
</Input>
|
</Input>
|
||||||
</Container>
|
</Container>
|
||||||
<Container>
|
<Container>
|
||||||
<Input>
|
<Input>
|
||||||
<Name>Input</Name>
|
<Name>Input</Name>
|
||||||
{isModelingInProgress ? (
|
<ModelInputDropdown {...inputProps} />
|
||||||
<InProgressDropdown />
|
|
||||||
) : (
|
|
||||||
<ModelInputDropdown {...inputProps} />
|
|
||||||
)}
|
|
||||||
</Input>
|
</Input>
|
||||||
</Container>
|
</Container>
|
||||||
<Container>
|
<Container>
|
||||||
<Input>
|
<Input>
|
||||||
<Name>Output</Name>
|
<Name>Output</Name>
|
||||||
{isModelingInProgress ? (
|
<ModelOutputDropdown {...inputProps} />
|
||||||
<InProgressDropdown />
|
|
||||||
) : (
|
|
||||||
<ModelOutputDropdown {...inputProps} />
|
|
||||||
)}
|
|
||||||
</Input>
|
</Input>
|
||||||
</Container>
|
</Container>
|
||||||
<Container>
|
<Container>
|
||||||
<Input>
|
<Input>
|
||||||
<Name>Kind</Name>
|
<Name>Kind</Name>
|
||||||
{isModelingInProgress ? (
|
<ModelKindDropdown {...inputProps} />
|
||||||
<InProgressDropdown />
|
|
||||||
) : (
|
|
||||||
<ModelKindDropdown {...inputProps} />
|
|
||||||
)}
|
|
||||||
</Input>
|
</Input>
|
||||||
</Container>
|
</Container>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -31,12 +31,6 @@ export function MethodModelingView({
|
|||||||
|
|
||||||
const [isMethodModified, setIsMethodModified] = useState<boolean>(false);
|
const [isMethodModified, setIsMethodModified] = useState<boolean>(false);
|
||||||
|
|
||||||
const [isModelingInProgress, setIsModelingInProgress] =
|
|
||||||
useState<boolean>(false);
|
|
||||||
|
|
||||||
const [isProcessedByAutoModel, setIsProcessedByAutoModel] =
|
|
||||||
useState<boolean>(false);
|
|
||||||
|
|
||||||
const modelingStatus = useMemo(
|
const modelingStatus = useMemo(
|
||||||
() => getModelingStatus(modeledMethods, isMethodModified),
|
() => getModelingStatus(modeledMethods, isMethodModified),
|
||||||
[modeledMethods, isMethodModified],
|
[modeledMethods, isMethodModified],
|
||||||
@@ -63,21 +57,11 @@ export function MethodModelingView({
|
|||||||
setMethod(undefined);
|
setMethod(undefined);
|
||||||
setModeledMethods([]);
|
setModeledMethods([]);
|
||||||
setIsMethodModified(false);
|
setIsMethodModified(false);
|
||||||
setIsModelingInProgress(false);
|
|
||||||
setIsProcessedByAutoModel(false);
|
|
||||||
break;
|
break;
|
||||||
case "setSelectedMethod":
|
case "setSelectedMethod":
|
||||||
setMethod(msg.method);
|
setMethod(msg.method);
|
||||||
setModeledMethods(msg.modeledMethods);
|
setModeledMethods(msg.modeledMethods);
|
||||||
setIsMethodModified(msg.isModified);
|
setIsMethodModified(msg.isModified);
|
||||||
setIsModelingInProgress(msg.isInProgress);
|
|
||||||
setIsProcessedByAutoModel(msg.processedByAutoModel);
|
|
||||||
break;
|
|
||||||
case "setInProgress":
|
|
||||||
setIsModelingInProgress(msg.inProgress);
|
|
||||||
break;
|
|
||||||
case "setProcessedByAutoModel":
|
|
||||||
setIsProcessedByAutoModel(msg.processedByAutoModel);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assertNever(msg);
|
assertNever(msg);
|
||||||
@@ -125,8 +109,6 @@ export function MethodModelingView({
|
|||||||
modelingStatus={modelingStatus}
|
modelingStatus={modelingStatus}
|
||||||
method={method}
|
method={method}
|
||||||
modeledMethods={modeledMethods}
|
modeledMethods={modeledMethods}
|
||||||
isModelingInProgress={isModelingInProgress}
|
|
||||||
isProcessedByAutoModel={isProcessedByAutoModel}
|
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import type { Method } from "../../model-editor/method";
|
import type { Method } from "../../model-editor/method";
|
||||||
import type { ModeledMethod } from "../../model-editor/modeled-method";
|
import type { ModeledMethod } from "../../model-editor/modeled-method";
|
||||||
import { isModelPending } from "../../model-editor/modeled-method";
|
|
||||||
import {
|
import {
|
||||||
canAddNewModeledMethod,
|
canAddNewModeledMethod,
|
||||||
canRemoveModeledMethod,
|
canRemoveModeledMethod,
|
||||||
@@ -15,7 +14,6 @@ import { ModeledMethodAlert } from "./ModeledMethodAlert";
|
|||||||
import type { QueryLanguage } from "../../common/query-language";
|
import type { QueryLanguage } from "../../common/query-language";
|
||||||
import { createEmptyModeledMethod } from "../../model-editor/modeled-method-empty";
|
import { createEmptyModeledMethod } from "../../model-editor/modeled-method-empty";
|
||||||
import { sendTelemetry } from "../common/telemetry";
|
import { sendTelemetry } from "../common/telemetry";
|
||||||
import type { ModelingStatus } from "../../model-editor/shared/modeling-status";
|
|
||||||
import type { ModelConfig } from "../../model-editor/languages";
|
import type { ModelConfig } from "../../model-editor/languages";
|
||||||
|
|
||||||
export type MultipleModeledMethodsPanelProps = {
|
export type MultipleModeledMethodsPanelProps = {
|
||||||
@@ -23,9 +21,6 @@ export type MultipleModeledMethodsPanelProps = {
|
|||||||
modelConfig: ModelConfig;
|
modelConfig: ModelConfig;
|
||||||
method: Method;
|
method: Method;
|
||||||
modeledMethods: ModeledMethod[];
|
modeledMethods: ModeledMethod[];
|
||||||
modelingStatus: ModelingStatus;
|
|
||||||
isModelingInProgress: boolean;
|
|
||||||
isProcessedByAutoModel: boolean;
|
|
||||||
onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void;
|
onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -66,9 +61,6 @@ export const MultipleModeledMethodsPanel = ({
|
|||||||
modelConfig,
|
modelConfig,
|
||||||
method,
|
method,
|
||||||
modeledMethods,
|
modeledMethods,
|
||||||
modelingStatus,
|
|
||||||
isModelingInProgress,
|
|
||||||
isProcessedByAutoModel,
|
|
||||||
onChange,
|
onChange,
|
||||||
}: MultipleModeledMethodsPanelProps) => {
|
}: MultipleModeledMethodsPanelProps) => {
|
||||||
const [selectedIndex, setSelectedIndex] = useState<number>(0);
|
const [selectedIndex, setSelectedIndex] = useState<number>(0);
|
||||||
@@ -163,12 +155,6 @@ export const MultipleModeledMethodsPanel = ({
|
|||||||
modelConfig={modelConfig}
|
modelConfig={modelConfig}
|
||||||
method={method}
|
method={method}
|
||||||
modeledMethod={modeledMethods[selectedIndex]}
|
modeledMethod={modeledMethods[selectedIndex]}
|
||||||
modelPending={isModelPending(
|
|
||||||
modeledMethods[selectedIndex],
|
|
||||||
modelingStatus,
|
|
||||||
isProcessedByAutoModel,
|
|
||||||
)}
|
|
||||||
isModelingInProgress={isModelingInProgress}
|
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
@@ -177,12 +163,6 @@ export const MultipleModeledMethodsPanel = ({
|
|||||||
modelConfig={modelConfig}
|
modelConfig={modelConfig}
|
||||||
method={method}
|
method={method}
|
||||||
modeledMethod={undefined}
|
modeledMethod={undefined}
|
||||||
modelPending={isModelPending(
|
|
||||||
modeledMethods[selectedIndex],
|
|
||||||
modelingStatus,
|
|
||||||
isProcessedByAutoModel,
|
|
||||||
)}
|
|
||||||
isModelingInProgress={isModelingInProgress}
|
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ describe(MethodModeling.name, () => {
|
|||||||
it("renders method modeling panel", () => {
|
it("renders method modeling panel", () => {
|
||||||
const method = createMethod();
|
const method = createMethod();
|
||||||
const modeledMethod = createSinkModeledMethod();
|
const modeledMethod = createSinkModeledMethod();
|
||||||
const isModelingInProgress = false;
|
|
||||||
const isProcessedByAutoModel = false;
|
|
||||||
const onChange = jest.fn();
|
const onChange = jest.fn();
|
||||||
|
|
||||||
render({
|
render({
|
||||||
@@ -23,8 +21,6 @@ describe(MethodModeling.name, () => {
|
|||||||
modelingStatus: "saved",
|
modelingStatus: "saved",
|
||||||
method,
|
method,
|
||||||
modeledMethods: [modeledMethod],
|
modeledMethods: [modeledMethod],
|
||||||
isModelingInProgress,
|
|
||||||
isProcessedByAutoModel,
|
|
||||||
onChange,
|
onChange,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,6 @@ describe(MethodModelingInputs.name, () => {
|
|||||||
const language = QueryLanguage.Java;
|
const language = QueryLanguage.Java;
|
||||||
const method = createMethod();
|
const method = createMethod();
|
||||||
const modeledMethod = createSinkModeledMethod();
|
const modeledMethod = createSinkModeledMethod();
|
||||||
const modelPending = false;
|
|
||||||
const isModelingInProgress = false;
|
|
||||||
const modelConfig = defaultModelConfig;
|
const modelConfig = defaultModelConfig;
|
||||||
const onChange = jest.fn();
|
const onChange = jest.fn();
|
||||||
|
|
||||||
@@ -28,8 +26,6 @@ describe(MethodModelingInputs.name, () => {
|
|||||||
language,
|
language,
|
||||||
method,
|
method,
|
||||||
modeledMethod,
|
modeledMethod,
|
||||||
modelPending,
|
|
||||||
isModelingInProgress,
|
|
||||||
modelConfig,
|
modelConfig,
|
||||||
onChange,
|
onChange,
|
||||||
});
|
});
|
||||||
@@ -56,8 +52,6 @@ describe(MethodModelingInputs.name, () => {
|
|||||||
language,
|
language,
|
||||||
method,
|
method,
|
||||||
modeledMethod,
|
modeledMethod,
|
||||||
modelPending,
|
|
||||||
isModelingInProgress,
|
|
||||||
modelConfig,
|
modelConfig,
|
||||||
onChange,
|
onChange,
|
||||||
});
|
});
|
||||||
@@ -80,8 +74,6 @@ describe(MethodModelingInputs.name, () => {
|
|||||||
language,
|
language,
|
||||||
method,
|
method,
|
||||||
modeledMethod,
|
modeledMethod,
|
||||||
modelPending,
|
|
||||||
isModelingInProgress,
|
|
||||||
modelConfig,
|
modelConfig,
|
||||||
onChange,
|
onChange,
|
||||||
});
|
});
|
||||||
@@ -96,8 +88,6 @@ describe(MethodModelingInputs.name, () => {
|
|||||||
language={language}
|
language={language}
|
||||||
method={method}
|
method={method}
|
||||||
modeledMethod={updatedModeledMethod}
|
modeledMethod={updatedModeledMethod}
|
||||||
modelPending={modelPending}
|
|
||||||
isModelingInProgress={isModelingInProgress}
|
|
||||||
modelConfig={modelConfig}
|
modelConfig={modelConfig}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
/>,
|
/>,
|
||||||
@@ -121,32 +111,4 @@ describe(MethodModelingInputs.name, () => {
|
|||||||
expect(modelOutputDropdown).toHaveValue("ReturnValue");
|
expect(modelOutputDropdown).toHaveValue("ReturnValue");
|
||||||
expect(modelKindDropdown).toHaveValue("local");
|
expect(modelKindDropdown).toHaveValue("local");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sets in progress dropdowns when modeling is in progress", () => {
|
|
||||||
render({
|
|
||||||
language,
|
|
||||||
method,
|
|
||||||
modeledMethod,
|
|
||||||
modelPending,
|
|
||||||
isModelingInProgress: true,
|
|
||||||
modelConfig,
|
|
||||||
onChange,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check that all the labels are rendered.
|
|
||||||
expect(screen.getByText("Model Type")).toBeInTheDocument();
|
|
||||||
expect(screen.getByText("Input")).toBeInTheDocument();
|
|
||||||
expect(screen.getByText("Output")).toBeInTheDocument();
|
|
||||||
expect(screen.getByText("Kind")).toBeInTheDocument();
|
|
||||||
|
|
||||||
// Check that all the dropdowns are rendered.
|
|
||||||
const dropdowns = screen.getAllByRole("combobox");
|
|
||||||
expect(dropdowns.length).toBe(4);
|
|
||||||
|
|
||||||
// Check that all the dropdowns are disabled and indicate have the value "Thinking...".
|
|
||||||
dropdowns.forEach((dropdown) => {
|
|
||||||
expect(dropdown).toBeDisabled();
|
|
||||||
expect(dropdown).toHaveValue("Thinking...");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -10,25 +10,18 @@ import { MultipleModeledMethodsPanel } from "../MultipleModeledMethodsPanel";
|
|||||||
import { userEvent } from "@testing-library/user-event";
|
import { userEvent } from "@testing-library/user-event";
|
||||||
import type { ModeledMethod } from "../../../model-editor/modeled-method";
|
import type { ModeledMethod } from "../../../model-editor/modeled-method";
|
||||||
import { QueryLanguage } from "../../../common/query-language";
|
import { QueryLanguage } from "../../../common/query-language";
|
||||||
import type { ModelingStatus } from "../../../model-editor/shared/modeling-status";
|
|
||||||
import { defaultModelConfig } from "../../../model-editor/languages";
|
import { defaultModelConfig } from "../../../model-editor/languages";
|
||||||
|
|
||||||
describe(MultipleModeledMethodsPanel.name, () => {
|
describe(MultipleModeledMethodsPanel.name, () => {
|
||||||
const language = QueryLanguage.Java;
|
const language = QueryLanguage.Java;
|
||||||
const method = createMethod();
|
const method = createMethod();
|
||||||
const isModelingInProgress = false;
|
|
||||||
const isProcessedByAutoModel = false;
|
|
||||||
const modelingStatus: ModelingStatus = "unmodeled";
|
|
||||||
const onChange = jest.fn<void, [string, ModeledMethod[]]>();
|
const onChange = jest.fn<void, [string, ModeledMethod[]]>();
|
||||||
const modelConfig = defaultModelConfig;
|
const modelConfig = defaultModelConfig;
|
||||||
|
|
||||||
const baseProps = {
|
const baseProps = {
|
||||||
language,
|
language,
|
||||||
method,
|
method,
|
||||||
modelingStatus,
|
|
||||||
isModelingInProgress,
|
|
||||||
modelConfig,
|
modelConfig,
|
||||||
isProcessedByAutoModel,
|
|
||||||
onChange,
|
onChange,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
import { styled } from "styled-components";
|
|
||||||
import { Dropdown } from "../common/Dropdown";
|
|
||||||
|
|
||||||
const StyledDropdown = styled(Dropdown)`
|
|
||||||
font-style: italic;
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const InProgressDropdown = () => {
|
|
||||||
return (
|
|
||||||
<StyledDropdown
|
|
||||||
value="Thinking..."
|
|
||||||
options={[]}
|
|
||||||
disabled={true}
|
|
||||||
disabledPlaceholder="Thinking..."
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
import { styled } from "styled-components";
|
import { styled } from "styled-components";
|
||||||
import { Dropdown } from "../common/Dropdown";
|
import { Dropdown } from "../common/Dropdown";
|
||||||
|
|
||||||
export const InputDropdown = styled(Dropdown)<{ $pending: boolean }>`
|
export const InputDropdown = styled(Dropdown)``;
|
||||||
font-style: ${(props) => (props.$pending ? "italic" : "normal")};
|
|
||||||
`;
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import {
|
|||||||
} from "@vscode/webview-ui-toolkit/react";
|
} from "@vscode/webview-ui-toolkit/react";
|
||||||
import type { ModelEditorViewState } from "../../model-editor/shared/view-state";
|
import type { ModelEditorViewState } from "../../model-editor/shared/view-state";
|
||||||
import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions";
|
import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions";
|
||||||
import { getCandidates } from "../../model-editor/shared/auto-model-candidates";
|
|
||||||
import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state";
|
import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state";
|
||||||
|
|
||||||
const LibraryContainer = styled.div`
|
const LibraryContainer = styled.div`
|
||||||
@@ -75,8 +74,6 @@ export type LibraryRowProps = {
|
|||||||
modeledMethodsMap: Record<string, ModeledMethod[]>;
|
modeledMethodsMap: Record<string, ModeledMethod[]>;
|
||||||
modifiedSignatures: Set<string>;
|
modifiedSignatures: Set<string>;
|
||||||
selectedSignatures: Set<string>;
|
selectedSignatures: Set<string>;
|
||||||
inProgressMethods: Set<string>;
|
|
||||||
processedByAutoModelMethods: Set<string>;
|
|
||||||
viewState: ModelEditorViewState;
|
viewState: ModelEditorViewState;
|
||||||
hideModeledMethods: boolean;
|
hideModeledMethods: boolean;
|
||||||
revealedMethodSignature: string | null;
|
revealedMethodSignature: string | null;
|
||||||
@@ -85,11 +82,6 @@ export type LibraryRowProps = {
|
|||||||
onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void;
|
onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void;
|
||||||
onMethodClick: (methodSignature: string) => void;
|
onMethodClick: (methodSignature: string) => void;
|
||||||
onSaveModelClick: (methodSignatures: string[]) => void;
|
onSaveModelClick: (methodSignatures: string[]) => void;
|
||||||
onGenerateFromLlmClick: (
|
|
||||||
dependencyName: string,
|
|
||||||
methodSignatures: string[],
|
|
||||||
) => void;
|
|
||||||
onStopGenerateFromLlmClick: (dependencyName: string) => void;
|
|
||||||
onGenerateFromSourceClick: () => void;
|
onGenerateFromSourceClick: () => void;
|
||||||
onModelDependencyClick: () => void;
|
onModelDependencyClick: () => void;
|
||||||
};
|
};
|
||||||
@@ -101,8 +93,6 @@ export const LibraryRow = ({
|
|||||||
modeledMethodsMap,
|
modeledMethodsMap,
|
||||||
modifiedSignatures,
|
modifiedSignatures,
|
||||||
selectedSignatures,
|
selectedSignatures,
|
||||||
inProgressMethods,
|
|
||||||
processedByAutoModelMethods,
|
|
||||||
viewState,
|
viewState,
|
||||||
hideModeledMethods,
|
hideModeledMethods,
|
||||||
revealedMethodSignature,
|
revealedMethodSignature,
|
||||||
@@ -111,8 +101,6 @@ export const LibraryRow = ({
|
|||||||
onChange,
|
onChange,
|
||||||
onMethodClick,
|
onMethodClick,
|
||||||
onSaveModelClick,
|
onSaveModelClick,
|
||||||
onGenerateFromLlmClick,
|
|
||||||
onStopGenerateFromLlmClick,
|
|
||||||
onGenerateFromSourceClick,
|
onGenerateFromSourceClick,
|
||||||
onModelDependencyClick,
|
onModelDependencyClick,
|
||||||
}: LibraryRowProps) => {
|
}: LibraryRowProps) => {
|
||||||
@@ -134,27 +122,6 @@ export const LibraryRow = ({
|
|||||||
}
|
}
|
||||||
}, [methods, revealedMethodSignature]);
|
}, [methods, revealedMethodSignature]);
|
||||||
|
|
||||||
const handleModelWithAI = useCallback(
|
|
||||||
async (e: React.MouseEvent) => {
|
|
||||||
onGenerateFromLlmClick(
|
|
||||||
title,
|
|
||||||
methods.map((m) => m.signature),
|
|
||||||
);
|
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
|
||||||
},
|
|
||||||
[title, methods, onGenerateFromLlmClick],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleStopModelWithAI = useCallback(
|
|
||||||
async (e: React.MouseEvent) => {
|
|
||||||
onStopGenerateFromLlmClick(title);
|
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
|
||||||
},
|
|
||||||
[title, onStopGenerateFromLlmClick],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleModelFromSource = useCallback(
|
const handleModelFromSource = useCallback(
|
||||||
async (e: React.MouseEvent) => {
|
async (e: React.MouseEvent) => {
|
||||||
onGenerateFromSourceClick();
|
onGenerateFromSourceClick();
|
||||||
@@ -186,21 +153,6 @@ export const LibraryRow = ({
|
|||||||
return methods.some((method) => modifiedSignatures.has(method.signature));
|
return methods.some((method) => modifiedSignatures.has(method.signature));
|
||||||
}, [methods, modifiedSignatures]);
|
}, [methods, modifiedSignatures]);
|
||||||
|
|
||||||
const canStopAutoModeling = useMemo(() => {
|
|
||||||
return methods.some((method) => inProgressMethods.has(method.signature));
|
|
||||||
}, [methods, inProgressMethods]);
|
|
||||||
|
|
||||||
const modelWithAIDisabled = useMemo(() => {
|
|
||||||
return (
|
|
||||||
getCandidates(
|
|
||||||
viewState.mode,
|
|
||||||
methods,
|
|
||||||
modeledMethodsMap,
|
|
||||||
processedByAutoModelMethods,
|
|
||||||
).length === 0
|
|
||||||
);
|
|
||||||
}, [methods, modeledMethodsMap, processedByAutoModelMethods, viewState.mode]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LibraryContainer>
|
<LibraryContainer>
|
||||||
<TitleContainer onClick={toggleExpanded} aria-expanded={isExpanded}>
|
<TitleContainer onClick={toggleExpanded} aria-expanded={isExpanded}>
|
||||||
@@ -219,22 +171,6 @@ export const LibraryRow = ({
|
|||||||
</ModeledPercentage>
|
</ModeledPercentage>
|
||||||
{hasUnsavedChanges ? <VSCodeTag>UNSAVED</VSCodeTag> : null}
|
{hasUnsavedChanges ? <VSCodeTag>UNSAVED</VSCodeTag> : null}
|
||||||
</NameContainer>
|
</NameContainer>
|
||||||
{viewState.showLlmButton && !canStopAutoModeling && (
|
|
||||||
<VSCodeButton
|
|
||||||
appearance="icon"
|
|
||||||
disabled={modelWithAIDisabled}
|
|
||||||
onClick={handleModelWithAI}
|
|
||||||
>
|
|
||||||
<Codicon name="lightbulb-autofix" label="Model with AI" />
|
|
||||||
Model with AI
|
|
||||||
</VSCodeButton>
|
|
||||||
)}
|
|
||||||
{viewState.showLlmButton && canStopAutoModeling && (
|
|
||||||
<VSCodeButton appearance="icon" onClick={handleStopModelWithAI}>
|
|
||||||
<Codicon name="debug-stop" label="Stop model with AI" />
|
|
||||||
Stop
|
|
||||||
</VSCodeButton>
|
|
||||||
)}
|
|
||||||
{viewState.showGenerateButton &&
|
{viewState.showGenerateButton &&
|
||||||
viewState.mode === Mode.Application && (
|
viewState.mode === Mode.Application && (
|
||||||
<VSCodeButton appearance="icon" onClick={handleModelFromSource}>
|
<VSCodeButton appearance="icon" onClick={handleModelFromSource}>
|
||||||
@@ -257,8 +193,6 @@ export const LibraryRow = ({
|
|||||||
modeledMethodsMap={modeledMethodsMap}
|
modeledMethodsMap={modeledMethodsMap}
|
||||||
modifiedSignatures={modifiedSignatures}
|
modifiedSignatures={modifiedSignatures}
|
||||||
selectedSignatures={selectedSignatures}
|
selectedSignatures={selectedSignatures}
|
||||||
inProgressMethods={inProgressMethods}
|
|
||||||
processedByAutoModelMethods={processedByAutoModelMethods}
|
|
||||||
viewState={viewState}
|
viewState={viewState}
|
||||||
hideModeledMethods={hideModeledMethods}
|
hideModeledMethods={hideModeledMethods}
|
||||||
revealedMethodSignature={revealedMethodSignature}
|
revealedMethodSignature={revealedMethodSignature}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import {
|
|||||||
VSCodeBadge,
|
VSCodeBadge,
|
||||||
VSCodeButton,
|
VSCodeButton,
|
||||||
VSCodeLink,
|
VSCodeLink,
|
||||||
VSCodeProgressRing,
|
|
||||||
} from "@vscode/webview-ui-toolkit/react";
|
} from "@vscode/webview-ui-toolkit/react";
|
||||||
import {
|
import {
|
||||||
forwardRef,
|
forwardRef,
|
||||||
@@ -17,13 +16,11 @@ import { vscode } from "../vscode-api";
|
|||||||
|
|
||||||
import type { Method } from "../../model-editor/method";
|
import type { Method } from "../../model-editor/method";
|
||||||
import type { ModeledMethod } from "../../model-editor/modeled-method";
|
import type { ModeledMethod } from "../../model-editor/modeled-method";
|
||||||
import { isModelPending } from "../../model-editor/modeled-method";
|
|
||||||
import { ModelKindDropdown } from "./ModelKindDropdown";
|
import { ModelKindDropdown } from "./ModelKindDropdown";
|
||||||
import { Mode } from "../../model-editor/shared/mode";
|
import { Mode } from "../../model-editor/shared/mode";
|
||||||
import { MethodClassifications } from "./MethodClassifications";
|
import { MethodClassifications } from "./MethodClassifications";
|
||||||
import { getModelingStatus } from "../../model-editor/shared/modeling-status";
|
import { getModelingStatus } from "../../model-editor/shared/modeling-status";
|
||||||
import { ModelingStatusIndicator } from "./ModelingStatusIndicator";
|
import { ModelingStatusIndicator } from "./ModelingStatusIndicator";
|
||||||
import { InProgressDropdown } from "./InProgressDropdown";
|
|
||||||
import { MethodName } from "./MethodName";
|
import { MethodName } from "./MethodName";
|
||||||
import { ModelTypeDropdown } from "./ModelTypeDropdown";
|
import { ModelTypeDropdown } from "./ModelTypeDropdown";
|
||||||
import { ModelInputDropdown } from "./ModelInputDropdown";
|
import { ModelInputDropdown } from "./ModelInputDropdown";
|
||||||
@@ -66,12 +63,6 @@ const ViewLink = styled(VSCodeLink)`
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const ProgressRing = styled(VSCodeProgressRing)`
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
margin-left: auto;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const CodiconRow = styled(VSCodeButton)`
|
const CodiconRow = styled(VSCodeButton)`
|
||||||
min-height: calc(var(--input-height) * 1px);
|
min-height: calc(var(--input-height) * 1px);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -83,8 +74,6 @@ export type MethodRowProps = {
|
|||||||
modeledMethods: ModeledMethod[];
|
modeledMethods: ModeledMethod[];
|
||||||
methodIsUnsaved: boolean;
|
methodIsUnsaved: boolean;
|
||||||
methodIsSelected: boolean;
|
methodIsSelected: boolean;
|
||||||
modelingInProgress: boolean;
|
|
||||||
processedByAutoModel: boolean;
|
|
||||||
viewState: ModelEditorViewState;
|
viewState: ModelEditorViewState;
|
||||||
revealedMethodSignature: string | null;
|
revealedMethodSignature: string | null;
|
||||||
inputAccessPathSuggestions?: AccessPathOption[];
|
inputAccessPathSuggestions?: AccessPathOption[];
|
||||||
@@ -116,13 +105,12 @@ export const MethodRow = (props: MethodRowProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
|
const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
|
||||||
(props, ref) => {
|
(props: MethodRowProps, ref) => {
|
||||||
const {
|
const {
|
||||||
method,
|
method,
|
||||||
modeledMethods: modeledMethodsProp,
|
modeledMethods: modeledMethodsProp,
|
||||||
methodIsUnsaved,
|
methodIsUnsaved,
|
||||||
methodIsSelected,
|
methodIsSelected,
|
||||||
processedByAutoModel,
|
|
||||||
viewState,
|
viewState,
|
||||||
revealedMethodSignature,
|
revealedMethodSignature,
|
||||||
inputAccessPathSuggestions,
|
inputAccessPathSuggestions,
|
||||||
@@ -269,140 +257,105 @@ const ModelableMethodRow = forwardRef<HTMLElement | undefined, MethodRowProps>(
|
|||||||
>
|
>
|
||||||
View
|
View
|
||||||
</ViewLink>
|
</ViewLink>
|
||||||
{props.modelingInProgress && <ProgressRing />}
|
|
||||||
</ApiOrMethodRow>
|
</ApiOrMethodRow>
|
||||||
</DataGridCell>
|
</DataGridCell>
|
||||||
{props.modelingInProgress && (
|
|
||||||
<>
|
|
||||||
<DataGridCell>
|
|
||||||
<InProgressDropdown />
|
|
||||||
</DataGridCell>
|
|
||||||
<DataGridCell>
|
|
||||||
<InProgressDropdown />
|
|
||||||
</DataGridCell>
|
|
||||||
<DataGridCell>
|
|
||||||
<InProgressDropdown />
|
|
||||||
</DataGridCell>
|
|
||||||
<DataGridCell>
|
|
||||||
<InProgressDropdown />
|
|
||||||
</DataGridCell>
|
|
||||||
<DataGridCell>
|
|
||||||
<CodiconRow appearance="icon" disabled={true}>
|
|
||||||
<Codicon name="add" label="Add new model" />
|
|
||||||
</CodiconRow>
|
|
||||||
</DataGridCell>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{!props.modelingInProgress && (
|
|
||||||
<>
|
|
||||||
{shownModeledMethods.map((modeledMethod, index) => {
|
|
||||||
const modelPending = isModelPending(
|
|
||||||
modeledMethod,
|
|
||||||
modelingStatus,
|
|
||||||
processedByAutoModel,
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
{shownModeledMethods.map((modeledMethod, index) => {
|
||||||
<DataGridRow key={index} focused={focusedIndex === index}>
|
return (
|
||||||
<DataGridCell>
|
<DataGridRow key={index} focused={focusedIndex === index}>
|
||||||
<ModelTypeDropdown
|
<DataGridCell>
|
||||||
language={viewState.language}
|
<ModelTypeDropdown
|
||||||
modelConfig={viewState.modelConfig}
|
language={viewState.language}
|
||||||
method={method}
|
modelConfig={viewState.modelConfig}
|
||||||
modeledMethod={modeledMethod}
|
method={method}
|
||||||
modelPending={modelPending}
|
modeledMethod={modeledMethod}
|
||||||
onChange={modeledMethodChangedHandlers[index]}
|
onChange={modeledMethodChangedHandlers[index]}
|
||||||
/>
|
|
||||||
</DataGridCell>
|
|
||||||
<DataGridCell>
|
|
||||||
{inputAccessPathSuggestions === undefined ? (
|
|
||||||
<ModelInputDropdown
|
|
||||||
language={viewState.language}
|
|
||||||
method={method}
|
|
||||||
modeledMethod={modeledMethod}
|
|
||||||
modelPending={modelPending}
|
|
||||||
onChange={modeledMethodChangedHandlers[index]}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<ModelInputSuggestBox
|
|
||||||
modeledMethod={modeledMethod}
|
|
||||||
suggestions={inputAccessPathSuggestions}
|
|
||||||
typePathSuggestions={outputAccessPathSuggestions ?? []}
|
|
||||||
onChange={modeledMethodChangedHandlers[index]}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</DataGridCell>
|
|
||||||
<DataGridCell>
|
|
||||||
{outputAccessPathSuggestions === undefined ? (
|
|
||||||
<ModelOutputDropdown
|
|
||||||
language={viewState.language}
|
|
||||||
method={method}
|
|
||||||
modeledMethod={modeledMethod}
|
|
||||||
modelPending={modelPending}
|
|
||||||
onChange={modeledMethodChangedHandlers[index]}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<ModelOutputSuggestBox
|
|
||||||
modeledMethod={modeledMethod}
|
|
||||||
suggestions={outputAccessPathSuggestions}
|
|
||||||
onChange={modeledMethodChangedHandlers[index]}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</DataGridCell>
|
|
||||||
<DataGridCell>
|
|
||||||
<ModelKindDropdown
|
|
||||||
language={viewState.language}
|
|
||||||
modeledMethod={modeledMethod}
|
|
||||||
modelPending={modelPending}
|
|
||||||
onChange={modeledMethodChangedHandlers[index]}
|
|
||||||
/>
|
|
||||||
</DataGridCell>
|
|
||||||
<DataGridCell>
|
|
||||||
<ModelButtonsContainer>
|
|
||||||
<ModelAlertsIndicator
|
|
||||||
viewState={viewState}
|
|
||||||
modeledMethod={modeledMethod}
|
|
||||||
evaluationRun={evaluationRun}
|
|
||||||
></ModelAlertsIndicator>
|
|
||||||
{index === 0 ? (
|
|
||||||
<CodiconRow
|
|
||||||
appearance="icon"
|
|
||||||
aria-label="Add new model"
|
|
||||||
onClick={(event: React.MouseEvent) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
handleAddModelClick();
|
|
||||||
}}
|
|
||||||
disabled={addModelButtonDisabled}
|
|
||||||
>
|
|
||||||
<Codicon name="add" />
|
|
||||||
</CodiconRow>
|
|
||||||
) : (
|
|
||||||
<CodiconRow
|
|
||||||
appearance="icon"
|
|
||||||
aria-label="Remove model"
|
|
||||||
onClick={(event: React.MouseEvent) => {
|
|
||||||
event.stopPropagation();
|
|
||||||
removeModelClickedHandlers[index]();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Codicon name="trash" />
|
|
||||||
</CodiconRow>
|
|
||||||
)}
|
|
||||||
</ModelButtonsContainer>
|
|
||||||
</DataGridCell>
|
|
||||||
</DataGridRow>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
{validationErrors.map((error, index) => (
|
|
||||||
<DataGridCell gridColumn="span 5" key={index}>
|
|
||||||
<ModeledMethodAlert
|
|
||||||
error={error}
|
|
||||||
setSelectedIndex={setFocusedIndex}
|
|
||||||
/>
|
/>
|
||||||
</DataGridCell>
|
</DataGridCell>
|
||||||
))}
|
<DataGridCell>
|
||||||
</>
|
{inputAccessPathSuggestions === undefined ? (
|
||||||
)}
|
<ModelInputDropdown
|
||||||
|
language={viewState.language}
|
||||||
|
method={method}
|
||||||
|
modeledMethod={modeledMethod}
|
||||||
|
onChange={modeledMethodChangedHandlers[index]}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ModelInputSuggestBox
|
||||||
|
modeledMethod={modeledMethod}
|
||||||
|
suggestions={inputAccessPathSuggestions}
|
||||||
|
typePathSuggestions={outputAccessPathSuggestions ?? []}
|
||||||
|
onChange={modeledMethodChangedHandlers[index]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</DataGridCell>
|
||||||
|
<DataGridCell>
|
||||||
|
{outputAccessPathSuggestions === undefined ? (
|
||||||
|
<ModelOutputDropdown
|
||||||
|
language={viewState.language}
|
||||||
|
method={method}
|
||||||
|
modeledMethod={modeledMethod}
|
||||||
|
onChange={modeledMethodChangedHandlers[index]}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ModelOutputSuggestBox
|
||||||
|
modeledMethod={modeledMethod}
|
||||||
|
suggestions={outputAccessPathSuggestions}
|
||||||
|
onChange={modeledMethodChangedHandlers[index]}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</DataGridCell>
|
||||||
|
<DataGridCell>
|
||||||
|
<ModelKindDropdown
|
||||||
|
language={viewState.language}
|
||||||
|
modeledMethod={modeledMethod}
|
||||||
|
onChange={modeledMethodChangedHandlers[index]}
|
||||||
|
/>
|
||||||
|
</DataGridCell>
|
||||||
|
<DataGridCell>
|
||||||
|
<ModelButtonsContainer>
|
||||||
|
<ModelAlertsIndicator
|
||||||
|
viewState={viewState}
|
||||||
|
modeledMethod={modeledMethod}
|
||||||
|
evaluationRun={evaluationRun}
|
||||||
|
></ModelAlertsIndicator>
|
||||||
|
{index === 0 ? (
|
||||||
|
<CodiconRow
|
||||||
|
appearance="icon"
|
||||||
|
aria-label="Add new model"
|
||||||
|
onClick={(event: React.MouseEvent) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
handleAddModelClick();
|
||||||
|
}}
|
||||||
|
disabled={addModelButtonDisabled}
|
||||||
|
>
|
||||||
|
<Codicon name="add" />
|
||||||
|
</CodiconRow>
|
||||||
|
) : (
|
||||||
|
<CodiconRow
|
||||||
|
appearance="icon"
|
||||||
|
aria-label="Remove model"
|
||||||
|
onClick={(event: React.MouseEvent) => {
|
||||||
|
event.stopPropagation();
|
||||||
|
removeModelClickedHandlers[index]();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Codicon name="trash" />
|
||||||
|
</CodiconRow>
|
||||||
|
)}
|
||||||
|
</ModelButtonsContainer>
|
||||||
|
</DataGridCell>
|
||||||
|
</DataGridRow>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{validationErrors.map((error, index) => (
|
||||||
|
<DataGridCell gridColumn="span 5" key={index}>
|
||||||
|
<ModeledMethodAlert
|
||||||
|
error={error}
|
||||||
|
setSelectedIndex={setFocusedIndex}
|
||||||
|
/>
|
||||||
|
</DataGridCell>
|
||||||
|
))}
|
||||||
</DataGridRow>
|
</DataGridRow>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -412,7 +365,7 @@ ModelableMethodRow.displayName = "ModelableMethodRow";
|
|||||||
const UnmodelableMethodRow = forwardRef<
|
const UnmodelableMethodRow = forwardRef<
|
||||||
HTMLElement | undefined,
|
HTMLElement | undefined,
|
||||||
MethodRowProps
|
MethodRowProps
|
||||||
>((props, ref) => {
|
>((props: MethodRowProps, ref) => {
|
||||||
const { method, viewState, revealedMethodSignature } = props;
|
const { method, viewState, revealedMethodSignature } = props;
|
||||||
|
|
||||||
const jumpToMethod = useCallback(
|
const jumpToMethod = useCallback(
|
||||||
|
|||||||
@@ -102,12 +102,6 @@ export function ModelEditor({
|
|||||||
new Set(),
|
new Set(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const [inProgressMethods, setInProgressMethods] = useState<Set<string>>(
|
|
||||||
new Set(),
|
|
||||||
);
|
|
||||||
const [processedByAutoModelMethods, setProcessedByAutoModelMethods] =
|
|
||||||
useState<Set<string>>(new Set());
|
|
||||||
|
|
||||||
const [hideModeledMethods, setHideModeledMethods] = useState(
|
const [hideModeledMethods, setHideModeledMethods] = useState(
|
||||||
initialHideModeledMethods,
|
initialHideModeledMethods,
|
||||||
);
|
);
|
||||||
@@ -153,14 +147,6 @@ export function ModelEditor({
|
|||||||
case "setModifiedMethods":
|
case "setModifiedMethods":
|
||||||
setModifiedSignatures(new Set(msg.methodSignatures));
|
setModifiedSignatures(new Set(msg.methodSignatures));
|
||||||
break;
|
break;
|
||||||
case "setInProgressMethods": {
|
|
||||||
setInProgressMethods(new Set(msg.methods));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "setProcessedByAutoModelMethods": {
|
|
||||||
setProcessedByAutoModelMethods(new Set(msg.methods));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "revealMethod":
|
case "revealMethod":
|
||||||
setRevealedMethodSignature(msg.methodSignature);
|
setRevealedMethodSignature(msg.methodSignature);
|
||||||
break;
|
break;
|
||||||
@@ -294,24 +280,6 @@ export function ModelEditor({
|
|||||||
});
|
});
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onGenerateFromLlmClick = useCallback(
|
|
||||||
(packageName: string, methodSignatures: string[]) => {
|
|
||||||
vscode.postMessage({
|
|
||||||
t: "generateMethodsFromLlm",
|
|
||||||
packageName,
|
|
||||||
methodSignatures,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const onStopGenerateFromLlmClick = useCallback((packageName: string) => {
|
|
||||||
vscode.postMessage({
|
|
||||||
t: "stopGeneratingMethodsFromLlm",
|
|
||||||
packageName,
|
|
||||||
});
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const onOpenDatabaseClick = useCallback(() => {
|
const onOpenDatabaseClick = useCallback(() => {
|
||||||
vscode.postMessage({
|
vscode.postMessage({
|
||||||
t: "openDatabase",
|
t: "openDatabase",
|
||||||
@@ -430,8 +398,6 @@ export function ModelEditor({
|
|||||||
modeledMethodsMap={modeledMethods}
|
modeledMethodsMap={modeledMethods}
|
||||||
modifiedSignatures={modifiedSignatures}
|
modifiedSignatures={modifiedSignatures}
|
||||||
selectedSignatures={selectedSignatures}
|
selectedSignatures={selectedSignatures}
|
||||||
inProgressMethods={inProgressMethods}
|
|
||||||
processedByAutoModelMethods={processedByAutoModelMethods}
|
|
||||||
viewState={viewState}
|
viewState={viewState}
|
||||||
hideModeledMethods={hideModeledMethods}
|
hideModeledMethods={hideModeledMethods}
|
||||||
revealedMethodSignature={revealedMethodSignature}
|
revealedMethodSignature={revealedMethodSignature}
|
||||||
@@ -440,8 +406,6 @@ export function ModelEditor({
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
onMethodClick={onMethodClick}
|
onMethodClick={onMethodClick}
|
||||||
onSaveModelClick={onSaveModelClick}
|
onSaveModelClick={onSaveModelClick}
|
||||||
onGenerateFromLlmClick={onGenerateFromLlmClick}
|
|
||||||
onStopGenerateFromLlmClick={onStopGenerateFromLlmClick}
|
|
||||||
onGenerateFromSourceClick={onGenerateFromSourceClick}
|
onGenerateFromSourceClick={onGenerateFromSourceClick}
|
||||||
onModelDependencyClick={onModelDependencyClick}
|
onModelDependencyClick={onModelDependencyClick}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ type Props = {
|
|||||||
language: QueryLanguage;
|
language: QueryLanguage;
|
||||||
method: Method;
|
method: Method;
|
||||||
modeledMethod: ModeledMethod | undefined;
|
modeledMethod: ModeledMethod | undefined;
|
||||||
modelPending: boolean;
|
|
||||||
onChange: (modeledMethod: ModeledMethod) => void;
|
onChange: (modeledMethod: ModeledMethod) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -23,7 +22,6 @@ export const ModelInputDropdown = ({
|
|||||||
language,
|
language,
|
||||||
method,
|
method,
|
||||||
modeledMethod,
|
modeledMethod,
|
||||||
modelPending,
|
|
||||||
onChange,
|
onChange,
|
||||||
}: Props): React.JSX.Element => {
|
}: Props): React.JSX.Element => {
|
||||||
const options = useMemo(() => {
|
const options = useMemo(() => {
|
||||||
@@ -80,7 +78,6 @@ export const ModelInputDropdown = ({
|
|||||||
value={value}
|
value={value}
|
||||||
options={options}
|
options={options}
|
||||||
disabled={!enabled}
|
disabled={!enabled}
|
||||||
$pending={modelPending}
|
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
aria-label="Input"
|
aria-label="Input"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -15,14 +15,12 @@ import { InputDropdown } from "./InputDropdown";
|
|||||||
type Props = {
|
type Props = {
|
||||||
language: QueryLanguage;
|
language: QueryLanguage;
|
||||||
modeledMethod: ModeledMethod | undefined;
|
modeledMethod: ModeledMethod | undefined;
|
||||||
modelPending: boolean;
|
|
||||||
onChange: (modeledMethod: ModeledMethod) => void;
|
onChange: (modeledMethod: ModeledMethod) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ModelKindDropdown = ({
|
export const ModelKindDropdown = ({
|
||||||
language,
|
language,
|
||||||
modeledMethod,
|
modeledMethod,
|
||||||
modelPending,
|
|
||||||
onChange,
|
onChange,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const predicate = useMemo(() => {
|
const predicate = useMemo(() => {
|
||||||
@@ -92,7 +90,6 @@ export const ModelKindDropdown = ({
|
|||||||
value={value}
|
value={value}
|
||||||
options={options}
|
options={options}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
$pending={modelPending}
|
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
aria-label="Kind"
|
aria-label="Kind"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ type Props = {
|
|||||||
language: QueryLanguage;
|
language: QueryLanguage;
|
||||||
method: Method;
|
method: Method;
|
||||||
modeledMethod: ModeledMethod | undefined;
|
modeledMethod: ModeledMethod | undefined;
|
||||||
modelPending: boolean;
|
|
||||||
onChange: (modeledMethod: ModeledMethod) => void;
|
onChange: (modeledMethod: ModeledMethod) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -23,7 +22,6 @@ export const ModelOutputDropdown = ({
|
|||||||
language,
|
language,
|
||||||
method,
|
method,
|
||||||
modeledMethod,
|
modeledMethod,
|
||||||
modelPending,
|
|
||||||
onChange,
|
onChange,
|
||||||
}: Props): React.JSX.Element => {
|
}: Props): React.JSX.Element => {
|
||||||
const options = useMemo(() => {
|
const options = useMemo(() => {
|
||||||
@@ -81,7 +79,6 @@ export const ModelOutputDropdown = ({
|
|||||||
value={value}
|
value={value}
|
||||||
options={options}
|
options={options}
|
||||||
disabled={!enabled}
|
disabled={!enabled}
|
||||||
$pending={modelPending}
|
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
aria-label="Output"
|
aria-label="Output"
|
||||||
/>
|
/>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user