Compare commits

..

243 Commits

Author SHA1 Message Date
Robert
edc2fe8454 v1.17.1
Some checks failed
Release / Release (push) Has been cancelled
Release / Publish to VS Code Marketplace (push) Has been cancelled
Release / Publish to Open VSX Registry (push) Has been cancelled
2025-01-23 12:06:33 +00:00
Koen Vlaswinkel
427c6031fe Merge pull request #3907 from github/github-action/bump-cli
Bump CLI Version to v2.20.2 for integration tests
2025-01-23 11:11:14 +01:00
Koen Vlaswinkel
6687669aad Fix supported CLI versions file 2025-01-23 10:56:02 +01:00
github-actions[bot]
2d3b62a021 Bump CLI version from v2.20.1 to v2.20.2 for integration tests 2025-01-22 14:52:43 +00:00
Koen Vlaswinkel
f8d010ad10 Merge pull request #3906 from github/koesie10/update-release-docs
Update release docs
2025-01-22 11:40:21 +01:00
Koen Vlaswinkel
c35f927436 Update release docs 2025-01-22 11:24:58 +01:00
Asger F
ffed4b634f Merge pull request #3843 from asgerf/asgerf/compare-perf-view
Add 'compare performance' view
2025-01-22 11:01:59 +01:00
Koen Vlaswinkel
13389358ac Merge pull request #3905 from github/koesie10/upgrade-vite
Upgrade vite
2025-01-22 09:48:27 +01:00
Koen Vlaswinkel
60f392cceb Upgrade vite 2025-01-22 09:29:23 +01:00
Koen Vlaswinkel
6f8d6f2541 Merge pull request #3904 from github/dependabot/npm_and_yarn/extensions/ql-vscode/npm_and_yarn-7064c9a8ac
Bump katex from 0.16.18 to 0.16.21 in /extensions/ql-vscode in the npm_and_yarn group
2025-01-20 10:40:52 +01:00
dependabot[bot]
3fbbf4045d Bump katex in /extensions/ql-vscode in the npm_and_yarn group
Bumps the npm_and_yarn group in /extensions/ql-vscode with 1 update: [katex](https://github.com/KaTeX/KaTeX).


Updates `katex` from 0.16.18 to 0.16.21
- [Release notes](https://github.com/KaTeX/KaTeX/releases)
- [Changelog](https://github.com/KaTeX/KaTeX/blob/main/CHANGELOG.md)
- [Commits](https://github.com/KaTeX/KaTeX/compare/v0.16.18...v0.16.21)

---
updated-dependencies:
- dependency-name: katex
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-20 09:25:53 +00:00
Asger F
666c26e6a1 Permit performance comparisons across DBs
The snippet seems to have been copied from 'findOtherQueryToCompare'
where it makes sense, but in the context of a performance comparison
we don't need this restriction.
2025-01-17 12:42:11 +01:00
Asger F
bba31c030a Add comment to clarify reverse parsing 2025-01-17 11:47:35 +01:00
Asger F
8b8d174781 Clean up some more TODOs 2025-01-17 11:36:08 +01:00
Asger F
370b17c0f5 Remove TODOs 2025-01-17 11:35:16 +01:00
Asger F
37dcd0822b Remove TODO that was just resolved 2025-01-17 11:34:54 +01:00
Asger F
f09210b033 Only record cache hits prior to first evaluation 2025-01-17 11:26:40 +01:00
Asger F
44d33d6d31 Merge branch 'asgerf/compare-perf-view' of github.com:asgerf/vscode-codeql into asgerf/compare-perf-view 2025-01-17 10:53:03 +01:00
Asger F
08bffab05f Add more description of the "struct of arrays" layout 2025-01-17 10:51:49 +01:00
Asger F
2293cc3537 Update extensions/ql-vscode/src/log-insights/log-scanner.ts
Co-authored-by: Andrew Eisenberg <aeisenberg@github.com>
2025-01-17 10:43:15 +01:00
Asger F
6f461e75a7 Update extensions/ql-vscode/package.json
Restrict to Canary

Co-authored-by: Andrew Eisenberg <aeisenberg@github.com>
2025-01-17 10:40:09 +01:00
dependabot[bot]
8b0a16ea14 Bump the storybook group in /extensions/ql-vscode with 12 updates (#3900)
Bumps the storybook group in /extensions/ql-vscode with 12 updates:

| Package | From | To |
| --- | --- | --- |
| [@storybook/addon-a11y](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/a11y) | `8.4.7` | `8.5.0` |
| [@storybook/addon-actions](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/actions) | `8.4.7` | `8.5.0` |
| [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/essentials) | `8.4.7` | `8.5.0` |
| [@storybook/addon-interactions](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/interactions) | `8.4.7` | `8.5.0` |
| [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links) | `8.4.7` | `8.5.0` |
| [@storybook/blocks](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/blocks) | `8.4.7` | `8.5.0` |
| [@storybook/components](https://github.com/storybookjs/storybook/tree/HEAD/code/deprecated/components) | `8.4.7` | `8.5.0` |
| [@storybook/manager-api](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/manager-api) | `8.4.7` | `8.5.0` |
| [@storybook/react](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/react) | `8.4.7` | `8.5.0` |
| [@storybook/react-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/react-vite) | `8.4.7` | `8.5.0` |
| [@storybook/theming](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/theming) | `8.4.7` | `8.5.0` |
| [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/cli) | `8.4.7` | `8.5.0` |


Updates `@storybook/addon-a11y` from 8.4.7 to 8.5.0
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.5.0/code/addons/a11y)

Updates `@storybook/addon-actions` from 8.4.7 to 8.5.0
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.5.0/code/addons/actions)

Updates `@storybook/addon-essentials` from 8.4.7 to 8.5.0
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.5.0/code/addons/essentials)

Updates `@storybook/addon-interactions` from 8.4.7 to 8.5.0
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.5.0/code/addons/interactions)

Updates `@storybook/addon-links` from 8.4.7 to 8.5.0
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.5.0/code/addons/links)

Updates `@storybook/blocks` from 8.4.7 to 8.5.0
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.5.0/code/lib/blocks)

Updates `@storybook/components` from 8.4.7 to 8.5.0
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.5.0/code/deprecated/components)

Updates `@storybook/manager-api` from 8.4.7 to 8.5.0
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.5.0/code/lib/manager-api)

Updates `@storybook/react` from 8.4.7 to 8.5.0
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.5.0/code/renderers/react)

Updates `@storybook/react-vite` from 8.4.7 to 8.5.0
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.5.0/code/frameworks/react-vite)

Updates `@storybook/theming` from 8.4.7 to 8.5.0
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.5.0/code/lib/theming)

Updates `storybook` from 8.4.7 to 8.5.0
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.5.0/code/lib/cli)

---
updated-dependencies:
- dependency-name: "@storybook/addon-a11y"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/addon-actions"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/addon-essentials"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/addon-interactions"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/blocks"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/components"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/manager-api"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/react"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/react-vite"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/theming"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: storybook
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-16 16:07:03 +00:00
Koen Vlaswinkel
c086a80384 Merge pull request #3898 from github/github-action/bump-node-version
Bump Node version to v20.18.1
2025-01-16 13:47:16 +01:00
github-actions[bot]
e947756a5a Bump Node version to v20.18.1 2025-01-16 12:25:55 +00:00
Koen Vlaswinkel
6cafa5d905 Merge pull request #3897 from github/github-action/bump-node-version
Bump Node version to v20.18.1
2025-01-15 16:15:09 +01:00
Koen Vlaswinkel
4607a452bd Fix type errors 2025-01-15 15:43:05 +01:00
github-actions[bot]
276405f743 Bump Node version to v20.18.1 2025-01-15 12:25:54 +00:00
Koen Vlaswinkel
056e45ad1e Merge pull request #3893 from github/dependabot/npm_and_yarn/extensions/ql-vscode/vscode/vsce-3.2.1
Bump @vscode/vsce from 2.24.0 to 3.2.1 in /extensions/ql-vscode
2025-01-14 12:00:56 +01:00
Koen Vlaswinkel
575628990e Fix qltest-discovery test after tmp upgrade 2025-01-13 16:58:32 +01:00
Koen Vlaswinkel
9f0a5f0daa Clean up files property and .vscodeignore file 2025-01-13 15:25:56 +01:00
Dave Bartolomeo
92e5181dd6 Merge pull request #3896 from github/github-action/bump-cli
Bump CLI Version to v2.20.1 for integration tests
2025-01-09 17:16:07 -05:00
github-actions[bot]
88924f1556 Bump CLI version from v2.20.0 to v2.20.1 for integration tests 2025-01-09 21:56:26 +00:00
Koen Vlaswinkel
26edfa5c43 Merge pull request #3890 from github/github-action/bump-node-version
Bump Node version to v20.18.1
2025-01-09 12:15:01 +01:00
github-actions[bot]
17bae27c34 Bump Node version to v20.18.1 2025-01-08 12:26:08 +00:00
Charis Kyriakou
49839a1a52 Remove support for CodeQL CLI versions older than v2.18.4 (#3895)
* Remove support for CodeQL CLI versions older than v2.18.4

* Update CHANGELOG
2025-01-06 11:37:39 +00:00
dependabot[bot]
60754a81d6 Bump @vscode/vsce from 2.24.0 to 3.2.1 in /extensions/ql-vscode
Bumps [@vscode/vsce](https://github.com/Microsoft/vsce) from 2.24.0 to 3.2.1.
- [Release notes](https://github.com/Microsoft/vsce/releases)
- [Commits](https://github.com/Microsoft/vsce/compare/v2.24.0...v3.2.1)

---
updated-dependencies:
- dependency-name: "@vscode/vsce"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-02 17:45:12 +00:00
Angela P Wen
bda74b83c0 Merge pull request #3894 from github/dependabot/npm_and_yarn/extensions/ql-vscode/lint-staged-15.3.0
Bump lint-staged from 15.2.10 to 15.3.0 in /extensions/ql-vscode
2025-01-02 09:44:15 -08:00
Angela P Wen
055c53aba1 Merge pull request #3892 from github/dependabot/npm_and_yarn/extensions/ql-vscode/gulp-esbuild-0.14.0
Bump gulp-esbuild from 0.12.1 to 0.14.0 in /extensions/ql-vscode
2025-01-02 09:43:05 -08:00
Angela P Wen
8e5f331cb5 Merge pull request #3891 from github/dependabot/npm_and_yarn/extensions/ql-vscode/typescript-eslint-7c217e476c
Bump the typescript-eslint group in /extensions/ql-vscode with 2 updates
2025-01-02 09:42:35 -08:00
dependabot[bot]
b4d925bbb2 Bump lint-staged from 15.2.10 to 15.3.0 in /extensions/ql-vscode
Bumps [lint-staged](https://github.com/lint-staged/lint-staged) from 15.2.10 to 15.3.0.
- [Release notes](https://github.com/lint-staged/lint-staged/releases)
- [Changelog](https://github.com/lint-staged/lint-staged/blob/master/CHANGELOG.md)
- [Commits](https://github.com/lint-staged/lint-staged/compare/v15.2.10...v15.3.0)

---
updated-dependencies:
- dependency-name: lint-staged
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-02 14:02:40 +00:00
dependabot[bot]
a9879d2da3 Bump gulp-esbuild from 0.12.1 to 0.14.0 in /extensions/ql-vscode
Bumps [gulp-esbuild](https://github.com/ym-project/gulp-esbuild) from 0.12.1 to 0.14.0.
- [Release notes](https://github.com/ym-project/gulp-esbuild/releases)
- [Commits](https://github.com/ym-project/gulp-esbuild/compare/v0.12.1...v0.14.0)

---
updated-dependencies:
- dependency-name: gulp-esbuild
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-02 13:58:53 +00:00
dependabot[bot]
3dcfefa0ae Bump the typescript-eslint group in /extensions/ql-vscode with 2 updates
Bumps the typescript-eslint group in /extensions/ql-vscode with 2 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser).


Updates `@typescript-eslint/eslint-plugin` from 8.18.2 to 8.19.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.19.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.18.2 to 8.19.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.19.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: typescript-eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: typescript-eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-02 13:58:41 +00:00
Angela P Wen
e81dda377a Merge pull request #3889 from github/dependabot/docker/extensions/ql-vscode/test/e2e/docker/codercom/code-server-4.96.2
Bump codercom/code-server from 4.96.1 to 4.96.2 in /extensions/ql-vscode/test/e2e/docker
2024-12-26 10:12:59 -08:00
Angela P Wen
1faaaff59e Merge pull request #3888 from github/dependabot/npm_and_yarn/extensions/ql-vscode/markdownlint-cli2-0.17.0
Bump markdownlint-cli2 from 0.13.0 to 0.17.0 in /extensions/ql-vscode
2024-12-26 10:12:37 -08:00
Angela P Wen
75f77bcfca Merge pull request #3886 from github/dependabot/npm_and_yarn/extensions/ql-vscode/typescript-eslint-d4fe8e2025
Bump the typescript-eslint group in /extensions/ql-vscode with 2 updates
2024-12-26 10:12:09 -08:00
Angela P Wen
94c576b255 Merge pull request #3885 from github/dependabot/npm_and_yarn/extensions/ql-vscode/storybook-c0121e82ca
Bump @storybook/csf from 0.1.12 to 0.1.13 in /extensions/ql-vscode in the storybook group
2024-12-26 10:11:48 -08:00
dependabot[bot]
acf7ccdf6a Bump codercom/code-server in /extensions/ql-vscode/test/e2e/docker
Bumps codercom/code-server from 4.96.1 to 4.96.2.

---
updated-dependencies:
- dependency-name: codercom/code-server
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-26 13:52:29 +00:00
dependabot[bot]
b24aedea99 Bump markdownlint-cli2 from 0.13.0 to 0.17.0 in /extensions/ql-vscode
Bumps [markdownlint-cli2](https://github.com/DavidAnson/markdownlint-cli2) from 0.13.0 to 0.17.0.
- [Changelog](https://github.com/DavidAnson/markdownlint-cli2/blob/main/CHANGELOG.md)
- [Commits](https://github.com/DavidAnson/markdownlint-cli2/compare/v0.13.0...v0.17.0)

---
updated-dependencies:
- dependency-name: markdownlint-cli2
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-26 13:16:22 +00:00
dependabot[bot]
29b0269a40 Bump the typescript-eslint group in /extensions/ql-vscode with 2 updates
Bumps the typescript-eslint group in /extensions/ql-vscode with 2 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser).


Updates `@typescript-eslint/eslint-plugin` from 8.18.1 to 8.18.2
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.18.2/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.18.1 to 8.18.2
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.18.2/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: typescript-eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: typescript-eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-26 13:12:31 +00:00
dependabot[bot]
00e27195d9 Bump @storybook/csf in /extensions/ql-vscode in the storybook group
Bumps the storybook group in /extensions/ql-vscode with 1 update: [@storybook/csf](https://github.com/ComponentDriven/csf).


Updates `@storybook/csf` from 0.1.12 to 0.1.13
- [Release notes](https://github.com/ComponentDriven/csf/releases)
- [Changelog](https://github.com/ComponentDriven/csf/blob/v0.1.13/CHANGELOG.md)
- [Commits](https://github.com/ComponentDriven/csf/compare/v0.1.12...v0.1.13)

---
updated-dependencies:
- dependency-name: "@storybook/csf"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-26 13:11:51 +00:00
Andrew Eisenberg
e54805fb06 Merge pull request #3884 from github/version/bump-to-v1.17.1
Bump version to v1.17.1
2024-12-20 14:15:19 -08:00
github-actions[bot]
6fc8b726f4 Bump version to v1.17.1 2024-12-20 20:28:17 +00:00
Andrew Eisenberg
daf1096389 Merge pull request #3883 from github/v1.17.0
v1.17.0
2024-12-20 12:27:07 -08:00
Andrew Eisenberg
e35bd1be8c Update changelog for version 1.7.0
Some checks failed
Release / Release (push) Has been cancelled
Release / Publish to VS Code Marketplace (push) Has been cancelled
Release / Publish to Open VSX Registry (push) Has been cancelled
Remove "Unreleased" section
2024-12-20 12:12:46 -08:00
Andrew Eisenberg
995a311d5f v1.17.0 2024-12-20 20:07:20 +00:00
Andrew Eisenberg
a86775f840 Merge pull request #3882 from github/koesie10/fix-azure-publish
Fix publishing to VS Code Marketplace
2024-12-20 08:42:02 -08:00
Koen Vlaswinkel
64e60f986c Fix publishing to VS Code Marketplace 2024-12-20 10:26:09 +01:00
Andrew Eisenberg
a47dc5555d Merge pull request #3877 from github/dependabot/npm_and_yarn/extensions/ql-vscode/typescript-eslint-ad245cece5
Bump the typescript-eslint group in /extensions/ql-vscode with 2 updates
2024-12-19 14:02:37 -08:00
Andrew Eisenberg
8c81ab73f9 Merge pull request #3881 from github/version/bump-to-v1.16.3
Bump version to v1.16.3
2024-12-19 13:57:58 -08:00
Andrew Eisenberg
99f082fb27 Merge pull request #3876 from github/dependabot/docker/extensions/ql-vscode/test/e2e/docker/codercom/code-server-4.96.1
Bump codercom/code-server from 4.95.3 to 4.96.1 in /extensions/ql-vscode/test/e2e/docker
2024-12-19 13:47:56 -08:00
Andrew Eisenberg
05926431da Merge branch 'main' into dependabot/npm_and_yarn/extensions/ql-vscode/typescript-eslint-ad245cece5 2024-12-19 13:47:08 -08:00
Andrew Eisenberg
52d20df1ae Merge pull request #3878 from github/dependabot/npm_and_yarn/extensions/ql-vscode/babel/preset-react-7.26.3
Bump @babel/preset-react from 7.25.9 to 7.26.3 in /extensions/ql-vscode
2024-12-19 13:45:48 -08:00
Andrew Eisenberg
370e192dfc Merge pull request #3879 from github/dependabot/npm_and_yarn/extensions/ql-vscode/eslint-plugin-jest-dom-5.5.0
Bump eslint-plugin-jest-dom from 5.4.0 to 5.5.0 in /extensions/ql-vscode
2024-12-19 13:45:20 -08:00
github-actions[bot]
db12100969 Bump version to v1.16.3 2024-12-19 21:40:18 +00:00
Andrew Eisenberg
bb435a9a28 Merge pull request #3880 from github/v1.16.2
v1.16.2
2024-12-19 13:36:37 -08:00
Andrew Eisenberg
7babc6421e v1.16.2
Some checks failed
Release / Release (push) Has been cancelled
Release / Publish to VS Code Marketplace (push) Has been cancelled
Release / Publish to Open VSX Registry (push) Has been cancelled
2024-12-19 21:20:57 +00:00
dependabot[bot]
8c37403a1c Bump eslint-plugin-jest-dom from 5.4.0 to 5.5.0 in /extensions/ql-vscode
Bumps [eslint-plugin-jest-dom](https://github.com/testing-library/eslint-plugin-jest-dom) from 5.4.0 to 5.5.0.
- [Release notes](https://github.com/testing-library/eslint-plugin-jest-dom/releases)
- [Commits](https://github.com/testing-library/eslint-plugin-jest-dom/compare/v5.4.0...v5.5.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-jest-dom
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-19 14:01:27 +00:00
dependabot[bot]
92a0fb1e34 Bump @babel/preset-react from 7.25.9 to 7.26.3 in /extensions/ql-vscode
Bumps [@babel/preset-react](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-react) from 7.25.9 to 7.26.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.3/packages/babel-preset-react)

---
updated-dependencies:
- dependency-name: "@babel/preset-react"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-19 14:01:11 +00:00
dependabot[bot]
162b255899 Bump the typescript-eslint group in /extensions/ql-vscode with 2 updates
Bumps the typescript-eslint group in /extensions/ql-vscode with 2 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser).


Updates `@typescript-eslint/eslint-plugin` from 8.18.0 to 8.18.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.18.1/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.18.0 to 8.18.1
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.18.1/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: typescript-eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: typescript-eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-19 14:00:58 +00:00
dependabot[bot]
2a8aede154 Bump codercom/code-server in /extensions/ql-vscode/test/e2e/docker
Bumps codercom/code-server from 4.95.3 to 4.96.1.

---
updated-dependencies:
- dependency-name: codercom/code-server
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-19 13:21:26 +00:00
dependabot[bot]
a3b8fea2b6 Bump the typescript-eslint group in /extensions/ql-vscode with 2 updates (#3867)
Bumps the typescript-eslint group in /extensions/ql-vscode with 2 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser).


Updates `@typescript-eslint/eslint-plugin` from 8.17.0 to 8.18.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.18.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.17.0 to 8.18.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.18.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: typescript-eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: typescript-eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alexander Eyers-Taylor <alexet@github.com>
2024-12-16 21:26:54 +00:00
Alexander Eyers-Taylor
07e9e44310 Add cache of query schemas to results view (#3862)
* Add cache of query schemas to results view

* Update changelog

* Don't call bqrsInfo with a page size of 0

* Update extensions/ql-vscode/CHANGELOG.md

Co-authored-by: Andrew Eisenberg <aeisenberg@github.com>

---------

Co-authored-by: Andrew Eisenberg <aeisenberg@github.com>
2024-12-13 17:05:00 +00:00
Koen Vlaswinkel
2c7021be0e Merge pull request #3874 from github/koesie10/upgrade-nanoid
Upgrade nanoid
2024-12-13 09:23:41 +01:00
dependabot[bot]
99535003e1 Bump @babel/preset-typescript in /extensions/ql-vscode (#3873)
Bumps [@babel/preset-typescript](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-typescript) from 7.23.3 to 7.26.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.0/packages/babel-preset-typescript)

---
updated-dependencies:
- dependency-name: "@babel/preset-typescript"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-12 19:35:37 +00:00
dependabot[bot]
8ab6990407 Bump @floating-ui/react from 0.26.28 to 0.27.0 in /extensions/ql-vscode (#3871)
Bumps [@floating-ui/react](https://github.com/floating-ui/floating-ui/tree/HEAD/packages/react) from 0.26.28 to 0.27.0.
- [Release notes](https://github.com/floating-ui/floating-ui/releases)
- [Changelog](https://github.com/floating-ui/floating-ui/blob/master/packages/react/CHANGELOG.md)
- [Commits](https://github.com/floating-ui/floating-ui/commits/@floating-ui/react@0.27.0/packages/react)

---
updated-dependencies:
- dependency-name: "@floating-ui/react"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-12 19:24:09 +00:00
Koen Vlaswinkel
36a3ee311a Upgrade nanoid 2024-12-12 16:54:11 +01:00
Koen Vlaswinkel
be48acdf0b Merge pull request #3870 from github/github-action/bump-node-version
Bump Node version to v20.18.1
2024-12-12 13:46:28 +01:00
github-actions[bot]
91beca7d85 Bump Node version to v20.18.1 2024-12-12 12:26:24 +00:00
dependabot[bot]
793748c2f4 Bump the storybook group in /extensions/ql-vscode with 13 updates (#3865)
Bumps the storybook group in /extensions/ql-vscode with 13 updates:

| Package | From | To |
| --- | --- | --- |
| [@storybook/addon-a11y](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/a11y) | `8.4.6` | `8.4.7` |
| [@storybook/addon-actions](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/actions) | `8.4.6` | `8.4.7` |
| [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/essentials) | `8.4.6` | `8.4.7` |
| [@storybook/addon-interactions](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/interactions) | `8.4.6` | `8.4.7` |
| [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links) | `8.4.6` | `8.4.7` |
| [@storybook/blocks](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/blocks) | `8.4.6` | `8.4.7` |
| [@storybook/components](https://github.com/storybookjs/storybook/tree/HEAD/code/deprecated/components) | `8.4.6` | `8.4.7` |
| [@storybook/icons](https://github.com/storybookjs/icons) | `1.2.12` | `1.3.0` |
| [@storybook/manager-api](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/manager-api) | `8.4.6` | `8.4.7` |
| [@storybook/react](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/react) | `8.4.6` | `8.4.7` |
| [@storybook/react-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/react-vite) | `8.4.6` | `8.4.7` |
| [@storybook/theming](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/theming) | `8.4.6` | `8.4.7` |
| [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/cli) | `8.4.6` | `8.4.7` |


Updates `@storybook/addon-a11y` from 8.4.6 to 8.4.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.7/code/addons/a11y)

Updates `@storybook/addon-actions` from 8.4.6 to 8.4.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.7/code/addons/actions)

Updates `@storybook/addon-essentials` from 8.4.6 to 8.4.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.7/code/addons/essentials)

Updates `@storybook/addon-interactions` from 8.4.6 to 8.4.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.7/code/addons/interactions)

Updates `@storybook/addon-links` from 8.4.6 to 8.4.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.7/code/addons/links)

Updates `@storybook/blocks` from 8.4.6 to 8.4.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.7/code/lib/blocks)

Updates `@storybook/components` from 8.4.6 to 8.4.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.7/code/deprecated/components)

Updates `@storybook/icons` from 1.2.12 to 1.3.0
- [Release notes](https://github.com/storybookjs/icons/releases)
- [Changelog](https://github.com/storybookjs/icons/blob/main/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/icons/compare/v1.2.12...v1.3.0)

Updates `@storybook/manager-api` from 8.4.6 to 8.4.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.7/code/lib/manager-api)

Updates `@storybook/react` from 8.4.6 to 8.4.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.7/code/renderers/react)

Updates `@storybook/react-vite` from 8.4.6 to 8.4.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.7/code/frameworks/react-vite)

Updates `@storybook/theming` from 8.4.6 to 8.4.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.7/code/lib/theming)

Updates `storybook` from 8.4.6 to 8.4.7
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.7/code/lib/cli)

---
updated-dependencies:
- dependency-name: "@storybook/addon-a11y"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-actions"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-essentials"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-interactions"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/blocks"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/components"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/icons"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: storybook
- dependency-name: "@storybook/manager-api"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/react"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/react-vite"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/theming"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-11 15:10:38 +00:00
dependabot[bot]
bf62444403 Bump @testing-library/react (#3866)
Bumps the testing-library group in /extensions/ql-vscode with 1 update: [@testing-library/react](https://github.com/testing-library/react-testing-library).


Updates `@testing-library/react` from 16.0.1 to 16.1.0
- [Release notes](https://github.com/testing-library/react-testing-library/releases)
- [Changelog](https://github.com/testing-library/react-testing-library/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/react-testing-library/compare/v16.0.1...v16.1.0)

---
updated-dependencies:
- dependency-name: "@testing-library/react"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: testing-library
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-11 15:10:14 +00:00
dependabot[bot]
de6bd05a29 Bump msw from 2.2.13 to 2.6.8 in /extensions/ql-vscode (#3868)
Bumps [msw](https://github.com/mswjs/msw) from 2.2.13 to 2.6.8.
- [Release notes](https://github.com/mswjs/msw/releases)
- [Changelog](https://github.com/mswjs/msw/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mswjs/msw/compare/v2.2.13...v2.6.8)

---
updated-dependencies:
- dependency-name: msw
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-11 15:00:38 +00:00
Koen Vlaswinkel
40fa5aaed8 Merge pull request #3863 from github/koesie10/change-dependabot-e2e
Change Dependabot Docker directory for e2e tests
2024-12-11 15:08:59 +01:00
Koen Vlaswinkel
24bb6bbf32 Merge pull request #3864 from github/github-action/bump-node-version
Bump Node version to v20.18.0
2024-12-11 13:49:02 +01:00
github-actions[bot]
25f5dbfdcf Bump Node version to v20.18.0 2024-12-11 12:26:19 +00:00
Koen Vlaswinkel
e98313a893 Change Dependabot Docker directory for e2e tests 2024-12-11 10:13:23 +01:00
Koen Vlaswinkel
7610de24c8 Merge pull request #3859 from github/koesie10/upgrade-e2e-versions
Upgrade code-server version for E2E tests
2024-12-11 10:10:32 +01:00
github-actions[bot]
a6da43b9e9 Bump CLI version from v2.19.4 to v2.20.0 for integration tests (#3861)
Co-authored-by: github-actions[bot] <github-actions@github.com>
2024-12-10 12:22:20 +00:00
dependabot[bot]
ffe8248040 Bump @babel/plugin-transform-modules-commonjs in /extensions/ql-vscode (#3857)
Bumps [@babel/plugin-transform-modules-commonjs](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-modules-commonjs) from 7.24.7 to 7.26.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.26.3/packages/babel-plugin-transform-modules-commonjs)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-modules-commonjs"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-09 09:26:37 +00:00
Koen Vlaswinkel
54e531d175 Upgrade code-server version for E2E tests 2024-12-06 11:28:01 +01:00
Koen Vlaswinkel
ff95caffd0 Merge pull request #3853 from github/koesie10/remove-telemetry-setting
Remove extension telemetry setting and use VS Code setting instead
2024-12-06 11:12:11 +01:00
Angela P Wen
da108fb115 Merge pull request #3856 from github/dependabot/npm_and_yarn/extensions/ql-vscode/playwright/test-1.49.0
Bump @playwright/test from 1.40.1 to 1.49.0 in /extensions/ql-vscode
2024-12-05 12:43:14 -08:00
dependabot[bot]
0d4850c811 Bump @playwright/test from 1.40.1 to 1.49.0 in /extensions/ql-vscode
Bumps [@playwright/test](https://github.com/microsoft/playwright) from 1.40.1 to 1.49.0.
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.40.1...v1.49.0)

---
updated-dependencies:
- dependency-name: "@playwright/test"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-05 20:20:49 +00:00
Angela P Wen
2b958743e7 Merge pull request #3855 from github/dependabot/npm_and_yarn/extensions/ql-vscode/typescript-eslint-263190dbf1
Bump the typescript-eslint group in /extensions/ql-vscode with 2 updates
2024-12-05 12:19:49 -08:00
Angela P Wen
a6859aba47 Merge pull request #3854 from github/dependabot/npm_and_yarn/extensions/ql-vscode/storybook-4b71ad56d7
Bump the storybook group in /extensions/ql-vscode with 13 updates
2024-12-05 12:19:28 -08:00
dependabot[bot]
bae16bb8d9 Bump the typescript-eslint group in /extensions/ql-vscode with 2 updates
Bumps the typescript-eslint group in /extensions/ql-vscode with 2 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser).


Updates `@typescript-eslint/eslint-plugin` from 8.16.0 to 8.17.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.17.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.16.0 to 8.17.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.17.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: typescript-eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: typescript-eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-05 13:56:02 +00:00
dependabot[bot]
85953fb235 Bump the storybook group in /extensions/ql-vscode with 13 updates
Bumps the storybook group in /extensions/ql-vscode with 13 updates:

| Package | From | To |
| --- | --- | --- |
| [@storybook/addon-a11y](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/a11y) | `8.4.5` | `8.4.6` |
| [@storybook/addon-actions](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/actions) | `8.4.5` | `8.4.6` |
| [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/essentials) | `8.4.5` | `8.4.6` |
| [@storybook/addon-interactions](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/interactions) | `8.4.5` | `8.4.6` |
| [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links) | `8.4.5` | `8.4.6` |
| [@storybook/blocks](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/blocks) | `8.4.5` | `8.4.6` |
| [@storybook/components](https://github.com/storybookjs/storybook/tree/HEAD/code/deprecated/components) | `8.4.5` | `8.4.6` |
| [@storybook/csf](https://github.com/ComponentDriven/csf) | `0.1.11` | `0.1.12` |
| [@storybook/manager-api](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/manager-api) | `8.4.5` | `8.4.6` |
| [@storybook/react](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/react) | `8.4.5` | `8.4.6` |
| [@storybook/react-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/react-vite) | `8.4.5` | `8.4.6` |
| [@storybook/theming](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/theming) | `8.4.5` | `8.4.6` |
| [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/cli) | `8.4.5` | `8.4.6` |


Updates `@storybook/addon-a11y` from 8.4.5 to 8.4.6
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.6/code/addons/a11y)

Updates `@storybook/addon-actions` from 8.4.5 to 8.4.6
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.6/code/addons/actions)

Updates `@storybook/addon-essentials` from 8.4.5 to 8.4.6
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.6/code/addons/essentials)

Updates `@storybook/addon-interactions` from 8.4.5 to 8.4.6
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.6/code/addons/interactions)

Updates `@storybook/addon-links` from 8.4.5 to 8.4.6
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.6/code/addons/links)

Updates `@storybook/blocks` from 8.4.5 to 8.4.6
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.6/code/lib/blocks)

Updates `@storybook/components` from 8.4.5 to 8.4.6
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.6/code/deprecated/components)

Updates `@storybook/csf` from 0.1.11 to 0.1.12
- [Release notes](https://github.com/ComponentDriven/csf/releases)
- [Changelog](https://github.com/ComponentDriven/csf/blob/v0.1.12/CHANGELOG.md)
- [Commits](https://github.com/ComponentDriven/csf/compare/v0.1.11...v0.1.12)

Updates `@storybook/manager-api` from 8.4.5 to 8.4.6
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.6/code/lib/manager-api)

Updates `@storybook/react` from 8.4.5 to 8.4.6
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.6/code/renderers/react)

Updates `@storybook/react-vite` from 8.4.5 to 8.4.6
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.6/code/frameworks/react-vite)

Updates `@storybook/theming` from 8.4.5 to 8.4.6
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.6/code/lib/theming)

Updates `storybook` from 8.4.5 to 8.4.6
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.6/code/lib/cli)

---
updated-dependencies:
- dependency-name: "@storybook/addon-a11y"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-actions"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-essentials"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-interactions"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/blocks"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/components"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/csf"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/manager-api"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/react"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/react-vite"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/theming"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-05 13:55:20 +00:00
Koen Vlaswinkel
de9fd276d1 Remove telemetry step from E2E tests 2024-12-05 12:12:02 +01:00
Koen Vlaswinkel
9506c17a79 Update CHANGELOG 2024-12-05 11:53:20 +01:00
Koen Vlaswinkel
036c60b494 Add notification when enabling telemetry 2024-12-05 11:47:48 +01:00
Koen Vlaswinkel
52beaeded2 Remove extension telemetry setting and use VS Code setting instead 2024-12-03 10:46:25 +01:00
Angela P Wen
b1a15d8340 Merge pull request #3849 from github/dependabot/npm_and_yarn/extensions/ql-vscode/vite-6.0.1
Bump vite from 5.4.6 to 6.0.1 in /extensions/ql-vscode
2024-12-02 10:52:38 -08:00
Koen Vlaswinkel
815af5cc48 Merge pull request #3852 from github/github-action/bump-cli
Bump CLI Version to v2.19.4 for integration tests
2024-12-02 17:14:08 +01:00
github-actions[bot]
b331cb019c Bump CLI version from v2.19.3 to v2.19.4 for integration tests 2024-12-02 13:46:31 +00:00
Stephan Brandauer
d03b3486ee Merge pull request #3845 from github/kaeluka/4654-fix-spurious-errors
Fix Spurious Compilation Errors
2024-12-02 11:30:47 +01:00
Stephan Brandauer
17542d1041 remove notification, when we are restarting the language client anyways 2024-12-02 08:16:37 +00:00
Stephan Brandauer
6fd3d205a5 fix typo 2024-11-29 08:31:30 +00:00
Stephan Brandauer
aca005a777 notify language client of updated packages from cli.ts 2024-11-29 08:16:19 +00:00
Nora
0f88b63392 Merge pull request #3850 from github/dependabot/npm_and_yarn/extensions/ql-vscode/types/react-18.3.12
Bump @types/react from 18.3.1 to 18.3.12 in /extensions/ql-vscode
2024-11-28 17:19:11 +01:00
Nora
52fe22a0be Merge pull request #3848 from github/dependabot/npm_and_yarn/extensions/ql-vscode/eslint-plugin-react-7.37.2
Bump eslint-plugin-react from 7.37.1 to 7.37.2 in /extensions/ql-vscode
2024-11-28 17:18:31 +01:00
Nora
89729d1831 Merge pull request #3847 from github/dependabot/npm_and_yarn/extensions/ql-vscode/typescript-eslint-3e3dd9b88a
Bump the typescript-eslint group in /extensions/ql-vscode with 2 updates
2024-11-28 17:18:18 +01:00
dependabot[bot]
b212ee6a4b Bump @types/react from 18.3.1 to 18.3.12 in /extensions/ql-vscode
Bumps [@types/react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react) from 18.3.1 to 18.3.12.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react)

---
updated-dependencies:
- dependency-name: "@types/react"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-28 13:20:54 +00:00
dependabot[bot]
580ed9f54f Bump vite from 5.4.6 to 6.0.1 in /extensions/ql-vscode
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 5.4.6 to 6.0.1.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/create-vite@6.0.1/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-28 13:20:44 +00:00
dependabot[bot]
52683302f2 Bump eslint-plugin-react from 7.37.1 to 7.37.2 in /extensions/ql-vscode
Bumps [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) from 7.37.1 to 7.37.2.
- [Release notes](https://github.com/jsx-eslint/eslint-plugin-react/releases)
- [Changelog](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.37.1...v7.37.2)

---
updated-dependencies:
- dependency-name: eslint-plugin-react
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-28 13:20:28 +00:00
dependabot[bot]
cc4513c927 Bump the typescript-eslint group in /extensions/ql-vscode with 2 updates
Bumps the typescript-eslint group in /extensions/ql-vscode with 2 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser).


Updates `@typescript-eslint/eslint-plugin` from 8.15.0 to 8.16.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.16.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.15.0 to 8.16.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.16.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: typescript-eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: typescript-eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-28 13:20:17 +00:00
Koen Vlaswinkel
f256c00ced Merge pull request #3846 from github/github-action/bump-node-version
Bump Node version to v20.18.0
2024-11-28 14:07:17 +01:00
github-actions[bot]
15be2d14b3 Bump Node version to v20.18.0 2024-11-28 12:26:14 +00:00
Koen Vlaswinkel
ff501552a3 Merge pull request #3844 from github/koesie10/fix-update-node-version
Fix update-node-version script
2024-11-27 14:35:31 +01:00
Asger F
aa528c6037 Remove unused export 2024-11-27 14:14:52 +01:00
Asger F
c99bf5bb9f Only warn about cache hits in comparison mode 2024-11-27 14:14:52 +01:00
Asger F
afa3d558c6 Factor header rows into a component 2024-11-27 14:14:51 +01:00
Asger F
b9d15511cb Hide "sort by" dropdown when there is no delta to sort by 2024-11-27 14:14:51 +01:00
Asger F
8a58279e67 Move "total" row to the top and render the metric 2024-11-27 14:14:51 +01:00
Asger F
d37469fc94 Add 'per evaluation' option next to metric 2024-11-27 14:14:51 +01:00
Asger F
2cae71c657 Add predicate-renaming support 2024-11-27 14:14:51 +01:00
Asger F
568f0827b2 Fix some crashes when pretty-printing an empty name
Predicate names can't be empty, but it can happen with the renaming feature added in the next commit.
2024-11-27 13:47:57 +01:00
Asger F
4a835b8711 Factor out rendering of table body to a memoized component 2024-11-27 13:47:57 +01:00
Asger F
ab00152ce2 Group together rows by fingerprinting 2024-11-27 13:47:57 +01:00
Asger F
48954c7d22 Rename TRow -> Row 2024-11-27 13:47:57 +01:00
Asger F
9a0699f50a Refactor predicate row into a separate component 2024-11-27 13:47:57 +01:00
Asger F
eec42c5532 Split renderAbsoluteValue into renderOptionalValue and renderPredicateMetric 2024-11-27 13:47:57 +01:00
Asger F
d008963602 Refactor OptionalValue 2024-11-27 13:47:57 +01:00
Asger F
9800fa1333 Use interface instead of type alias for TRow 2024-11-27 13:47:57 +01:00
Asger F
62f3b4f696 Reformat code again
Only contains formatting changes
2024-11-27 13:47:57 +01:00
Asger F
6f7eb74496 Reset hasCacheMismatch when rebuilding 'rows' 2024-11-27 13:47:57 +01:00
Asger F
6568b569a1 Also useMemo the 'total' row computation 2024-11-27 13:47:57 +01:00
Asger F
558d957eb7 Reformat code 2024-11-27 13:47:57 +01:00
Asger F
20f6e3d45c Use useMemo a few places to speed up UI interactions 2024-11-27 13:47:57 +01:00
Asger F
b05ec33ba3 Use useMemo for 'nameSet' 2024-11-27 13:47:57 +01:00
Asger F
1d2c2cfcf9 Allow word wrap to break anywhere 2024-11-27 13:47:56 +01:00
Taus
e039f6bc52 Simplify view when not in comparison mode 2024-11-27 13:47:56 +01:00
Taus
6d4427e59c compare-perf: Add support for selecting a single run as input
A very hacky implementation that simply instantiates an empty
`PerformanceOverviewScanner` as the "from" column (i.e. with all values
empty). To see it in action, select a single query run in the query
history and pick "Compare Performance" from the context menu. Then
select the "Single run" option when prompted.
2024-11-27 13:47:56 +01:00
Esben Sparre Andreasen
412338c717 feat: parallel log scanning 2024-11-27 13:47:56 +01:00
Asger F
ccf2dc64ac Simplify datasets assignment 2024-11-27 13:47:56 +01:00
Asger F
453aa833f2 Check for nullness of 'data' in a separate component
This ensures we can use hooks after the check in the main component
2024-11-27 13:47:56 +01:00
Asger F
260bf0e8d1 Add option to choose metric 2024-11-27 13:47:56 +01:00
Taus
876c5b6091 Colorize positive/negative deltas 2024-11-27 13:47:56 +01:00
Asger F
317e52c0e7 Also abbreviate RA names in predicate overview 2024-11-27 13:47:56 +01:00
Asger F
03ca407713 Make "..." clickable to reveal abbreviated name 2024-11-27 13:47:56 +01:00
Asger F
58afeba1ac Apply a background color to the pipeline header rows 2024-11-27 13:47:56 +01:00
Asger F
8268d6812f Apply styling to RA predicate names 2024-11-27 13:47:56 +01:00
Asger F
70ec5704c8 Make RAPrettyPrinter generate JSX fragments 2024-11-27 13:47:55 +01:00
Taus
aaf23eae72 compare-perf: Add support for sorting options
Adds a dropdown with (at present) two options: sorting by delta and
sorting by absolute delta.
2024-11-27 13:47:55 +01:00
Asger F
96aa770e85 Show evaluation and iteration counts in table 2024-11-27 13:47:55 +01:00
Asger F
3b0697771d Add Compare Performance command (WIP) 2024-11-27 13:47:55 +01:00
Koen Vlaswinkel
a5b64d6459 Fix update-node-version script 2024-11-27 13:30:04 +01:00
Stephan Brandauer
60c15a0eb2 Merge pull request #3842 from github/update-docs
update ambiguous docs
2024-11-26 10:39:32 +01:00
Stephan Brandauer
f5cd48d9d9 update ambiguous docs 2024-11-26 09:26:48 +00:00
Koen Vlaswinkel
94434f4397 Merge pull request #3841 from github/koesie10/bump-cross-spawn
Bump cross-spawn version
2024-11-26 09:21:36 +01:00
Koen Vlaswinkel
f77ae4cd69 Bump cross-spawn version 2024-11-25 14:02:01 +01:00
Koen Vlaswinkel
5a3aeb6332 Merge pull request #3839 from github/koesie10/switch-to-execa
Remove unmaintained child-process-promise
2024-11-25 14:00:45 +01:00
Koen Vlaswinkel
86f37f408f Merge pull request #3840 from github/github-action/bump-node-version
Bump Node version to v20.18.0
2024-11-25 09:38:26 +01:00
github-actions[bot]
5d7db66902 Bump Node version to v20.18.0 2024-11-24 12:25:50 +00:00
Koen Vlaswinkel
87a470dde6 Remove unmaintained child-process-promise 2024-11-22 14:23:19 +01:00
Asger F
dba6718649 Merge pull request #3833 from asgerf/asgerf/graph-viewer-scrollpos
Avoid resetting scroll position after clicking a node in the graph viewer
2024-11-22 10:19:27 +01:00
Koen Vlaswinkel
2bcdf93996 Merge pull request #3836 from github/dependabot/npm_and_yarn/extensions/ql-vscode/styled-components-6.1.13
Bump styled-components from 6.1.9 to 6.1.13 in /extensions/ql-vscode
2024-11-21 14:48:59 +01:00
Koen Vlaswinkel
1468b2830b Merge pull request #3837 from github/dependabot/npm_and_yarn/extensions/ql-vscode/floating-ui/react-0.26.28
Bump @floating-ui/react from 0.26.24 to 0.26.28 in /extensions/ql-vscode
2024-11-21 14:47:22 +01:00
Koen Vlaswinkel
5a078d35e0 Merge pull request #3835 from github/dependabot/npm_and_yarn/extensions/ql-vscode/typescript-eslint-a27836cc37
Bump the typescript-eslint group in /extensions/ql-vscode with 2 updates
2024-11-21 14:47:03 +01:00
Koen Vlaswinkel
5ebc76a005 Merge pull request #3834 from github/dependabot/npm_and_yarn/extensions/ql-vscode/storybook-fa5ae6ca14
Bump the storybook group in /extensions/ql-vscode with 12 updates
2024-11-21 14:46:36 +01:00
dependabot[bot]
badaedd1fe Bump @floating-ui/react from 0.26.24 to 0.26.28 in /extensions/ql-vscode
Bumps [@floating-ui/react](https://github.com/floating-ui/floating-ui/tree/HEAD/packages/react) from 0.26.24 to 0.26.28.
- [Release notes](https://github.com/floating-ui/floating-ui/releases)
- [Changelog](https://github.com/floating-ui/floating-ui/blob/master/packages/react/CHANGELOG.md)
- [Commits](https://github.com/floating-ui/floating-ui/commits/@floating-ui/react@0.26.28/packages/react)

---
updated-dependencies:
- dependency-name: "@floating-ui/react"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-21 13:34:35 +00:00
dependabot[bot]
06fb1bed5a Bump styled-components from 6.1.9 to 6.1.13 in /extensions/ql-vscode
Bumps [styled-components](https://github.com/styled-components/styled-components) from 6.1.9 to 6.1.13.
- [Release notes](https://github.com/styled-components/styled-components/releases)
- [Commits](https://github.com/styled-components/styled-components/compare/v6.1.9...v6.1.13)

---
updated-dependencies:
- dependency-name: styled-components
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-21 13:34:22 +00:00
dependabot[bot]
15d2e4ee6b Bump the typescript-eslint group in /extensions/ql-vscode with 2 updates
Bumps the typescript-eslint group in /extensions/ql-vscode with 2 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser).


Updates `@typescript-eslint/eslint-plugin` from 8.14.0 to 8.15.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.15.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.14.0 to 8.15.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.15.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: typescript-eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: typescript-eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-21 13:34:08 +00:00
dependabot[bot]
4c2e0ccdda Bump the storybook group in /extensions/ql-vscode with 12 updates
Bumps the storybook group in /extensions/ql-vscode with 12 updates:

| Package | From | To |
| --- | --- | --- |
| [@storybook/addon-a11y](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/a11y) | `8.4.3` | `8.4.5` |
| [@storybook/addon-actions](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/actions) | `8.4.3` | `8.4.5` |
| [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/essentials) | `8.4.3` | `8.4.5` |
| [@storybook/addon-interactions](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/interactions) | `8.4.3` | `8.4.5` |
| [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links) | `8.4.3` | `8.4.5` |
| [@storybook/blocks](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/blocks) | `8.4.3` | `8.4.5` |
| [@storybook/components](https://github.com/storybookjs/storybook/tree/HEAD/code/deprecated/components) | `8.4.3` | `8.4.5` |
| [@storybook/manager-api](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/manager-api) | `8.4.3` | `8.4.5` |
| [@storybook/react](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/react) | `8.4.3` | `8.4.5` |
| [@storybook/react-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/react-vite) | `8.4.3` | `8.4.5` |
| [@storybook/theming](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/theming) | `8.4.3` | `8.4.5` |
| [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/cli) | `8.4.3` | `8.4.5` |


Updates `@storybook/addon-a11y` from 8.4.3 to 8.4.5
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.5/code/addons/a11y)

Updates `@storybook/addon-actions` from 8.4.3 to 8.4.5
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.5/code/addons/actions)

Updates `@storybook/addon-essentials` from 8.4.3 to 8.4.5
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.5/code/addons/essentials)

Updates `@storybook/addon-interactions` from 8.4.3 to 8.4.5
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.5/code/addons/interactions)

Updates `@storybook/addon-links` from 8.4.3 to 8.4.5
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.5/code/addons/links)

Updates `@storybook/blocks` from 8.4.3 to 8.4.5
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.5/code/lib/blocks)

Updates `@storybook/components` from 8.4.3 to 8.4.5
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.5/code/deprecated/components)

Updates `@storybook/manager-api` from 8.4.3 to 8.4.5
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.5/code/lib/manager-api)

Updates `@storybook/react` from 8.4.3 to 8.4.5
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.5/code/renderers/react)

Updates `@storybook/react-vite` from 8.4.3 to 8.4.5
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.5/code/frameworks/react-vite)

Updates `@storybook/theming` from 8.4.3 to 8.4.5
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.5/code/lib/theming)

Updates `storybook` from 8.4.3 to 8.4.5
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.5/code/lib/cli)

---
updated-dependencies:
- dependency-name: "@storybook/addon-a11y"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-actions"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-essentials"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-interactions"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/blocks"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/components"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/manager-api"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/react"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/react-vite"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/theming"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-21 13:33:33 +00:00
Asger F
b840c38886 Merge pull request #3831 from asgerf/asgerf/streaming-jsonl
Add streaming 'jsonl' parser
2024-11-21 08:47:49 +01:00
Asger F
99175e78b0 Avoid resetting scroll position after clicking a node in the graph viewer 2024-11-20 14:48:46 +01:00
Asger F
46c284d2ed Merge pull request #3803 from asgerf/asgerf/factor-out-evaluator-log-paths
Refactor: Store EvaluatorLogPaths object on LocalQueryInfo
2024-11-20 14:24:46 +01:00
Koen Vlaswinkel
1ac725b1c4 Merge pull request #3832 from github/koesie10/azure-mi-publishing-docs
Remove docs about Azure DevOps PAT
2024-11-20 13:17:31 +01:00
Asger F
eee593973d Fix typo in 'evaluatorLogPaths' 2024-11-20 11:22:32 +01:00
Asger F
57e2b51b43 Add a file comment to the benchmark script 2024-11-20 11:19:31 +01:00
Asger F
b90cfb670b Move some calls into the try block 2024-11-20 11:12:37 +01:00
Asger F
d05cdf49ec Fix missing import 2024-11-20 11:11:42 +01:00
Asger F
38849f70f5 Apply suggestions from code review
Co-authored-by: Andrew Eisenberg <aeisenberg@github.com>
2024-11-20 11:06:10 +01:00
Koen Vlaswinkel
6d665ea5c8 Remove docs about Azure DevOps PAT 2024-11-20 10:45:38 +01:00
Andrew Eisenberg
025737a18b Merge pull request #3797 from reitowo/main-multiple-folder
Allow import all database subfolders by selecting a folder
2024-11-19 15:15:24 -08:00
Andrew Eisenberg
e7e95e2511 Clarify CHANGELOG.md 2024-11-19 08:51:31 -08:00
reito
8b3add82b1 fix: remove title of importing status 2024-11-20 00:05:42 +08:00
reito
5b854bc1cd fix: step message 2024-11-20 00:05:42 +08:00
reito
9f1fd2c8af fix: trim error 2024-11-20 00:05:42 +08:00
reito
17a6076732 fix: get error message 2024-11-20 00:05:42 +08:00
reito
1b007c2586 Apply suggestions from code review
Co-authored-by: Andrew Eisenberg <aeisenberg@github.com>
2024-11-20 00:05:42 +08:00
reito
1f6a7afffa fix: unify descriptions. 2024-11-20 00:05:42 +08:00
reito
5d4f75b72e fix: testproj need to be with a dot 2024-11-20 00:05:42 +08:00
reito
8170c46042 feat: extract common logic 2024-11-20 00:05:42 +08:00
reito
a93bf1469b Apply suggestions from code review
Co-authored-by: Andrew Eisenberg <aeisenberg@github.com>
2024-11-20 00:05:42 +08:00
reito
775e6dc354 fix: unify naming 2024-11-20 00:05:42 +08:00
reito
c84331e1a3 fix: revert extra return type 2024-11-20 00:05:42 +08:00
reito
955f8c8ab4 feat: skip 0 folders 2024-11-20 00:05:42 +08:00
reito
1e749ec793 feat: move import folders out of original function, optimize logs 2024-11-20 00:05:42 +08:00
reito
fb6fac8803 更新 package.json
Co-authored-by: Andrew Eisenberg <aeisenberg@github.com>
2024-11-20 00:05:42 +08:00
reito
1ec341a744 更新 CHANGELOG.md
Co-authored-by: Andrew Eisenberg <aeisenberg@github.com>
2024-11-20 00:05:42 +08:00
reito
736dc46b63 Allow import all database subfolders by selecting a folder 2024-11-20 00:05:42 +08:00
Asger F
bb1da9c6ff Explain why we need to stream and why not use readline 2024-11-19 13:28:41 +01:00
Asger F
2cde3b9c2f Add benchmark script
The current build setup doesn't seem to have a concept for benchmark
scripts, so for now you'll have to run it with something like ts-node.
2024-11-19 11:23:46 +01:00
Asger F
fac7961e2d Add streaming jsonl parser 2024-11-19 11:23:02 +01:00
Asger F
4e32a108a6 Make 'log' field non-nullable again. 2024-11-19 11:12:21 +01:00
Koen Vlaswinkel
9a0bff6ebb Merge pull request #3830 from github/koesie10/azure-mi-publishing
Publish extension using Azure credentials
2024-11-19 10:54:55 +01:00
Koen Vlaswinkel
d5f3c77690 Publish extension using Azure credentials 2024-11-18 14:00:40 +01:00
Asger F
ea45e389c4 Merge pull request #3802 from asgerf/asgerf/use-message-from-extension
Add useMessageFromExtension hook
2024-11-18 12:47:42 +01:00
Andrew Eisenberg
873d4e3e7e Merge pull request #3827 from github/aeisenberg/ci-badge 2024-11-15 10:30:25 -08:00
Andrew Eisenberg
eca125c24e Use correct CI badge URL
See docs https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/adding-a-workflow-status-badge.
2024-11-15 10:03:36 -08:00
Koen Vlaswinkel
6162d268ba Merge pull request #3812 from github/dependabot/npm_and_yarn/extensions/ql-vscode/typescript-eslint-8fcb7ca9e0
Bump the typescript-eslint group in /extensions/ql-vscode with 2 updates
2024-11-14 14:51:30 +01:00
Koen Vlaswinkel
6ba2a19c5e Merge pull request #3811 from github/dependabot/npm_and_yarn/extensions/ql-vscode/storybook-ab8a791d36
Bump the storybook group in /extensions/ql-vscode with 12 updates
2024-11-14 14:51:14 +01:00
Koen Vlaswinkel
d97db8c02a Merge pull request #3813 from github/dependabot/npm_and_yarn/extensions/ql-vscode/markdownlint-cli2-formatter-pretty-0.0.7
Bump markdownlint-cli2-formatter-pretty from 0.0.6 to 0.0.7 in /extensions/ql-vscode
2024-11-14 14:50:54 +01:00
Koen Vlaswinkel
acc7025555 Merge pull request #3814 from github/dependabot/npm_and_yarn/extensions/ql-vscode/eslint-import-resolver-typescript-3.6.3
Bump eslint-import-resolver-typescript from 3.6.1 to 3.6.3 in /extensions/ql-vscode
2024-11-14 14:50:35 +01:00
dependabot[bot]
84a31a940e Bump eslint-import-resolver-typescript in /extensions/ql-vscode
Bumps [eslint-import-resolver-typescript](https://github.com/import-js/eslint-import-resolver-typescript) from 3.6.1 to 3.6.3.
- [Release notes](https://github.com/import-js/eslint-import-resolver-typescript/releases)
- [Changelog](https://github.com/import-js/eslint-import-resolver-typescript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/import-js/eslint-import-resolver-typescript/compare/v3.6.1...v3.6.3)

---
updated-dependencies:
- dependency-name: eslint-import-resolver-typescript
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-14 13:14:53 +00:00
dependabot[bot]
24cee731fe Bump markdownlint-cli2-formatter-pretty in /extensions/ql-vscode
Bumps [markdownlint-cli2-formatter-pretty](https://github.com/DavidAnson/markdownlint-cli2) from 0.0.6 to 0.0.7.
- [Changelog](https://github.com/DavidAnson/markdownlint-cli2/blob/main/CHANGELOG.md)
- [Commits](https://github.com/DavidAnson/markdownlint-cli2/compare/v0.0.6...v0.0.7)

---
updated-dependencies:
- dependency-name: markdownlint-cli2-formatter-pretty
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-14 13:14:42 +00:00
dependabot[bot]
471bf28bb3 Bump the typescript-eslint group in /extensions/ql-vscode with 2 updates
Bumps the typescript-eslint group in /extensions/ql-vscode with 2 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser).


Updates `@typescript-eslint/eslint-plugin` from 8.13.0 to 8.14.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.14.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.13.0 to 8.14.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.14.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: typescript-eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: typescript-eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-14 13:14:31 +00:00
dependabot[bot]
8ce5b920eb Bump the storybook group in /extensions/ql-vscode with 12 updates
Bumps the storybook group in /extensions/ql-vscode with 12 updates:

| Package | From | To |
| --- | --- | --- |
| [@storybook/addon-a11y](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/a11y) | `8.4.2` | `8.4.3` |
| [@storybook/addon-actions](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/actions) | `8.4.2` | `8.4.3` |
| [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/essentials) | `8.4.2` | `8.4.3` |
| [@storybook/addon-interactions](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/interactions) | `8.4.2` | `8.4.3` |
| [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links) | `8.4.2` | `8.4.3` |
| [@storybook/blocks](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/blocks) | `8.4.2` | `8.4.3` |
| [@storybook/components](https://github.com/storybookjs/storybook/tree/HEAD/code/deprecated/components) | `8.4.2` | `8.4.3` |
| [@storybook/manager-api](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/manager-api) | `8.4.2` | `8.4.3` |
| [@storybook/react](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/react) | `8.4.2` | `8.4.3` |
| [@storybook/react-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/react-vite) | `8.4.2` | `8.4.3` |
| [@storybook/theming](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/theming) | `8.4.2` | `8.4.3` |
| [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/cli) | `8.4.2` | `8.4.3` |


Updates `@storybook/addon-a11y` from 8.4.2 to 8.4.3
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.3/code/addons/a11y)

Updates `@storybook/addon-actions` from 8.4.2 to 8.4.3
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.3/code/addons/actions)

Updates `@storybook/addon-essentials` from 8.4.2 to 8.4.3
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.3/code/addons/essentials)

Updates `@storybook/addon-interactions` from 8.4.2 to 8.4.3
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.3/code/addons/interactions)

Updates `@storybook/addon-links` from 8.4.2 to 8.4.3
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.3/code/addons/links)

Updates `@storybook/blocks` from 8.4.2 to 8.4.3
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.3/code/lib/blocks)

Updates `@storybook/components` from 8.4.2 to 8.4.3
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.3/code/deprecated/components)

Updates `@storybook/manager-api` from 8.4.2 to 8.4.3
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.3/code/lib/manager-api)

Updates `@storybook/react` from 8.4.2 to 8.4.3
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.3/code/renderers/react)

Updates `@storybook/react-vite` from 8.4.2 to 8.4.3
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.3/code/frameworks/react-vite)

Updates `@storybook/theming` from 8.4.2 to 8.4.3
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.3/code/lib/theming)

Updates `storybook` from 8.4.2 to 8.4.3
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.3/code/lib/cli)

---
updated-dependencies:
- dependency-name: "@storybook/addon-a11y"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-actions"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-essentials"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-interactions"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/blocks"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/components"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/manager-api"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/react"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/react-vite"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/theming"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-14 13:13:53 +00:00
Asger F
fa3e3ff669 Initialise object even if evaluator log paths were omitted 2024-11-13 13:30:38 +01:00
Asger F
e146e7a314 Refactor: Store EvaluatorLogPaths object on LocalQueryInfo
Previously the fields from EvaluatorLogPaths were copied 1:1 into LocalQueryInfo
but under different names. It seems easier to keep track of the different kinds of logs if
they are called the same everywhere.
2024-11-13 09:39:26 +01:00
Asger F
bd38355591 Fixup some unused imports 2024-11-12 15:33:04 +01:00
Asger F
06b45394dd Fixup indentation
This commit only contains formatting changes
2024-11-12 15:26:37 +01:00
Asger F
c9fbafb919 Factor out hook for receiving messages from the extension 2024-11-12 15:25:52 +01:00
Koen Vlaswinkel
5969c6f5d3 Merge pull request #3801 from github/dependabot/npm_and_yarn/extensions/ql-vscode/npm_and_yarn-616e94b022
Bump the npm_and_yarn group in /extensions/ql-vscode with 2 updates
2024-11-12 14:17:01 +01:00
dependabot[bot]
687aceca8f Bump the npm_and_yarn group in /extensions/ql-vscode with 2 updates
Bumps the npm_and_yarn group in /extensions/ql-vscode with 2 updates: [cookie](https://github.com/jshttp/cookie) and [@bundled-es-modules/cookie](https://github.com/bundled-es-modules/cookie).


Updates `cookie` from 0.5.0 to 0.7.2
- [Release notes](https://github.com/jshttp/cookie/releases)
- [Commits](https://github.com/jshttp/cookie/compare/v0.5.0...v0.7.2)

Updates `@bundled-es-modules/cookie` from 2.0.0 to 2.0.1
- [Commits](https://github.com/bundled-es-modules/cookie/commits)

---
updated-dependencies:
- dependency-name: cookie
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: "@bundled-es-modules/cookie"
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-11-12 13:03:01 +00:00
Koen Vlaswinkel
811972e87b Merge pull request #3799 from github/koesie10/modernize-query-history-labels
Use standard configuration variable format for history item labels
2024-11-12 14:01:49 +01:00
Koen Vlaswinkel
71cd892afa Merge pull request #3800 from github/koesie10/fix-variant-analysis-submission-integration-tests
Fix and re-enable variant analysis submission integration tests
2024-11-12 10:25:56 +01:00
Koen Vlaswinkel
11bc465fca Fix and re-enable variant analysis submission integration tests 2024-11-11 15:54:17 +01:00
Koen Vlaswinkel
eeeeadd06d Use standard configuration variable format for history item labels 2024-11-08 13:40:40 +01:00
Koen Vlaswinkel
b44c6024f4 Merge pull request #3792 from github/github-action/bump-node-version
Bump Node version to v20.18.0
2024-11-08 09:45:18 +01:00
Koen Vlaswinkel
67ae86067e Merge pull request #3798 from github/github-action/bump-cli
Bump CLI Version to v2.19.3 for integration tests
2024-11-08 09:44:43 +01:00
github-actions[bot]
7c1cb87647 Bump CLI version from v2.19.2 to v2.19.3 for integration tests 2024-11-07 17:27:16 +00:00
dependabot[bot]
7b2901568b Bump the storybook group in /extensions/ql-vscode with 12 updates (#3793)
Bumps the storybook group in /extensions/ql-vscode with 12 updates:

| Package | From | To |
| --- | --- | --- |
| [@storybook/addon-a11y](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/a11y) | `8.4.0` | `8.4.2` |
| [@storybook/addon-actions](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/actions) | `8.4.0` | `8.4.2` |
| [@storybook/addon-essentials](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/essentials) | `8.4.0` | `8.4.2` |
| [@storybook/addon-interactions](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/interactions) | `8.4.0` | `8.4.2` |
| [@storybook/addon-links](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/links) | `8.4.0` | `8.4.2` |
| [@storybook/blocks](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/blocks) | `8.4.0` | `8.4.2` |
| [@storybook/components](https://github.com/storybookjs/storybook/tree/HEAD/code/deprecated/components) | `8.4.0` | `8.4.2` |
| [@storybook/manager-api](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/manager-api) | `8.4.0` | `8.4.2` |
| [@storybook/react](https://github.com/storybookjs/storybook/tree/HEAD/code/renderers/react) | `8.4.0` | `8.4.2` |
| [@storybook/react-vite](https://github.com/storybookjs/storybook/tree/HEAD/code/frameworks/react-vite) | `8.4.0` | `8.4.2` |
| [@storybook/theming](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/theming) | `8.4.0` | `8.4.2` |
| [storybook](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/cli) | `8.4.0` | `8.4.2` |


Updates `@storybook/addon-a11y` from 8.4.0 to 8.4.2
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.2/code/addons/a11y)

Updates `@storybook/addon-actions` from 8.4.0 to 8.4.2
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.2/code/addons/actions)

Updates `@storybook/addon-essentials` from 8.4.0 to 8.4.2
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.2/code/addons/essentials)

Updates `@storybook/addon-interactions` from 8.4.0 to 8.4.2
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.2/code/addons/interactions)

Updates `@storybook/addon-links` from 8.4.0 to 8.4.2
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.2/code/addons/links)

Updates `@storybook/blocks` from 8.4.0 to 8.4.2
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.2/code/lib/blocks)

Updates `@storybook/components` from 8.4.0 to 8.4.2
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.2/code/deprecated/components)

Updates `@storybook/manager-api` from 8.4.0 to 8.4.2
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.2/code/lib/manager-api)

Updates `@storybook/react` from 8.4.0 to 8.4.2
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.2/code/renderers/react)

Updates `@storybook/react-vite` from 8.4.0 to 8.4.2
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.2/code/frameworks/react-vite)

Updates `@storybook/theming` from 8.4.0 to 8.4.2
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.2/code/lib/theming)

Updates `storybook` from 8.4.0 to 8.4.2
- [Release notes](https://github.com/storybookjs/storybook/releases)
- [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md)
- [Commits](https://github.com/storybookjs/storybook/commits/v8.4.2/code/lib/cli)

---
updated-dependencies:
- dependency-name: "@storybook/addon-a11y"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-actions"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-essentials"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-interactions"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/addon-links"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/blocks"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/components"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/manager-api"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/react"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/react-vite"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: "@storybook/theming"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
- dependency-name: storybook
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: storybook
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 16:57:50 +00:00
dependabot[bot]
deaaeb82b5 Bump @testing-library/jest-dom (#3794)
Bumps the testing-library group in /extensions/ql-vscode with 1 update: [@testing-library/jest-dom](https://github.com/testing-library/jest-dom).


Updates `@testing-library/jest-dom` from 6.6.2 to 6.6.3
- [Release notes](https://github.com/testing-library/jest-dom/releases)
- [Changelog](https://github.com/testing-library/jest-dom/blob/main/CHANGELOG.md)
- [Commits](https://github.com/testing-library/jest-dom/compare/v6.6.2...v6.6.3)

---
updated-dependencies:
- dependency-name: "@testing-library/jest-dom"
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: testing-library
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 16:57:15 +00:00
dependabot[bot]
fdc925bad7 Bump the typescript-eslint group in /extensions/ql-vscode with 2 updates (#3795)
Bumps the typescript-eslint group in /extensions/ql-vscode with 2 updates: [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) and [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser).


Updates `@typescript-eslint/eslint-plugin` from 8.12.2 to 8.13.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.13.0/packages/eslint-plugin)

Updates `@typescript-eslint/parser` from 8.12.2 to 8.13.0
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.13.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: typescript-eslint
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
  dependency-group: typescript-eslint
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 16:56:47 +00:00
dependabot[bot]
86b5a8bbee Bump @types/react-dom from 18.3.0 to 18.3.1 in /extensions/ql-vscode (#3796)
Bumps [@types/react-dom](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-dom) from 18.3.0 to 18.3.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-dom)

---
updated-dependencies:
- dependency-name: "@types/react-dom"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-07 16:55:42 +00:00
github-actions[bot]
563ad5b09d Bump Node version to v20.18.0 2024-11-07 12:25:56 +00:00
Koen Vlaswinkel
e3c79e48b4 Merge pull request #3788 from github/koesie10/fix-update-node-version
Fix update-node-version script for non-existent @types/node version
2024-11-07 09:34:44 +01:00
Anders Starcke Henriksen
8e99bc93c0 Merge pull request #3791 from github/version/bump-to-v1.16.2
Bump version to v1.16.2
2024-11-06 14:20:49 +01:00
github-actions[bot]
7373919843 Bump version to v1.16.2 2024-11-06 13:00:00 +00:00
Anders Starcke Henriksen
1f8996dbe7 Merge pull request #3790 from github/v1.16.1
Release v1.16.1
2024-11-06 13:58:43 +01:00
Koen Vlaswinkel
20a8976d8f Use vite-node instead of ts-node 2024-11-06 13:47:22 +01:00
Koen Vlaswinkel
e7d76f7605 Always try updating Node version 2024-11-06 13:46:55 +01:00
Koen Vlaswinkel
80a6116bef Fix update-node-version script for non-existent @types/node version 2024-11-04 15:35:18 +01:00
71 changed files with 6440 additions and 2700 deletions

View File

@@ -38,7 +38,7 @@ updates:
labels:
- "Update dependencies"
- package-ecosystem: docker
directory: "extensions/ql-vscode/test/e2e"
directory: "extensions/ql-vscode/test/e2e/docker"
schedule:
interval: "weekly"
day: "thursday" # Thursday is arbitrary

View File

@@ -124,8 +124,9 @@ jobs:
needs: build
environment: publish-vscode-marketplace
runs-on: ubuntu-latest
env:
VSCE_TOKEN: ${{ secrets.VSCE_TOKEN }}
permissions:
contents: read
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
@@ -139,9 +140,16 @@ jobs:
with:
name: vscode-codeql-extension
- name: Azure User-assigned managed identity login
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
allow-no-subscriptions: true
enable-AzPSSession: true
- name: Publish to Registry
run: |
npx @vscode/vsce publish -p $VSCE_TOKEN --packagePath *.vsix
run: npx @vscode/vsce publish --azure-credential --packagePath *.vsix
open-vsx-publish:
name: Publish to Open VSX Registry

View File

@@ -34,7 +34,7 @@ jobs:
- name: Update Node version
working-directory: extensions/ql-vscode
run: |
npx ts-node scripts/update-node-version.ts
npx vite-node scripts/update-node-version.ts
shell: bash
- name: Get current Node version
working-directory: extensions/ql-vscode

View File

@@ -78,7 +78,7 @@ $ vscode/scripts/code-cli.sh --install-extension dist/vscode-codeql-*.vsix # if
### Debugging
You can use VS Code to debug the extension without explicitly installing it. Just open this directory as a workspace in VS Code, and hit `F5` to start a debugging session.
You can use VS Code to debug the extension without explicitly installing it. Just open this repository's root directory as a workspace in VS Code, and hit `F5` to start a debugging session.
### Storybook

View File

@@ -6,7 +6,7 @@ The extension is released. You can download it from the [Visual Studio Marketpla
To see what has changed in the last few versions of the extension, see the [Changelog](https://github.com/github/vscode-codeql/blob/main/extensions/ql-vscode/CHANGELOG.md).
[![CI status badge](https://github.com/github/vscode-codeql/workflows/Build%20Extension/badge.svg)](https://github.com/github/vscode-codeql/actions?query=workflow%3A%22Build+Extension%22+branch%3Amain)
[![CI status badge](https://github.com/github/vscode-codeql/actions/workflows/main.yml/badge.svg?branch=main)](https://github.com/github/vscode-codeql/actions?query=workflow%3A%22Build+Extension%22+branch%3Amain)
[![VS Marketplace badge](https://vsmarketplacebadges.dev/version/github.vscode-codeql.svg)](https://marketplace.visualstudio.com/items?itemName=github.vscode-codeql)
## Features

View File

@@ -1,6 +1,5 @@
# Releasing (write access required)
1. Make sure the needed authentication keys are valid. Most likely the Azure DevOps PAT needs to be regenerated. See below.
1. Determine the new version number. We default to increasing the patch version number, but make our own judgement about whether a change is big enough to warrant a minor version bump. Common reasons for a minor bump could include:
- Making substantial new features available to all users. This can include lifting a feature flag.
- Breakage in compatibility with recent versions of the CLI.
@@ -61,7 +60,7 @@
## Secrets and authentication for publishing
Repository administrators, will need to manage the authentication keys for publishing to the VS Code marketplace and Open VSX. Each requires an authentication token.
Repository administrators will need to manage the authentication keys for publishing to the VS Code marketplace and Open VSX. Each requires an authentication token.
To regenerate the Open VSX token:
@@ -70,4 +69,4 @@ To regenerate the Open VSX token:
1. Go to the [Access Tokens](https://open-vsx.org/user-settings/tokens) page and generate a new token.
1. Update the secret in the `publish-open-vsx` environment in the project settings.
To regenerate the VSCode Marketplace token, please see our internal documentation. Note that Azure DevOps PATs expire every 7 days and must be regenerated.
Publishing to the VS Code Marketplace is done using a user-assigned managed identity and should not require the token to be manually updated.

View File

@@ -1 +1 @@
v20.16.0
v20.18.1

View File

@@ -1,17 +1 @@
.vs/**
.vscode/**
.vscode-test/**
typings/**
out/test/**
out/vscode-tests/**
**/@types/**
**/*.ts
test/**
src/**
**/*.map
.gitignore
gulpfile.js/**
tsconfig.json
.prettierrc
vsc-extension-quickstart.md
node_modules/**

View File

@@ -1,5 +1,15 @@
# CodeQL for Visual Studio Code: Changelog
## 1.17.1 - 23 January 2025
- Remove support for CodeQL CLI versions older than 2.18.4. [#3895](https://github.com/github/vscode-codeql/pull/3895)
## 1.17.0 - 20 December 2024
- Add a palette command that allows importing all databases directly inside of a parent folder. [#3797](https://github.com/github/vscode-codeql/pull/3797)
- Only use VS Code telemetry settings instead of using `codeQL.telemetry.enableTelemetry` [#3853](https://github.com/github/vscode-codeql/pull/3853)
- Improve the performance of the results view with large numbers of results. [#3862](https://github.com/github/vscode-codeql/pull/3862)
## 1.16.1 - 6 November 2024
- Support result columns of type `QlBuiltins::BigInt` in quick evaluations. [#3647](https://github.com/github/vscode-codeql/pull/3647)

View File

@@ -1,6 +1,6 @@
import { resolve } from "path";
import { deployPackage } from "./deploy";
import { spawn } from "child-process-promise";
import { spawn } from "cross-spawn";
export async function packageExtension(): Promise<void> {
const deployedPackage = await deployPackage();
@@ -16,16 +16,22 @@ export async function packageExtension(): Promise<void> {
`${deployedPackage.name}-${deployedPackage.version}.vsix`,
),
"--no-dependencies",
"--skip-license",
];
const proc = spawn(resolve(__dirname, "../node_modules/.bin/vsce"), args, {
cwd: deployedPackage.distPath,
});
proc.childProcess.stdout!.on("data", (data) => {
console.log(data.toString());
});
proc.childProcess.stderr!.on("data", (data) => {
console.error(data.toString());
stdio: ["ignore", "inherit", "inherit"],
});
await proc;
await new Promise((resolve, reject) => {
proc.on("error", reject);
proc.on("close", (code) => {
if (code === 0) {
resolve(undefined);
} else {
reject(new Error(`Failed to package extension with code ${code}`));
}
});
});
}

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@
"description": "CodeQL for Visual Studio Code",
"author": "GitHub",
"private": true,
"version": "1.16.1",
"version": "1.17.1",
"publisher": "GitHub",
"license": "MIT",
"icon": "media/VS-marketplace-CodeQL-icon.png",
@@ -14,7 +14,7 @@
},
"engines": {
"vscode": "^1.90.0",
"node": "^20.16.0",
"node": "^20.18.1",
"npm": ">=7.20.6"
},
"categories": [
@@ -42,13 +42,6 @@
"workspaceContains:.git"
],
"main": "./out/extension",
"files": [
"gen/*.js",
"media/**",
"out/**",
"package.json",
"language-configuration.json"
],
"contributes": {
"configurationDefaults": {
"[ql]": {
@@ -302,8 +295,8 @@
"properties": {
"codeQL.queryHistory.format": {
"type": "string",
"default": "%q on %d - %s %r [%t]",
"markdownDescription": "Default string for how to label query history items.\n* %t is the time of the query\n* %q is the human-readable query name\n* %f is the query file name\n* %d is the database name\n* %r is the number of results\n* %s is a status string"
"default": "${queryName} on ${databaseName} - ${status} ${resultCount} [${startTime}]",
"markdownDescription": "Default string for how to label query history items.\n\nThe following variables are supported:\n* **${startTime}** - the time of the query\n* **${queryName}** - the human-readable query name\n* **${queryFileBasename}** - the query file's base name\n* **${queryLanguage}** - the query language\n* **${databaseName}** - the database name\n* **${resultCount}** - the number of results\n* **${status}** - a status string"
},
"codeQL.queryHistory.ttl": {
"type": "number",
@@ -497,16 +490,6 @@
"title": "Telemetry",
"order": 11,
"properties": {
"codeQL.telemetry.enableTelemetry": {
"type": "boolean",
"default": false,
"scope": "application",
"markdownDescription": "Specifies whether to send CodeQL usage telemetry. This setting AND the one of the global telemetry settings (`#telemetry.enableTelemetry#` or `#telemetry.telemetryLevel#`) must be enabled for telemetry to be sent to GitHub. For more information, see the [telemetry documentation](https://codeql.github.com/docs/codeql-for-visual-studio-code/about-telemetry-in-codeql-for-visual-studio-code)",
"tags": [
"telemetry",
"usesOnlineServices"
]
},
"codeQL.telemetry.logTelemetry": {
"type": "boolean",
"default": false,
@@ -839,6 +822,10 @@
"command": "codeQL.chooseDatabaseFolder",
"title": "CodeQL: Choose Database from Folder"
},
{
"command": "codeQL.chooseDatabaseFoldersParent",
"title": "CodeQL: Import All Databases Directly Contained in a Parent Folder"
},
{
"command": "codeQL.chooseDatabaseArchive",
"title": "CodeQL: Choose Database from Archive"
@@ -955,6 +942,10 @@
"command": "codeQLQueryHistory.compareWith",
"title": "Compare Results"
},
{
"command": "codeQLQueryHistory.comparePerformanceWith",
"title": "Compare Performance"
},
{
"command": "codeQLQueryHistory.openOnGithub",
"title": "View Logs"
@@ -1226,6 +1217,11 @@
"group": "3_queryHistory@0",
"when": "viewItem == rawResultsItem || viewItem == interpretedResultsItem"
},
{
"command": "codeQLQueryHistory.comparePerformanceWith",
"group": "3_queryHistory@1",
"when": "viewItem == rawResultsItem && config.codeQL.canary || viewItem == interpretedResultsItem && config.codeQL.canary"
},
{
"command": "codeQLQueryHistory.showQueryLog",
"group": "4_queryHistory@4",
@@ -1729,6 +1725,10 @@
"command": "codeQLQueryHistory.compareWith",
"when": "false"
},
{
"command": "codeQLQueryHistory.comparePerformanceWith",
"when": "false"
},
{
"command": "codeQLQueryHistory.sortByName",
"when": "false"
@@ -1968,7 +1968,7 @@
"prepare": "cd ../.. && husky"
},
"dependencies": {
"@floating-ui/react": "^0.26.24",
"@floating-ui/react": "^0.27.0",
"@octokit/plugin-retry": "^7.1.2",
"@octokit/plugin-throttling": "^9.3.2",
"@octokit/rest": "^21.0.2",
@@ -1977,13 +1977,12 @@
"@vscode/debugprotocol": "^1.68.0",
"@vscode/webview-ui-toolkit": "^1.0.1",
"ajv": "^8.11.0",
"child-process-promise": "^2.2.1",
"chokidar": "^3.6.0",
"d3": "^7.9.0",
"d3-graphviz": "^5.0.2",
"fs-extra": "^11.1.1",
"js-yaml": "^4.1.0",
"msw": "^2.2.13",
"msw": "^2.6.8",
"nanoid": "^5.0.7",
"p-queue": "^8.0.1",
"proper-lockfile": "^4.1.2",
@@ -1993,7 +1992,7 @@
"source-map": "^0.7.4",
"source-map-support": "^0.5.21",
"stream-json": "^1.7.3",
"styled-components": "^6.1.9",
"styled-components": "^6.1.13",
"tmp": "^0.2.1",
"tmp-promise": "^3.0.2",
"tree-kill": "^1.2.2",
@@ -2005,32 +2004,32 @@
},
"devDependencies": {
"@babel/core": "^7.24.6",
"@babel/plugin-transform-modules-commonjs": "^7.24.7",
"@babel/plugin-transform-modules-commonjs": "^7.26.3",
"@babel/preset-env": "^7.24.4",
"@babel/preset-react": "^7.25.9",
"@babel/preset-typescript": "^7.21.4",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
"@faker-js/faker": "^9.0.3",
"@github/markdownlint-github": "^0.6.3",
"@microsoft/eslint-formatter-sarif": "^3.1.0",
"@playwright/test": "^1.40.1",
"@storybook/addon-a11y": "^8.4.0",
"@storybook/addon-actions": "^8.4.0",
"@storybook/addon-essentials": "^8.4.0",
"@storybook/addon-interactions": "^8.4.0",
"@storybook/addon-links": "^8.4.0",
"@playwright/test": "^1.49.0",
"@storybook/addon-a11y": "^8.5.0",
"@storybook/addon-actions": "^8.5.0",
"@storybook/addon-essentials": "^8.5.0",
"@storybook/addon-interactions": "^8.5.0",
"@storybook/addon-links": "^8.5.0",
"@storybook/blocks": "^8.0.2",
"@storybook/components": "^8.4.0",
"@storybook/csf": "^0.1.11",
"@storybook/icons": "^1.2.12",
"@storybook/manager-api": "^8.4.0",
"@storybook/react": "^8.4.0",
"@storybook/react-vite": "^8.4.0",
"@storybook/components": "^8.5.0",
"@storybook/csf": "^0.1.13",
"@storybook/icons": "^1.3.0",
"@storybook/manager-api": "^8.5.0",
"@storybook/react": "^8.5.0",
"@storybook/react-vite": "^8.5.0",
"@storybook/theming": "^8.2.4",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.2",
"@testing-library/react": "^16.0.1",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.1.0",
"@testing-library/user-event": "^14.5.2",
"@types/child-process-promise": "^2.2.1",
"@types/cross-spawn": "^6.0.6",
"@types/d3": "^7.4.0",
"@types/d3-graphviz": "^2.6.6",
"@types/del": "^4.0.0",
@@ -2039,10 +2038,10 @@
"@types/gulp-replace": "^1.1.0",
"@types/jest": "^29.5.12",
"@types/js-yaml": "^4.0.6",
"@types/node": "20.16.*",
"@types/node": "20.17.*",
"@types/proper-lockfile": "^4.1.4",
"@types/react": "^18.3.1",
"@types/react-dom": "^18.3.0",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@types/sarif": "^2.1.2",
"@types/semver": "^7.5.8",
"@types/stream-json": "^1.7.1",
@@ -2052,43 +2051,44 @@
"@types/tmp": "^0.2.6",
"@types/vscode": "1.90.0",
"@types/yauzl": "^2.10.3",
"@typescript-eslint/eslint-plugin": "^8.12.2",
"@typescript-eslint/parser": "^8.12.2",
"@typescript-eslint/eslint-plugin": "^8.19.0",
"@typescript-eslint/parser": "^8.19.0",
"@vscode/test-electron": "^2.3.9",
"@vscode/vsce": "^2.24.0",
"@vscode/vsce": "^3.2.1",
"ansi-colors": "^4.1.1",
"applicationinsights": "^2.9.5",
"cosmiconfig": "^9.0.0",
"cross-env": "^7.0.3",
"cross-spawn": "^7.0.6",
"del": "^6.0.0",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.0.0",
"eslint-import-resolver-typescript": "^3.6.1",
"eslint-import-resolver-typescript": "^3.6.3",
"eslint-plugin-deprecation": "^3.0.0",
"eslint-plugin-etc": "^2.0.2",
"eslint-plugin-github": "^5.0.1",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest-dom": "^5.4.0",
"eslint-plugin-jest-dom": "^5.5.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-react": "^7.37.1",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-storybook": "^0.8.0",
"glob": "^11.0.0",
"gulp": "^5.0.0",
"gulp-esbuild": "^0.12.1",
"gulp-esbuild": "^0.14.0",
"gulp-replace": "^1.1.3",
"gulp-typescript": "^5.0.1",
"husky": "^9.1.5",
"jest": "^29.0.3",
"jest-environment-jsdom": "^29.0.3",
"jest-runner-vscode": "^3.0.1",
"lint-staged": "^15.2.10",
"markdownlint-cli2": "^0.13.0",
"markdownlint-cli2-formatter-pretty": "^0.0.6",
"lint-staged": "^15.3.0",
"markdownlint-cli2": "^0.17.0",
"markdownlint-cli2-formatter-pretty": "^0.0.7",
"npm-run-all": "^4.1.5",
"patch-package": "^8.0.0",
"prettier": "^3.2.5",
"storybook": "^8.4.0",
"storybook": "^8.5.0",
"tar-stream": "^3.1.7",
"through2": "^4.0.2",
"ts-jest": "^29.2.5",
@@ -2096,7 +2096,7 @@
"ts-node": "^10.9.2",
"ts-unused-exports": "^10.1.0",
"typescript": "^5.6.2",
"vite": "^5.4.6",
"vite": "^6.0.1",
"vite-node": "^2.0.5"
},
"lint-staged": {

View File

@@ -1,8 +1,9 @@
import { join, resolve } from "path";
import { execSync } from "child_process";
import { outputFile, readFile, readJSON } from "fs-extra";
import { outputFile, readJSON } from "fs-extra";
import { getVersionInformation } from "./util/vscode-versions";
import { fetchJson } from "./util/fetch";
import { SemVer } from "semver";
const extensionDirectory = resolve(__dirname, "..");
@@ -10,6 +11,29 @@ interface Release {
tag_name: string;
}
interface NpmViewError {
error: {
code: string;
summary: string;
detail: string;
};
}
interface ExecError extends Error {
status: number;
stdout: string;
}
function isExecError(e: unknown): e is ExecError {
return (
e instanceof Error &&
"status" in e &&
typeof e.status === "number" &&
"stdout" in e &&
typeof e.stdout === "string"
);
}
async function updateNodeVersion() {
const latestVsCodeRelease = await fetchJson<Release>(
"https://api.github.com/repos/microsoft/vscode/releases/latest",
@@ -23,19 +47,7 @@ async function updateNodeVersion() {
`VS Code ${versionInformation.vscodeVersion} uses Electron ${versionInformation.electronVersion} and Node ${versionInformation.nodeVersion}`,
);
let currentNodeVersion = (
await readFile(join(extensionDirectory, ".nvmrc"), "utf8")
).trim();
if (currentNodeVersion.startsWith("v")) {
currentNodeVersion = currentNodeVersion.slice(1);
}
if (currentNodeVersion === versionInformation.nodeVersion) {
console.log("Node version is already up to date");
return;
}
console.log("Node version needs to be updated, updating now");
console.log("Updating files related to the Node version");
await outputFile(
join(extensionDirectory, ".nvmrc"),
@@ -49,6 +61,8 @@ async function updateNodeVersion() {
"utf8",
);
const nodeVersion = new SemVer(versionInformation.nodeVersion);
// The @types/node version needs to match the first two parts of the Node
// version, e.g. if the Node version is 18.17.3, the @types/node version
// should be 18.17.*. This corresponds with the documentation at
@@ -56,13 +70,56 @@ async function updateNodeVersion() {
// "The patch version of the type declaration package is unrelated to the library patch version. This allows
// Definitely Typed to safely update type declarations for the same major/minor version of a library."
// 18.17.* is equivalent to >=18.17.0 <18.18.0
const typesNodeVersion = versionInformation.nodeVersion
.split(".")
.slice(0, 2)
.join(".");
// In some cases, the @types/node version matching the exact Node version may not exist, in which case we'll try
// the next lower minor version, and so on, until we find a version that exists.
const typesNodeSemver = new SemVer(nodeVersion);
typesNodeSemver.patch = 0;
// eslint-disable-next-line no-constant-condition
while (true) {
const typesNodeVersion = `${typesNodeSemver.major}.${typesNodeSemver.minor}.*`;
try {
// Check that this version actually exists
console.log(`Checking if @types/node@${typesNodeVersion} exists`);
execSync(`npm view --json "@types/node@${typesNodeVersion}"`, {
encoding: "utf-8",
stdio: "pipe",
maxBuffer: 10 * 1024 * 1024,
});
console.log(`@types/node@${typesNodeVersion} exists`);
// If it exists, we can break out of this loop
break;
} catch (e: unknown) {
if (!isExecError(e)) {
throw e;
}
const error = JSON.parse(e.stdout) as NpmViewError;
if (error.error.code !== "E404") {
throw new Error(error.error.detail);
}
console.log(
`@types/node package doesn't exist for ${typesNodeVersion}, trying a lower version (${error.error.summary})`,
);
// This means the version doesn't exist, so we'll try decrementing the minor version
typesNodeSemver.minor -= 1;
if (typesNodeSemver.minor < 0) {
throw new Error(
`Could not find a suitable @types/node version for Node ${nodeVersion.format()}`,
);
}
}
}
packageJson.engines.node = `^${versionInformation.nodeVersion}`;
packageJson.devDependencies["@types/node"] = `${typesNodeVersion}.*`;
packageJson.devDependencies["@types/node"] =
`${typesNodeSemver.major}.${typesNodeSemver.minor}.*`;
await outputFile(
join(extensionDirectory, "package.json"),

View File

@@ -1,5 +1,5 @@
import { EOL } from "os";
import { spawn } from "child-process-promise";
import { spawn } from "cross-spawn";
import type { ChildProcessWithoutNullStreams } from "child_process";
import { spawn as spawnChildProcess } from "child_process";
import { readFile } from "fs-extra";
@@ -37,6 +37,7 @@ import { LOGGING_FLAGS } from "./cli-command";
import type { CliFeatures, VersionAndFeatures } from "./cli-version";
import { ExitCodeError, getCliError } from "./cli-errors";
import { UserCancellationException } from "../common/vscode/progress";
import type { LanguageClient } from "vscode-languageclient/node";
/**
* The version of the SARIF format that we are using.
@@ -277,6 +278,7 @@ export class CodeQLCliServer implements Disposable {
constructor(
private readonly app: App,
private readonly languageClient: LanguageClient,
private distributionProvider: DistributionProvider,
private cliConfig: CliConfig,
public readonly logger: Logger,
@@ -716,13 +718,7 @@ export class CodeQLCliServer implements Disposable {
// Spawn the CodeQL process
const codeqlPath = await this.getCodeQlPath();
const childPromise = spawn(codeqlPath, args);
// Avoid a runtime message about unhandled rejection.
childPromise.catch(() => {
/**/
});
const child = childPromise.childProcess;
const child = spawn(codeqlPath, args);
let cancellationRegistration: Disposable | undefined = undefined;
try {
@@ -735,16 +731,28 @@ export class CodeQLCliServer implements Disposable {
}
if (logger !== undefined) {
// The human-readable output goes to stderr.
void logStream(child.stderr!, logger);
void logStream(child.stderr, logger);
}
for await (const event of splitStreamAtSeparators(child.stdout!, [
"\0",
])) {
for await (const event of splitStreamAtSeparators(child.stdout, ["\0"])) {
yield event;
}
await childPromise;
await new Promise((resolve, reject) => {
child.on("error", reject);
child.on("close", (code) => {
if (code === 0) {
resolve(undefined);
} else {
reject(
new Error(
`${command} ${commandArgs.join(" ")} failed with code ${code}`,
),
);
}
});
});
} finally {
if (cancellationRegistration !== undefined) {
cancellationRegistration.dispose();
@@ -1578,11 +1586,13 @@ export class CodeQLCliServer implements Disposable {
async packAdd(dir: string, queryLanguage: QueryLanguage) {
const args = ["--dir", dir];
args.push(`codeql/${queryLanguage}-all`);
return this.runCodeQlCliCommand(
const ret = await this.runCodeQlCliCommand(
["pack", "add"],
args,
`Adding and installing ${queryLanguage} pack dependency.`,
);
await this.notifyPackInstalled();
return ret;
}
/**
@@ -1617,16 +1627,18 @@ export class CodeQLCliServer implements Disposable {
args.push(
// Allow prerelease packs from the ql submodule.
"--allow-prerelease",
// Allow the use of --additional-packs argument without issueing a warning
// Allow the use of --additional-packs argument without issuing a warning
"--no-strict-mode",
...this.getAdditionalPacksArg(workspaceFolders),
);
}
return this.runJsonCodeQlCliCommandWithAuthentication(
const ret = await this.runJsonCodeQlCliCommandWithAuthentication(
["pack", "install"],
args,
"Installing pack dependencies",
);
await this.notifyPackInstalled();
return ret;
}
/**
@@ -1744,6 +1756,17 @@ export class CodeQLCliServer implements Disposable {
this._versionChangedListeners.push(listener);
}
/**
* This method should be called after a pack has been installed.
*
* This restarts the language client. Restarting the language client has the
* effect of removing compilation errors in open ql/qll files that are caused
* by the pack not having been installed previously.
*/
private async notifyPackInstalled() {
await this.languageClient.restart();
}
private async refreshVersion(): Promise<VersionAndFeatures> {
const distribution = await this.distributionProvider.getDistribution();
switch (distribution.kind) {
@@ -1881,7 +1904,7 @@ function shouldDebugCliServer() {
export class CliVersionConstraint {
// 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.
public static OLDEST_SUPPORTED_CLI_VERSION = new SemVer("2.16.6");
public static OLDEST_SUPPORTED_CLI_VERSION = new SemVer("2.18.4");
constructor(private readonly cli: CodeQLCliServer) {
/**/

View File

@@ -180,6 +180,7 @@ export type QueryHistoryCommands = {
"codeQLQueryHistory.removeHistoryItemContextInline": TreeViewContextMultiSelectionCommandFunction<QueryHistoryInfo>;
"codeQLQueryHistory.renameItem": TreeViewContextMultiSelectionCommandFunction<QueryHistoryInfo>;
"codeQLQueryHistory.compareWith": TreeViewContextMultiSelectionCommandFunction<QueryHistoryInfo>;
"codeQLQueryHistory.comparePerformanceWith": TreeViewContextMultiSelectionCommandFunction<QueryHistoryInfo>;
"codeQLQueryHistory.showEvalLog": TreeViewContextMultiSelectionCommandFunction<QueryHistoryInfo>;
"codeQLQueryHistory.showEvalLogSummary": TreeViewContextMultiSelectionCommandFunction<QueryHistoryInfo>;
"codeQLQueryHistory.showEvalLogViewer": TreeViewContextMultiSelectionCommandFunction<QueryHistoryInfo>;
@@ -211,6 +212,7 @@ export type LanguageSelectionCommands = {
export type LocalDatabasesCommands = {
// Command palette commands
"codeQL.chooseDatabaseFolder": () => Promise<void>;
"codeQL.chooseDatabaseFoldersParent": () => Promise<void>;
"codeQL.chooseDatabaseArchive": () => Promise<void>;
"codeQL.chooseDatabaseInternet": () => Promise<void>;
"codeQL.chooseDatabaseGithub": () => Promise<void>;
@@ -348,7 +350,9 @@ export type MockGitHubApiServerCommands = {
"codeQL.mockGitHubApiServer.startRecording": () => Promise<void>;
"codeQL.mockGitHubApiServer.saveScenario": () => Promise<void>;
"codeQL.mockGitHubApiServer.cancelRecording": () => Promise<void>;
"codeQL.mockGitHubApiServer.loadScenario": () => Promise<void>;
"codeQL.mockGitHubApiServer.loadScenario": (
scenario?: string,
) => Promise<void>;
"codeQL.mockGitHubApiServer.unloadScenario": () => Promise<void>;
};

View File

@@ -27,6 +27,7 @@ import type {
} from "./raw-result-types";
import type { AccessPathSuggestionOptions } from "../model-editor/suggestions";
import type { ModelEvaluationRunState } from "../model-editor/shared/model-evaluation-run-state";
import type { PerformanceComparisonDataFromLog } from "../log-insights/performance-comparison";
/**
* This module contains types and code that are shared between
@@ -396,6 +397,17 @@ export interface SetComparisonsMessage {
readonly message: string | undefined;
}
export type ToComparePerformanceViewMessage = SetPerformanceComparisonQueries;
export interface SetPerformanceComparisonQueries {
readonly t: "setPerformanceComparison";
readonly from: PerformanceComparisonDataFromLog;
readonly to: PerformanceComparisonDataFromLog;
readonly comparison: boolean;
}
export type FromComparePerformanceViewMessage = CommonFromViewMessages;
export type QueryCompareResult =
| RawQueryCompareResult
| InterpretedQueryCompareResult;

View File

@@ -1,26 +1,61 @@
import { readFile } from "fs-extra";
import { stat } from "fs/promises";
import { createReadStream } from "fs-extra";
import type { BaseLogger } from "./logging";
const doubleLineBreakRegexp = /\n\r?\n/;
/**
* Read a file consisting of multiple JSON objects. Each object is separated from the previous one
* by a double newline sequence. This is basically a more human-readable form of JSONL.
*
* The current implementation reads the entire text of the document into memory, but in the future
* it will stream the document to improve the performance with large documents.
*
* @param path The path to the file.
* @param handler Callback to be invoked for each top-level JSON object in order.
*/
export async function readJsonlFile<T>(
path: string,
handler: (value: T) => Promise<void>,
logger?: BaseLogger,
): Promise<void> {
const logSummary = await readFile(path, "utf-8");
// Stream the data as large evaluator logs won't fit in memory.
// Also avoid using 'readline' as it is slower than our manual line splitting.
void logger?.log(
`Parsing ${path} (${(await stat(path)).size / 1024 / 1024} MB)...`,
);
return new Promise((resolve, reject) => {
const stream = createReadStream(path, { encoding: "utf8" });
let buffer = "";
stream.on("data", async (chunk: string | Buffer) => {
if (typeof chunk !== "string") {
// This should never happen because we specify the encoding as "utf8".
throw new Error("Invalid chunk");
}
// Remove newline delimiters because summary is in .jsonl format.
const jsonSummaryObjects: string[] = logSummary.split(/\r?\n\r?\n/g);
for (const obj of jsonSummaryObjects) {
const jsonObj = JSON.parse(obj) as T;
await handler(jsonObj);
}
const parts = (buffer + chunk).split(doubleLineBreakRegexp);
buffer = parts.pop()!;
if (parts.length > 0) {
try {
stream.pause();
for (const part of parts) {
await handler(JSON.parse(part));
}
stream.resume();
} catch (e) {
stream.destroy();
reject(e);
}
}
});
stream.on("end", async () => {
try {
if (buffer.trim().length > 0) {
await handler(JSON.parse(buffer));
}
void logger?.log(`Finished parsing ${path}`);
resolve();
} catch (e) {
reject(e);
}
});
stream.on("error", reject);
});
}

View File

@@ -63,26 +63,33 @@ export class VSCodeMockGitHubApiServer extends DisposableObject {
);
}
public async loadScenario(): Promise<void> {
public async loadScenario(scenario?: string): Promise<void> {
const scenariosPath = await this.getScenariosPath();
if (!scenariosPath) {
return;
}
const scenarioNames = await this.server.getScenarioNames(scenariosPath);
const scenarioQuickPickItems = scenarioNames.map((s) => ({ label: s }));
const quickPickOptions = {
placeHolder: "Select a scenario to load",
};
const selectedScenario = await window.showQuickPick<QuickPickItem>(
scenarioQuickPickItems,
quickPickOptions,
);
if (!selectedScenario) {
return;
let scenarioName = scenario;
if (!scenarioName) {
const scenarioNames = await this.server.getScenarioNames(scenariosPath);
const scenarioQuickPickItems = scenarioNames.map((s) => ({ label: s }));
const quickPickOptions = {
placeHolder: "Select a scenario to load",
};
const selectedScenario = await window.showQuickPick<QuickPickItem>(
scenarioQuickPickItems,
quickPickOptions,
);
if (!selectedScenario) {
return;
}
scenarioName = selectedScenario.label;
}
const scenarioName = selectedScenario.label;
if (!this.server.isListening && this.app.mode === AppMode.Test) {
await this.startServer();
}
await this.server.loadScenario(scenarioName, scenariosPath);
@@ -94,12 +101,12 @@ export class VSCodeMockGitHubApiServer extends DisposableObject {
true,
);
await window.showInformationMessage(`Loaded scenario '${scenarioName}'`);
void window.showInformationMessage(`Loaded scenario '${scenarioName}'`);
}
public async unloadScenario(): Promise<void> {
if (!this.server.isScenarioLoaded) {
await window.showInformationMessage("No scenario currently loaded");
void window.showInformationMessage("No scenario currently loaded");
} else {
await this.server.unloadScenario();
await this.app.commands.execute(
@@ -107,7 +114,11 @@ export class VSCodeMockGitHubApiServer extends DisposableObject {
"codeQL.mockGitHubApiServer.scenarioLoaded",
false,
);
await window.showInformationMessage("Unloaded scenario");
void window.showInformationMessage("Unloaded scenario");
}
if (this.server.isListening && this.app.mode === AppMode.Test) {
await this.stopServer();
}
}
@@ -139,7 +150,7 @@ export class VSCodeMockGitHubApiServer extends DisposableObject {
true,
);
await window.showInformationMessage(
void window.showInformationMessage(
'Recording scenario. To save the scenario, use the "CodeQL Mock GitHub API Server: Save Scenario" command.',
);
}
@@ -221,7 +232,10 @@ export class VSCodeMockGitHubApiServer extends DisposableObject {
return scenariosPath;
}
if (this.app.mode === AppMode.Development) {
if (
this.app.mode === AppMode.Development ||
this.app.mode === AppMode.Test
) {
const developmentScenariosPath = path.join(
this.app.extensionPath,
"src/common/mock-gh-api/scenarios",

View File

@@ -41,6 +41,13 @@ export abstract class AbstractWebview<
constructor(protected readonly app: App) {}
public hidePanel() {
if (this.panel !== undefined) {
this.panel.dispose();
this.panel = undefined;
}
}
public async restoreView(panel: WebviewPanel): Promise<void> {
this.panel = panel;
const config = await this.getPanelConfig();

View File

@@ -1,4 +1,4 @@
import { env, Uri, window } from "vscode";
import { window } from "vscode";
/**
* Opens a modal dialog for the user to make a yes/no choice.
@@ -34,50 +34,6 @@ export async function showBinaryChoiceDialog(
return chosenItem?.title === yesItem.title;
}
/**
* Opens a modal dialog for the user to make a yes/no choice.
*
* @param message The message to show.
* @param modal If true (the default), show a modal dialog box, otherwise dialog is non-modal and can
* be closed even if the user does not make a choice.
*
* @return
* `true` if the user clicks 'Yes',
* `false` if the user clicks 'No' or cancels the dialog,
* `undefined` if the dialog is closed without the user making a choice.
*/
export async function showBinaryChoiceWithUrlDialog(
message: string,
url: string,
): Promise<boolean | undefined> {
const urlItem = { title: "More Information", isCloseAffordance: false };
const yesItem = { title: "Yes", isCloseAffordance: false };
const noItem = { title: "No", isCloseAffordance: true };
let chosenItem;
// Keep the dialog open as long as the user is clicking the 'more information' option.
// To prevent an infinite loop, if the user clicks 'more information' 5 times, close the dialog and return cancelled
let count = 0;
do {
chosenItem = await window.showInformationMessage(
message,
{ modal: true },
urlItem,
yesItem,
noItem,
);
if (chosenItem === urlItem) {
await env.openExternal(Uri.parse(url, true));
}
count++;
} while (chosenItem === urlItem && count < 5);
if (!chosenItem || chosenItem.title === urlItem.title) {
return undefined;
}
return chosenItem.title === yesItem.title;
}
/**
* Show an information message with a customisable action.
* @param message The message to show.

View File

@@ -1,26 +1,15 @@
import type {
Extension,
ExtensionContext,
ConfigurationChangeEvent,
} from "vscode";
import { ConfigurationTarget, env } from "vscode";
import type { Extension, ExtensionContext } from "vscode";
import { ConfigurationTarget, env, Uri, window } from "vscode";
import TelemetryReporter from "vscode-extension-telemetry";
import {
ConfigListener,
CANARY_FEATURES,
ENABLE_TELEMETRY,
LOG_TELEMETRY,
isIntegrationTestMode,
isCanary,
} from "../../config";
import { ENABLE_TELEMETRY, isCanary, LOG_TELEMETRY } from "../../config";
import type { TelemetryClient } from "applicationinsights";
import { extLogger } from "../logging/vscode";
import { UserCancellationException } from "./progress";
import { showBinaryChoiceWithUrlDialog } from "./dialog";
import type { RedactableError } from "../errors";
import type { SemVer } from "semver";
import type { AppTelemetry } from "../telemetry";
import type { EnvelopeTelemetry } from "applicationinsights/out/Declarations/Contracts";
import type { Disposable } from "../disposable-object";
// Key is injected at build time through the APP_INSIGHTS_KEY environment variable.
const key = "REPLACE-APP-INSIGHTS-KEY";
@@ -55,80 +44,25 @@ const baseDataPropertiesToRemove = [
const NOT_SET_CLI_VERSION = "not-set";
export class ExtensionTelemetryListener
extends ConfigListener
implements AppTelemetry
{
private reporter?: TelemetryReporter;
export class ExtensionTelemetryListener implements AppTelemetry, Disposable {
private readonly reporter: TelemetryReporter;
private cliVersionStr = NOT_SET_CLI_VERSION;
constructor(
private readonly id: string,
private readonly version: string,
private readonly key: string,
private readonly ctx: ExtensionContext,
) {
super();
env.onDidChangeTelemetryEnabled(async () => {
await this.initialize();
});
}
/**
* This function handles changes to relevant configuration elements. There are 2 configuration
* ids that this function cares about:
*
* * `codeQL.telemetry.enableTelemetry`: If this one has changed, then we need to re-initialize
* the reporter and the reporter may wind up being removed.
* * `codeQL.canary`: A change here could possibly re-trigger a dialog popup.
*
* Note that the global telemetry setting also gate-keeps whether or not to send telemetry events
* to Application Insights. However, this gatekeeping happens inside of the vscode-extension-telemetry
* package. So, this does not need to be handled here.
*
* @param e the configuration change event
*/
async handleDidChangeConfiguration(
e: ConfigurationChangeEvent,
): Promise<void> {
if (e.affectsConfiguration(ENABLE_TELEMETRY.qualifiedName)) {
await this.initialize();
}
// Re-request telemetry so that users can see the dialog again.
// Re-request if codeQL.canary is being set to `true` and telemetry
// is not currently enabled.
if (
e.affectsConfiguration(CANARY_FEATURES.qualifiedName) &&
CANARY_FEATURES.getValue() &&
!ENABLE_TELEMETRY.getValue()
) {
await this.setTelemetryRequested(false);
await this.requestTelemetryPermission();
}
}
async initialize() {
await this.requestTelemetryPermission();
this.disposeReporter();
if (ENABLE_TELEMETRY.getValue<boolean>()) {
this.createReporter();
}
}
private createReporter() {
constructor(id: string, version: string, key: string) {
// We can always initialize this and send events using it because the vscode-extension-telemetry will check
// whether the `telemetry.telemetryLevel` setting is enabled.
this.reporter = new TelemetryReporter(
this.id,
this.version,
this.key,
id,
version,
key,
/* anonymize stack traces */ true,
);
this.push(this.reporter);
this.addTelemetryProcessor();
}
private addTelemetryProcessor() {
// The appInsightsClient field is private but we want to access it anyway
const client = this.reporter["appInsightsClient"] as TelemetryClient;
if (client) {
@@ -151,14 +85,10 @@ export class ExtensionTelemetryListener
}
dispose() {
super.dispose();
void this.reporter?.dispose();
void this.reporter.dispose();
}
sendCommandUsage(name: string, executionTime: number, error?: Error): void {
if (!this.reporter) {
return;
}
const status = !error
? CommandCompletion.Success
: error instanceof UserCancellationException
@@ -178,10 +108,6 @@ export class ExtensionTelemetryListener
}
sendUIInteraction(name: string): void {
if (!this.reporter) {
return;
}
this.reporter.sendTelemetryEvent(
"ui-interaction",
{
@@ -197,10 +123,6 @@ export class ExtensionTelemetryListener
error: RedactableError,
extraProperties?: { [key: string]: string },
): void {
if (!this.reporter) {
return;
}
const properties: { [key: string]: string } = {
isCanary: isCanary().toString(),
cliVersion: this.cliVersionStr,
@@ -215,10 +137,6 @@ export class ExtensionTelemetryListener
}
sendConfigInformation(config: Record<string, string>): void {
if (!this.reporter) {
return;
}
this.reporter.sendTelemetryEvent(
"config",
{
@@ -230,37 +148,6 @@ export class ExtensionTelemetryListener
);
}
/**
* Displays a popup asking the user if they want to enable telemetry
* for this extension.
*/
async requestTelemetryPermission() {
if (!this.wasTelemetryRequested()) {
// if global telemetry is disabled, avoid showing the dialog or making any changes
let result = undefined;
if (
env.isTelemetryEnabled &&
// Avoid showing the dialog if we are in integration test mode.
!isIntegrationTestMode()
) {
// Extension won't start until this completes.
result = await showBinaryChoiceWithUrlDialog(
"Does the CodeQL Extension by GitHub have your permission to collect usage data and metrics to help us improve CodeQL for VSCode?",
"https://codeql.github.com/docs/codeql-for-visual-studio-code/about-telemetry-in-codeql-for-visual-studio-code",
);
}
if (result !== undefined) {
await Promise.all([
this.setTelemetryRequested(true),
ENABLE_TELEMETRY.updateValue<boolean>(
result,
ConfigurationTarget.Global,
),
]);
}
}
}
/**
* Exposed for testing
*/
@@ -271,21 +158,45 @@ export class ExtensionTelemetryListener
set cliVersion(version: SemVer | undefined) {
this.cliVersionStr = version ? version.toString() : NOT_SET_CLI_VERSION;
}
}
private disposeReporter() {
if (this.reporter) {
void this.reporter.dispose();
this.reporter = undefined;
async function notifyTelemetryChange() {
const continueItem = { title: "Continue", isCloseAffordance: false };
const vsCodeTelemetryItem = {
title: "More Information about VS Code Telemetry",
isCloseAffordance: false,
};
const codeqlTelemetryItem = {
title: "More Information about CodeQL Telemetry",
isCloseAffordance: false,
};
let chosenItem;
do {
chosenItem = await window.showInformationMessage(
"The CodeQL extension now follows VS Code's telemetry settings. VS Code telemetry is currently enabled. Learn how to update your telemetry settings by clicking the links below.",
{ modal: true },
continueItem,
vsCodeTelemetryItem,
codeqlTelemetryItem,
);
if (chosenItem === vsCodeTelemetryItem) {
await env.openExternal(
Uri.parse(
"https://code.visualstudio.com/docs/getstarted/telemetry",
true,
),
);
}
}
private wasTelemetryRequested(): boolean {
return !!this.ctx.globalState.get<boolean>("telemetry-request-viewed");
}
private async setTelemetryRequested(newValue: boolean): Promise<void> {
await this.ctx.globalState.update("telemetry-request-viewed", newValue);
}
if (chosenItem === codeqlTelemetryItem) {
await env.openExternal(
Uri.parse(
"https://docs.github.com/en/code-security/codeql-for-vs-code/using-the-advanced-functionality-of-the-codeql-for-vs-code-extension/telemetry-in-codeql-for-visual-studio-code",
true,
),
);
}
} while (chosenItem !== continueItem);
}
/**
@@ -301,15 +212,28 @@ export async function initializeTelemetry(
if (telemetryListener !== undefined) {
throw new Error("Telemetry is already initialized");
}
if (ENABLE_TELEMETRY.getValue<boolean | undefined>() === false) {
if (env.isTelemetryEnabled) {
// Await this so that the user is notified before any telemetry is sent
await notifyTelemetryChange();
}
// Remove the deprecated telemetry setting
ENABLE_TELEMETRY.updateValue(undefined, ConfigurationTarget.Global);
ENABLE_TELEMETRY.updateValue(undefined, ConfigurationTarget.Workspace);
ENABLE_TELEMETRY.updateValue(
undefined,
ConfigurationTarget.WorkspaceFolder,
);
}
telemetryListener = new ExtensionTelemetryListener(
extension.id,
extension.packageJSON.version,
key,
ctx,
);
// do not await initialization, since doing so will sometimes cause a modal popup.
// this is a particular problem during integration tests, which will hang if a modal popup is displayed.
void telemetryListener.initialize();
ctx.subscriptions.push(telemetryListener);
return telemetryListener;
}

View File

@@ -7,6 +7,7 @@ import type { App } from "../app";
export type WebviewKind =
| "results"
| "compare"
| "compare-performance"
| "variant-analysis"
| "data-flow-paths"
| "model-editor"

View File

@@ -0,0 +1,108 @@
import { statSync } from "fs";
import { ViewColumn } from "vscode";
import type { App } from "../common/app";
import { redactableError } from "../common/errors";
import type {
FromComparePerformanceViewMessage,
ToComparePerformanceViewMessage,
} from "../common/interface-types";
import type { Logger } from "../common/logging";
import { showAndLogExceptionWithTelemetry } from "../common/logging";
import { extLogger } from "../common/logging/vscode";
import type { WebviewPanelConfig } from "../common/vscode/abstract-webview";
import { AbstractWebview } from "../common/vscode/abstract-webview";
import { withProgress } from "../common/vscode/progress";
import { telemetryListener } from "../common/vscode/telemetry";
import type { HistoryItemLabelProvider } from "../query-history/history-item-label-provider";
import { PerformanceOverviewScanner } from "../log-insights/performance-comparison";
import { scanLog } from "../log-insights/log-scanner";
import type { ResultsView } from "../local-queries";
export class ComparePerformanceView extends AbstractWebview<
ToComparePerformanceViewMessage,
FromComparePerformanceViewMessage
> {
constructor(
app: App,
public logger: Logger,
public labelProvider: HistoryItemLabelProvider,
private resultsView: ResultsView,
) {
super(app);
}
async showResults(fromJsonLog: string, toJsonLog: string) {
const panel = await this.getPanel();
panel.reveal(undefined, false);
// Close the results viewer as it will have opened when the user clicked the query in the history view
// (which they must do as part of the UI interaction for opening the performance view).
// The performance view generally needs a lot of width so it's annoying to have the result viewer open.
this.resultsView.hidePanel();
await this.waitForPanelLoaded();
function scanLogWithProgress(log: string, logDescription: string) {
const bytes = statSync(log).size;
return withProgress(
async (progress) =>
scanLog(log, new PerformanceOverviewScanner(), progress),
{
title: `Scanning evaluator log ${logDescription} (${(bytes / 1024 / 1024).toFixed(1)} MB)`,
},
);
}
const [fromPerf, toPerf] = await Promise.all([
fromJsonLog === ""
? new PerformanceOverviewScanner()
: scanLogWithProgress(fromJsonLog, "1/2"),
scanLogWithProgress(toJsonLog, fromJsonLog === "" ? "1/1" : "2/2"),
]);
await this.postMessage({
t: "setPerformanceComparison",
from: fromPerf.getData(),
to: toPerf.getData(),
comparison: fromJsonLog !== "",
});
}
protected getPanelConfig(): WebviewPanelConfig {
return {
viewId: "comparePerformanceView",
title: "Compare CodeQL Performance",
viewColumn: ViewColumn.Active,
preserveFocus: true,
view: "compare-performance",
};
}
protected onPanelDispose(): void {}
protected async onMessage(
msg: FromComparePerformanceViewMessage,
): Promise<void> {
switch (msg.t) {
case "viewLoaded":
this.onWebViewLoaded();
break;
case "telemetry":
telemetryListener?.sendUIInteraction(msg.action);
break;
case "unhandledError":
void showAndLogExceptionWithTelemetry(
extLogger,
telemetryListener,
redactableError(
msg.error,
)`Unhandled error in performance comparison view: ${msg.error.message}`,
);
break;
}
}
}

View File

@@ -165,6 +165,8 @@ const ROOT_SETTING = new Setting("codeQL");
const TELEMETRY_SETTING = new Setting("telemetry", ROOT_SETTING);
export const LOG_TELEMETRY = new Setting("logTelemetry", TELEMETRY_SETTING);
// Legacy setting that is no longer used, but is used for showing a message when the user upgrades.
export const ENABLE_TELEMETRY = new Setting(
"enableTelemetry",
TELEMETRY_SETTING,

View File

@@ -16,6 +16,7 @@ import {
ThemeIcon,
ThemeColor,
workspace,
FileType,
} from "vscode";
import { pathExists, stat, readdir, remove } from "fs-extra";
@@ -36,6 +37,7 @@ import {
import {
showAndLogExceptionWithTelemetry,
showAndLogErrorMessage,
showAndLogInformationMessage,
} from "../common/logging";
import type { DatabaseFetcher } from "./database-fetcher";
import { asError, asyncFilter, getErrorMessage } from "../common/helpers-pure";
@@ -267,6 +269,8 @@ export class DatabaseUI extends DisposableObject {
"codeQL.getCurrentDatabase": this.handleGetCurrentDatabase.bind(this),
"codeQL.chooseDatabaseFolder":
this.handleChooseDatabaseFolderFromPalette.bind(this),
"codeQL.chooseDatabaseFoldersParent":
this.handleChooseDatabaseFoldersParentFromPalette.bind(this),
"codeQL.chooseDatabaseArchive":
this.handleChooseDatabaseArchiveFromPalette.bind(this),
"codeQL.chooseDatabaseInternet":
@@ -359,6 +363,12 @@ export class DatabaseUI extends DisposableObject {
);
}
private async handleChooseDatabaseFoldersParentFromPalette(): Promise<void> {
return withProgress(async (progress) => {
await this.chooseDatabasesParentFolder(progress);
});
}
private async handleSetDefaultTourDatabase(): Promise<void> {
return withProgress(
async () => {
@@ -956,6 +966,32 @@ export class DatabaseUI extends DisposableObject {
}
}
/**
* Import database from uri. Returns the imported database, or `undefined` if the
* operation was unsuccessful or canceled.
*/
private async importDatabase(
uri: Uri,
byFolder: boolean,
progress: ProgressCallback,
): Promise<DatabaseItem | undefined> {
if (byFolder && !uri.fsPath.endsWith(".testproj")) {
const fixedUri = await this.fixDbUri(uri);
// we are selecting a database folder
return await this.databaseManager.openDatabase(fixedUri, {
type: "folder",
});
} else {
// we are selecting a database archive or a .testproj.
// Unzip archives (if an archive) and copy into a workspace-controlled area
// before importing.
return await this.databaseFetcher.importLocalDatabase(
uri.toString(true),
progress,
);
}
}
/**
* Ask the user for a database directory. Returns the chosen database, or `undefined` if the
* operation was canceled.
@@ -969,21 +1005,89 @@ export class DatabaseUI extends DisposableObject {
return undefined;
}
if (byFolder && !uri.fsPath.endsWith("testproj")) {
const fixedUri = await this.fixDbUri(uri);
// we are selecting a database folder
return await this.databaseManager.openDatabase(fixedUri, {
type: "folder",
return await this.importDatabase(uri, byFolder, progress);
}
/**
* Ask the user for a parent directory that contains all databases.
* Returns all valid databases, or `undefined` if the operation was canceled.
*/
private async chooseDatabasesParentFolder(
progress: ProgressCallback,
): Promise<DatabaseItem[] | undefined> {
const uri = await chooseDatabaseDir(true);
if (!uri) {
return undefined;
}
const databases: DatabaseItem[] = [];
const failures: string[] = [];
const entries = await workspace.fs.readDirectory(uri);
const validFileTypes = [FileType.File, FileType.Directory];
for (const [index, entry] of entries.entries()) {
progress({
step: index + 1,
maxStep: entries.length,
message: `Importing '${entry[0]}'`,
});
const subProgress: ProgressCallback = (p) => {
progress({
step: index + 1,
maxStep: entries.length,
message: `Importing '${entry[0]}': (${p.step}/${p.maxStep}) ${p.message}`,
});
};
if (!validFileTypes.includes(entry[1])) {
void this.app.logger.log(
`Skipping import for '${entry}', invalid file type: ${entry[1]}`,
);
continue;
}
try {
const databaseUri = Uri.joinPath(uri, entry[0]);
void this.app.logger.log(`Importing from ${databaseUri}`);
const database = await this.importDatabase(
databaseUri,
entry[1] === FileType.Directory,
subProgress,
);
if (database) {
databases.push(database);
} else {
failures.push(entry[0]);
}
} catch (e) {
failures.push(`${entry[0]}: ${getErrorMessage(e)}`.trim());
}
}
if (failures.length) {
void showAndLogErrorMessage(
this.app.logger,
`Failed to import ${failures.length} database(s), successfully imported ${databases.length} database(s).`,
{
fullMessage: `Failed to import ${failures.length} database(s), successfully imported ${databases.length} database(s).\nFailed databases:\n - ${failures.join("\n - ")}`,
},
);
} else if (databases.length === 0) {
void showAndLogErrorMessage(
this.app.logger,
`No database folder to import.`,
);
return undefined;
} else {
// we are selecting a database archive or a testproj.
// Unzip archives (if an archive) and copy into a workspace-controlled area
// before importing.
return await this.databaseFetcher.importLocalDatabase(
uri.toString(true),
progress,
void showAndLogInformationMessage(
this.app.logger,
`Successfully imported ${databases.length} database(s).`,
);
}
return databases;
}
/**

View File

@@ -135,6 +135,7 @@ import { LanguageContextStore } from "./language-context-store";
import { LanguageSelectionPanel } from "./language-selection-panel/language-selection-panel";
import { GitHubDatabasesModule } from "./databases/github-databases";
import { DatabaseFetcher } from "./databases/database-fetcher";
import { ComparePerformanceView } from "./compare-performance/compare-performance-view";
/**
* extension.ts
@@ -748,9 +749,13 @@ async function activateWithInstalledDistribution(
);
ctx.subscriptions.push(qlConfigurationListener);
void extLogger.log("Initializing CodeQL language server.");
const languageClient = createLanguageClient(qlConfigurationListener);
void extLogger.log("Initializing CodeQL cli server...");
const cliServer = new CodeQLCliServer(
app,
languageClient,
distributionManager,
new CliConfigListener(),
extLogger,
@@ -924,6 +929,11 @@ async function activateWithInstalledDistribution(
from: CompletedLocalQueryInfo,
to: CompletedLocalQueryInfo,
): Promise<void> => showResultsForComparison(compareView, from, to),
async (
from: CompletedLocalQueryInfo,
to: CompletedLocalQueryInfo | undefined,
): Promise<void> =>
showPerformanceComparison(comparePerformanceView, from, to),
);
ctx.subscriptions.push(qhm);
@@ -949,6 +959,15 @@ async function activateWithInstalledDistribution(
);
ctx.subscriptions.push(compareView);
void extLogger.log("Initializing performance comparison view.");
const comparePerformanceView = new ComparePerformanceView(
app,
queryServerLogger,
labelProvider,
localQueryResultsView,
);
ctx.subscriptions.push(comparePerformanceView);
void extLogger.log("Initializing source archive filesystem provider.");
archiveFilesystemProvider_activate(ctx, dbm);
@@ -961,9 +980,6 @@ async function activateWithInstalledDistribution(
ctx.subscriptions.push(tmpDirDisposal);
void extLogger.log("Initializing CodeQL language server.");
const languageClient = createLanguageClient(qlConfigurationListener);
const localQueries = new LocalQueries(
app,
qs,
@@ -1190,6 +1206,30 @@ async function showResultsForComparison(
}
}
async function showPerformanceComparison(
view: ComparePerformanceView,
from: CompletedLocalQueryInfo,
to: CompletedLocalQueryInfo | undefined,
): Promise<void> {
let fromLog = from.evaluatorLogPaths?.jsonSummary;
let toLog = to?.evaluatorLogPaths?.jsonSummary;
if (to === undefined) {
toLog = fromLog;
fromLog = "";
}
if (fromLog === undefined || toLog === undefined) {
return extLogger.showWarningMessage(
`Cannot compare performance as the structured logs are missing. Did they queries complete normally?`,
);
}
await extLogger.log(
`Comparing performance of ${from.getQueryName()} and ${to?.getQueryName() ?? "baseline"}`,
);
await view.showResults(fromLog, toLog);
}
function addUnhandledRejectionListener() {
const handler = (error: unknown) => {
// This listener will be triggered for errors from other extensions as

View File

@@ -7,6 +7,7 @@ export class CachedOperation<S extends unknown[], U> {
private readonly operation: (t: string, ...args: S) => Promise<U>;
private readonly cached: Map<string, U>;
private readonly lru: string[];
private generation: number;
private readonly inProgressCallbacks: Map<
string,
Array<[(u: U) => void, (reason?: Error) => void]>
@@ -17,6 +18,7 @@ export class CachedOperation<S extends unknown[], U> {
private cacheSize = 100,
) {
this.operation = operation;
this.generation = 0;
this.lru = [];
this.inProgressCallbacks = new Map<
string,
@@ -46,7 +48,7 @@ export class CachedOperation<S extends unknown[], U> {
inProgressCallback.push([resolve, reject]);
});
}
const origGeneration = this.generation;
// Otherwise compute the new value, but leave a callback to allow sharing work
const callbacks: Array<[(u: U) => void, (reason?: Error) => void]> = [];
this.inProgressCallbacks.set(t, callbacks);
@@ -54,6 +56,11 @@ export class CachedOperation<S extends unknown[], U> {
const result = await this.operation(t, ...args);
callbacks.forEach((f) => f[0](result));
this.inProgressCallbacks.delete(t);
if (this.generation !== origGeneration) {
// Cache was reset in the meantime so don't trust this
// result enough to cache it.
return result;
}
if (this.lru.length > this.cacheSize) {
const toRemove = this.lru.shift()!;
this.cached.delete(toRemove);
@@ -69,4 +76,11 @@ export class CachedOperation<S extends unknown[], U> {
this.inProgressCallbacks.delete(t);
}
}
reset() {
this.cached.clear();
this.lru.length = 0;
this.generation++;
this.inProgressCallbacks.clear();
}
}

View File

@@ -75,6 +75,7 @@ import type { App } from "../common/app";
import type { Disposable } from "../common/disposable-object";
import type { RawResultSet } from "../common/raw-result-types";
import type { BqrsResultSetSchema } from "../common/bqrs-cli-types";
import { CachedOperation } from "../language-support/contextual/cached-operation";
/**
* results-view.ts
@@ -177,6 +178,8 @@ export class ResultsView extends AbstractWebview<
// Event listeners that should be disposed of when the view is disposed.
private disposableEventListeners: Disposable[] = [];
private schemaCache: CachedOperation<[], BqrsResultSetSchema[]>;
constructor(
app: App,
private databaseManager: DatabaseManager,
@@ -206,6 +209,10 @@ export class ResultsView extends AbstractWebview<
}
}),
);
this.schemaCache = new CachedOperation(
this.getResultSetSchemasImpl.bind(this),
);
}
public getCommands(): ResultsViewCommands {
@@ -420,6 +427,7 @@ export class ResultsView extends AbstractWebview<
);
return;
}
this.schemaCache.reset();
// Notify the webview that it should expect new results.
await this.postMessage({ t: "resultsUpdating" });
await this._displayedQuery.completedQuery.updateSortState(
@@ -610,6 +618,12 @@ export class ResultsView extends AbstractWebview<
selectedTable = "",
): Promise<BqrsResultSetSchema[]> {
const resultsPath = completedQuery.getResultsPath(selectedTable);
return this.schemaCache.get(resultsPath);
}
private async getResultSetSchemasImpl(
resultsPath: string,
): Promise<BqrsResultSetSchema[]> {
const schemas = await this.cliServer.bqrsInfo(
resultsPath,
PAGE_SIZE.getValue(),

View File

@@ -94,19 +94,19 @@ export class LogScannerService extends DisposableObject {
public async scanEvalLog(query: QueryHistoryInfo | undefined): Promise<void> {
this.diagnosticCollection.clear();
if (
query?.t !== "local" ||
query.evalLogSummaryLocation === undefined ||
query.jsonEvalLogSummaryLocation === undefined
) {
if (query?.t !== "local" || query.evaluatorLogPaths === undefined) {
return;
}
const diagnostics = await this.scanLog(
query.jsonEvalLogSummaryLocation,
query.evalLogSummarySymbolsLocation,
);
const uri = Uri.file(query.evalLogSummaryLocation);
const { summarySymbols, jsonSummary, humanReadableSummary } =
query.evaluatorLogPaths;
if (jsonSummary === undefined || humanReadableSummary === undefined) {
return;
}
const diagnostics = await this.scanLog(jsonSummary, summarySymbols);
const uri = Uri.file(humanReadableSummary);
this.diagnosticCollection.set(uri, diagnostics);
}

View File

@@ -1,6 +1,7 @@
import type { SummaryEvent } from "./log-summary";
import { readJsonlFile } from "../common/jsonl-reader";
import type { Disposable } from "../common/disposable-object";
import { readJsonlFile } from "../common/jsonl-reader";
import type { ProgressCallback } from "../common/vscode/progress";
import type { SummaryEvent } from "./log-summary";
/**
* Callback interface used to report diagnostics from a log scanner.
@@ -112,3 +113,27 @@ export class EvaluationLogScannerSet {
scanners.forEach((scanner) => scanner.onDone());
}
}
/**
* Scan the evaluator summary log using the given scanner. For convenience, returns the scanner.
*
* @param jsonSummaryLocation The file path of the JSON summary log.
* @param scanner The scanner to process events from the log
*/
export async function scanLog<T extends EvaluationLogScanner>(
jsonSummaryLocation: string,
scanner: T,
progress?: ProgressCallback,
): Promise<T> {
progress?.({
// all scans have step 1 - the backing progress tracker allows increments instead of steps - but for now we are happy with a tiny UI that says what is happening
message: `Scanning ...`,
step: 1,
maxStep: 2,
});
await readJsonlFile<SummaryEvent>(jsonSummaryLocation, async (obj) => {
scanner.onEvent(obj);
});
scanner.onDone();
return scanner;
}

View File

@@ -33,6 +33,7 @@ interface ResultEventBase extends SummaryEventBase {
export interface ComputeSimple extends ResultEventBase {
evaluationStrategy: "COMPUTE_SIMPLE";
ra: Ra;
millis: number;
pipelineRuns?: [PipelineRun];
queryCausingWork?: string;
dependencies: { [key: string]: string };
@@ -42,6 +43,7 @@ export interface ComputeRecursive extends ResultEventBase {
evaluationStrategy: "COMPUTE_RECURSIVE";
deltaSizes: number[];
ra: Ra;
millis: number;
pipelineRuns: PipelineRun[];
queryCausingWork?: string;
dependencies: { [key: string]: string };

View File

@@ -0,0 +1,183 @@
import type { EvaluationLogScanner } from "./log-scanner";
import type { SummaryEvent } from "./log-summary";
export interface PipelineSummary {
steps: string[];
/** Total counts for each step in the RA array, across all iterations */
counts: number[];
}
/**
* Data extracted from a log for the purpose of doing a performance comparison.
*
* Memory compactness is important since we keep this data in memory; once for
* each side of the comparison.
*
* This object must be able to survive a `postMessage` transfer from the extension host
* to a web view (which rules out `Map` values, for example).
*/
export interface PerformanceComparisonDataFromLog {
/**
* Names of predicates mentioned in the log.
*
* For compactness, details of these predicates are stored in a "struct of arrays" style.
*
* All fields (except those ending with `Indices`) should contain an array of the same length as `names`;
* details of a given predicate should be stored at the same index in each of those arrays.
*/
names: string[];
/** Number of milliseconds spent evaluating the `i`th predicate from the `names` array. */
timeCosts: number[];
/** Number of tuples seen in pipelines evaluating the `i`th predicate from the `names` array. */
tupleCosts: number[];
/** Number of iterations seen when evaluating the `i`th predicate from the `names` array. */
iterationCounts: number[];
/** Number of executions of pipelines evaluating the `i`th predicate from the `names` array. */
evaluationCounts: number[];
/**
* List of indices into the `names` array for which we have seen a cache hit.
*/
cacheHitIndices: number[];
/**
* List of indices into the `names` array where the predicate was deemed empty due to a sentinel check.
*/
sentinelEmptyIndices: number[];
/**
* All the pipeline runs seen for the `i`th predicate from the `names` array.
*/
pipelineSummaryList: Array<Record<string, PipelineSummary>>;
}
export class PerformanceOverviewScanner implements EvaluationLogScanner {
private readonly nameToIndex = new Map<string, number>();
private readonly data: PerformanceComparisonDataFromLog = {
names: [],
timeCosts: [],
tupleCosts: [],
cacheHitIndices: [],
sentinelEmptyIndices: [],
pipelineSummaryList: [],
evaluationCounts: [],
iterationCounts: [],
};
private getPredicateIndex(name: string): number {
const { nameToIndex } = this;
let index = nameToIndex.get(name);
if (index === undefined) {
index = nameToIndex.size;
nameToIndex.set(name, index);
const {
names,
timeCosts,
tupleCosts,
iterationCounts,
evaluationCounts,
pipelineSummaryList,
} = this.data;
names.push(name);
timeCosts.push(0);
tupleCosts.push(0);
iterationCounts.push(0);
evaluationCounts.push(0);
pipelineSummaryList.push({});
}
return index;
}
getData(): PerformanceComparisonDataFromLog {
return this.data;
}
onEvent(event: SummaryEvent): void {
if (
event.completionType !== undefined &&
event.completionType !== "SUCCESS"
) {
return; // Skip any evaluation that wasn't successful
}
switch (event.evaluationStrategy) {
case "EXTENSIONAL":
case "COMPUTED_EXTENSIONAL": {
break;
}
case "CACHE_HIT":
case "CACHACA": {
// Record a cache hit, but only if the predicate has not been seen before.
// We're mainly interested in the reuse of caches from an earlier query run as they can distort comparisons.
if (!this.nameToIndex.has(event.predicateName)) {
this.data.cacheHitIndices.push(
this.getPredicateIndex(event.predicateName),
);
}
break;
}
case "SENTINEL_EMPTY": {
this.data.sentinelEmptyIndices.push(
this.getPredicateIndex(event.predicateName),
);
break;
}
case "COMPUTE_RECURSIVE":
case "COMPUTE_SIMPLE":
case "IN_LAYER": {
const index = this.getPredicateIndex(event.predicateName);
let totalTime = 0;
let totalTuples = 0;
if (event.evaluationStrategy !== "IN_LAYER") {
totalTime += event.millis;
} else {
// IN_LAYER events do no record of their total time.
// Make a best-effort estimate by adding up the positive iteration times (they can be negative).
for (const millis of event.predicateIterationMillis ?? []) {
if (millis > 0) {
totalTime += millis;
}
}
}
const {
timeCosts,
tupleCosts,
iterationCounts,
evaluationCounts,
pipelineSummaryList,
} = this.data;
const pipelineSummaries = pipelineSummaryList[index];
for (const { counts, raReference } of event.pipelineRuns ?? []) {
// Get or create the pipeline summary for this RA
const pipelineSummary = (pipelineSummaries[raReference] ??= {
steps: event.ra[raReference],
counts: counts.map(() => 0),
});
const { counts: totalTuplesPerStep } = pipelineSummary;
for (let i = 0, length = counts.length; i < length; ++i) {
const count = counts[i];
if (count < 0) {
// Empty RA lines have a tuple count of -1. Do not count them when aggregating.
// But retain the fact that this step had a negative count for rendering purposes.
totalTuplesPerStep[i] = count;
continue;
}
totalTuples += count;
totalTuplesPerStep[i] += count;
}
}
timeCosts[index] += totalTime;
tupleCosts[index] += totalTuples;
iterationCounts[index] += event.pipelineRuns?.length ?? 0;
evaluationCounts[index] += 1;
break;
}
}
}
onDone(): void {}
}

View File

@@ -12,17 +12,36 @@ import type { VariantAnalysisHistoryItem } from "./variant-analysis-history-item
import { assertNever } from "../common/helpers-pure";
import { pluralize } from "../common/word";
import { humanizeQueryStatus } from "./query-status";
import { substituteConfigVariables } from "../common/config-template";
interface InterpolateReplacements {
t: string; // Start time
q: string; // Query name
d: string; // Database/repositories count
r: string; // Result count/Empty
s: string; // Status
f: string; // Query file name
l: string; // Query language
"%": "%"; // Percent sign
}
type LabelVariables = {
startTime: string;
queryName: string;
databaseName: string;
resultCount: string;
status: string;
queryFileBasename: string;
queryLanguage: string;
};
const legacyVariableInterpolateReplacements: Record<
keyof LabelVariables,
string
> = {
startTime: "t",
queryName: "q",
databaseName: "d",
resultCount: "r",
status: "s",
queryFileBasename: "f",
queryLanguage: "l",
};
// If any of the "legacy" variables are used, we need to use legacy interpolation.
const legacyLabelRegex = new RegExp(
`%([${Object.values(legacyVariableInterpolateReplacements).join("")}%])`,
"g",
);
export class HistoryItemLabelProvider {
constructor(private config: QueryHistoryConfig) {
@@ -30,21 +49,26 @@ export class HistoryItemLabelProvider {
}
getLabel(item: QueryHistoryInfo) {
let replacements: InterpolateReplacements;
let variables: LabelVariables;
switch (item.t) {
case "local":
replacements = this.getLocalInterpolateReplacements(item);
variables = this.getLocalVariables(item);
break;
case "variant-analysis":
replacements = this.getVariantAnalysisInterpolateReplacements(item);
variables = this.getVariantAnalysisVariables(item);
break;
default:
assertNever(item);
}
const rawLabel = item.userSpecifiedLabel ?? (this.config.format || "%q");
const rawLabel =
item.userSpecifiedLabel ?? (this.config.format || "${queryName}");
return this.interpolate(rawLabel, replacements);
if (legacyLabelRegex.test(rawLabel)) {
return this.legacyInterpolate(rawLabel, variables);
}
return substituteConfigVariables(rawLabel, variables).replace(/\s+/g, " ");
}
/**
@@ -59,55 +83,60 @@ export class HistoryItemLabelProvider {
: getRawQueryName(item);
}
private interpolate(
private legacyInterpolate(
rawLabel: string,
replacements: InterpolateReplacements,
variables: LabelVariables,
): string {
const label = rawLabel.replace(
/%(.)/g,
(match, key: keyof InterpolateReplacements) => {
const replacement = replacements[key];
return replacement !== undefined ? replacement : match;
const replacements = Object.entries(variables).reduce(
(acc, [key, value]) => {
acc[
legacyVariableInterpolateReplacements[key as keyof LabelVariables]
] = value;
return acc;
},
{
"%": "%",
} as Record<string, string>,
);
const label = rawLabel.replace(/%(.)/g, (match, key: string) => {
const replacement = replacements[key];
return replacement !== undefined ? replacement : match;
});
return label.replace(/\s+/g, " ");
}
private getLocalInterpolateReplacements(
item: LocalQueryInfo,
): InterpolateReplacements {
private getLocalVariables(item: LocalQueryInfo): LabelVariables {
const { resultCount = 0, message = "in progress" } =
item.completedQuery || {};
return {
t: item.startTime,
q: item.getQueryName(),
d: item.databaseName,
r: `(${resultCount} results)`,
s: message,
f: item.getQueryFileName(),
l: this.getLanguageLabel(item),
"%": "%",
startTime: item.startTime,
queryName: item.getQueryName(),
databaseName: item.databaseName,
resultCount: `(${resultCount} results)`,
status: message,
queryFileBasename: item.getQueryFileName(),
queryLanguage: this.getLanguageLabel(item),
};
}
private getVariantAnalysisInterpolateReplacements(
private getVariantAnalysisVariables(
item: VariantAnalysisHistoryItem,
): InterpolateReplacements {
): LabelVariables {
const resultCount = item.resultCount
? `(${pluralize(item.resultCount, "result", "results")})`
: "";
return {
t: new Date(item.variantAnalysis.executionStartTime).toLocaleString(
env.language,
),
q: `${item.variantAnalysis.query.name} (${item.variantAnalysis.language})`,
d: buildRepoLabel(item),
r: resultCount,
s: humanizeQueryStatus(item.status),
f: basename(item.variantAnalysis.query.filePath),
l: this.getLanguageLabel(item),
"%": "%",
startTime: new Date(
item.variantAnalysis.executionStartTime,
).toLocaleString(env.language),
queryName: `${item.variantAnalysis.query.name} (${item.variantAnalysis.language})`,
databaseName: buildRepoLabel(item),
resultCount,
status: humanizeQueryStatus(item.status),
queryFileBasename: basename(item.variantAnalysis.query.filePath),
queryLanguage: this.getLanguageLabel(item),
};
}

View File

@@ -149,6 +149,10 @@ export class QueryHistoryManager extends DisposableObject {
from: CompletedLocalQueryInfo,
to: CompletedLocalQueryInfo,
) => Promise<void>,
private readonly doComparePerformanceCallback: (
from: CompletedLocalQueryInfo,
to: CompletedLocalQueryInfo | undefined,
) => Promise<void>,
) {
super();
@@ -263,6 +267,8 @@ export class QueryHistoryManager extends DisposableObject {
"query",
),
"codeQLQueryHistory.compareWith": this.handleCompareWith.bind(this),
"codeQLQueryHistory.comparePerformanceWith":
this.handleComparePerformanceWith.bind(this),
"codeQLQueryHistory.showEvalLog": createSingleSelectionCommand(
this.app.logger,
this.handleShowEvalLog.bind(this),
@@ -679,6 +685,39 @@ export class QueryHistoryManager extends DisposableObject {
}
}
async handleComparePerformanceWith(
singleItem: QueryHistoryInfo,
multiSelect: QueryHistoryInfo[] | undefined,
) {
multiSelect ||= [singleItem];
if (
!this.isSuccessfulCompletedLocalQueryInfo(singleItem) ||
!multiSelect.every(this.isSuccessfulCompletedLocalQueryInfo)
) {
throw new Error(
"Please only select local queries that have completed successfully.",
);
}
const fromItem = this.getFromQueryToCompare(singleItem, multiSelect);
let toItem: CompletedLocalQueryInfo | undefined = undefined;
try {
toItem = await this.findOtherQueryToComparePerformance(
fromItem,
multiSelect,
);
} catch (e) {
void showAndLogErrorMessage(
this.app.logger,
`Failed to compare queries: ${getErrorMessage(e)}`,
);
}
await this.doComparePerformanceCallback(fromItem, toItem);
}
async handleItemClicked(item: QueryHistoryInfo) {
this.treeDataProvider.setCurrentItem(item);
@@ -781,7 +820,7 @@ export class QueryHistoryManager extends DisposableObject {
private async warnNoEvalLogSummary(item: LocalQueryInfo) {
const evalLogLocation =
item.evalLogLocation ?? item.initialInfo.outputDir?.evalLogPath;
item.evaluatorLogPaths?.log ?? item.initialInfo.outputDir?.evalLogPath;
// Summary log file doesn't exist.
if (evalLogLocation && (await pathExists(evalLogLocation))) {
@@ -801,7 +840,7 @@ export class QueryHistoryManager extends DisposableObject {
}
const evalLogLocation =
item.evalLogLocation ?? item.initialInfo.outputDir?.evalLogPath;
item.evaluatorLogPaths?.log ?? item.initialInfo.outputDir?.evalLogPath;
if (evalLogLocation && (await pathExists(evalLogLocation))) {
await tryOpenExternalFile(this.app.commands, evalLogLocation);
@@ -816,12 +855,15 @@ export class QueryHistoryManager extends DisposableObject {
}
// If the summary file location wasn't saved, display error
if (!item.evalLogSummaryLocation) {
if (!item.evaluatorLogPaths?.humanReadableSummary) {
await this.warnNoEvalLogSummary(item);
return;
}
await tryOpenExternalFile(this.app.commands, item.evalLogSummaryLocation);
await tryOpenExternalFile(
this.app.commands,
item.evaluatorLogPaths.humanReadableSummary,
);
}
async handleShowEvalLogViewer(item: QueryHistoryInfo) {
@@ -830,7 +872,7 @@ export class QueryHistoryManager extends DisposableObject {
}
// If the JSON summary file location wasn't saved, display error
if (item.jsonEvalLogSummaryLocation === undefined) {
if (item.evaluatorLogPaths?.jsonSummary === undefined) {
await this.warnNoEvalLogSummary(item);
return;
}
@@ -838,7 +880,7 @@ export class QueryHistoryManager extends DisposableObject {
// TODO(angelapwen): Stream the file in.
try {
const evalLogData: EvalLogData[] = await parseViewerData(
item.jsonEvalLogSummaryLocation,
item.evaluatorLogPaths.jsonSummary,
);
const evalLogTreeBuilder = new EvalLogTreeBuilder(
item.getQueryName(),
@@ -847,7 +889,7 @@ export class QueryHistoryManager extends DisposableObject {
this.evalLogViewer.updateRoots(await evalLogTreeBuilder.getRoots());
} catch {
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.evaluatorLogPaths.jsonSummary}.`,
);
}
}
@@ -1073,6 +1115,7 @@ export class QueryHistoryManager extends DisposableObject {
detail: item.completedQuery.message,
query: item,
}));
if (comparableQueryLabels.length < 1) {
throw new Error("No other queries available to compare with.");
}
@@ -1081,6 +1124,52 @@ export class QueryHistoryManager extends DisposableObject {
return choice?.query;
}
private async findOtherQueryToComparePerformance(
fromItem: CompletedLocalQueryInfo,
allSelectedItems: CompletedLocalQueryInfo[],
): Promise<CompletedLocalQueryInfo | undefined> {
// If exactly 2 items are selected, return the one that
// isn't being used as the "from" item.
if (allSelectedItems.length === 2) {
const otherItem =
fromItem === allSelectedItems[0]
? allSelectedItems[1]
: allSelectedItems[0];
return otherItem;
}
if (allSelectedItems.length > 2) {
throw new Error("Please select no more than 2 queries.");
}
// Otherwise, present a dialog so the user can choose the item they want to use.
const comparableQueryLabels = this.treeDataProvider.allHistory
.filter(this.isSuccessfulCompletedLocalQueryInfo)
.filter((otherItem) => otherItem !== fromItem)
.map((item) => ({
label: this.labelProvider.getLabel(item),
description: item.databaseName,
detail: item.completedQuery.message,
query: item,
}));
const comparableQueryLabelsWithDefault = [
{
label: "Single run",
description:
"Look at the performance of this run, compared to a trivial baseline",
detail: undefined,
query: undefined,
},
...comparableQueryLabels,
];
if (comparableQueryLabelsWithDefault.length < 1) {
throw new Error("No other queries available to compare with.");
}
const choice = await window.showQuickPick(comparableQueryLabelsWithDefault);
return choice?.query;
}
/**
* Updates the compare with source query. This ensures that all compare command invocations
* when exactly 2 queries are selected always have the proper _from_ query. Always use

View File

@@ -25,10 +25,10 @@ export function mapLocalQueryInfoToDto(
return {
initialInfo: mapInitialQueryInfoToDto(query.initialInfo),
t: "local",
evalLogLocation: query.evalLogLocation,
evalLogSummaryLocation: query.evalLogSummaryLocation,
jsonEvalLogSummaryLocation: query.jsonEvalLogSummaryLocation,
evalLogSummarySymbolsLocation: query.evalLogSummarySymbolsLocation,
evalLogLocation: query.evaluatorLogPaths?.log,
evalLogSummaryLocation: query.evaluatorLogPaths?.humanReadableSummary,
jsonEvalLogSummaryLocation: query.evaluatorLogPaths?.jsonSummary,
evalLogSummarySymbolsLocation: query.evaluatorLogPaths?.summarySymbols,
failureReason: query.failureReason,
completedQuery:
query.completedQuery && mapCompletedQueryToDto(query.completedQuery),

View File

@@ -32,10 +32,15 @@ export function mapLocalQueryItemToDomainModel(
localQuery.failureReason,
localQuery.completedQuery &&
mapCompletedQueryInfoToDomainModel(localQuery.completedQuery),
localQuery.evalLogLocation,
localQuery.evalLogSummaryLocation,
localQuery.jsonEvalLogSummaryLocation,
localQuery.evalLogSummarySymbolsLocation,
localQuery.evalLogLocation
? {
log: localQuery.evalLogLocation,
humanReadableSummary: localQuery.evalLogSummaryLocation,
jsonSummary: localQuery.jsonEvalLogSummaryLocation,
summarySymbols: localQuery.evalLogSummarySymbolsLocation,
endSummary: undefined,
}
: undefined,
);
}

View File

@@ -200,10 +200,7 @@ export class LocalQueryInfo {
private cancellationSource?: CancellationTokenSource, // used to cancel in progress queries
public failureReason?: string,
public completedQuery?: CompletedQueryInfo,
public evalLogLocation?: string,
public evalLogSummaryLocation?: string,
public jsonEvalLogSummaryLocation?: string,
public evalLogSummarySymbolsLocation?: string,
public evaluatorLogPaths?: EvaluatorLogPaths,
) {
/**/
}
@@ -229,10 +226,7 @@ export class LocalQueryInfo {
/** Sets the paths to the various structured evaluator logs. */
public setEvaluatorLogPaths(logPaths: EvaluatorLogPaths): void {
this.evalLogLocation = logPaths.log;
this.evalLogSummaryLocation = logPaths.humanReadableSummary;
this.jsonEvalLogSummaryLocation = logPaths.jsonSummary;
this.evalLogSummarySymbolsLocation = logPaths.summarySymbols;
this.evaluatorLogPaths = logPaths;
}
/**

View File

@@ -244,7 +244,7 @@ export class QueryEvaluationInfo extends QueryOutputDir {
*/
async chooseResultSet(cliServer: CodeQLCliServer) {
const resultSets = (
await cliServer.bqrsInfo(this.resultsPaths.resultsPath, 0)
await cliServer.bqrsInfo(this.resultsPaths.resultsPath)
)["result-sets"];
if (!resultSets.length) {
return undefined;

View File

@@ -0,0 +1,31 @@
import { styled } from "styled-components";
import { WarningIcon } from "./icon/WarningIcon";
const WarningBoxDiv = styled.div`
max-width: 100em;
padding: 0.5em 1em;
border: 1px solid var(--vscode-widget-border);
box-shadow: var(--vscode-widget-shadow) 0px 3px 8px;
display: flex;
`;
const IconPane = styled.p`
width: 3em;
flex-shrink: 0;
text-align: center;
`;
export interface WarningBoxProps {
children: React.ReactNode;
}
export function WarningBox(props: WarningBoxProps) {
return (
<WarningBoxDiv>
<IconPane>
<WarningIcon />
</IconPane>
<p>{props.children}</p>
</WarningBoxDiv>
);
}

View File

@@ -6,3 +6,4 @@ export * from "./HorizontalSpace";
export * from "./SectionTitle";
export * from "./VerticalSpace";
export * from "./ViewTitle";
export * from "./WarningBox";

View File

@@ -0,0 +1,27 @@
import { useEffect } from "react";
/**
* Invokes the given callback when a message is received from the extension.
*/
export function useMessageFromExtension<T>(
onEvent: (event: T) => void,
onEventDependencies: unknown[],
): void {
useEffect(() => {
const listener = (evt: MessageEvent) => {
if (evt.origin === window.origin) {
onEvent(evt.data as T);
} else {
// sanitize origin
const origin = evt.origin.replace(/\n|\r/g, "");
console.error(`Invalid event origin ${origin}`);
}
};
window.addEventListener("message", listener);
return () => {
window.removeEventListener("message", listener);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, onEventDependencies);
}

View File

@@ -0,0 +1,862 @@
import type { ChangeEvent } from "react";
import {
Fragment,
memo,
useDeferredValue,
useMemo,
useRef,
useState,
} from "react";
import type {
SetPerformanceComparisonQueries,
ToComparePerformanceViewMessage,
} from "../../common/interface-types";
import { useMessageFromExtension } from "../common/useMessageFromExtension";
import type {
PerformanceComparisonDataFromLog,
PipelineSummary,
} from "../../log-insights/performance-comparison";
import { formatDecimal } from "../../common/number";
import { styled } from "styled-components";
import { Codicon, ViewTitle, WarningBox } from "../common";
import { abbreviateRANames, abbreviateRASteps } from "./RAPrettyPrinter";
import { Renaming, RenamingInput } from "./RenamingInput";
const enum AbsentReason {
NotSeen = "NotSeen",
CacheHit = "CacheHit",
Sentinel = "Sentinel",
}
type Optional<T> = AbsentReason | T;
function isPresent<T>(x: Optional<T>): x is T {
return typeof x !== "string";
}
interface PredicateInfo {
tuples: number;
evaluationCount: number;
iterationCount: number;
timeCost: number;
pipelines: Record<string, PipelineSummary>;
}
class ComparisonDataset {
public nameToIndex = new Map<string, number>();
public cacheHitIndices: Set<number>;
public sentinelEmptyIndices: Set<number>;
constructor(public data: PerformanceComparisonDataFromLog) {
const { names } = data;
const { nameToIndex } = this;
for (let i = 0; i < names.length; i++) {
nameToIndex.set(names[i], i);
}
this.cacheHitIndices = new Set(data.cacheHitIndices);
this.sentinelEmptyIndices = new Set(data.sentinelEmptyIndices);
}
getTupleCountInfo(name: string): Optional<PredicateInfo> {
const { data, nameToIndex, cacheHitIndices, sentinelEmptyIndices } = this;
const index = nameToIndex.get(name);
if (index == null) {
return AbsentReason.NotSeen;
}
const tupleCost = data.tupleCosts[index];
if (tupleCost === 0) {
if (sentinelEmptyIndices.has(index)) {
return AbsentReason.Sentinel;
} else if (cacheHitIndices.has(index)) {
return AbsentReason.CacheHit;
}
}
return {
evaluationCount: data.evaluationCounts[index],
iterationCount: data.iterationCounts[index],
timeCost: data.timeCosts[index],
tuples: tupleCost,
pipelines: data.pipelineSummaryList[index],
};
}
}
function renderOptionalValue(x: Optional<number>, unit?: string) {
switch (x) {
case AbsentReason.NotSeen:
return <AbsentNumberCell>n/a</AbsentNumberCell>;
case AbsentReason.CacheHit:
return <AbsentNumberCell>cache hit</AbsentNumberCell>;
case AbsentReason.Sentinel:
return <AbsentNumberCell>sentinel empty</AbsentNumberCell>;
default:
return (
<NumberCell>
{formatDecimal(x)}
{renderUnit(unit)}
</NumberCell>
);
}
}
function renderPredicateMetric(
x: Optional<PredicateInfo>,
metric: Metric,
isPerEvaluation: boolean,
) {
return renderOptionalValue(
metricGetOptional(metric, x, isPerEvaluation),
metric.unit,
);
}
function renderDelta(x: number, unit?: string) {
const sign = x > 0 ? "+" : "";
return (
<NumberCell className={x > 0 ? "bad-value" : x < 0 ? "good-value" : ""}>
{sign}
{formatDecimal(x)}
{renderUnit(unit)}
</NumberCell>
);
}
function renderUnit(unit: string | undefined) {
return unit == null ? "" : ` ${unit}`;
}
function orderBy<T>(fn: (x: T) => number | string) {
return (x: T, y: T) => {
const fx = fn(x);
const fy = fn(y);
return fx === fy ? 0 : fx < fy ? -1 : 1;
};
}
const ChevronCell = styled.td`
width: 1em !important;
`;
const NameHeader = styled.th`
text-align: left;
`;
const NumberHeader = styled.th`
text-align: right;
width: 10em !important;
`;
const NameCell = styled.td``;
const NumberCell = styled.td`
text-align: right;
width: 10em !important;
&.bad-value {
color: var(--vscode-problemsErrorIcon-foreground);
tr.expanded & {
color: inherit;
}
}
&.good-value {
color: var(--vscode-problemsInfoIcon-foreground);
tr.expanded & {
color: inherit;
}
}
`;
const AbsentNumberCell = styled.td`
text-align: right;
color: var(--vscode-disabledForeground);
tr.expanded & {
color: inherit;
}
width: 10em !important;
`;
const Table = styled.table`
border-collapse: collapse;
width: 100%;
border-spacing: 0;
background-color: var(--vscode-background);
color: var(--vscode-foreground);
& td {
padding: 0.5em;
}
& th {
padding: 0.5em;
}
&.expanded {
border: 1px solid var(--vscode-list-activeSelectionBackground);
margin-bottom: 1em;
}
word-break: break-all;
`;
const PredicateTR = styled.tr`
cursor: pointer;
&.expanded {
background-color: var(--vscode-list-activeSelectionBackground);
color: var(--vscode-list-activeSelectionForeground);
position: sticky;
top: 0;
}
& .codicon-chevron-right {
visibility: hidden;
}
&:hover:not(.expanded) {
background-color: var(--vscode-list-hoverBackground);
& .codicon-chevron-right {
visibility: visible;
}
}
`;
const PipelineStepTR = styled.tr`
& td {
padding-top: 0.3em;
padding-bottom: 0.3em;
}
`;
const Dropdown = styled.select``;
interface PipelineStepProps {
before: number | undefined;
after: number | undefined;
comparison: boolean;
step: React.ReactNode;
}
/**
* Row with details of a pipeline step, or one of the high-level stats appearing above the pipelines (evaluation/iteration counts).
*/
function PipelineStep(props: PipelineStepProps) {
let { before, after, comparison, step } = props;
if (before != null && before < 0) {
before = undefined;
}
if (after != null && after < 0) {
after = undefined;
}
const delta = before != null && after != null ? after - before : undefined;
return (
<PipelineStepTR>
<ChevronCell />
{comparison && (
<NumberCell>{before != null ? formatDecimal(before) : ""}</NumberCell>
)}
<NumberCell>{after != null ? formatDecimal(after) : ""}</NumberCell>
{comparison && (delta != null ? renderDelta(delta) : <td></td>)}
<NameCell>{step}</NameCell>
</PipelineStepTR>
);
}
const HeaderTR = styled.tr`
background-color: var(--vscode-sideBar-background);
`;
interface HeaderRowProps {
hasBefore?: boolean;
hasAfter?: boolean;
comparison: boolean;
title: React.ReactNode;
}
function HeaderRow(props: HeaderRowProps) {
const { comparison, hasBefore, hasAfter, title } = props;
return (
<HeaderTR>
<ChevronCell />
{comparison ? (
<>
<NumberHeader>{hasBefore ? "Before" : ""}</NumberHeader>
<NumberHeader>{hasAfter ? "After" : ""}</NumberHeader>
<NumberHeader>{hasBefore && hasAfter ? "Delta" : ""}</NumberHeader>
</>
) : (
<NumberHeader>Value</NumberHeader>
)}
<NameHeader>{title}</NameHeader>
</HeaderTR>
);
}
interface HighLevelStatsProps {
before: Optional<PredicateInfo>;
after: Optional<PredicateInfo>;
comparison: boolean;
}
function HighLevelStats(props: HighLevelStatsProps) {
const { before, after, comparison } = props;
const hasBefore = isPresent(before);
const hasAfter = isPresent(after);
const showEvaluationCount =
(hasBefore && before.evaluationCount > 1) ||
(hasAfter && after.evaluationCount > 1);
return (
<>
<HeaderRow
hasBefore={hasBefore}
hasAfter={hasAfter}
title="Stats"
comparison={comparison}
/>
{showEvaluationCount && (
<PipelineStep
before={hasBefore ? before.evaluationCount : undefined}
after={hasAfter ? after.evaluationCount : undefined}
comparison={comparison}
step="Number of evaluations"
/>
)}
<PipelineStep
before={
hasBefore ? before.iterationCount / before.evaluationCount : undefined
}
after={
hasAfter ? after.iterationCount / after.evaluationCount : undefined
}
comparison={comparison}
step={
showEvaluationCount
? "Number of iterations per evaluation"
: "Number of iterations"
}
/>
</>
);
}
interface Row {
name: string;
before: Optional<PredicateInfo>;
after: Optional<PredicateInfo>;
diff: number;
}
/**
* A set of predicates that have been grouped together because their names have the same fingerprint.
*/
interface RowGroup {
name: string;
rows: Row[];
before: Optional<number>;
after: Optional<number>;
diff: number;
}
function getSortOrder(sortOrder: "delta" | "absDelta") {
if (sortOrder === "absDelta") {
return orderBy((row: { diff: number }) => -Math.abs(row.diff));
}
return orderBy((row: { diff: number }) => row.diff);
}
interface Metric {
title: string;
get(info: PredicateInfo): number;
unit?: string;
}
const metrics: Record<string, Metric> = {
tuples: {
title: "Tuple count",
get: (info) => info.tuples,
},
time: {
title: "Time spent",
get: (info) => info.timeCost,
unit: "ms",
},
evaluations: {
title: "Evaluations",
get: (info) => info.evaluationCount,
},
iterationsTotal: {
title: "Iterations",
get: (info) => info.iterationCount,
},
};
function metricGetOptional(
metric: Metric,
info: Optional<PredicateInfo>,
isPerEvaluation: boolean,
): Optional<number> {
if (!isPresent(info)) {
return info;
}
const value = metric.get(info);
return isPerEvaluation ? (value / info.evaluationCount) | 0 : value;
}
function addOptionals(a: Optional<number>, b: Optional<number>) {
if (isPresent(a) && isPresent(b)) {
return a + b;
}
if (isPresent(a)) {
return a;
}
if (isPresent(b)) {
return b;
}
if (a === b) {
return a; // If absent for the same reason, preserve that reason
}
return 0; // Otherwise collapse to zero
}
/**
* Returns a "fingerprint" from the given name, which is used to group together similar names.
*/
function getNameFingerprint(name: string, renamings: Renaming[]) {
for (const { patternRegexp, replacement } of renamings) {
if (patternRegexp != null) {
name = name.replace(patternRegexp, replacement);
}
}
return name;
}
function Chevron({ expanded }: { expanded: boolean }) {
return <Codicon name={expanded ? "chevron-down" : "chevron-right"} />;
}
function union<T>(a: Set<T> | T[], b: Set<T> | T[]) {
const result = new Set(a);
for (const x of b) {
result.add(x);
}
return result;
}
export function ComparePerformance(_: Record<string, never>) {
const [data, setData] = useState<
SetPerformanceComparisonQueries | undefined
>();
useMessageFromExtension<ToComparePerformanceViewMessage>(
(msg) => {
setData(msg);
},
[setData],
);
if (!data) {
return <div>Loading performance comparison...</div>;
}
return <ComparePerformanceWithData data={data} />;
}
function ComparePerformanceWithData(props: {
data: SetPerformanceComparisonQueries;
}) {
const { data } = props;
const { from, to } = useMemo(
() => ({
from: new ComparisonDataset(data.from),
to: new ComparisonDataset(data.to),
}),
[data],
);
const comparison = data?.comparison;
const [hideCacheHits, setHideCacheHits] = useState(false);
const [sortOrder, setSortOrder] = useState<"delta" | "absDelta">("absDelta");
const [metric, setMetric] = useState<Metric>(metrics.tuples);
const [isPerEvaluation, setPerEvaluation] = useState(false);
const nameSet = useMemo(
() => union(from.data.names, to.data.names),
[from, to],
);
const hasCacheHitMismatch = useRef(false);
const rows: Row[] = useMemo(() => {
hasCacheHitMismatch.current = false;
return Array.from(nameSet)
.map((name) => {
const before = from.getTupleCountInfo(name);
const after = to.getTupleCountInfo(name);
const beforeValue = metricGetOptional(metric, before, isPerEvaluation);
const afterValue = metricGetOptional(metric, after, isPerEvaluation);
if (beforeValue === afterValue) {
return undefined!;
}
if (
before === AbsentReason.CacheHit ||
after === AbsentReason.CacheHit
) {
hasCacheHitMismatch.current = true;
if (hideCacheHits) {
return undefined!;
}
}
const diff =
(isPresent(afterValue) ? afterValue : 0) -
(isPresent(beforeValue) ? beforeValue : 0);
return { name, before, after, diff } satisfies Row;
})
.filter((x) => !!x)
.sort(getSortOrder(sortOrder));
}, [nameSet, from, to, metric, hideCacheHits, sortOrder, isPerEvaluation]);
const { totalBefore, totalAfter, totalDiff } = useMemo(() => {
let totalBefore = 0;
let totalAfter = 0;
let totalDiff = 0;
for (const row of rows) {
totalBefore += isPresent(row.before) ? metric.get(row.before) : 0;
totalAfter += isPresent(row.after) ? metric.get(row.after) : 0;
totalDiff += row.diff;
}
return { totalBefore, totalAfter, totalDiff };
}, [rows, metric]);
const [renamings, setRenamings] = useState<Renaming[]>(() => [
new Renaming("#[0-9a-f]{8}(?![0-9a-f])", "#"),
]);
// Use deferred value to avoid expensive re-rendering for every keypress in the renaming editor
const deferredRenamings = useDeferredValue(renamings);
const rowGroups = useMemo(() => {
const groupedRows = new Map<string, Row[]>();
for (const row of rows) {
const fingerprint = getNameFingerprint(row.name, deferredRenamings);
const rows = groupedRows.get(fingerprint);
if (rows) {
rows.push(row);
} else {
groupedRows.set(fingerprint, [row]);
}
}
return Array.from(groupedRows.entries())
.map(([fingerprint, rows]) => {
const before = rows
.map((row) => metricGetOptional(metric, row.before, isPerEvaluation))
.reduce(addOptionals);
const after = rows
.map((row) => metricGetOptional(metric, row.after, isPerEvaluation))
.reduce(addOptionals);
return {
name: rows.length === 1 ? rows[0].name : fingerprint,
before,
after,
diff:
(isPresent(after) ? after : 0) - (isPresent(before) ? before : 0),
rows,
} satisfies RowGroup;
})
.sort(getSortOrder(sortOrder));
}, [rows, metric, sortOrder, deferredRenamings, isPerEvaluation]);
const rowGroupNames = useMemo(
() => abbreviateRANames(rowGroups.map((group) => group.name)),
[rowGroups],
);
return (
<>
<ViewTitle>Performance comparison</ViewTitle>
{comparison && hasCacheHitMismatch.current && (
<WarningBox>
<strong>Inconsistent cache hits</strong>
<br />
Some predicates had a cache hit on one side but not the other. For
more accurate results, try running the{" "}
<strong>CodeQL: Clear Cache</strong> command before each query.
<br />
<br />
<label>
<input
type="checkbox"
checked={hideCacheHits}
onChange={() => setHideCacheHits(!hideCacheHits)}
/>
Hide predicates with cache hits
</label>
</WarningBox>
)}
<RenamingInput renamings={renamings} setRenamings={setRenamings} />
Compare{" "}
<Dropdown
onChange={(e: ChangeEvent<HTMLSelectElement>) =>
setMetric(metrics[e.target.value])
}
>
{Object.entries(metrics).map(([key, value]) => (
<option key={key} value={key}>
{value.title}
</option>
))}
</Dropdown>{" "}
<Dropdown
onChange={(e: ChangeEvent<HTMLSelectElement>) =>
setPerEvaluation(e.target.value === "per-evaluation")
}
>
<option value="total">Overall</option>
<option value="per-evaluation">Per evaluation</option>
</Dropdown>{" "}
{comparison && (
<>
sorted by{" "}
<Dropdown
onChange={(e: ChangeEvent<HTMLSelectElement>) =>
setSortOrder(e.target.value as "delta" | "absDelta")
}
value={sortOrder}
>
<option value="delta">Delta</option>
<option value="absDelta">Absolute delta</option>
</Dropdown>
</>
)}
<Table>
<thead>
<HeaderRow comparison={comparison} title="Predicate" />
</thead>
<tbody>
<tr key="total">
<ChevronCell />
{comparison && renderOptionalValue(totalBefore, metric.unit)}
{renderOptionalValue(totalAfter, metric.unit)}
{comparison && renderDelta(totalDiff, metric.unit)}
<NameCell>
<strong>TOTAL</strong>
</NameCell>
</tr>
<tr key="spacing">
<td colSpan={5} style={{ height: "1em" }}></td>
</tr>
</tbody>
</Table>
<PredicateTable
rowGroups={rowGroups}
rowGroupNames={rowGroupNames}
comparison={comparison}
metric={metric}
isPerEvaluation={isPerEvaluation}
/>
</>
);
}
interface PredicateTableProps {
rowGroups: RowGroup[];
rowGroupNames: React.ReactNode[];
comparison: boolean;
metric: Metric;
isPerEvaluation: boolean;
}
function PredicateTableRaw(props: PredicateTableProps) {
const { comparison, metric, rowGroupNames, rowGroups, isPerEvaluation } =
props;
return rowGroups.map((rowGroup, rowGroupIndex) => (
<PredicateRowGroup
key={rowGroupIndex}
renderedName={rowGroupNames[rowGroupIndex]}
rowGroup={rowGroup}
comparison={comparison}
metric={metric}
isPerEvaluation={isPerEvaluation}
/>
));
}
const PredicateTable = memo(PredicateTableRaw);
interface PredicateRowGroupProps {
renderedName: React.ReactNode;
rowGroup: RowGroup;
comparison: boolean;
metric: Metric;
isPerEvaluation: boolean;
}
function PredicateRowGroup(props: PredicateRowGroupProps) {
const { renderedName, rowGroup, comparison, metric, isPerEvaluation } = props;
const [isExpanded, setExpanded] = useState(false);
const rowNames = useMemo(
() => abbreviateRANames(rowGroup.rows.map((row) => row.name)),
[rowGroup],
);
if (rowGroup.rows.length === 1) {
return <PredicateRow row={rowGroup.rows[0]} {...props} />;
}
return (
<Table className={isExpanded ? "expanded" : ""}>
<tbody>
<PredicateTR
className={isExpanded ? "expanded" : ""}
key={"main"}
onClick={() => setExpanded(!isExpanded)}
>
<ChevronCell>
<Chevron expanded={isExpanded} />
</ChevronCell>
{comparison && renderOptionalValue(rowGroup.before)}
{renderOptionalValue(rowGroup.after)}
{comparison && renderDelta(rowGroup.diff, metric.unit)}
<NameCell>
{renderedName} ({rowGroup.rows.length} predicates)
</NameCell>
</PredicateTR>
{isExpanded &&
rowGroup.rows.map((row, rowIndex) => (
<tr key={rowIndex}>
<td colSpan={5}>
<PredicateRow
renderedName={rowNames[rowIndex]}
row={row}
comparison={comparison}
metric={metric}
isPerEvaluation={isPerEvaluation}
/>
</td>
</tr>
))}
</tbody>
</Table>
);
}
interface PredicateRowProps {
renderedName: React.ReactNode;
row: Row;
comparison: boolean;
metric: Metric;
isPerEvaluation: boolean;
}
function PredicateRow(props: PredicateRowProps) {
const [isExpanded, setExpanded] = useState(false);
const { renderedName, row, comparison, metric, isPerEvaluation } = props;
const evaluationFactorBefore =
isPerEvaluation && isPresent(row.before) ? row.before.evaluationCount : 1;
const evaluationFactorAfter =
isPerEvaluation && isPresent(row.after) ? row.after.evaluationCount : 1;
return (
<Table className={isExpanded ? "expanded" : ""}>
<tbody>
<PredicateTR
className={isExpanded ? "expanded" : ""}
key={"main"}
onClick={() => setExpanded(!isExpanded)}
>
<ChevronCell>
<Chevron expanded={isExpanded} />
</ChevronCell>
{comparison &&
renderPredicateMetric(row.before, metric, isPerEvaluation)}
{renderPredicateMetric(row.after, metric, isPerEvaluation)}
{comparison && renderDelta(row.diff, metric.unit)}
<NameCell>{renderedName}</NameCell>
</PredicateTR>
{isExpanded && (
<>
<HighLevelStats
before={row.before}
after={row.after}
comparison={comparison}
/>
{collatePipelines(
isPresent(row.before) ? row.before.pipelines : {},
isPresent(row.after) ? row.after.pipelines : {},
).map(({ name, first, second }, pipelineIndex) => (
<Fragment key={pipelineIndex}>
<HeaderRow
hasBefore={first != null}
hasAfter={second != null}
comparison={comparison}
title={
<>
Tuple counts for &apos;{name}&apos; pipeline
{comparison &&
(first == null
? " (after)"
: second == null
? " (before)"
: "")}
</>
}
/>
{abbreviateRASteps(first?.steps ?? second!.steps).map(
(step, index) => (
<PipelineStep
key={index}
before={
first &&
(first.counts[index] / evaluationFactorBefore) | 0
}
after={
second &&
(second.counts[index] / evaluationFactorAfter) | 0
}
comparison={comparison}
step={step}
/>
),
)}
</Fragment>
))}
</>
)}
</tbody>
</Table>
);
}
interface PipelinePair {
name: string;
first: PipelineSummary | undefined;
second: PipelineSummary | undefined;
}
function collatePipelines(
before: Record<string, PipelineSummary>,
after: Record<string, PipelineSummary>,
): PipelinePair[] {
const result: PipelinePair[] = [];
for (const [name, first] of Object.entries(before)) {
const second = after[name];
if (second == null) {
result.push({ name, first, second: undefined });
} else if (samePipeline(first.steps, second.steps)) {
result.push({ name, first, second });
} else {
result.push({ name, first, second: undefined });
result.push({ name, first: undefined, second });
}
}
for (const [name, second] of Object.entries(after)) {
if (before[name] == null) {
result.push({ name, first: undefined, second });
}
}
return result;
}
function samePipeline(a: string[], b: string[]) {
return a.length === b.length && a.every((x, i) => x === b[i]);
}

View File

@@ -0,0 +1,282 @@
import { Fragment, useState } from "react";
import { styled } from "styled-components";
/**
* A set of names, for generating unambiguous abbreviations.
*/
class NameSet {
private readonly abbreviations = new Map<string, React.ReactNode>();
constructor(readonly names: string[]) {
const qnames = names.map(parseName);
const builder = new TrieBuilder();
qnames
.map((qname) => builder.visitQName(qname))
.forEach((r, index) => {
this.abbreviations.set(names[index], r.abbreviate(true));
});
}
public getAbbreviation(name: string): React.ReactNode {
return this.abbreviations.get(name) ?? name;
}
}
/** Name parsed into the form `prefix::name<args>` */
interface QualifiedName {
prefix?: QualifiedName;
name: string;
args?: QualifiedName[];
}
function qnameToString(name: QualifiedName): string {
const parts: string[] = [];
if (name.prefix != null) {
parts.push(qnameToString(name.prefix));
parts.push("::");
}
parts.push(name.name);
if (name.args != null && name.args.length > 0) {
parts.push("<");
parts.push(name.args.map(qnameToString).join(","));
parts.push(">");
}
return parts.join("");
}
function tokeniseName(text: string) {
return Array.from(text.matchAll(/:+|<|>|,|"[^"]+"|`[^`]+`|[^:<>,"`]+/g));
}
function parseName(text: string): QualifiedName {
const tokens = tokeniseName(text);
function next() {
return tokens.pop()![0];
}
function peek() {
return tokens[tokens.length - 1][0];
}
function skipToken(token: string) {
if (tokens.length > 0 && peek() === token) {
tokens.pop();
return true;
} else {
return false;
}
}
function parseQName(): QualifiedName {
// Note that the tokens stream is parsed in reverse order. This is simpler, but may look confusing initially.
let args: QualifiedName[] | undefined;
if (skipToken(">")) {
args = [];
while (tokens.length > 0 && peek() !== "<") {
args.push(parseQName());
skipToken(",");
}
args.reverse();
skipToken("<");
}
const name = tokens.length === 0 ? "" : next();
const prefix = skipToken("::") ? parseQName() : undefined;
return {
prefix,
name,
args,
};
}
const result = parseQName();
if (tokens.length > 0) {
// It's a parse error if we did not consume all tokens.
// Just treat the whole text as the 'name'.
return { prefix: undefined, name: text, args: undefined };
}
return result;
}
class TrieNode {
children = new Map<string, TrieNode>();
constructor(readonly index: number) {}
}
interface VisitResult {
node: TrieNode;
abbreviate: (isRoot?: boolean) => React.ReactNode;
}
class TrieBuilder {
root = new TrieNode(0);
nextId = 1;
getOrCreate(trieNode: TrieNode, child: string) {
const { children } = trieNode;
let node = children.get(child);
if (node == null) {
node = new TrieNode(this.nextId++);
children.set(child, node);
}
return node;
}
visitQName(qname: QualifiedName): VisitResult {
const prefix =
qname.prefix != null ? this.visitQName(qname.prefix) : undefined;
const trieNodeBeforeArgs = this.getOrCreate(
prefix?.node ?? this.root,
qname.name,
);
let trieNode = trieNodeBeforeArgs;
const args = qname.args?.map((arg) => this.visitQName(arg));
if (args != null) {
const argKey = args.map((arg) => arg.node.index).join(",");
trieNode = this.getOrCreate(trieNodeBeforeArgs, argKey);
}
return {
node: trieNode,
abbreviate: (isRoot = false) => {
const result: React.ReactNode[] = [];
if (prefix != null) {
result.push(prefix.abbreviate());
result.push("::");
}
const { name } = qname;
const hash = name.indexOf("#");
if (hash !== -1 && isRoot) {
const shortName = name.substring(0, hash);
result.push(<IdentifierSpan>{shortName}</IdentifierSpan>);
result.push(name.substring(hash));
} else {
result.push(isRoot ? <IdentifierSpan>{name}</IdentifierSpan> : name);
}
if (args != null) {
result.push("<");
if (trieNodeBeforeArgs.children.size === 1) {
const argsText = qname
.args!.map((arg) => qnameToString(arg))
.join(",");
result.push(<ExpandableNamePart>{argsText}</ExpandableNamePart>);
} else {
let first = true;
for (const arg of args) {
result.push(arg.abbreviate());
if (first) {
first = false;
} else {
result.push(",");
}
}
}
result.push(">");
}
return result;
},
};
}
}
const ExpandableTextButton = styled.button`
background: none;
border: none;
cursor: pointer;
padding: 0;
color: inherit;
&:hover {
background-color: rgba(128, 128, 128, 0.2);
}
`;
interface ExpandableNamePartProps {
children: React.ReactNode;
}
function ExpandableNamePart(props: ExpandableNamePartProps) {
const [isExpanded, setExpanded] = useState(false);
return (
<ExpandableTextButton
onClick={(event: Event) => {
setExpanded(!isExpanded);
event.stopPropagation();
}}
>
{isExpanded ? props.children : "..."}
</ExpandableTextButton>
);
}
/**
* Span enclosing an entire qualified name.
*
* Can be used to gray out uninteresting parts of the name, though this looks worse than expected.
*/
const QNameSpan = styled.span`
/* color: var(--vscode-disabledForeground); */
`;
/** Span enclosing the innermost identifier, e.g. the `foo` in `A::B<X>::foo#abc` */
const IdentifierSpan = styled.span`
font-weight: 600;
`;
/** Span enclosing keywords such as `JOIN` and `WITH`. */
const KeywordSpan = styled.span`
font-weight: 500;
`;
const nameTokenRegex = /\b[^ (]+\b/g;
function traverseMatches(
text: string,
regex: RegExp,
callbacks: {
onMatch: (match: RegExpMatchArray) => void;
onText: (text: string) => void;
},
) {
const matches = Array.from(text.matchAll(regex));
let lastIndex = 0;
for (const match of matches) {
const before = text.substring(lastIndex, match.index);
if (before !== "") {
callbacks.onText(before);
}
callbacks.onMatch(match);
lastIndex = match.index + match[0].length;
}
const after = text.substring(lastIndex);
if (after !== "") {
callbacks.onText(after);
}
}
export function abbreviateRASteps(steps: string[]): React.ReactNode[] {
const nameTokens = steps.flatMap((step) =>
Array.from(step.matchAll(nameTokenRegex)).map((tok) => tok[0]),
);
const nameSet = new NameSet(nameTokens.filter((name) => name.includes("::")));
return steps.map((step, index) => {
const result: React.ReactNode[] = [];
traverseMatches(step, nameTokenRegex, {
onMatch(match) {
const text = match[0];
if (text.includes("::")) {
result.push(<QNameSpan>{nameSet.getAbbreviation(text)}</QNameSpan>);
} else if (/[A-Z]+/.test(text)) {
result.push(<KeywordSpan>{text}</KeywordSpan>);
} else {
result.push(match[0]);
}
},
onText(text) {
result.push(text);
},
});
return <Fragment key={index}>{result}</Fragment>;
});
}
export function abbreviateRANames(names: string[]): React.ReactNode[] {
const nameSet = new NameSet(names);
return names.map((name) => nameSet.getAbbreviation(name));
}

View File

@@ -0,0 +1,106 @@
import type { ChangeEvent } from "react";
import { styled } from "styled-components";
import {
VSCodeButton,
VSCodeTextField,
} from "@vscode/webview-ui-toolkit/react";
import { Codicon } from "../common";
export class Renaming {
patternRegexp: RegExp | undefined;
constructor(
public pattern: string,
public replacement: string,
) {
this.patternRegexp = tryCompilePattern(pattern);
}
}
function tryCompilePattern(pattern: string): RegExp | undefined {
try {
return new RegExp(pattern, "i");
} catch {
return undefined;
}
}
const Input = styled(VSCodeTextField)`
width: 20em;
`;
const Row = styled.div`
display: flex;
padding-bottom: 0.25em;
`;
const Details = styled.details`
padding: 1em;
`;
interface RenamingInputProps {
renamings: Renaming[];
setRenamings: (renamings: Renaming[]) => void;
}
export function RenamingInput(props: RenamingInputProps) {
const { renamings, setRenamings } = props;
return (
<Details>
<summary>Predicate renaming</summary>
<p>
The following regexp replacements are applied to every predicate name on
both sides. Predicates whose names clash after renaming are grouped
together. Can be used to correlate predicates that were renamed between
the two runs.
<br />
Can also be used to group related predicates, for example, renaming{" "}
<code>.*ssa.*</code> to <code>SSA</code> will group all SSA-related
predicates together.
</p>
{renamings.map((renaming, index) => (
<Row key={index}>
<Input
value={renaming.pattern}
placeholder="Pattern"
onInput={(e: ChangeEvent<HTMLInputElement>) => {
const newRenamings = [...renamings];
newRenamings[index] = new Renaming(
e.target.value,
renaming.replacement,
);
setRenamings(newRenamings);
}}
>
<Codicon name="search" slot="start" />
</Input>
<Input
value={renaming.replacement}
placeholder="Replacement"
onInput={(e: ChangeEvent<HTMLInputElement>) => {
const newRenamings = [...renamings];
newRenamings[index] = new Renaming(
renaming.pattern,
e.target.value,
);
setRenamings(newRenamings);
}}
></Input>
<VSCodeButton
onClick={() =>
setRenamings(renamings.filter((_, i) => i !== index))
}
>
<Codicon name="trash" />
</VSCodeButton>
<br />
</Row>
))}
<VSCodeButton
onClick={() => setRenamings([...renamings, new Renaming("", "")])}
>
Add renaming rule
</VSCodeButton>
</Details>
);
}

View File

@@ -0,0 +1,8 @@
import type { WebviewDefinition } from "../webview-definition";
import { ComparePerformance } from "./ComparePerformance";
const definition: WebviewDefinition = {
component: <ComparePerformance />,
};
export default definition;

View File

@@ -1,4 +1,4 @@
import { useState, useEffect, useRef } from "react";
import { useState, useRef } from "react";
import { styled } from "styled-components";
import type {
@@ -16,6 +16,7 @@ import CompareTable from "./CompareTable";
import "../results/resultsView.css";
import { assertNever } from "../../common/helpers-pure";
import { useMessageFromExtension } from "../common/useMessageFromExtension";
const Header = styled.div`
display: flex;
@@ -50,115 +51,101 @@ export function Compare(_: Record<string, never>): React.JSX.Element {
comparison?.result &&
(comparison.result.to.length || comparison.result.from.length);
useEffect(() => {
const listener = (evt: MessageEvent) => {
if (evt.origin === window.origin) {
const msg: ToCompareViewMessage = evt.data;
switch (msg.t) {
case "setComparisonQueryInfo":
setQueryInfo(msg);
break;
case "setComparisons":
setComparison(msg);
break;
case "streamingComparisonSetup":
setComparison(null);
streamingComparisonRef.current = msg;
break;
case "streamingComparisonAddResults": {
const prev = streamingComparisonRef.current;
if (prev === null) {
console.warn(
'Received "streamingComparisonAddResults" before "streamingComparisonSetup"',
useMessageFromExtension<ToCompareViewMessage>((msg) => {
switch (msg.t) {
case "setComparisonQueryInfo":
setQueryInfo(msg);
break;
case "setComparisons":
setComparison(msg);
break;
case "streamingComparisonSetup":
setComparison(null);
streamingComparisonRef.current = msg;
break;
case "streamingComparisonAddResults": {
const prev = streamingComparisonRef.current;
if (prev === null) {
console.warn(
'Received "streamingComparisonAddResults" before "streamingComparisonSetup"',
);
break;
}
if (prev.id !== msg.id) {
console.warn(
'Received "streamingComparisonAddResults" with different id, ignoring',
);
break;
}
let result: QueryCompareResult;
switch (prev.result.kind) {
case "raw":
if (msg.result.kind !== "raw") {
throw new Error(
"Streaming comparison: expected raw results, got interpreted results",
);
break;
}
if (prev.id !== msg.id) {
console.warn(
'Received "streamingComparisonAddResults" with different id, ignoring',
);
break;
}
let result: QueryCompareResult;
switch (prev.result.kind) {
case "raw":
if (msg.result.kind !== "raw") {
throw new Error(
"Streaming comparison: expected raw results, got interpreted results",
);
}
result = {
...prev.result,
from: [...prev.result.from, ...msg.result.from],
to: [...prev.result.to, ...msg.result.to],
};
break;
case "interpreted":
if (msg.result.kind !== "interpreted") {
throw new Error(
"Streaming comparison: expected interpreted results, got raw results",
);
}
result = {
...prev.result,
from: [...prev.result.from, ...msg.result.from],
to: [...prev.result.to, ...msg.result.to],
};
break;
default:
throw new Error("Unexpected comparison result kind");
}
streamingComparisonRef.current = {
...prev,
result,
result = {
...prev.result,
from: [...prev.result.from, ...msg.result.from],
to: [...prev.result.to, ...msg.result.to],
};
break;
}
case "streamingComparisonComplete":
if (streamingComparisonRef.current === null) {
console.warn(
'Received "streamingComparisonComplete" before "streamingComparisonSetup"',
case "interpreted":
if (msg.result.kind !== "interpreted") {
throw new Error(
"Streaming comparison: expected interpreted results, got raw results",
);
setComparison(null);
break;
}
if (streamingComparisonRef.current.id !== msg.id) {
console.warn(
'Received "streamingComparisonComplete" with different id, ignoring',
);
break;
}
setComparison({
...streamingComparisonRef.current,
t: "setComparisons",
});
streamingComparisonRef.current = null;
break;
case "setUserSettings":
setUserSettings(msg.userSettings);
result = {
...prev.result,
from: [...prev.result.from, ...msg.result.from],
to: [...prev.result.to, ...msg.result.to],
};
break;
default:
assertNever(msg);
throw new Error("Unexpected comparison result kind");
}
} else {
// sanitize origin
const origin = evt.origin.replace(/\n|\r/g, "");
console.error(`Invalid event origin ${origin}`);
}
};
window.addEventListener("message", listener);
return () => {
window.removeEventListener("message", listener);
};
streamingComparisonRef.current = {
...prev,
result,
};
break;
}
case "streamingComparisonComplete":
if (streamingComparisonRef.current === null) {
console.warn(
'Received "streamingComparisonComplete" before "streamingComparisonSetup"',
);
setComparison(null);
break;
}
if (streamingComparisonRef.current.id !== msg.id) {
console.warn(
'Received "streamingComparisonComplete" with different id, ignoring',
);
break;
}
setComparison({
...streamingComparisonRef.current,
t: "setComparisons",
});
streamingComparisonRef.current = null;
break;
case "setUserSettings":
setUserSettings(msg.userSettings);
break;
default:
assertNever(msg);
}
}, []);
if (!queryInfo || !comparison) {

View File

@@ -1,7 +1,8 @@
import { useEffect, useState } from "react";
import { useState } from "react";
import type { ToDataFlowPathsMessage } from "../../common/interface-types";
import type { DataFlowPaths as DataFlowPathsDomainModel } from "../../variant-analysis/shared/data-flow-paths";
import { DataFlowPaths } from "./DataFlowPaths";
import { useMessageFromExtension } from "../common/useMessageFromExtension";
export type DataFlowPathsViewProps = {
dataFlowPaths?: DataFlowPathsDomainModel;
@@ -14,28 +15,12 @@ export function DataFlowPathsView({
DataFlowPathsDomainModel | undefined
>(initialDataFlowPaths);
useEffect(() => {
const listener = (evt: MessageEvent) => {
if (evt.origin === window.origin) {
const msg: ToDataFlowPathsMessage = evt.data;
if (msg.t === "setDataFlowPaths") {
setDataFlowPaths(msg.dataFlowPaths);
useMessageFromExtension<ToDataFlowPathsMessage>((msg) => {
setDataFlowPaths(msg.dataFlowPaths);
// Scroll to the top of the page when we're rendering
// new data flow paths.
window.scrollTo(0, 0);
}
} else {
// sanitize origin
const origin = evt.origin.replace(/\n|\r/g, "");
console.error(`Invalid event origin ${origin}`);
}
};
window.addEventListener("message", listener);
return () => {
window.removeEventListener("message", listener);
};
// Scroll to the top of the page when we're rendering
// new data flow paths.
window.scrollTo(0, 0);
}, []);
if (!dataFlowPaths) {

View File

@@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from "react";
import { useMemo, useState } from "react";
import { MethodModeling } from "./MethodModeling";
import { getModelingStatus } from "../../model-editor/shared/modeling-status";
import type { Method } from "../../model-editor/method";
@@ -12,6 +12,7 @@ import { NoMethodSelected } from "./NoMethodSelected";
import type { MethodModelingPanelViewState } from "../../model-editor/shared/view-state";
import { MethodAlreadyModeled } from "./MethodAlreadyModeled";
import { defaultModelConfig } from "../../model-editor/languages";
import { useMessageFromExtension } from "../common/useMessageFromExtension";
type Props = {
initialViewState?: MethodModelingPanelViewState;
@@ -36,47 +37,33 @@ export function MethodModelingView({
[modeledMethods, isMethodModified],
);
useEffect(() => {
const listener = (evt: MessageEvent) => {
if (evt.origin === window.origin) {
const msg: ToMethodModelingMessage = evt.data;
switch (msg.t) {
case "setMethodModelingPanelViewState":
setViewState(msg.viewState);
break;
case "setInModelingMode":
setInModelingMode(msg.inModelingMode);
break;
case "setMultipleModeledMethods":
setModeledMethods(msg.modeledMethods);
break;
case "setMethodModified":
setIsMethodModified(msg.isModified);
break;
case "setNoMethodSelected":
setMethod(undefined);
setModeledMethods([]);
setIsMethodModified(false);
break;
case "setSelectedMethod":
setMethod(msg.method);
setModeledMethods(msg.modeledMethods);
setIsMethodModified(msg.isModified);
break;
default:
assertNever(msg);
}
} else {
// sanitize origin
const origin = evt.origin.replace(/\n|\r/g, "");
console.error(`Invalid event origin ${origin}`);
}
};
window.addEventListener("message", listener);
return () => {
window.removeEventListener("message", listener);
};
useMessageFromExtension<ToMethodModelingMessage>((msg) => {
switch (msg.t) {
case "setMethodModelingPanelViewState":
setViewState(msg.viewState);
break;
case "setInModelingMode":
setInModelingMode(msg.inModelingMode);
break;
case "setMultipleModeledMethods":
setModeledMethods(msg.modeledMethods);
break;
case "setMethodModified":
setIsMethodModified(msg.isModified);
break;
case "setNoMethodSelected":
setMethod(undefined);
setModeledMethods([]);
setIsMethodModified(false);
break;
case "setSelectedMethod":
setMethod(msg.method);
setModeledMethods(msg.modeledMethods);
setIsMethodModified(msg.isModified);
break;
default:
assertNever(msg);
}
}, []);
if (!inModelingMode || !viewState?.language) {

View File

@@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { useCallback, useMemo, useState } from "react";
import { styled } from "styled-components";
import { ModelAlertsHeader } from "./ModelAlertsHeader";
import type { ModelAlertsViewState } from "../../model-editor/shared/view-state";
@@ -18,6 +18,7 @@ import {
} from "../../model-editor/shared/model-alerts-filter-sort";
import type { ModelAlertsFilterSortState } from "../../model-editor/shared/model-alerts-filter-sort";
import type { ModeledMethod } from "../../model-editor/modeled-method";
import { useMessageFromExtension } from "../common/useMessageFromExtension";
type Props = {
initialViewState?: ModelAlertsViewState;
@@ -67,47 +68,33 @@ export function ModelAlerts({
null,
);
useEffect(() => {
const listener = (evt: MessageEvent) => {
if (evt.origin === window.origin) {
const msg: ToModelAlertsMessage = evt.data;
switch (msg.t) {
case "setModelAlertsViewState": {
setViewState(msg.viewState);
break;
}
case "setVariantAnalysis": {
setVariantAnalysis(msg.variantAnalysis);
break;
}
case "setRepoResults": {
setRepoResults((oldRepoResults) => {
const newRepoIds = msg.repoResults.map((r) => r.repositoryId);
return [
...oldRepoResults.filter(
(v) => !newRepoIds.includes(v.repositoryId),
),
...msg.repoResults,
];
});
break;
}
case "revealModel": {
setRevealedModel(msg.modeledMethod);
break;
}
}
} else {
// sanitize origin
const origin = evt.origin.replace(/\n|\r/g, "");
console.error(`Invalid event origin ${origin}`);
useMessageFromExtension<ToModelAlertsMessage>((msg) => {
switch (msg.t) {
case "setModelAlertsViewState": {
setViewState(msg.viewState);
break;
}
};
window.addEventListener("message", listener);
return () => {
window.removeEventListener("message", listener);
};
case "setVariantAnalysis": {
setVariantAnalysis(msg.variantAnalysis);
break;
}
case "setRepoResults": {
setRepoResults((oldRepoResults) => {
const newRepoIds = msg.repoResults.map((r) => r.repositoryId);
return [
...oldRepoResults.filter(
(v) => !newRepoIds.includes(v.repositoryId),
),
...msg.repoResults,
];
});
break;
}
case "revealModel": {
setRevealedModel(msg.modeledMethod);
break;
}
}
}, []);
const modelAlerts = useMemo(() => {

View File

@@ -21,6 +21,7 @@ import { INITIAL_HIDE_MODELED_METHODS_VALUE } from "../../model-editor/shared/hi
import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions";
import type { ModelEvaluationRunState } from "../../model-editor/shared/model-evaluation-run-state";
import { ModelEvaluation } from "./ModelEvaluation";
import { useMessageFromExtension } from "../common/useMessageFromExtension";
const LoadingContainer = styled.div`
text-align: center;
@@ -129,47 +130,33 @@ export function ModelEditor({
AccessPathSuggestionOptions | undefined
>(undefined);
useEffect(() => {
const listener = (evt: MessageEvent) => {
if (evt.origin === window.origin) {
const msg: ToModelEditorMessage = evt.data;
switch (msg.t) {
case "setModelEditorViewState":
setViewState(msg.viewState);
break;
case "setMethods":
setMethods(msg.methods);
break;
case "setModeledAndModifiedMethods":
setModeledMethods(msg.methods);
setModifiedSignatures(new Set(msg.modifiedMethodSignatures));
break;
case "setModifiedMethods":
setModifiedSignatures(new Set(msg.methodSignatures));
break;
case "revealMethod":
setRevealedMethodSignature(msg.methodSignature);
break;
case "setAccessPathSuggestions":
setAccessPathSuggestions(msg.accessPathSuggestions);
break;
case "setModelEvaluationRun":
setEvaluationRun(msg.run);
break;
default:
assertNever(msg);
}
} else {
// sanitize origin
const origin = evt.origin.replace(/\n|\r/g, "");
console.error(`Invalid event origin ${origin}`);
}
};
window.addEventListener("message", listener);
return () => {
window.removeEventListener("message", listener);
};
useMessageFromExtension<ToModelEditorMessage>((msg) => {
switch (msg.t) {
case "setModelEditorViewState":
setViewState(msg.viewState);
break;
case "setMethods":
setMethods(msg.methods);
break;
case "setModeledAndModifiedMethods":
setModeledMethods(msg.methods);
setModifiedSignatures(new Set(msg.modifiedMethodSignatures));
break;
case "setModifiedMethods":
setModifiedSignatures(new Set(msg.methodSignatures));
break;
case "revealMethod":
setRevealedMethodSignature(msg.methodSignature);
break;
case "setAccessPathSuggestions":
setAccessPathSuggestions(msg.accessPathSuggestions);
break;
case "setModelEvaluationRun":
setEvaluationRun(msg.run);
break;
default:
assertNever(msg);
}
}, []);
useEffect(() => {

View File

@@ -48,7 +48,10 @@ export function Graph({ graphData, databaseUri }: GraphProps) {
d.attributes["xlink:href"] = "#";
d.attributes["href"] = "#";
loc.uri = `file://${loc.uri}`;
select(this).on("click", () => jumpToLocation(loc, databaseUri));
select(this).on("click", (event: Event) => {
jumpToLocation(loc, databaseUri);
event.preventDefault(); // Avoid resetting scroll position
});
}
}
if ("fill" in d.attributes) {

View File

@@ -16,11 +16,12 @@ import {
DEFAULT_USER_SETTINGS,
GRAPH_TABLE_NAME,
} from "../../common/interface-types";
import { useMessageFromExtension } from "../common/useMessageFromExtension";
import { ResultTables } from "./ResultTables";
import { onNavigation } from "./navigation";
import "./resultsView.css";
import { useCallback, useEffect, useState } from "react";
import { useCallback, useState } from "react";
/**
* ResultsApp.tsx
@@ -113,8 +114,8 @@ export function ResultsApp() {
[],
);
const handleMessage = useCallback(
(msg: IntoResultsViewMsg): void => {
useMessageFromExtension<IntoResultsViewMsg>(
(msg) => {
switch (msg.t) {
case "setUserSettings":
setUserSettings(msg.userSettings);
@@ -189,26 +190,6 @@ export function ResultsApp() {
[updateStateWithNewResultsInfo],
);
const vscodeMessageHandler = useCallback(
(evt: MessageEvent) => {
// sanitize origin
const origin = evt.origin.replace(/\n|\r/g, "");
if (evt.origin === window.origin) {
handleMessage(evt.data as IntoResultsViewMsg);
} else {
console.error(`Invalid event origin ${origin}`);
}
},
[handleMessage],
);
useEffect(() => {
window.addEventListener("message", vscodeMessageHandler);
return () => {
window.removeEventListener("message", vscodeMessageHandler);
};
}, [vscodeMessageHandler]);
const { displayedResults, nextResultsInfo, isExpectingResultsUpdate } = state;
if (
displayedResults.results !== null &&

View File

@@ -1,4 +1,4 @@
import { useCallback, useEffect, useState } from "react";
import { useCallback, useState } from "react";
import type {
VariantAnalysis as VariantAnalysisDomainModel,
@@ -13,6 +13,7 @@ import type { ToVariantAnalysisMessage } from "../../common/interface-types";
import { vscode } from "../vscode-api";
import { defaultFilterSortState } from "../../variant-analysis/shared/variant-analysis-filter-sort";
import { sendTelemetry, useTelemetryOnChange } from "../common/telemetry";
import { useMessageFromExtension } from "../common/useMessageFromExtension";
export type VariantAnalysisProps = {
variantAnalysis?: VariantAnalysisDomainModel;
@@ -77,49 +78,31 @@ export function VariantAnalysis({
debounceTimeoutMillis: 1000,
});
useEffect(() => {
const listener = (evt: MessageEvent) => {
if (evt.origin === window.origin) {
const msg: ToVariantAnalysisMessage = evt.data;
if (msg.t === "setVariantAnalysis") {
setVariantAnalysis(msg.variantAnalysis);
vscode.setState({
variantAnalysisId: msg.variantAnalysis.id,
});
} else if (msg.t === "setFilterSortState") {
setFilterSortState(msg.filterSortState);
} else if (msg.t === "setRepoResults") {
setRepoResults((oldRepoResults) => {
const newRepoIds = msg.repoResults.map((r) => r.repositoryId);
return [
...oldRepoResults.filter(
(v) => !newRepoIds.includes(v.repositoryId),
),
...msg.repoResults,
];
});
} else if (msg.t === "setRepoStates") {
setRepoStates((oldRepoStates) => {
const newRepoIds = msg.repoStates.map((r) => r.repositoryId);
return [
...oldRepoStates.filter(
(v) => !newRepoIds.includes(v.repositoryId),
),
...msg.repoStates,
];
});
}
} else {
// sanitize origin
const origin = evt.origin.replace(/\n|\r/g, "");
console.error(`Invalid event origin ${origin}`);
}
};
window.addEventListener("message", listener);
return () => {
window.removeEventListener("message", listener);
};
useMessageFromExtension<ToVariantAnalysisMessage>((msg) => {
if (msg.t === "setVariantAnalysis") {
setVariantAnalysis(msg.variantAnalysis);
vscode.setState({
variantAnalysisId: msg.variantAnalysis.id,
});
} else if (msg.t === "setFilterSortState") {
setFilterSortState(msg.filterSortState);
} else if (msg.t === "setRepoResults") {
setRepoResults((oldRepoResults) => {
const newRepoIds = msg.repoResults.map((r) => r.repositoryId);
return [
...oldRepoResults.filter((v) => !newRepoIds.includes(v.repositoryId)),
...msg.repoResults,
];
});
} else if (msg.t === "setRepoStates") {
setRepoStates((oldRepoStates) => {
const newRepoIds = msg.repoStates.map((r) => r.repositoryId);
return [
...oldRepoStates.filter((v) => !newRepoIds.includes(v.repositoryId)),
...msg.repoStates,
];
});
}
}, []);
const copyRepositoryList = useCallback(() => {

View File

@@ -6,6 +6,7 @@ import { registerUnhandledErrorListener } from "./common/errors";
import type { WebviewDefinition } from "./webview-definition";
import compareView from "./compare";
import comparePerformance from "./compare-performance";
import dataFlowPathsView from "./data-flow-paths";
import methodModelingView from "./method-modeling";
import modelEditorView from "./model-editor";
@@ -18,6 +19,7 @@ import "@vscode/codicons/dist/codicon.css";
const views: Record<string, WebviewDefinition> = {
compare: compareView,
"compare-performance": comparePerformance,
"data-flow-paths": dataFlowPathsView,
"method-modeling": methodModelingView,
"model-editor": modelEditorView,

View File

@@ -1,7 +1,6 @@
[
"v2.19.2",
"v2.20.2",
"v2.19.4",
"v2.18.4",
"v2.17.6",
"v2.16.6",
"nightly"
]

View File

@@ -0,0 +1,87 @@
/**
* Benchmarks the jsonl-parser against a reference implementation and checks that it generates
* the same output.
*
* Usage:
*
* ts-node json-reader.bench.ts [evaluator-log.summary.jsonl] [count]
*
* The log file defaults to a small checked-in log and count defaults to 100
* (and should be lowered significantly for large files).
*
* At the time of writing it is about as fast as the synchronous reference implementation,
* but doesn't run out of memory for large files.
*/
import { readFile } from "fs-extra";
import { readJsonlFile } from "../../src/common/jsonl-reader";
import { performance } from "perf_hooks";
import { join } from "path";
/** An "obviously correct" implementation to test against. */
async function readJsonlReferenceImpl<T>(
path: string,
handler: (value: T) => Promise<void>,
): Promise<void> {
const logSummary = await readFile(path, "utf-8");
// Remove newline delimiters because summary is in .jsonl format.
const jsonSummaryObjects: string[] = logSummary.split(/\r?\n\r?\n/g);
for (const obj of jsonSummaryObjects) {
const jsonObj = JSON.parse(obj) as T;
await handler(jsonObj);
}
}
type ParserFn = (
text: string,
callback: (v: unknown) => Promise<void>,
) => Promise<void>;
const parsers: Record<string, ParserFn> = {
readJsonlReferenceImpl,
readJsonlFile,
};
async function main() {
const args = process.argv.slice(2);
const file =
args.length > 0
? args[0]
: join(
__dirname,
"../unit-tests/data/evaluator-log-summaries/bad-join-order.jsonl",
);
const numTrials = args.length > 1 ? Number(args[1]) : 100;
const referenceValues: any[] = [];
await readJsonlReferenceImpl(file, async (event) => {
referenceValues.push(event);
});
const referenceValueString = JSON.stringify(referenceValues);
// Do warm-up runs and check against reference implementation
for (const [name, parser] of Object.entries(parsers)) {
const values: unknown[] = [];
await parser(file, async (event) => {
values.push(event);
});
if (JSON.stringify(values) !== referenceValueString) {
console.error(`${name}: failed to match reference implementation`);
}
}
for (const [name, parser] of Object.entries(parsers)) {
const startTime = performance.now();
for (let i = 0; i < numTrials; ++i) {
await Promise.all([
parser(file, async () => {}),
parser(file, async () => {}),
]);
}
const duration = performance.now() - startTime;
const durationPerTrial = duration / numTrials;
console.log(`${name}: ${durationPerTrial.toFixed(1)} ms`);
}
}
main().catch((err: unknown) => {
console.error(err);
});

View File

@@ -9,12 +9,12 @@ Setup
- install playwright if you haven't yet (`npx playwright install`)
- go to the e2e test folder on your terminal
- make sure docker is running
- run `docker-compose build`
- run `docker-compose up`
- run `docker compose build`
- run `docker compose up`
Run tests
- run `npx playwright test --ui` from the e2e test folder to follow the test while it's running. This UI has a 'locator' tool with which elements on the test screen can be found
- run `npx playwright test --ui` from the e2e test folder to follow the test while it's running. This UI has a 'locator' tool with which elements on the test screen can be found
- use `npx playwright test --debug` to follow the test in real time and interact with the interface, e.g. press enter or input into fields, stop and start
During the test elements are created in the docker volume, e.g. the downloaded database or query data. This might interfer with other tests or when running a test twice. If that happens restart your docker volume by using `docker-compose down -v` and `docker-compose up`. Sometimes already existing queries from former runs change the input the extension needs.

View File

@@ -1,5 +1,3 @@
version: "3.8"
services:
code-server:
build:
@@ -38,7 +36,7 @@ services:
depends_on:
- files-init
files-init:
image: alpine:3.19.1
image: alpine:3.21.0
restart: "no"
# Since we're not running the code-server container using the same user as our host user,
# we need to set the permissions on the mounted volumes to match the user inside the container.

View File

@@ -1,4 +1,4 @@
FROM codercom/code-server:4.23.1
FROM codercom/code-server:4.96.2
USER root

View File

@@ -1,6 +1,5 @@
{
"workbench.startupEditor": "none",
"security.workspace.trust.enabled": false,
"codeQL.cli.executablePath": "/opt/codeql/codeql",
"codeQL.telemetry.enableTelemetry": false
"codeQL.cli.executablePath": "/opt/codeql/codeql"
}

View File

@@ -5,11 +5,6 @@ test("run query and open it from history", async ({ page }) => {
await page.getByRole("tab", { name: "CodeQL" }).locator("a").click();
// decline extension telemetry
await page.getByRole("button", { name: "No", exact: true }).click({
timeout: 60000,
});
await page.keyboard.press("Control+Shift+P");
await page.keyboard.type("Create Query");
await page.keyboard.press("Enter");

View File

@@ -3,18 +3,12 @@ import { resolve } from "path";
import type { TextDocument } from "vscode";
import { authentication, commands, window, workspace } from "vscode";
import { MockGitHubApiServer } from "../../../../src/common/mock-gh-api/mock-gh-api-server";
import { mockedQuickPickItem } from "../../utils/mocking.helpers";
import { setRemoteControllerRepo } from "../../../../src/config";
import { getActivatedExtension } from "../../global.helper";
import { createVSCodeCommandManager } from "../../../../src/common/vscode/commands";
import type { AllCommands } from "../../../../src/common/commands";
const mockServer = new MockGitHubApiServer();
beforeAll(() => mockServer.startServer("bypass"));
afterEach(() => mockServer.unloadScenario());
afterAll(() => mockServer.stopServer());
async function showQlDocument(name: string): Promise<TextDocument> {
const folderPath = workspace.workspaceFolders![0].uri.fsPath;
const documentPath = resolve(folderPath, name);
@@ -24,7 +18,7 @@ async function showQlDocument(name: string): Promise<TextDocument> {
}
// MSW can't intercept fetch requests made in VS Code, so we are skipping these tests for now
describe.skip("Variant Analysis Submission Integration", () => {
describe("Variant Analysis Submission Integration", () => {
const commandManager = createVSCodeCommandManager<AllCommands>();
let quickPickSpy: jest.SpiedFunction<typeof window.showQuickPick>;
let executeCommandSpy: jest.SpiedFunction<typeof commands.executeCommand>;
@@ -54,9 +48,16 @@ describe.skip("Variant Analysis Submission Integration", () => {
await getActivatedExtension();
});
afterAll(async () => {
await commandManager.execute("codeQL.mockGitHubApiServer.unloadScenario");
});
describe("Successful scenario", () => {
beforeEach(async () => {
await mockServer.loadScenario("mrva-problem-query-success");
await commandManager.execute(
"codeQL.mockGitHubApiServer.loadScenario",
"mrva-problem-query-success",
);
});
it("opens the variant analysis view", async () => {
@@ -81,7 +82,10 @@ describe.skip("Variant Analysis Submission Integration", () => {
describe("Missing controller repo", () => {
beforeEach(async () => {
await mockServer.loadScenario("mrva-missing-controller-repo");
await commandManager.execute(
"codeQL.mockGitHubApiServer.loadScenario",
"mrva-missing-controller-repo",
);
});
it("shows the error message", async () => {
@@ -108,7 +112,10 @@ describe.skip("Variant Analysis Submission Integration", () => {
describe("Submission failure", () => {
beforeEach(async () => {
await mockServer.loadScenario("mrva-submission-failure");
await commandManager.execute(
"codeQL.mockGitHubApiServer.loadScenario",
"mrva-submission-failure",
);
});
it("shows the error message", async () => {

View File

@@ -3,6 +3,7 @@ import { Uri } from "vscode";
import { remove } from "fs-extra";
import { join } from "path";
import { isIOError } from "../../../../src/common/files";
import { QLTestDiscovery } from "../../../../src/query-testing/qltest-discovery";
import type { DirectoryResult } from "tmp-promise";
import { dir } from "tmp-promise";
@@ -49,7 +50,15 @@ describe("qltest-discovery", () => {
});
afterEach(async () => {
await directory.cleanup();
try {
await directory.cleanup();
} catch (e) {
if (isIOError(e) && e.code === "ENOENT") {
// This is fine, the directory was already removed
} else {
throw e;
}
}
});
it("should run discovery", async () => {

View File

@@ -1,7 +1,6 @@
import { window } from "vscode";
import {
showBinaryChoiceDialog,
showBinaryChoiceWithUrlDialog,
showInformationMessageWithAction,
showNeverAskAgainDialog,
} from "../../../../../src/common/vscode/dialog";
@@ -68,57 +67,6 @@ describe("showInformationMessageWithAction", () => {
});
});
describe("showBinaryChoiceWithUrlDialog", () => {
let showInformationMessageSpy: jest.SpiedFunction<
typeof window.showInformationMessage
>;
beforeEach(() => {
showInformationMessageSpy = jest
.spyOn(window, "showInformationMessage")
.mockResolvedValue(undefined);
});
const resolveArg =
(index: number) =>
(...args: any[]) =>
Promise.resolve(args[index]);
it("should show a binary choice dialog with a url and return `yes`", async () => {
// pretend user clicks on the url twice and then clicks 'yes'
showInformationMessageSpy
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(3));
const val = await showBinaryChoiceWithUrlDialog("xxx", "invalid:url");
expect(val).toBe(true);
});
it("should show a binary choice dialog with a url and return `no`", async () => {
// pretend user clicks on the url twice and then clicks 'no'
showInformationMessageSpy
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(4));
const val = await showBinaryChoiceWithUrlDialog("xxx", "invalid:url");
expect(val).toBe(false);
});
it("should show a binary choice dialog and exit after clcking `more info` 5 times", async () => {
// pretend user clicks on the url twice and then clicks 'no'
showInformationMessageSpy
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2))
.mockImplementation(resolveArg(2));
const val = await showBinaryChoiceWithUrlDialog("xxx", "invalid:url");
// No choice was made
expect(val).toBeUndefined();
expect(showInformationMessageSpy).toHaveBeenCalledTimes(5);
});
});
describe("showNeverAskAgainDialog", () => {
let showInformationMessageSpy: jest.SpiedFunction<
typeof window.showInformationMessage

View File

@@ -3,9 +3,7 @@ import type { ExtensionContext } from "vscode";
export function createMockExtensionContext(): ExtensionContext {
return {
globalState: {
_state: {
"telemetry-request-viewed": true,
} as Record<string, any>,
_state: {} as Record<string, any>,
get(key: string) {
return this._state[key];
},

View File

@@ -21,241 +21,489 @@ describe("HistoryItemLabelProvider", () => {
beforeEach(() => {
config = {
format: "xxx %q xxx",
format: "xxx ${queryName} xxx",
ttlInMillis: 0,
onDidChangeConfiguration: jest.fn(),
};
labelProvider = new HistoryItemLabelProvider(config);
});
describe("local queries", () => {
it("should interpolate query when user specified", () => {
const fqi = createMockLocalQueryInfo({
startTime: date,
userSpecifiedLabel,
resultCount: 456,
hasMetadata: true,
describe("modern format", () => {
describe("local queries", () => {
it("should interpolate query when user specified", () => {
const fqi = createMockLocalQueryInfo({
startTime: date,
userSpecifiedLabel,
resultCount: 456,
hasMetadata: true,
});
expect(labelProvider.getLabel(fqi)).toBe("user-specified-name");
fqi.userSpecifiedLabel =
"${startTime} ${queryName} ${databaseName} ${status} ${queryFileBasename} ${resultCount} %";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`,
);
fqi.userSpecifiedLabel = "%t %q %d %s %f %r %%::%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %::${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`,
);
});
expect(labelProvider.getLabel(fqi)).toBe("user-specified-name");
it("should interpolate query when not user specified", () => {
const fqi = createMockLocalQueryInfo({
startTime: date,
resultCount: 456,
hasMetadata: true,
});
fqi.userSpecifiedLabel = "%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`,
);
expect(labelProvider.getLabel(fqi)).toBe("xxx query-name xxx");
fqi.userSpecifiedLabel = "%t %q %d %s %f %r %%::%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %::${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`,
);
config.format =
"${startTime} ${queryName} ${databaseName} ${status} ${queryFileBasename} ${resultCount} %";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`,
);
config.format =
"${startTime} ${queryName} ${databaseName} ${status} ${queryFileBasename} ${resultCount} %::${startTime} ${queryName} ${databaseName} ${status} ${queryFileBasename} ${resultCount} %";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %::${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`,
);
});
it("should get query short label", () => {
const fqi = createMockLocalQueryInfo({
startTime: date,
userSpecifiedLabel,
hasMetadata: true,
resultCount: 456,
});
// fall back on user specified if one exists.
expect(labelProvider.getShortLabel(fqi)).toBe("user-specified-name");
// use query name if no user-specified label exists
fqi.userSpecifiedLabel = undefined;
expect(labelProvider.getShortLabel(fqi)).toBe("query-name");
// use file name if no user-specified label exists and the query is not yet completed (meaning it has no results)
const fqi2 = createMockLocalQueryInfo({
startTime: date,
hasMetadata: true,
});
expect(labelProvider.getShortLabel(fqi2)).toBe("query-file.ql");
});
});
it("should interpolate query when not user specified", () => {
const fqi = createMockLocalQueryInfo({
startTime: date,
resultCount: 456,
hasMetadata: true,
describe("variant analyses", () => {
it("should interpolate query when user specified", () => {
const fqi = createMockVariantAnalysisHistoryItem({
userSpecifiedLabel,
executionStartTime,
});
expect(labelProvider.getLabel(fqi)).toBe(userSpecifiedLabel);
fqi.userSpecifiedLabel =
"${startTime} ${queryName} ${databaseName} ${status} %";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 1/3 repositories in progress %`,
);
fqi.userSpecifiedLabel =
"${startTime} ${queryName} ${databaseName} ${status} %::${startTime} ${queryName} ${databaseName} ${status} %";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 1/3 repositories in progress %::${dateStr} a-query-name (javascript) 1/3 repositories in progress %`,
);
});
expect(labelProvider.getLabel(fqi)).toBe("xxx query-name xxx");
it("should interpolate query when not user-specified", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
executionStartTime,
resultCount: 16,
});
config.format = "%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`,
);
expect(labelProvider.getLabel(fqi)).toBe(
"xxx a-query-name (javascript) xxx",
);
config.format = "%t %q %d %s %f %r %%::%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %::${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`,
);
});
config.format =
"${startTime} ${queryName} ${databaseName} ${status} ${queryFileBasename} ${resultCount} %";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 1/3 repositories completed a-query-file-path (16 results) %`,
);
it("should get query short label", () => {
const fqi = createMockLocalQueryInfo({
startTime: date,
userSpecifiedLabel,
hasMetadata: true,
resultCount: 456,
config.format =
"${startTime} ${queryName} ${databaseName} ${status} ${queryFileBasename} ${resultCount} %::${startTime} ${queryName} ${databaseName} ${status} ${queryFileBasename} ${resultCount} %";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 1/3 repositories completed a-query-file-path (16 results) %::${dateStr} a-query-name (javascript) 1/3 repositories completed a-query-file-path (16 results) %`,
);
});
// fall back on user specified if one exists.
expect(labelProvider.getShortLabel(fqi)).toBe("user-specified-name");
it("should get query short label", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
executionStartTime,
userSpecifiedLabel,
});
// use query name if no user-specified label exists
fqi.userSpecifiedLabel = undefined;
expect(labelProvider.getShortLabel(fqi)).toBe("query-name");
// fall back on user specified if one exists.
expect(labelProvider.getShortLabel(fqi)).toBe("user-specified-name");
// use file name if no user-specified label exists and the query is not yet completed (meaning it has no results)
const fqi2 = createMockLocalQueryInfo({
startTime: date,
hasMetadata: true,
// use query name if no user-specified label exists
const fqi2 = createMockVariantAnalysisHistoryItem({});
expect(labelProvider.getShortLabel(fqi2)).toBe("a-query-name");
});
describe("when results are present", () => {
it("should display results if there are any", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
resultCount: 16,
variantAnalysis: createMockVariantAnalysis({
status: VariantAnalysisStatus.Succeeded,
executionStartTime,
scannedRepos: createMockScannedRepos([
VariantAnalysisRepoStatus.Succeeded,
VariantAnalysisRepoStatus.Succeeded,
]),
}),
});
config.format =
"${startTime} ${queryName} ${databaseName} ${status} ${queryFileBasename} ${resultCount} %";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 2/2 repositories completed a-query-file-path (16 results) %`,
);
});
});
describe("when results are not present", () => {
it("should skip displaying them", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
resultCount: 0,
variantAnalysis: createMockVariantAnalysis({
status: VariantAnalysisStatus.Succeeded,
executionStartTime,
scannedRepos: createMockScannedRepos([
VariantAnalysisRepoStatus.Succeeded,
VariantAnalysisRepoStatus.Succeeded,
]),
}),
});
config.format =
"${startTime} ${queryName} ${databaseName} ${status} ${queryFileBasename} ${resultCount} %";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 2/2 repositories completed a-query-file-path %`,
);
});
});
describe("when extra whitespace is present in the middle of the label", () => {
it("should squash it down to a single whitespace", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
resultCount: 0,
variantAnalysis: createMockVariantAnalysis({
status: VariantAnalysisStatus.Succeeded,
executionStartTime,
scannedRepos: createMockScannedRepos([
VariantAnalysisRepoStatus.Succeeded,
VariantAnalysisRepoStatus.Succeeded,
]),
}),
});
config.format =
"${startTime} ${queryName} ${databaseName} ${status} ${queryFileBasename} ${resultCount} %";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 2/2 repositories completed a-query-file-path %`,
);
});
});
describe("when extra whitespace is present at the start of the label", () => {
it("should squash it down to a single whitespace", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
resultCount: 0,
variantAnalysis: createMockVariantAnalysis({
status: VariantAnalysisStatus.Succeeded,
executionStartTime,
scannedRepos: createMockScannedRepos([
VariantAnalysisRepoStatus.Succeeded,
VariantAnalysisRepoStatus.Succeeded,
]),
}),
});
config.format =
" ${startTime} ${queryName} ${databaseName} ${status} ${queryFileBasename} ${resultCount} %";
expect(labelProvider.getLabel(fqi)).toBe(
` ${dateStr} a-query-name (javascript) 2/2 repositories completed a-query-file-path %`,
);
});
});
describe("when extra whitespace is present at the end of the label", () => {
it("should squash it down to a single whitespace", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
resultCount: 0,
variantAnalysis: createMockVariantAnalysis({
status: VariantAnalysisStatus.Succeeded,
executionStartTime,
scannedRepos: createMockScannedRepos([
VariantAnalysisRepoStatus.Succeeded,
VariantAnalysisRepoStatus.Succeeded,
]),
}),
});
config.format =
"${startTime} ${queryName} ${databaseName} ${status} ${queryFileBasename} ${resultCount} % ";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 2/2 repositories completed a-query-file-path % `,
);
});
});
expect(labelProvider.getShortLabel(fqi2)).toBe("query-file.ql");
});
});
describe("variant analyses", () => {
it("should interpolate query when user specified", () => {
const fqi = createMockVariantAnalysisHistoryItem({
userSpecifiedLabel,
executionStartTime,
describe("legacy format", () => {
describe("local queries", () => {
it("should interpolate query when user specified", () => {
const fqi = createMockLocalQueryInfo({
startTime: date,
userSpecifiedLabel,
resultCount: 456,
hasMetadata: true,
});
expect(labelProvider.getLabel(fqi)).toBe("user-specified-name");
fqi.userSpecifiedLabel = "%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`,
);
fqi.userSpecifiedLabel = "%t %q %d %s %f %r %%::%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %::${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`,
);
});
expect(labelProvider.getLabel(fqi)).toBe(userSpecifiedLabel);
it("should interpolate query when not user specified", () => {
const fqi = createMockLocalQueryInfo({
startTime: date,
resultCount: 456,
hasMetadata: true,
});
fqi.userSpecifiedLabel = "%t %q %d %s %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 1/3 repositories in progress %`,
);
expect(labelProvider.getLabel(fqi)).toBe("xxx query-name xxx");
fqi.userSpecifiedLabel = "%t %q %d %s %%::%t %q %d %s %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 1/3 repositories in progress %::${dateStr} a-query-name (javascript) 1/3 repositories in progress %`,
);
});
config.format = "%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`,
);
it("should interpolate query when not user-specified", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
executionStartTime,
resultCount: 16,
config.format = "%t %q %d %s %f %r %%::%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %::${dateStr} query-name db-name finished in 0 seconds query-file.ql (456 results) %`,
);
});
expect(labelProvider.getLabel(fqi)).toBe(
"xxx a-query-name (javascript) xxx",
);
it("should get query short label", () => {
const fqi = createMockLocalQueryInfo({
startTime: date,
userSpecifiedLabel,
hasMetadata: true,
resultCount: 456,
});
config.format = "%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 1/3 repositories completed a-query-file-path (16 results) %`,
);
// fall back on user specified if one exists.
expect(labelProvider.getShortLabel(fqi)).toBe("user-specified-name");
config.format = "%t %q %d %s %f %r %%::%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 1/3 repositories completed a-query-file-path (16 results) %::${dateStr} a-query-name (javascript) 1/3 repositories completed a-query-file-path (16 results) %`,
);
// use query name if no user-specified label exists
fqi.userSpecifiedLabel = undefined;
expect(labelProvider.getShortLabel(fqi)).toBe("query-name");
// use file name if no user-specified label exists and the query is not yet completed (meaning it has no results)
const fqi2 = createMockLocalQueryInfo({
startTime: date,
hasMetadata: true,
});
expect(labelProvider.getShortLabel(fqi2)).toBe("query-file.ql");
});
});
it("should get query short label", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
executionStartTime,
userSpecifiedLabel,
describe("variant analyses", () => {
it("should interpolate query when user specified", () => {
const fqi = createMockVariantAnalysisHistoryItem({
userSpecifiedLabel,
executionStartTime,
});
expect(labelProvider.getLabel(fqi)).toBe(userSpecifiedLabel);
fqi.userSpecifiedLabel = "%t %q %d %s %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 1/3 repositories in progress %`,
);
fqi.userSpecifiedLabel = "%t %q %d %s %%::%t %q %d %s %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 1/3 repositories in progress %::${dateStr} a-query-name (javascript) 1/3 repositories in progress %`,
);
});
// fall back on user specified if one exists.
expect(labelProvider.getShortLabel(fqi)).toBe("user-specified-name");
// use query name if no user-specified label exists
const fqi2 = createMockVariantAnalysisHistoryItem({});
expect(labelProvider.getShortLabel(fqi2)).toBe("a-query-name");
});
describe("when results are present", () => {
it("should display results if there are any", () => {
it("should interpolate query when not user-specified", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
executionStartTime,
resultCount: 16,
variantAnalysis: createMockVariantAnalysis({
status: VariantAnalysisStatus.Succeeded,
executionStartTime,
scannedRepos: createMockScannedRepos([
VariantAnalysisRepoStatus.Succeeded,
VariantAnalysisRepoStatus.Succeeded,
]),
}),
});
expect(labelProvider.getLabel(fqi)).toBe(
"xxx a-query-name (javascript) xxx",
);
config.format = "%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 2/2 repositories completed a-query-file-path (16 results) %`,
`${dateStr} a-query-name (javascript) 1/3 repositories completed a-query-file-path (16 results) %`,
);
config.format = "%t %q %d %s %f %r %%::%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 1/3 repositories completed a-query-file-path (16 results) %::${dateStr} a-query-name (javascript) 1/3 repositories completed a-query-file-path (16 results) %`,
);
});
});
describe("when results are not present", () => {
it("should skip displaying them", () => {
it("should get query short label", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
resultCount: 0,
variantAnalysis: createMockVariantAnalysis({
status: VariantAnalysisStatus.Succeeded,
executionStartTime,
scannedRepos: createMockScannedRepos([
VariantAnalysisRepoStatus.Succeeded,
VariantAnalysisRepoStatus.Succeeded,
]),
}),
variantAnalysisStatus: VariantAnalysisStatus.Succeeded,
executionStartTime,
userSpecifiedLabel,
});
config.format = "%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 2/2 repositories completed a-query-file-path %`,
);
// fall back on user specified if one exists.
expect(labelProvider.getShortLabel(fqi)).toBe("user-specified-name");
// use query name if no user-specified label exists
const fqi2 = createMockVariantAnalysisHistoryItem({});
expect(labelProvider.getShortLabel(fqi2)).toBe("a-query-name");
});
});
describe("when extra whitespace is present in the middle of the label", () => {
it("should squash it down to a single whitespace", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
resultCount: 0,
variantAnalysis: createMockVariantAnalysis({
status: VariantAnalysisStatus.Succeeded,
executionStartTime,
scannedRepos: createMockScannedRepos([
VariantAnalysisRepoStatus.Succeeded,
VariantAnalysisRepoStatus.Succeeded,
]),
}),
describe("when results are present", () => {
it("should display results if there are any", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
resultCount: 16,
variantAnalysis: createMockVariantAnalysis({
status: VariantAnalysisStatus.Succeeded,
executionStartTime,
scannedRepos: createMockScannedRepos([
VariantAnalysisRepoStatus.Succeeded,
VariantAnalysisRepoStatus.Succeeded,
]),
}),
});
config.format = "%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 2/2 repositories completed a-query-file-path (16 results) %`,
);
});
config.format = "%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 2/2 repositories completed a-query-file-path %`,
);
});
});
describe("when extra whitespace is present at the start of the label", () => {
it("should squash it down to a single whitespace", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
resultCount: 0,
variantAnalysis: createMockVariantAnalysis({
status: VariantAnalysisStatus.Succeeded,
executionStartTime,
scannedRepos: createMockScannedRepos([
VariantAnalysisRepoStatus.Succeeded,
VariantAnalysisRepoStatus.Succeeded,
]),
}),
describe("when results are not present", () => {
it("should skip displaying them", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
resultCount: 0,
variantAnalysis: createMockVariantAnalysis({
status: VariantAnalysisStatus.Succeeded,
executionStartTime,
scannedRepos: createMockScannedRepos([
VariantAnalysisRepoStatus.Succeeded,
VariantAnalysisRepoStatus.Succeeded,
]),
}),
});
config.format = "%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 2/2 repositories completed a-query-file-path %`,
);
});
config.format = " %t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
` ${dateStr} a-query-name (javascript) 2/2 repositories completed a-query-file-path %`,
);
});
});
describe("when extra whitespace is present at the end of the label", () => {
it("should squash it down to a single whitespace", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
resultCount: 0,
variantAnalysis: createMockVariantAnalysis({
status: VariantAnalysisStatus.Succeeded,
executionStartTime,
scannedRepos: createMockScannedRepos([
VariantAnalysisRepoStatus.Succeeded,
VariantAnalysisRepoStatus.Succeeded,
]),
}),
describe("when extra whitespace is present in the middle of the label", () => {
it("should squash it down to a single whitespace", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
resultCount: 0,
variantAnalysis: createMockVariantAnalysis({
status: VariantAnalysisStatus.Succeeded,
executionStartTime,
scannedRepos: createMockScannedRepos([
VariantAnalysisRepoStatus.Succeeded,
VariantAnalysisRepoStatus.Succeeded,
]),
}),
});
config.format = "%t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 2/2 repositories completed a-query-file-path %`,
);
});
});
describe("when extra whitespace is present at the start of the label", () => {
it("should squash it down to a single whitespace", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
resultCount: 0,
variantAnalysis: createMockVariantAnalysis({
status: VariantAnalysisStatus.Succeeded,
executionStartTime,
scannedRepos: createMockScannedRepos([
VariantAnalysisRepoStatus.Succeeded,
VariantAnalysisRepoStatus.Succeeded,
]),
}),
});
config.format = " %t %q %d %s %f %r %%";
expect(labelProvider.getLabel(fqi)).toBe(
` ${dateStr} a-query-name (javascript) 2/2 repositories completed a-query-file-path %`,
);
});
});
describe("when extra whitespace is present at the end of the label", () => {
it("should squash it down to a single whitespace", () => {
const fqi = createMockVariantAnalysisHistoryItem({
historyItemStatus: QueryStatus.Completed,
resultCount: 0,
variantAnalysis: createMockVariantAnalysis({
status: VariantAnalysisStatus.Succeeded,
executionStartTime,
scannedRepos: createMockScannedRepos([
VariantAnalysisRepoStatus.Succeeded,
VariantAnalysisRepoStatus.Succeeded,
]),
}),
});
config.format = "%t %q %d %s %f %r %% ";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 2/2 repositories completed a-query-file-path % `,
);
});
config.format = "%t %q %d %s %f %r %% ";
expect(labelProvider.getLabel(fqi)).toBe(
`${dateStr} a-query-name (javascript) 2/2 repositories completed a-query-file-path % `,
);
});
});
});

View File

@@ -38,6 +38,7 @@ describe("HistoryTreeDataProvider", () => {
let app: App;
let configListener: QueryHistoryConfigListener;
const doCompareCallback = jest.fn();
const doComparePerformanceCallback = jest.fn();
let queryHistoryManager: QueryHistoryManager;
@@ -506,6 +507,7 @@ describe("HistoryTreeDataProvider", () => {
}),
languageContext,
doCompareCallback,
doComparePerformanceCallback,
);
(qhm.treeDataProvider as any).history = [...allHistory];
await workspace.saveAll();

View File

@@ -40,6 +40,7 @@ describe("QueryHistoryManager", () => {
typeof variantAnalysisManagerStub.cancelVariantAnalysis
>;
const doCompareCallback = jest.fn();
const doComparePerformanceCallback = jest.fn();
let executeCommand: jest.MockedFn<
(commandName: string, ...args: any[]) => Promise<any>
@@ -939,6 +940,7 @@ describe("QueryHistoryManager", () => {
}),
new LanguageContextStore(mockApp),
doCompareCallback,
doComparePerformanceCallback,
);
(qhm.treeDataProvider as any).history = [...allHistory];
await workspace.saveAll();

View File

@@ -105,6 +105,7 @@ describe("Variant Analyses and QueryHistoryManager", () => {
}),
new LanguageContextStore(app),
asyncNoop,
asyncNoop,
);
disposables.push(qhm);

View File

@@ -291,7 +291,7 @@ describe("query-results", () => {
});
const finished = new Promise((res, rej) => {
validSarifStream.addListener("close", res);
validSarifStream.addListener("close", () => res(undefined));
validSarifStream.addListener("error", rej);
});
@@ -357,7 +357,7 @@ describe("query-results", () => {
});
const finished = new Promise((res, rej) => {
invalidSarifStream.addListener("close", res);
invalidSarifStream.addListener("close", () => res(undefined));
invalidSarifStream.addListener("error", rej);
});

View File

@@ -1,14 +1,10 @@
import TelemetryReporter from "vscode-extension-telemetry";
import type { ExtensionContext } from "vscode";
import { workspace, ConfigurationTarget, window, env } from "vscode";
import { workspace, env } from "vscode";
import {
ExtensionTelemetryListener,
telemetryListener as globalTelemetryListener,
} from "../../../src/common/vscode/telemetry";
import { UserCancellationException } from "../../../src/common/vscode/progress";
import { ENABLE_TELEMETRY } from "../../../src/config";
import { createMockExtensionContext } from "./index";
import { vscodeGetConfigurationMock } from "../test-config";
import { redactableError } from "../../../src/common/errors";
import { SemVer } from "semver";
@@ -17,10 +13,7 @@ import { SemVer } from "semver";
jest.setTimeout(10000);
describe("telemetry reporting", () => {
let originalTelemetryExtension: boolean | undefined;
let originalTelemetryGlobal: string | undefined;
let isCanary: string;
let ctx: ExtensionContext;
let telemetryListener: ExtensionTelemetryListener;
let sendTelemetryEventSpy: jest.SpiedFunction<
@@ -29,22 +22,8 @@ describe("telemetry reporting", () => {
let sendTelemetryErrorEventSpy: jest.SpiedFunction<
typeof TelemetryReporter.prototype.sendTelemetryErrorEvent
>;
let disposeSpy: jest.SpiedFunction<
typeof TelemetryReporter.prototype.dispose
>;
let isTelemetryEnabledSpy: jest.SpyInstance<
typeof env.isTelemetryEnabled,
[]
>;
let showInformationMessageSpy: jest.SpiedFunction<
typeof window.showInformationMessage
>;
beforeEach(async () => {
vscodeGetConfigurationMock.mockRestore();
try {
// in case a previous test has accidentally activated this extension,
// need to disable it first.
@@ -52,44 +31,24 @@ describe("telemetry reporting", () => {
// specified in the package.json.
globalTelemetryListener?.dispose();
ctx = createMockExtensionContext();
sendTelemetryEventSpy = jest
.spyOn(TelemetryReporter.prototype, "sendTelemetryEvent")
.mockReturnValue(undefined);
sendTelemetryErrorEventSpy = jest
.spyOn(TelemetryReporter.prototype, "sendTelemetryErrorEvent")
.mockReturnValue(undefined);
disposeSpy = jest
.spyOn(TelemetryReporter.prototype, "dispose")
.mockResolvedValue(undefined);
showInformationMessageSpy = jest
.spyOn(window, "showInformationMessage")
.mockResolvedValue(undefined);
originalTelemetryExtension = workspace
.getConfiguration()
.get<boolean>("codeQL.telemetry.enableTelemetry");
originalTelemetryGlobal = workspace
.getConfiguration()
.get<string>("telemetry.telemetryLevel");
isCanary = (!!workspace
.getConfiguration()
.get<boolean>("codeQL.canary")).toString();
// each test will default to telemetry being enabled
isTelemetryEnabledSpy = jest
.spyOn(env, "isTelemetryEnabled", "get")
.mockReturnValue(true);
await setTelemetryLevel("telemetry", "all");
await enableTelemetry("codeQL.telemetry", true);
jest.spyOn(env, "isTelemetryEnabled", "get").mockReturnValue(true);
telemetryListener = new ExtensionTelemetryListener(
"my-id",
"1.2.3",
"fake-key",
ctx,
);
await wait(100);
} catch (e) {
@@ -99,18 +58,9 @@ describe("telemetry reporting", () => {
afterEach(async () => {
telemetryListener?.dispose();
// await wait(100);
try {
await setTelemetryLevel("telemetry", originalTelemetryGlobal);
await enableTelemetry("codeQL.telemetry", originalTelemetryExtension);
} catch (e) {
console.error(e);
}
});
it("should initialize telemetry when 'codeQL.telemetry.enableTelemetry' is enabled and global 'telemetry.telemetryLevel' is 'all'", async () => {
await telemetryListener.initialize();
it("should initialize telemetry", async () => {
expect(telemetryListener._reporter).toBeDefined();
const reporter: any = telemetryListener._reporter;
expect(reporter.extensionId).toBe("my-id");
@@ -118,80 +68,7 @@ describe("telemetry reporting", () => {
expect(reporter.userOptIn).toBe(true); // enabled
});
it("should initialize telemetry when global 'telemetry.telemetryLevel' is 'off'", async () => {
isTelemetryEnabledSpy.mockReturnValue(false);
await setTelemetryLevel("telemetry", "off");
await telemetryListener.initialize();
expect(telemetryListener._reporter).toBeDefined();
const reporter: any = telemetryListener._reporter;
expect(reporter.userOptIn).toBe(false); // disabled
});
it("should not initialize telemetry when extension option disabled", async () => {
await enableTelemetry("codeQL.telemetry", false);
await telemetryListener.initialize();
expect(telemetryListener._reporter).toBeUndefined();
});
it("should not initialize telemetry when both options disabled", async () => {
await enableTelemetry("codeQL.telemetry", false);
isTelemetryEnabledSpy.mockReturnValue(false);
await setTelemetryLevel("telemetry", "off");
await telemetryListener.initialize();
expect(telemetryListener._reporter).toBeUndefined();
});
it("should dispose telemetry object when re-initializing and should not add multiple", async () => {
await telemetryListener.initialize();
expect(telemetryListener._reporter).toBeDefined();
const firstReporter = telemetryListener._reporter;
await telemetryListener.initialize();
expect(telemetryListener._reporter).toBeDefined();
expect(telemetryListener._reporter).not.toBe(firstReporter);
expect(disposeSpy).toHaveBeenCalledTimes(1);
// initializing a third time continues to dispose
await telemetryListener.initialize();
expect(disposeSpy).toHaveBeenCalledTimes(2);
});
it("should reinitialize reporter when extension setting changes", async () => {
await telemetryListener.initialize();
expect(disposeSpy).not.toHaveBeenCalled();
expect(telemetryListener._reporter).toBeDefined();
// this disables the reporter
await enableTelemetry("codeQL.telemetry", false);
expect(telemetryListener._reporter).toBeUndefined();
expect(disposeSpy).toHaveBeenCalledTimes(1);
// creates a new reporter, but does not dispose again
await enableTelemetry("codeQL.telemetry", true);
expect(telemetryListener._reporter).toBeDefined();
expect(disposeSpy).toHaveBeenCalledTimes(1);
});
it("should set userOptIn to false when global setting changes", async () => {
await telemetryListener.initialize();
const reporter: any = telemetryListener._reporter;
expect(reporter.userOptIn).toBe(true); // enabled
isTelemetryEnabledSpy.mockReturnValue(false);
await setTelemetryLevel("telemetry", "off");
expect(reporter.userOptIn).toBe(false); // disabled
});
it("should send an event", async () => {
await telemetryListener.initialize();
telemetryListener.sendCommandUsage("command-id", 1234, undefined);
expect(sendTelemetryEventSpy).toHaveBeenCalledWith(
@@ -208,8 +85,6 @@ describe("telemetry reporting", () => {
});
it("should send a command usage event with an error", async () => {
await telemetryListener.initialize();
telemetryListener.sendCommandUsage(
"command-id",
1234,
@@ -230,7 +105,6 @@ describe("telemetry reporting", () => {
});
it("should send a command usage event with a cli version", async () => {
await telemetryListener.initialize();
telemetryListener.cliVersion = new SemVer("1.2.3");
telemetryListener.sendCommandUsage(
@@ -274,39 +148,7 @@ describe("telemetry reporting", () => {
expect(sendTelemetryErrorEventSpy).not.toHaveBeenCalled();
});
it("should avoid sending an event when telemetry is disabled", async () => {
await telemetryListener.initialize();
await enableTelemetry("codeQL.telemetry", false);
telemetryListener.sendCommandUsage("command-id", 1234, undefined);
telemetryListener.sendCommandUsage("command-id", 1234, new Error());
expect(sendTelemetryEventSpy).not.toHaveBeenCalled();
expect(sendTelemetryErrorEventSpy).not.toHaveBeenCalled();
});
it("should send an event when telemetry is re-enabled", async () => {
await telemetryListener.initialize();
await enableTelemetry("codeQL.telemetry", false);
await enableTelemetry("codeQL.telemetry", true);
telemetryListener.sendCommandUsage("command-id", 1234, undefined);
expect(sendTelemetryEventSpy).toHaveBeenCalledWith(
"command-usage",
{
name: "command-id",
status: "Success",
isCanary,
cliVersion: "not-set",
},
{ executionTime: 1234 },
);
expect(sendTelemetryErrorEventSpy).not.toHaveBeenCalled();
});
it("should filter undesired properties from telemetry payload", async () => {
await telemetryListener.initialize();
// Reach into the internal appInsights client to grab our telemetry processor.
const telemetryProcessor: Function = (telemetryListener._reporter as any)
.appInsightsClient._telemetryProcessors[0];
@@ -340,118 +182,7 @@ describe("telemetry reporting", () => {
});
});
const resolveArg =
(index: number) =>
(...args: any[]) =>
Promise.resolve(args[index]);
it("should request permission if popup has never been seen before", async () => {
showInformationMessageSpy.mockImplementation(
resolveArg(3 /* "yes" item */),
);
await ctx.globalState.update("telemetry-request-viewed", false);
expect(env.isTelemetryEnabled).toBe(true);
await enableTelemetry("codeQL.telemetry", false);
await telemetryListener.initialize();
// Wait for user's selection to propagate in settings.
await wait(500);
// Dialog opened, user clicks "yes" and telemetry enabled
expect(showInformationMessageSpy).toHaveBeenCalledTimes(1);
expect(ENABLE_TELEMETRY.getValue()).toBe(true);
expect(ctx.globalState.get("telemetry-request-viewed")).toBe(true);
});
it("should prevent telemetry if permission is denied", async () => {
showInformationMessageSpy.mockImplementation(resolveArg(4 /* "no" item */));
await ctx.globalState.update("telemetry-request-viewed", false);
await enableTelemetry("codeQL.telemetry", true);
await telemetryListener.initialize();
// Dialog opened, user clicks "no" and telemetry disabled
expect(showInformationMessageSpy).toHaveBeenCalledTimes(1);
expect(ENABLE_TELEMETRY.getValue()).toBe(false);
expect(ctx.globalState.get("telemetry-request-viewed")).toBe(true);
});
it("should unchange telemetry if permission dialog is dismissed", async () => {
showInformationMessageSpy.mockResolvedValue(undefined /* cancelled */);
await ctx.globalState.update("telemetry-request-viewed", false);
// this causes requestTelemetryPermission to be called
await enableTelemetry("codeQL.telemetry", false);
// Dialog opened, and user closes without interacting with it
expect(showInformationMessageSpy).toHaveBeenCalledTimes(1);
expect(ENABLE_TELEMETRY.getValue()).toBe(false);
// dialog was canceled, so should not have marked as viewed
expect(ctx.globalState.get("telemetry-request-viewed")).toBe(false);
});
it("should unchange telemetry if permission dialog is cancelled if starting as true", async () => {
await enableTelemetry("codeQL.telemetry", false);
// as before, except start with telemetry enabled. It should _stay_ enabled if the
// dialog is canceled.
showInformationMessageSpy.mockResolvedValue(undefined /* cancelled */);
await ctx.globalState.update("telemetry-request-viewed", false);
// this causes requestTelemetryPermission to be called
await enableTelemetry("codeQL.telemetry", true);
// Dialog opened, and user closes without interacting with it
// Telemetry state should not have changed
expect(showInformationMessageSpy).toHaveBeenCalledTimes(1);
expect(ENABLE_TELEMETRY.getValue()).toBe(true);
// dialog was canceled, so should not have marked as viewed
expect(ctx.globalState.get("telemetry-request-viewed")).toBe(false);
});
it("should avoid showing dialog if global telemetry is disabled", async () => {
// when telemetry is disabled globally, we never want to show the
// opt in/out dialog. We just assume that codeql telemetry should
// remain disabled as well.
// If the user ever turns global telemetry back on, then we can
// show the dialog.
isTelemetryEnabledSpy.mockReturnValue(false);
await setTelemetryLevel("telemetry", "off");
await ctx.globalState.update("telemetry-request-viewed", false);
await telemetryListener.initialize();
// popup should not be shown even though we have initialized telemetry
expect(showInformationMessageSpy).not.toHaveBeenCalled();
});
// This test is failing because codeQL.canary is not a registered configuration.
// We do not want to have it registered because we don't want this item
// appearing in the settings page. It needs to only be set by users we tell
// about it to.
// At this point, I see no other way of testing re-requesting permission.
xit("should request permission again when user changes canary setting", async () => {
// initially, both canary and telemetry are false
await workspace.getConfiguration().update("codeQL.canary", false);
await enableTelemetry("codeQL.telemetry", false);
await ctx.globalState.update("telemetry-request-viewed", true);
await telemetryListener.initialize();
showInformationMessageSpy.mockResolvedValue(undefined /* cancelled */);
// set canary to true
await workspace.getConfiguration().update("codeQL.canary", true);
// now, we should have to click through the telemetry requestor again
expect(ctx.globalState.get("telemetry-request-viewed")).toBe(false);
expect(showInformationMessageSpy).toHaveBeenCalledTimes(1);
});
it("should send a ui-interaction telemetry event", async () => {
await telemetryListener.initialize();
telemetryListener.sendUIInteraction("test");
expect(sendTelemetryEventSpy).toHaveBeenCalledWith(
@@ -467,8 +198,6 @@ describe("telemetry reporting", () => {
});
it("should send a ui-interaction telemetry event with a cli version", async () => {
await telemetryListener.initialize();
telemetryListener.cliVersion = new SemVer("1.2.3");
telemetryListener.sendUIInteraction("test");
@@ -485,8 +214,6 @@ describe("telemetry reporting", () => {
});
it("should send an error telemetry event", async () => {
await telemetryListener.initialize();
telemetryListener.sendError(redactableError`test`);
expect(sendTelemetryEventSpy).not.toHaveBeenCalled();
@@ -503,7 +230,6 @@ describe("telemetry reporting", () => {
});
it("should send an error telemetry event with a cli version", async () => {
await telemetryListener.initialize();
telemetryListener.cliVersion = new SemVer("1.2.3");
telemetryListener.sendError(redactableError`test`);
@@ -522,8 +248,6 @@ describe("telemetry reporting", () => {
});
it("should redact error message contents", async () => {
await telemetryListener.initialize();
telemetryListener.sendError(
redactableError`test message with secret information: ${42} and more ${"secret"} parts`,
);
@@ -543,8 +267,6 @@ describe("telemetry reporting", () => {
});
it("should send config telemetry event", async () => {
await telemetryListener.initialize();
telemetryListener.sendConfigInformation({
testKey: "testValue",
testKey2: "42",
@@ -563,26 +285,6 @@ describe("telemetry reporting", () => {
expect(sendTelemetryErrorEventSpy).not.toHaveBeenCalled();
});
async function enableTelemetry(section: string, value: boolean | undefined) {
await workspace
.getConfiguration(section)
.update("enableTelemetry", value, ConfigurationTarget.Global);
// Need to wait some time since the onDidChangeConfiguration listeners fire
// asynchronously. Must ensure they to complete in order to have a successful test.
await wait(100);
}
async function setTelemetryLevel(section: string, value: string | undefined) {
await workspace
.getConfiguration(section)
.update("telemetryLevel", value, ConfigurationTarget.Global);
// Need to wait some time since the onDidChangeConfiguration listeners fire
// asynchronously. Must ensure they to complete in order to have a successful test.
await wait(100);
}
async function wait(ms = 0) {
return new Promise((resolve) => setTimeout(resolve, ms));
}