diff --git a/.config/1espt/PipelineAutobaseliningConfig.yml b/.config/1espt/PipelineAutobaseliningConfig.yml new file mode 100644 index 000000000000..0ad187981e37 --- /dev/null +++ b/.config/1espt/PipelineAutobaseliningConfig.yml @@ -0,0 +1,21 @@ +## DO NOT MODIFY THIS FILE MANUALLY. This is part of auto-baselining from 1ES Pipeline Templates. Go to [https://aka.ms/1espt-autobaselining] for more details. + +pipelines: + 111: + retail: + source: + credscan: + lastModifiedDate: 2024-09-10 + eslint: + lastModifiedDate: 2024-09-10 + psscriptanalyzer: + lastModifiedDate: 2024-09-10 + armory: + lastModifiedDate: 2024-09-10 + binary: + credscan: + lastModifiedDate: 2025-02-04 + binskim: + lastModifiedDate: 2025-02-04 + spotbugs: + lastModifiedDate: 2025-02-04 diff --git a/.config/guardian/.gdnbaselines b/.config/guardian/.gdnbaselines new file mode 100644 index 000000000000..55412ff93e56 --- /dev/null +++ b/.config/guardian/.gdnbaselines @@ -0,0 +1,255 @@ +{ + "properties": { + "helpUri": "https://eng.ms/docs/microsoft-security/security/azure-security/cloudai-security-fundamentals-engineering/security-integration/guardian-wiki/microsoft-guardian/general/baselines" + }, + "version": "1.0.0", + "baselines": { + "default": { + "name": "default", + "createdDate": "2025-01-28 06:29:05Z", + "lastUpdatedDate": "2025-01-28 06:29:05Z" + } + }, + "results": { + "ea3b2bf4f5b3d0bd8a6ad35cc61e49f2a1596660fd66d17d740e4806e7ed7dcc": { + "signature": "ea3b2bf4f5b3d0bd8a6ad35cc61e49f2a1596660fd66d17d740e4806e7ed7dcc", + "alternativeSignatures": [ + "ff528c0b5a010ae7b5e9178b004a8b816a429a28ba98ce8336466b490a09dcef" + ], + "target": ".build/win32-arm64/system-setup/VSCodeSetup-arm64-1.97.0-insider.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2009", + "createdDate": "2025-01-30 19:19:49Z", + "expirationDate": "2025-07-19 21:12:48Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-01-30 21:12:48Z" + }, + "12babbc85192ed1c8d927693da788537c1eef199bbecbe226f940a2d0e97637c": { + "signature": "12babbc85192ed1c8d927693da788537c1eef199bbecbe226f940a2d0e97637c", + "alternativeSignatures": [ + "35b0519e201e56fb87fc6fb085e6fb1df5b89715142bb9086a5b2006e0fd4ced" + ], + "target": ".build/win32-arm64/system-setup/VSCodeSetup-arm64-1.97.0-insider.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2018", + "createdDate": "2025-01-30 19:19:49Z", + "expirationDate": "2025-07-19 21:12:48Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-01-30 21:12:48Z" + }, + "49163bd1dc9d965d3baced1694dc8c43305b8bf96e884f478d8e4bd124454ba0": { + "signature": "49163bd1dc9d965d3baced1694dc8c43305b8bf96e884f478d8e4bd124454ba0", + "alternativeSignatures": [ + "aa80bcf44aa8ddd20fb9802e9032c1257048b973896a944ded70bb195f060b2a" + ], + "target": ".build/win32-arm64/user-setup/VSCodeUserSetup-arm64-1.97.0-insider.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2009", + "createdDate": "2025-01-30 19:21:17Z", + "expirationDate": "2025-07-19 21:12:48Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-01-30 21:12:48Z" + }, + "c405af02e021c3a473d4e45ec4daa658db1527ea7430c6be968d182e7b50fbd1": { + "signature": "c405af02e021c3a473d4e45ec4daa658db1527ea7430c6be968d182e7b50fbd1", + "alternativeSignatures": [ + "619d2a1a77f55b4181493b8cfdf09be5261e539115752af2e4938f5ac04af132" + ], + "target": ".build/win32-arm64/user-setup/VSCodeUserSetup-arm64-1.97.0-insider.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2018", + "createdDate": "2025-01-30 19:21:17Z", + "expirationDate": "2025-07-19 21:12:48Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-01-30 21:12:48Z" + }, + "71b8515b2eb51cfd5eace11cedb15189d51ce9e479095a5938334416088cbc03": { + "signature": "71b8515b2eb51cfd5eace11cedb15189d51ce9e479095a5938334416088cbc03", + "alternativeSignatures": [ + "b34279fc5fec828b8dcd9ca873804e85d7d9cd78554ec109d2dd493351a7a244" + ], + "target": ".build/win32-x64/system-setup/VSCodeSetup-x64-1.97.0-insider.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2009", + "createdDate": "2025-01-30 19:51:51Z", + "expirationDate": "2025-07-19 21:12:48Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-01-30 21:12:48Z" + }, + "9238de77a5320039def14694d1b6f501cc2288f13c9c688d2e0501fc5a56ee61": { + "signature": "9238de77a5320039def14694d1b6f501cc2288f13c9c688d2e0501fc5a56ee61", + "alternativeSignatures": [ + "1d17616a549e9f36d814c4e802d651b1af453ce0a23d4478eef39be81adcc16b" + ], + "target": ".build/win32-x64/system-setup/VSCodeSetup-x64-1.97.0-insider.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2018", + "createdDate": "2025-01-30 19:51:51Z", + "expirationDate": "2025-07-19 21:12:48Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-01-30 21:12:48Z" + }, + "bad8b698b48c1da9ece953903581c66bf98bc829ae1a6adcd3b5c2056a6fcd01": { + "signature": "bad8b698b48c1da9ece953903581c66bf98bc829ae1a6adcd3b5c2056a6fcd01", + "alternativeSignatures": [ + "057376d31b97e8ce3ecf6a180a553b932d7e5be6e2b07a08027d5dfabe35e82c" + ], + "target": ".build/win32-x64/user-setup/VSCodeUserSetup-x64-1.97.0-insider.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2009", + "createdDate": "2025-01-30 19:53:13Z", + "expirationDate": "2025-07-19 21:12:48Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-01-30 21:12:48Z" + }, + "cc7c248b0fd4c105e9a393ae232bf0d314ec50e65357a5e7e7d68f6f10c77077": { + "signature": "cc7c248b0fd4c105e9a393ae232bf0d314ec50e65357a5e7e7d68f6f10c77077", + "alternativeSignatures": [ + "f3867098aff3368682df9926e85a35ec05cf905f27d0c157430021c3169f899d" + ], + "target": ".build/win32-x64/user-setup/VSCodeUserSetup-x64-1.97.0-insider.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2018", + "createdDate": "2025-01-30 19:53:13Z", + "expirationDate": "2025-07-19 21:12:48Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-01-30 21:12:48Z" + }, + "8c53250a171412b84dedcbb22cdab9ec365d9b52d74b09c070097fff45372de0": { + "signature": "8c53250a171412b84dedcbb22cdab9ec365d9b52d74b09c070097fff45372de0", + "alternativeSignatures": [ + "314267784b0ea867006e00b809a93498fae3264e42d1a3a7745ab13180a5b6ef" + ], + "target": ".build/win32-arm64/system-setup/VSCodeSetup-arm64-1.98.0-insider.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2009", + "createdDate": "2025-02-04 06:16:33Z", + "expirationDate": "2025-07-24 07:25:17Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-02-04 07:25:17Z" + }, + "a6a58d971da858f4af219672cef73ffd0aacc47f1e2c12b8b44a428e1330d3de": { + "signature": "a6a58d971da858f4af219672cef73ffd0aacc47f1e2c12b8b44a428e1330d3de", + "alternativeSignatures": [ + "4e40f2f1683f0bf2245f35d0ebbcf2f446274d84b1db09d8e76ddfdcad5d4479" + ], + "target": ".build/win32-arm64/system-setup/VSCodeSetup-arm64-1.98.0-insider.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2018", + "createdDate": "2025-02-04 06:16:33Z", + "expirationDate": "2025-07-24 07:25:17Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-02-04 07:25:17Z" + }, + "90e0f060e01e4a55620f609ac3241b62e8f54a059e9f4d292e93a4305fd3c39e": { + "signature": "90e0f060e01e4a55620f609ac3241b62e8f54a059e9f4d292e93a4305fd3c39e", + "alternativeSignatures": [ + "377fe43ff8404d07f4a6ca763175004f360397ded6cf5d55b655646ada90e39c" + ], + "target": ".build/win32-arm64/user-setup/VSCodeUserSetup-arm64-1.98.0-insider.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2009", + "createdDate": "2025-02-04 06:17:54Z", + "expirationDate": "2025-07-24 07:25:17Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-02-04 07:25:17Z" + }, + "f36c3dc19566098a923877d16d6ebfcbd971f8fcd8210afb8f5558fb5ba1f203": { + "signature": "f36c3dc19566098a923877d16d6ebfcbd971f8fcd8210afb8f5558fb5ba1f203", + "alternativeSignatures": [ + "1af1f475c1617701e3d7a8fd465916bcc60c3125b8807af5d47d49137d9d468c" + ], + "target": ".build/win32-arm64/user-setup/VSCodeUserSetup-arm64-1.98.0-insider.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2018", + "createdDate": "2025-02-04 06:17:54Z", + "expirationDate": "2025-07-24 07:25:17Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-02-04 07:25:17Z" + }, + "71193d108c53bb802f5c491276365bcff0645fb380be57288f3fbd6896166d3a": { + "signature": "71193d108c53bb802f5c491276365bcff0645fb380be57288f3fbd6896166d3a", + "alternativeSignatures": [ + "420cae2e6e34b93d7b74fc1ffddfdf23b57650ae989d838bb2d67f28e4e1db0e" + ], + "target": ".build/win32-x64/system-setup/VSCodeSetup-x64-1.98.0-insider.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2009", + "createdDate": "2025-02-04 07:11:19Z", + "expirationDate": "2025-07-24 07:25:17Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-02-04 07:25:17Z" + }, + "444c302f49bdedcafe772322a09727b2279e3265d99deb2e307defeae3ef200b": { + "signature": "444c302f49bdedcafe772322a09727b2279e3265d99deb2e307defeae3ef200b", + "alternativeSignatures": [ + "4ff6ccbdb0745d43d3b61f82fb2f4d8a64fe9787525df81a6d7b825e79282085" + ], + "target": ".build/win32-x64/system-setup/VSCodeSetup-x64-1.98.0-insider.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2018", + "createdDate": "2025-02-04 07:11:19Z", + "expirationDate": "2025-07-24 07:25:17Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-02-04 07:25:17Z" + }, + "4670c7c096a69ca428429ffa1f5250aac9f2e07beac0ffe587ffb37bdb1da4d4": { + "signature": "4670c7c096a69ca428429ffa1f5250aac9f2e07beac0ffe587ffb37bdb1da4d4", + "alternativeSignatures": [ + "7cead96cb508ab6e37e27bcc0f8b7ed8d0761b77f4793958c46c5ff3892ab1b6" + ], + "target": ".build/win32-x64/user-setup/VSCodeUserSetup-x64-1.98.0-insider.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2009", + "createdDate": "2025-02-04 07:13:22Z", + "expirationDate": "2025-07-24 07:25:17Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-02-04 07:25:17Z" + }, + "a359b4a5ed2378a73f3bba93e3fb1c595db7423c3082635d12d101bbeb0a51b8": { + "signature": "a359b4a5ed2378a73f3bba93e3fb1c595db7423c3082635d12d101bbeb0a51b8", + "alternativeSignatures": [ + "125b52a21ef619a95e695085deb9492280bcf2c1decdd5e87e6416af5982d02d" + ], + "target": ".build/win32-x64/user-setup/VSCodeUserSetup-x64-1.98.0-insider.exe", + "memberOf": [ + "default" + ], + "tool": "binskim", + "ruleId": "BA2018", + "createdDate": "2025-02-04 07:13:22Z", + "expirationDate": "2025-07-24 07:25:17Z", + "justification": "This error is baselined with an expiration date of 180 days from 2025-02-04 07:25:17Z" + } + } +} \ No newline at end of file diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 9be15a6afa1e..d68cd2b2f9ca 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/devcontainers/typescript-node:20-bookworm +FROM mcr.microsoft.com/devcontainers/typescript-node:24-bookworm@sha256:634d3ffc8c37508259a23462b838fd85a9e5bb80dcdd0b31d673aa33dc4c8592 ADD install-vscode.sh /root/ RUN /root/install-vscode.sh diff --git a/.devcontainer/README.md b/.devcontainer/README.md index 5f6e1cd99f3a..3d1e15e5d550 100644 --- a/.devcontainer/README.md +++ b/.devcontainer/README.md @@ -1,6 +1,6 @@ # Code - OSS Development Container -[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue&logo=visualstudiocode)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/vscode) +[![Open in Dev Containers](https://img.shields.io/static/v1?label=Dev%20Containers&message=Open&color=blue)](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/microsoft/vscode) This repository includes configuration for a development container for working with Code - OSS in a local container or using [GitHub Codespaces](https://github.com/features/codespaces). @@ -56,7 +56,7 @@ Next: **[Try it out!](#try-it)** You may see improved VNC responsiveness when accessing a codespace from VS Code client since you can use a [VNC Viewer][def]. Here's how to do it. -1. Install [Visual Studio Code Stable](https://code.visualstudio.com/) or [Insiders](https://code.visualstudio.com/insiders/) and the the [GitHub Codespaces extension](https://marketplace.visualstudio.com/items?itemName=GitHub.codespaces). +1. Install [Visual Studio Code Stable](https://code.visualstudio.com/) or [Insiders](https://code.visualstudio.com/insiders/) and the [GitHub Codespaces extension](https://marketplace.visualstudio.com/items?itemName=GitHub.codespaces). > **Note:** The GitHub Codespaces extension requires the Visual Studio Code distribution of Code - OSS. diff --git a/.eslint-ignore b/.eslint-ignore index 12da4a432e15..da66a76c3960 100644 --- a/.eslint-ignore +++ b/.eslint-ignore @@ -12,6 +12,10 @@ **/extensions/markdown-math/notebook-out/** **/extensions/notebook-renderers/renderer-out/index.js **/extensions/simple-browser/media/index.js +**/extensions/terminal-suggest/src/completions/upstream/** +**/extensions/terminal-suggest/src/shell/zshBuiltinsCache.ts +**/extensions/terminal-suggest/src/shell/fishBuiltinsCache.ts +**/extensions/terminal-suggest/third_party/** **/extensions/typescript-language-features/test-workspace/** **/extensions/typescript-language-features/extension.webpack.config.js **/extensions/typescript-language-features/extension-browser.webpack.config.js diff --git a/.eslint-plugin-local/vscode-dts-region-comments.ts b/.eslint-plugin-local/vscode-dts-region-comments.ts deleted file mode 100644 index b08dc6357bbf..000000000000 --- a/.eslint-plugin-local/vscode-dts-region-comments.ts +++ /dev/null @@ -1,40 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as eslint from 'eslint'; - -export = new class ApiEventNaming implements eslint.Rule.RuleModule { - - readonly meta: eslint.Rule.RuleMetaData = { - messages: { - comment: 'region comments should start with a camel case identifier, `:`, then either a GH issue link or owner, e.g #region myProposalName: https://github.com/microsoft/vscode/issues/', - }, - schema: false, - }; - - create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { - - const sourceCode = context.getSourceCode(); - - return { - ['Program']: (_node: any) => { - for (const comment of sourceCode.getAllComments()) { - if (comment.type !== 'Line') { - continue; - } - if (!/^\s*#region /.test(comment.value)) { - continue; - } - if (!/^\s*#region ([a-z]+): (@[a-z]+|https:\/\/github.com\/microsoft\/vscode\/issues\/\d+)/i.test(comment.value)) { - context.report({ - node: comment, - messageId: 'comment', - }); - } - } - } - }; - } -}; diff --git a/.eslint-plugin-local/vscode-dts-use-export.ts b/.eslint-plugin-local/vscode-dts-use-export.ts new file mode 100644 index 000000000000..904feaeec36b --- /dev/null +++ b/.eslint-plugin-local/vscode-dts-use-export.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TSESTree } from '@typescript-eslint/utils'; +import * as eslint from 'eslint'; + +export = new class VscodeDtsUseExport implements eslint.Rule.RuleModule { + + readonly meta: eslint.Rule.RuleMetaData = { + messages: { + useExport: `Public api types must use 'export'`, + }, + schema: false, + }; + + create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + return { + ['TSModuleDeclaration :matches(TSInterfaceDeclaration, ClassDeclaration, VariableDeclaration, TSEnumDeclaration, TSTypeAliasDeclaration)']: (node: any) => { + const parent = (node).parent; + if (parent && parent.type !== TSESTree.AST_NODE_TYPES.ExportNamedDeclaration) { + context.report({ + node, + messageId: 'useExport' + }); + } + } + }; + } +}; + diff --git a/.github/classifier.json b/.github/classifier.json index 5f322e25edd5..93ca6f3293c3 100644 --- a/.github/classifier.json +++ b/.github/classifier.json @@ -127,7 +127,7 @@ "layout": {"assign": ["benibenj"]}, "lcd-text-rendering": {"assign": []}, "list-widget": {"assign": ["joaomoreno"]}, - "live-preview": {"assign": ["andreamah"]}, + "live-preview": {"assign": []}, "log": {"assign": ["sandy081"]}, "markdown": {"assign": ["mjbvz"]}, "marketplace": {"assign": ["isidorn"]}, @@ -199,9 +199,9 @@ "sash-widget": {"assign": ["joaomoreno"]}, "scm": {"assign": ["lszomoru"]}, "screencast-mode": {"assign": ["joaomoreno"]}, - "search": {"assign": ["andreamah", "roblourens"]}, - "search-api": {"assign": ["andreamah", "roblourens"]}, - "search-editor": {"assign": ["andreamah", "roblourens"]}, + "search": {"assign": ["osortega"]}, + "search-api": {"assign": ["osortega"]}, + "search-editor": {"assign": ["osortega"]}, "search-replace": {"assign": ["sandy081"]}, "semantic-tokens": {"assign": ["alexdima", "aeschli"]}, "server": {"assign": ["alexdima"]}, @@ -215,7 +215,6 @@ "snap": {"assign": ["deepak1556"]}, "snippets": {"assign": ["jrieken"]}, "splitview-widget": {"assign": ["joaomoreno"]}, - "ssh": {"assign": ["eleanorjboyd"]}, "suggest": {"assign": ["jrieken"]}, "table-widget": {"assign": ["joaomoreno"]}, "tasks": {"assign": ["meganrogge"], "accuracy": 0.85}, diff --git a/.github/commands.yml b/.github/commands.yml deleted file mode 100644 index 5073658e5260..000000000000 --- a/.github/commands.yml +++ /dev/null @@ -1,13 +0,0 @@ -{ - perform: true, - commands: - [ - { - type: "comment", - name: "findDuplicates", - allowUsers: ["cleidigh", "usernamehw", "gjsjohnmurray", "IllusionMH"], - action: "comment", - comment: "Potential duplicates:\n${potentialDuplicates}", - }, - ], -} diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000000..6f20c66c400e --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,57 @@ +# Coding Guidelines + +## Introduction + +These are VS Code coding guidelines. Please also review our [Source Code Organisation](https://github.com/microsoft/vscode/wiki/Source-Code-Organization) page. + +## Indentation + +We use tabs, not spaces. + +## Naming Conventions + +* Use PascalCase for `type` names +* Use PascalCase for `enum` values +* Use camelCase for `function` and `method` names +* Use camelCase for `property` names and `local variables` +* Use whole words in names when possible + +## Types + +* Do not export `types` or `functions` unless you need to share it across multiple components +* Do not introduce new `types` or `values` to the global namespace + +## Comments + +* When there are comments for `functions`, `interfaces`, `enums`, and `classes` use JSDoc style comments + +## Strings + +* Use "double quotes" for strings shown to the user that need to be externalized (localized) +* Use 'single quotes' otherwise +* All strings visible to the user need to be externalized + +## Style + +* Use arrow functions `=>` over anonymous function expressions +* Only surround arrow function parameters when necessary. For example, `(x) => x + x` is wrong but the following are correct: + +```javascript +x => x + x +(x, y) => x + y +(x: T, y: T) => x === y +``` + +* Always surround loop and conditional bodies with curly braces +* Open curly braces always go on the same line as whatever necessitates them +* Parenthesized constructs should have no surrounding whitespace. A single space follows commas, colons, and semicolons in those constructs. For example: + +```javascript +for (let i = 0, n = str.length; i < 10; i++) { + if (x < 10) { + foo(); + } +} + +function f(x: number, y: string): void { } +``` diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5ace4600a1f2..1e1402fa5aea 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,594 @@ updates: directory: "/" schedule: interval: "weekly" + + + - package-ecosystem: docker + directory: /.devcontainer + schedule: + interval: daily + + - package-ecosystem: npm + directory: /.eslint-plugin-local + schedule: + interval: daily + + - package-ecosystem: npm + directory: /.vscode/extensions/vscode-selfhost-import-aid + schedule: + interval: daily + + - package-ecosystem: npm + directory: /.vscode/extensions/vscode-selfhost-test-provider + schedule: + interval: daily + + - package-ecosystem: npm + directory: /build/builtin + schedule: + interval: daily + + - package-ecosystem: npm + directory: /build/monaco + schedule: + interval: daily + + - package-ecosystem: npm + directory: /build/npm/gyp + schedule: + interval: daily + + - package-ecosystem: npm + directory: /build + schedule: + interval: daily + + - package-ecosystem: cargo + directory: /build/win32 + schedule: + interval: daily + + - package-ecosystem: cargo + directory: /cli + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/bat + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/clojure + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/coffeescript + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/configuration-editing + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/cpp + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/csharp + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/css-language-features + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/css-language-features/server + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/css + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/dart + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/debug-auto-launch + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/debug-server-ready + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/diff + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/docker + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/emmet + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/extension-editing + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/fsharp + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/git-base + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/git + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/github-authentication + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/github + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/go + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/groovy + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/grunt + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/gulp + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/handlebars + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/hlsl + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/html-language-features + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/html-language-features/server + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/html + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/ini + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/ipynb + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/jake + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/java + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/javascript + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/json-language-features + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/json-language-features/server + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/json + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/julia + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/latex + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/less + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/log + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/lua + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/make + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/markdown-basics + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/markdown-language-features + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/markdown-math + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/media-preview + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/merge-conflict + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/microsoft-authentication + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/microsoft-authentication/packageMocks/keytar + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/notebook-renderers + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/npm + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/objective-c + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/perl + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/php-language-features + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/php + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/powershell + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/pug + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/python + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/r + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/razor + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/references-view + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/restructuredtext + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/ruby + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/rust + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/scss + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/search-result + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/shaderlab + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/shellscript + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/simple-browser + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/sql + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/swift + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/terminal-suggest + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-abyss + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-defaults + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-kimbie-dark + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-monokai-dimmed + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-monokai + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-quietlight + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-red + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-seti + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-solarized-dark + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-solarized-light + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-tomorrow-night-blue + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/tunnel-forwarding + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/typescript-basics + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/typescript-language-features + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/vb + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/vscode-api-tests + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/vscode-colorize-perf-tests + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/vscode-colorize-tests + schedule: + interval: daily + + - package-ecosystem: docker + directory: /extensions/vscode-colorize-tests/test/colorize-fixtures + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/vscode-test-resolver + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/xml + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/yaml + schedule: + interval: daily + + - package-ecosystem: npm + directory: / + schedule: + interval: daily + + - package-ecosystem: npm + directory: /remote + schedule: + interval: daily + + - package-ecosystem: npm + directory: /remote/web + schedule: + interval: daily + + - package-ecosystem: npm + directory: /scripts + schedule: + interval: daily + + - package-ecosystem: npm + directory: /test/automation + schedule: + interval: daily + + - package-ecosystem: npm + directory: /test/integration/browser + schedule: + interval: daily + + - package-ecosystem: npm + directory: /test/leaks + schedule: + interval: daily + + - package-ecosystem: npm + directory: /test/monaco + schedule: + interval: daily + + - package-ecosystem: npm + directory: /test + schedule: + interval: daily + + - package-ecosystem: npm + directory: /test/smoke + schedule: + interval: daily + + - package-ecosystem: npm + directory: /test/unit/node + schedule: + interval: daily diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml deleted file mode 100644 index d5f166a4aae8..000000000000 --- a/.github/workflows/basic.yml +++ /dev/null @@ -1,176 +0,0 @@ -name: Basic checks - -on: workflow_dispatch - -# on: -# push: -# branches: -# - main -# pull_request: -# branches: -# - main - -jobs: - main: - if: github.ref != 'refs/heads/main' - name: Compilation, Unit and Integration Tests - runs-on: ubuntu-latest - timeout-minutes: 40 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - # TODO: rename azure-pipelines/linux/xvfb.init to github-actions - - name: Setup Build Environment - run: | - sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb - sudo chmod +x /etc/init.d/xvfb - sudo update-rc.d xvfb defaults - sudo service xvfb start - - - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - - name: Compute node modules cache key - id: nodeModulesCacheKey - run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - - name: Cache node modules - id: cacheNodeModules - uses: actions/cache@v4 - with: - path: "**/node_modules" - key: ${{ runner.os }}-cacheNodeModulesLinux-${{ steps.nodeModulesCacheKey.outputs.value }} - - name: Get npm cache directory path - id: npmCacheDirPath - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - name: Cache npm directory - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v4 - with: - path: ${{ steps.npmCacheDirPath.outputs.dir }} - key: ${{ runner.os }}-npmCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-npmCacheDir- - - name: Execute npm - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - run: npm ci - - - name: Compile and Download - run: npm exec -- npm-run-all -lp compile "electron x64" - - - name: Run Unit Tests - id: electron-unit-tests - run: DISPLAY=:10 ./scripts/test.sh - - - name: Run Integration Tests (Electron) - id: electron-integration-tests - run: DISPLAY=:10 ./scripts/test-integration.sh - - hygiene: - if: github.ref != 'refs/heads/main' - name: Hygiene and Layering - runs-on: ubuntu-latest - timeout-minutes: 40 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - - name: Compute node modules cache key - id: nodeModulesCacheKey - run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - - name: Cache node modules - id: cacheNodeModules - uses: actions/cache@v4 - with: - path: "**/node_modules" - key: ${{ runner.os }}-cacheNodeModulesLinux-${{ steps.nodeModulesCacheKey.outputs.value }} - - name: Get npm cache directory path - id: npmCacheDirPath - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - name: Cache npm directory - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v4 - with: - path: ${{ steps.npmCacheDirPath.outputs.dir }} - key: ${{ runner.os }}-npmCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-npmCacheDir- - - name: Execute npm - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - run: npm ci - - - name: Run Hygiene Checks - run: npm run gulp hygiene - - - name: Run Valid Layers Checks - run: npm run valid-layers-check - - - name: Compile /build/ - run: npm run compile - working-directory: build - - - name: Check clean git state - run: ./.github/workflows/check-clean-git-state.sh - - - name: Run eslint - run: npm run eslint - - - name: Run vscode-dts Compile Checks - run: npm run vscode-dts-compile-check - - - name: Run Trusted Types Checks - run: npm run tsec-compile-check - - warm-cache: - name: Warm up node modules cache - if: github.ref == 'refs/heads/main' - runs-on: ubuntu-latest - timeout-minutes: 40 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - - name: Compute node modules cache key - id: nodeModulesCacheKey - run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - - name: Cache node modules - id: cacheNodeModules - uses: actions/cache@v4 - with: - path: "**/node_modules" - key: ${{ runner.os }}-cacheNodeModulesLinux-${{ steps.nodeModulesCacheKey.outputs.value }} - - name: Get npm cache directory path - id: npmCacheDirPath - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - name: Cache npm directory - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v4 - with: - path: ${{ steps.npmCacheDirPath.outputs.dir }} - key: ${{ runner.os }}-npmCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-npmCacheDir- - - name: Execute npm - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - run: npm ci diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 359027206b3d..000000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,316 +0,0 @@ -name: CI - -on: workflow_dispatch - -# on: -# push: -# branches: -# - main -# - release/* -# pull_request: -# branches: -# - main -# - release/* - -jobs: - windows: - name: Windows - runs-on: windows-2022 - timeout-minutes: 60 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - - uses: actions/setup-python@v5 - with: - python-version: "2.x" - - - name: Compute node modules cache key - id: nodeModulesCacheKey - run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - - name: Cache node_modules archive - id: cacheNodeModules - uses: actions/cache@v4 - with: - path: ".build/node_modules_cache" - key: "${{ runner.os }}-cacheNodeModulesArchive-${{ steps.nodeModulesCacheKey.outputs.value }}" - - name: Extract node_modules archive - if: ${{ steps.cacheNodeModules.outputs.cache-hit == 'true' }} - run: 7z.exe x .build/node_modules_cache/cache.7z -aos - - name: Get npm cache directory path - id: npmCacheDirPath - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - name: Cache npm directory - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v4 - with: - path: ${{ steps.npmCacheDirPath.outputs.dir }} - key: ${{ runner.os }}-npmCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-npmCacheDir- - - name: Execute npm - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - env: - npm_config_foreground_scripts: "true" - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - run: npm ci - - name: Create node_modules archive - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: | - mkdir -Force .build - node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt - mkdir -Force .build/node_modules_cache - 7z.exe a .build/node_modules_cache/cache.7z -mx3 `@.build/node_modules_list.txt - - - name: Compile and Download - run: npm exec -- npm-run-all -lp compile "electron x64" playwright-install download-builtin-extensions - - - name: Compile Integration Tests - run: npm run compile - working-directory: test/integration/browser - - - name: Run Unit Tests (Electron) - run: .\scripts\test.bat - - - name: Run Unit Tests (node.js) - run: npm run test-node - - - name: Run Unit Tests (Browser, Chromium) - run: npm run test-browser-no-install -- --browser chromium - - - name: Run Integration Tests (Electron) - run: .\scripts\test-integration.bat - - - name: Run Integration Tests (Browser, Firefox) - timeout-minutes: 20 - run: .\scripts\test-web-integration.bat --browser firefox - - - name: Run Integration Tests (Remote) - timeout-minutes: 20 - run: .\scripts\test-remote-integration.bat - - linux: - name: Linux - runs-on: ubuntu-latest - timeout-minutes: 40 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - # TODO: rename azure-pipelines/linux/xvfb.init to github-actions - - name: Setup Build Environment - run: | - sudo apt-get update - sudo apt-get install -y libxkbfile-dev pkg-config libkrb5-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 - sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb - sudo chmod +x /etc/init.d/xvfb - sudo update-rc.d xvfb defaults - sudo service xvfb start - - - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - - name: Compute node modules cache key - id: nodeModulesCacheKey - run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - - name: Cache node modules - id: cacheNodeModules - uses: actions/cache@v4 - with: - path: "**/node_modules" - key: ${{ runner.os }}-cacheNodeModulesLinux-${{ steps.nodeModulesCacheKey.outputs.value }} - - name: Get npm cache directory path - id: npmCacheDirPath - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - name: Cache npm directory - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v4 - with: - path: ${{ steps.npmCacheDirPath.outputs.dir }} - key: ${{ runner.os }}-npmCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-npmCacheDir- - - name: Execute npm - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - run: npm ci - - - name: Compile and Download - run: npm exec -- npm-run-all -lp compile "electron x64" playwright-install download-builtin-extensions - - - name: Compile Integration Tests - run: npm run compile - working-directory: test/integration/browser - - - name: Run Unit Tests (Electron) - id: electron-unit-tests - run: DISPLAY=:10 ./scripts/test.sh - - - name: Run Unit Tests (node.js) - id: nodejs-unit-tests - run: npm run test-node - - - name: Run Unit Tests (Browser, Chromium) - id: browser-unit-tests - run: DISPLAY=:10 npm run test-browser-no-install -- --browser chromium - - - name: Run Integration Tests (Electron) - id: electron-integration-tests - run: DISPLAY=:10 ./scripts/test-integration.sh - - - name: Run Integration Tests (Browser, Chromium) - id: browser-integration-tests - run: DISPLAY=:10 ./scripts/test-web-integration.sh --browser chromium - - - name: Run Integration Tests (Remote) - id: electron-remote-integration-tests - timeout-minutes: 15 - run: DISPLAY=:10 ./scripts/test-remote-integration.sh - - darwin: - name: macOS - runs-on: macos-latest - timeout-minutes: 40 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - - name: Compute node modules cache key - id: nodeModulesCacheKey - run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - - name: Cache node modules - id: cacheNodeModules - uses: actions/cache@v4 - with: - path: "**/node_modules" - key: ${{ runner.os }}-cacheNodeModulesMacOS-${{ steps.nodeModulesCacheKey.outputs.value }} - - name: Get npm cache directory path - id: npmCacheDirPath - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - name: Cache npm directory - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v4 - with: - path: ${{ steps.npmCacheDirPath.outputs.dir }} - key: ${{ runner.os }}-npmCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-npmCacheDir- - - name: Execute npm - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - run: npm ci - - - name: Compile and Download - run: npm exec -- npm-run-all -lp compile "electron x64" playwright-install download-builtin-extensions - - - name: Compile Integration Tests - run: npm run compile - working-directory: test/integration/browser - - # This is required for SecretStorage unittests - - name: Create temporary keychain - run: | - security create-keychain -p pwd $RUNNER_TEMP/buildagent.keychain - security default-keychain -s $RUNNER_TEMP/buildagent.keychain - security unlock-keychain -p pwd $RUNNER_TEMP/buildagent.keychain - - - name: Run Unit Tests (Electron) - run: DISPLAY=:10 ./scripts/test.sh - - - name: Run Unit Tests (node.js) - run: npm run test-node - - - name: Run Unit Tests (Browser, Chromium) - run: DISPLAY=:10 npm run test-browser-no-install -- --browser chromium - - - name: Run Integration Tests (Electron) - run: DISPLAY=:10 ./scripts/test-integration.sh - - - name: Run Integration Tests (Browser, Webkit) - run: DISPLAY=:10 ./scripts/test-web-integration.sh --browser webkit - - - name: Run Integration Tests (Remote) - timeout-minutes: 15 - run: DISPLAY=:10 ./scripts/test-remote-integration.sh - - hygiene: - name: Hygiene and Layering - runs-on: ubuntu-latest - timeout-minutes: 40 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - - name: Compute node modules cache key - id: nodeModulesCacheKey - run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - - name: Cache node modules - id: cacheNodeModules - uses: actions/cache@v4 - with: - path: "**/node_modules" - key: ${{ runner.os }}-cacheNodeModulesLinux-${{ steps.nodeModulesCacheKey.outputs.value }} - - name: Get npm cache directory path - id: npmCacheDirPath - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - name: Cache npm directory - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v4 - with: - path: ${{ steps.npmCacheDirPath.outputs.dir }} - key: ${{ runner.os }}-npmCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-npmCacheDir- - - name: Execute npm - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - run: npm ci - - - name: Download Playwright - run: npm run playwright-install - - - name: Run Hygiene Checks - run: npm run gulp hygiene - - - name: Run Valid Layers Checks - run: npm run valid-layers-check - - - name: Compile /build/ - run: npm run compile - working-directory: build - - - name: Check clean git state - run: ./.github/workflows/check-clean-git-state.sh - - - name: Run eslint - run: npm run eslint - - - name: Run vscode-dts Compile Checks - run: npm run vscode-dts-compile-check - - - name: Run Trusted Types Checks - run: npm run tsec-compile-check diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 000000000000..83c9c121aafb --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,27 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, +# surfacing known-vulnerable versions of the packages declared or updated in the PR. +# Once installed, if the workflow run is marked as required, +# PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - name: 'Checkout Repository' + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: 'Dependency Review' + uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 diff --git a/.github/workflows/monaco-editor.yml b/.github/workflows/monaco-editor.yml deleted file mode 100644 index 426999ce43b4..000000000000 --- a/.github/workflows/monaco-editor.yml +++ /dev/null @@ -1,97 +0,0 @@ -name: Monaco Editor checks - -on: - push: - branches: - - main - - release/* - pull_request: - branches: - - main - - release/* - -jobs: - main: - name: Monaco Editor checks - runs-on: ubuntu-latest - timeout-minutes: 40 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - - name: Compute node modules cache key - id: nodeModulesCacheKey - run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - - name: Cache node modules - id: cacheNodeModules - uses: actions/cache@v4 - with: - path: "**/node_modules" - key: ${{ runner.os }}-cacheNodeModules20-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-cacheNodeModules20- - - name: Get npm cache directory path - id: npmCacheDirPath - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - name: Cache npm directory - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v4 - with: - path: ${{ steps.npmCacheDirPath.outputs.dir }} - key: ${{ runner.os }}-npmCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-npmCacheDir- - - name: Install libkrb5-dev - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: | - sudo apt update - sudo apt install -y libkrb5-dev - - name: Execute npm - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - run: | - npm ci - - - name: Download Playwright - run: npm run playwright-install - - - name: Run Monaco Editor Checks - run: npm run monaco-compile-check - - - name: Editor Distro & ESM - run: npm run gulp editor-esm - - - name: Editor ESM sources check - working-directory: ./test/monaco - run: npm run esm-check - - - name: Typings validation prep - run: | - mkdir typings-test - - - name: Typings validation - working-directory: ./typings-test - run: | - npm init -yp - ../node_modules/.bin/tsc --init - echo "import '../out-monaco-editor-core';" > a.ts - ../node_modules/.bin/tsc --noEmit - - - name: Package Editor with Webpack - working-directory: ./test/monaco - run: npm run bundle-webpack - - - name: Compile Editor Tests - working-directory: ./test/monaco - run: npm run compile - - - name: Run Editor Tests - timeout-minutes: 5 - working-directory: ./test/monaco - run: npm run test diff --git a/.github/workflows/no-package-lock-changes.yml b/.github/workflows/no-package-lock-changes.yml deleted file mode 100644 index 45d5d17407bf..000000000000 --- a/.github/workflows/no-package-lock-changes.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Prevent package-lock.json changes in PRs -on: [pull_request] - -jobs: - main: - name: Prevent package-lock.json changes in PRs - runs-on: ubuntu-latest - steps: - - uses: octokit/request-action@v2.x - id: get_permissions - with: - route: GET /repos/microsoft/vscode/collaborators/{username}/permission - username: ${{ github.event.pull_request.user.login }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Set control output variable - id: control - run: | - echo "user: ${{ github.event.pull_request.user.login }}" - echo "role: ${{ fromJson(steps.get_permissions.outputs.data).permission }}" - echo "is dependabot: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }}" - echo "should_run: ${{ !contains(fromJson('["admin", "maintain", "write"]'), fromJson(steps.get_permissions.outputs.data).permission) }}" - echo "should_run=${{ !contains(fromJson('["admin", "maintain", "write"]'), fromJson(steps.get_permissions.outputs.data).permission) && github.event.pull_request.user.login != 'dependabot[bot]' }}" >> $GITHUB_OUTPUT - - name: Get file changes - uses: trilom/file-changes-action@ce38c8ce2459ca3c303415eec8cb0409857b4272 - if: ${{ steps.control.outputs.should_run == 'true' }} - - name: Check for lockfile changes - if: ${{ steps.control.outputs.should_run == 'true' }} - run: | - cat $HOME/files.json | jq -e 'any(test("package-lock\\.json$|Cargo\\.lock$")) | not' \ - || (echo "Changes to package-lock.json/Cargo.lock files aren't allowed in PRs." && exit 1) diff --git a/.github/workflows/no-yarn-lock-changes.yml b/.github/workflows/no-yarn-lock-changes.yml deleted file mode 100644 index 57082a28b1cc..000000000000 --- a/.github/workflows/no-yarn-lock-changes.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Prevent yarn.lock changes in PRs -on: [pull_request] - -jobs: - main: - name: Prevent yarn.lock changes in PRs - runs-on: ubuntu-latest - steps: - - uses: octokit/request-action@v2.x - id: get_permissions - with: - route: GET /repos/microsoft/vscode/collaborators/{username}/permission - username: ${{ github.event.pull_request.user.login }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Set control output variable - id: control - run: | - echo "user: ${{ github.event.pull_request.user.login }}" - echo "role: ${{ fromJson(steps.get_permissions.outputs.data).permission }}" - echo "is dependabot: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }}" - echo "should_run: ${{ !contains(fromJson('["admin", "maintain", "write"]'), fromJson(steps.get_permissions.outputs.data).permission) }}" - echo "should_run=${{ !contains(fromJson('["admin", "maintain", "write"]'), fromJson(steps.get_permissions.outputs.data).permission) && github.event.pull_request.user.login != 'dependabot[bot]' }}" >> $GITHUB_OUTPUT - - name: Get file changes - uses: trilom/file-changes-action@ce38c8ce2459ca3c303415eec8cb0409857b4272 - if: ${{ steps.control.outputs.should_run == 'true' }} - - name: Check for lockfile changes - if: ${{ steps.control.outputs.should_run == 'true' }} - run: | - cat $HOME/files.json | jq -e 'any(test("yarn\\.lock$|Cargo\\.lock$")) | not' \ - || (echo "Changes to yarn.lock/Cargo.lock files aren't allowed in PRs." && exit 1) diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml new file mode 100644 index 000000000000..aba535cf52fc --- /dev/null +++ b/.github/workflows/scorecards.yml @@ -0,0 +1,81 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '20 7 * * 2' + push: + branches: ["Main"] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + contents: read + actions: read + # To allow GraphQL ListCommits to work + issues: read + pull-requests: read + # To detect SAST tools + checks: read + + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - name: "Checkout code" + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecards on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@cdefb33c0f6224e58673d9004f47f7cb3e328b89 # v4.31.10 + with: + sarif_file: results.sarif diff --git a/.github/workflows/telemetry.yml b/.github/workflows/telemetry.yml index d29ea6c58dac..64d557f4a042 100644 --- a/.github/workflows/telemetry.yml +++ b/.github/workflows/telemetry.yml @@ -1,19 +1,29 @@ name: 'Telemetry' on: pull_request: +permissions: + contents: read + jobs: check-metdata: name: 'Check metadata' + permissions: + contents: read runs-on: 'ubuntu-latest' steps: - - uses: 'actions/checkout@v4' + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + + - uses: 'actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8' # v6.0.1 - - uses: 'actions/setup-node@v4' + - uses: 'actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238' # v6.2.0 with: node-version: 'lts/*' - name: 'Run vscode-telemetry-extractor' - run: 'npx --package=@vscode/telemetry-extractor --yes vscode-telemetry-extractor -s .' + run: 'npx --package=@vscode/telemetry-extractor@1.14.0 --yes vscode-telemetry-extractor -s .' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.npmrc b/.npmrc index 8addc73f673a..71d98e6c5834 100644 --- a/.npmrc +++ b/.npmrc @@ -1,6 +1,6 @@ disturl="https://electronjs.org/headers" -target="32.1.2" -ms_build_id="10323173" +target="34.3.2" +ms_build_id="11161073" runtime="electron" build_from_source="true" legacy-peer-deps="true" diff --git a/.nvmrc b/.nvmrc index 3516580bbbc0..0254b1e633c7 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20.17.0 +20.18.2 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000000..43c74cd8f368 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,39 @@ +repos: +- repo: https://github.com/digitalpulp/pre-commit-php + rev: 1.4.0 + hooks: + - id: php-lint-all +- repo: https://github.com/gherynos/pre-commit-java + rev: v0.2.4 + hooks: + - id: Checkstyle +- repo: https://github.com/gitleaks/gitleaks + rev: v8.16.3 + hooks: + - id: gitleaks +- repo: https://github.com/golangci/golangci-lint + rev: v1.52.2 + hooks: + - id: golangci-lint +- repo: https://github.com/jumanjihouse/pre-commit-hooks + rev: 3.0.0 + hooks: + - id: RuboCop + - id: shellcheck +- repo: https://github.com/pocc/pre-commit-hooks + rev: v1.3.5 + hooks: + - id: cpplint +- repo: https://github.com/pre-commit/mirrors-eslint + rev: v8.38.0 + hooks: + - id: eslint +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: end-of-file-fixer + - id: trailing-whitespace +- repo: https://github.com/pylint-dev/pylint + rev: v2.17.2 + hooks: + - id: pylint diff --git a/.vscode-test.js b/.vscode-test.js index 1e5de54470c6..823ef615f4f7 100644 --- a/.vscode-test.js +++ b/.vscode-test.js @@ -19,7 +19,7 @@ const { defineConfig } = require('@vscode/test-cli'); * A list of extension folders who have opted into tests, or configuration objects. * Edit me to add more! * - * @type {Array & { label: string })>} + * @type {Array & { label: string }>} */ const extensions = [ { @@ -42,6 +42,16 @@ const extensions = [ workspaceFolder: `extensions/vscode-colorize-tests/test`, mocha: { timeout: 60_000 } }, + { + label: 'terminal-suggest', + workspaceFolder: path.join(os.tmpdir(), `terminal-suggest-${Math.floor(Math.random() * 100000)}`), + mocha: { timeout: 60_000 } + }, + { + label: 'vscode-colorize-perf-tests', + workspaceFolder: `extensions/vscode-colorize-perf-tests/test`, + mocha: { timeout: 6000_000 } + }, { label: 'configuration-editing', workspaceFolder: path.join(os.tmpdir(), `confeditout-${Math.floor(Math.random() * 100000)}`), @@ -55,6 +65,20 @@ const extensions = [ { label: 'microsoft-authentication', mocha: { timeout: 60_000 } + }, + { + label: 'vscode-api-tests-folder', + extensionDevelopmentPath: `extensions/vscode-api-tests`, + workspaceFolder: `extensions/vscode-api-tests/testWorkspace`, + mocha: { timeout: 60_000 }, + files: 'extensions/vscode-api-tests/out/singlefolder-tests/**/*.test.js', + }, + { + label: 'vscode-api-tests-workspace', + extensionDevelopmentPath: `extensions/vscode-api-tests`, + workspaceFolder: `extensions/vscode-api-tests/testworkspace.code-workspace`, + mocha: { timeout: 60_000 }, + files: 'extensions/vscode-api-tests/out/workspace-tests/**/*.test.js', } ]; @@ -65,9 +89,12 @@ const defaultLaunchArgs = process.env.API_TESTS_EXTRA_ARGS?.split(' ') || [ const config = defineConfig(extensions.map(extension => { /** @type {import('@vscode/test-cli').TestConfiguration} */ - const config = typeof extension === 'object' - ? { files: `extensions/${extension.label}/out/**/*.test.js`, ...extension } - : { files: `extensions/${extension}/out/**/*.test.js`, label: extension }; + const config = { + platform: 'desktop', + files: `extensions/${extension.label}/out/**/*.test.js`, + extensionDevelopmentPath: `extensions/${extension.label}`, + ...extension, + }; config.mocha ??= {}; if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { diff --git a/.vscode/extensions/vscode-selfhost-import-aid/package-lock.json b/.vscode/extensions/vscode-selfhost-import-aid/package-lock.json index 5f526aca3e9e..899595c323f7 100644 --- a/.vscode/extensions/vscode-selfhost-import-aid/package-lock.json +++ b/.vscode/extensions/vscode-selfhost-import-aid/package-lock.json @@ -9,16 +9,17 @@ "version": "0.0.1", "license": "MIT", "dependencies": { - "typescript": "5.5.4" + "typescript": "5.9.3" }, "engines": { "vscode": "^1.88.0" } }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/.vscode/extensions/vscode-selfhost-import-aid/package.json b/.vscode/extensions/vscode-selfhost-import-aid/package.json index 30f09f9c8e5b..396eba235d1f 100644 --- a/.vscode/extensions/vscode-selfhost-import-aid/package.json +++ b/.vscode/extensions/vscode-selfhost-import-aid/package.json @@ -24,6 +24,6 @@ "watch": "gulp watch-extension:vscode-selfhost-import-aid" }, "dependencies": { - "typescript": "5.5.4" + "typescript": "5.9.3" } } diff --git a/.vscode/extensions/vscode-selfhost-test-provider/package-lock.json b/.vscode/extensions/vscode-selfhost-test-provider/package-lock.json index a71a68e4e365..08980be9b8d5 100644 --- a/.vscode/extensions/vscode-selfhost-test-provider/package-lock.json +++ b/.vscode/extensions/vscode-selfhost-test-provider/package-lock.json @@ -9,14 +9,14 @@ "version": "0.4.0", "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "ansi-styles": "^5.2.0", - "cockatiel": "^3.1.3", - "istanbul-to-vscode": "^2.0.1" + "@jridgewell/trace-mapping": "^0.3.31", + "ansi-styles": "^6.2.3", + "cockatiel": "^3.2.1", + "istanbul-to-vscode": "^2.1.1" }, "devDependencies": { - "@types/mocha": "^10.0.6", - "@types/node": "20.x" + "@types/mocha": "^10.0.10", + "@types/node": "25.x" }, "engines": { "vscode": "^1.88.0" @@ -36,9 +36,10 @@ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -50,52 +51,58 @@ "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" }, "node_modules/@types/mocha": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", - "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", - "dev": true + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.12.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz", - "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/cockatiel": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.1.3.tgz", - "integrity": "sha512-xC759TpZ69d7HhfDp8m2WkRwEUiCkxY8Ee2OQH/3H6zmy2D/5Sm+zSTbPRa+V2QyjDtpMvjOIAOVjA2gp6N1kQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz", + "integrity": "sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==", + "license": "MIT", "engines": { "node": ">=16" } }, "node_modules/istanbul-to-vscode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/istanbul-to-vscode/-/istanbul-to-vscode-2.0.1.tgz", - "integrity": "sha512-V9Hhr7kX3UvkvkaT1lK3AmCRPkaIAIogQBrduTpNiLTkp1eVsybnJhWiDSVeCQap/3aGeZ2019oIivhX9MNsCQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/istanbul-to-vscode/-/istanbul-to-vscode-2.1.1.tgz", + "integrity": "sha512-F4IvCWYzWJkFfbYFjLUgxndE6g4EVWQH+rObEQ8OexBGTLhQ7e6VQ8p9f4BVrQmbNmo0nRGIGF34XqARXXgMjA==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "^2.0.6" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/.vscode/extensions/vscode-selfhost-test-provider/package.json b/.vscode/extensions/vscode-selfhost-test-provider/package.json index 3548b00ba81f..2a579793a118 100644 --- a/.vscode/extensions/vscode-selfhost-test-provider/package.json +++ b/.vscode/extensions/vscode-selfhost-test-provider/package.json @@ -4,8 +4,7 @@ "description": "Test provider for the VS Code project", "enabledApiProposals": [ "testObserver", - "testRelatedCode", - "attributableCoverage" + "testRelatedCode" ], "engines": { "vscode": "^1.88.0" @@ -78,13 +77,13 @@ "test": "npx mocha --ui tdd 'out/*.test.js'" }, "devDependencies": { - "@types/mocha": "^10.0.6", - "@types/node": "20.x" + "@types/mocha": "^10.0.10", + "@types/node": "25.x" }, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "ansi-styles": "^5.2.0", - "cockatiel": "^3.1.3", - "istanbul-to-vscode": "^2.0.1" + "@jridgewell/trace-mapping": "^0.3.31", + "ansi-styles": "^6.2.3", + "cockatiel": "^3.2.1", + "istanbul-to-vscode": "^2.1.1" } } diff --git a/.vscode/extensions/vscode-selfhost-test-provider/src/coverageProvider.ts b/.vscode/extensions/vscode-selfhost-test-provider/src/coverageProvider.ts index 3fff7c5b6378..a1d1c162dbd4 100644 --- a/.vscode/extensions/vscode-selfhost-test-provider/src/coverageProvider.ts +++ b/.vscode/extensions/vscode-selfhost-test-provider/src/coverageProvider.ts @@ -142,7 +142,7 @@ class ScriptCoverageTracker { } } -export class V8CoverageFile extends vscode.FileCoverage2 { +export class V8CoverageFile extends vscode.FileCoverage { public details: vscode.StatementCoverage[] = []; constructor( diff --git a/.vscode/extensions/vscode-selfhost-test-provider/src/extension.ts b/.vscode/extensions/vscode-selfhost-test-provider/src/extension.ts index 2732ef3b3f61..cbb8d50bf995 100644 --- a/.vscode/extensions/vscode-selfhost-test-provider/src/extension.ts +++ b/.vscode/extensions/vscode-selfhost-test-provider/src/extension.ts @@ -86,10 +86,11 @@ export async function activate(context: vscode.ExtensionContext) { }, uri => ctrl.items.get(uri.toString().toLowerCase())); ctrl.relatedCodeProvider = graph; - context.subscriptions.push( - new FailureTracker(context, folder.uri.fsPath), - fileChangedEmitter.event(e => graph.didChange(e.uri, e.removed)), - ); + if (context.storageUri) { + context.subscriptions.push(new FailureTracker(context.storageUri.fsPath, folder.uri.fsPath)); + } + + context.subscriptions.push(fileChangedEmitter.event(e => graph.didChange(e.uri, e.removed))); }); const createRunHandler = ( diff --git a/.vscode/extensions/vscode-selfhost-test-provider/src/failureTracker.ts b/.vscode/extensions/vscode-selfhost-test-provider/src/failureTracker.ts index e04d4beede4c..5bed5dd63e33 100644 --- a/.vscode/extensions/vscode-selfhost-test-provider/src/failureTracker.ts +++ b/.vscode/extensions/vscode-selfhost-test-provider/src/failureTracker.ts @@ -33,8 +33,8 @@ export class FailureTracker { private readonly logFile: string; private logs?: ITrackedRemediation[]; - constructor(context: vscode.ExtensionContext, private readonly rootDir: string) { - this.logFile = join(context.globalStorageUri.fsPath, '.build/vscode-test-failures.json'); + constructor(storageLocation: string, private readonly rootDir: string) { + this.logFile = join(storageLocation, '.build/vscode-test-failures.json'); mkdirSync(dirname(this.logFile), { recursive: true }); const oldLogFile = join(rootDir, '.build/vscode-test-failures.json'); diff --git a/.vscode/extensions/vscode-selfhost-test-provider/tsconfig.json b/.vscode/extensions/vscode-selfhost-test-provider/tsconfig.json index 56d6859c3e3b..e40cd7d74929 100644 --- a/.vscode/extensions/vscode-selfhost-test-provider/tsconfig.json +++ b/.vscode/extensions/vscode-selfhost-test-provider/tsconfig.json @@ -11,7 +11,6 @@ "src/**/*", "../../../src/vscode-dts/vscode.d.ts", "../../../src/vscode-dts/vscode.proposed.testObserver.d.ts", - "../../../src/vscode-dts/vscode.proposed.testRelatedCode.d.ts", - "../../../src/vscode-dts/vscode.proposed.attributableCoverage.d.ts" + "../../../src/vscode-dts/vscode.proposed.testRelatedCode.d.ts" ] } diff --git a/.vscode/launch.json b/.vscode/launch.json index b2e25927a8a7..f922b5d9c803 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -202,6 +202,24 @@ "order": 5 } }, + { + "type": "extensionHost", + "request": "launch", + "name": "VS Code Tokenizer Performance Tests", + "runtimeExecutable": "${execPath}", + "args": [ + "${workspaceFolder}/extensions/vscode-colorize-perf-tests/test", + "--extensionDevelopmentPath=${workspaceFolder}/extensions/vscode-colorize-perf-tests", + "--extensionTestsPath=${workspaceFolder}/extensions/vscode-colorize-perf-tests/out" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], + "presentation": { + "group": "5_tests", + "order": 6 + } + }, { "type": "chrome", "request": "attach", @@ -233,7 +251,51 @@ "timeout": 0, "env": { "VSCODE_EXTHOST_WILL_SEND_SOCKET": null, - "VSCODE_SKIP_PRELAUNCH": "1" + "VSCODE_SKIP_PRELAUNCH": "1", + }, + "cleanUp": "wholeBrowser", + "runtimeArgs": [ + "--inspect-brk=5875", + "--no-cached-data", + "--crash-reporter-directory=${workspaceFolder}/.profile-oss/crashes", + // for general runtime freezes: https://github.com/microsoft/vscode/issues/127861#issuecomment-904144910 + "--disable-features=CalculateNativeWinOcclusion", + "--disable-extension=vscode.vscode-api-tests" + ], + "userDataDir": "${userHome}/.vscode-oss-dev", + "webRoot": "${workspaceFolder}", + "cascadeTerminateToConfigurations": [ + "Attach to Extension Host" + ], + "pauseForSourceMap": false, + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], + "browserLaunchLocation": "workspace", + "presentation": { + "hidden": true, + }, + }, + { + // To debug observables you also need the extension "ms-vscode.debug-value-editor" + "type": "chrome", + "request": "launch", + "name": "Launch VS Code Internal (Dev Debug)", + "windows": { + "runtimeExecutable": "${workspaceFolder}/scripts/code.bat" + }, + "osx": { + "runtimeExecutable": "${workspaceFolder}/scripts/code.sh" + }, + "linux": { + "runtimeExecutable": "${workspaceFolder}/scripts/code.sh" + }, + "port": 9222, + "timeout": 0, + "env": { + "VSCODE_EXTHOST_WILL_SEND_SOCKET": null, + "VSCODE_SKIP_PRELAUNCH": "1", + "VSCODE_DEV_DEBUG": "1", }, "cleanUp": "wholeBrowser", "runtimeArgs": [ @@ -257,10 +319,6 @@ "presentation": { "hidden": true, }, - // This is read by the vscode-diagnostic-tools extension - "vscode-diagnostic-tools.debuggerScripts": [ - "${workspaceFolder}/scripts/hot-reload-injected-script.js" - ] }, { "type": "node", @@ -554,6 +612,21 @@ "order": 1 } }, + { + "name": "VS Code (Debug Observables)", + "stopAll": true, + "configurations": [ + "Launch VS Code Internal (Dev Debug)", + "Attach to Main Process", + "Attach to Extension Host", + "Attach to Shared Process", + ], + "preLaunchTask": "Ensure Prelaunch Dependencies", + "presentation": { + "group": "0_vscode", + "order": 1 + } + }, { "name": "Search, Renderer, and Main processes", "configurations": [ diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index 35ea9042ef5c..3a0eaec922fa 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPO=repo:microsoft/vscode\n$MILESTONE=milestone:\"October 2024\"" + "value": "$REPO=repo:microsoft/vscode\n$MILESTONE=milestone:\"March 2025\"" }, { "kind": 1, diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index 9f6624a13233..ca93d5033384 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"September 2024\"" + "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\r\n\r\n$MILESTONE=milestone:\"February 2025\"" }, { "kind": 1, @@ -97,7 +97,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:closed reason:completed label:verification-needed -label:verified" + "value": "$REPOS $MILESTONE is:issue is:closed reason:completed label:verification-needed -label:verified -label:on-testplan" }, { "kind": 1, @@ -112,7 +112,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:z-author-verified -label:unreleased -label:*not-reproducible" + "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:z-author-verified -label:unreleased -label:*not-reproducible -label:*out-of-scope" }, { "kind": 1, diff --git a/.vscode/notebooks/grooming.github-issues b/.vscode/notebooks/grooming.github-issues index 8bb9a246d5f7..6d9deb71792d 100644 --- a/.vscode/notebooks/grooming.github-issues +++ b/.vscode/notebooks/grooming.github-issues @@ -7,46 +7,46 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\r\n$repos=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\r" + "value": "// list of repos we work in\r\n$repos=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\r\n\r\n$assignee=@me\r\n" }, { "kind": 1, "language": "markdown", - "value": "#### Missing Type label\r" + "value": "#### Missing Type label\r\n" }, { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open type:issue -label:bug -label:\"info-needed\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream -label:polish -label:testplan-item -label:error-telemetry -label:engineering -label:endgame-plan\r" + "value": "$repos assignee:$assignee is:open type:issue -label:bug -label:\"info-needed\" -label:feature-request -label:under-discussion -label:debt -label:plan-item -label:upstream -label:polish -label:testplan-item -label:error-telemetry -label:engineering -label:endgame-plan\r\n" }, { "kind": 1, "language": "markdown", - "value": "#### Missing Area Label\r\n\r\nFeature area labels are light or strong blue (`1d76db` or `c5def5`) and they denote a specific feature or feature area, like `editor-clipboard` or `file-explorer`\r" + "value": "#### Missing Area Label\r\n\r\nFeature area labels are light or strong blue (`1d76db` or `c5def5`) and they denote a specific feature or feature area, like `editor-clipboard` or `file-explorer`\r\n" }, { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode assignee:@me is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:cloud-changes -label:code-lens -label:command-center -label:comments -label:config -label:containers -label:context-keys -label:continue-working-on -label:css-less-scss -label:custom-editors -label:debug -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-widgets -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:emmet-parse -label:error-list -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:html -label:icon-brand -label:icons-product -label:image-preview -label:inlay-hints -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:L10N -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:native-file-dialog -label:network -label:notebook -label:notebook-accessibility -label:notebook-api -label:notebook-builtin-renderers -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-commands -label:notebook-commenting -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-math -label:notebook-minimap -label:notebook-multiselect -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-rendering -label:notebook-serialization -label:notebook-serverless-web -label:notebook-statusbar -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:notebook-sticky-scroll -label:notebook-format -label:notebook-code-actions -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-sticky-scroll -label:terminal-tabs -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:undo-redo -label:unicode-highlight -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom -label:inline-chat -label:panel-chat -label:quick-chat -label:audio-cue -label:tasks -label:error-list -label:winget -label:tree-views -label:freeze-slow-crash-leak -label:engineering" + "value": "repo:microsoft/vscode assignee:$assignee is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:cloud-changes -label:code-lens -label:command-center -label:comments -label:config -label:containers -label:context-keys -label:continue-working-on -label:css-less-scss -label:custom-editors -label:debug -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-widgets -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet -label:emmet-parse -label:error-list -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:html -label:icon-brand -label:icons-product -label:image-preview -label:inlay-hints -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:issue-bot -label:issue-reporter -label:javascript -label:json -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:chat -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:native-file-dialog -label:network -label:notebook -label:notebook-accessibility -label:notebook-api -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-commands -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-serialization -label:notebook-statusbar -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:notebook-sticky-scroll -label:notebook-format -label:notebook-code-actions -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-sticky-scroll -label:terminal-tabs -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:undo-redo -label:unicode-highlight -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom -label:inline-chat -label:panel-chat -label:quick-chat -label:tasks -label:error-list -label:winget -label:tree-views -label:freeze-slow-crash-leak -label:engineering -label:cross-file-editing -label:microsoft-authentication -label:github-authentication -label:lm-access -label:secret-storage" }, { "kind": 1, "language": "markdown", - "value": "### Missing Milestone\r" + "value": "### Missing Milestone\r\n" }, { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open type:issue no:milestone -label:info-needed -label:triage-needed\r" + "value": "$repos assignee:$assignee is:open type:issue no:milestone -label:info-needed -label:triage-needed -label:confirmation-pending -label:under-discussion\r\n" }, { "kind": 1, "language": "markdown", - "value": "#### Not Actionable\r" + "value": "#### Not Actionable\r\n" }, { "kind": 2, "language": "github-issues", - "value": "$repos assignee:@me is:open label:\"info-needed\"\r" + "value": "$repos assignee:$assignee is:open label:\"info-needed\"\r\n" } ] \ No newline at end of file diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index f17345ed7f6d..0fd05ece4853 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"September 2024\"\n\n$MINE=assignee:@me" + "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"February 2025\"\n\n$MINE=assignee:@me" }, { "kind": 1, @@ -62,7 +62,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request label:verification-needed -label:verified" + "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request label:verification-needed -label:verified -label:on-testplan" }, { "kind": 1, @@ -87,7 +87,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed -assignee:@me -label:verified -label:z-author-verified label:feature-request label:verification-needed -label:verification-steps-needed -label:unreleased" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed -assignee:@me -label:verified -label:z-author-verified label:feature-request label:verification-needed -label:verification-steps-needed -label:unreleased -label:on-testplan" }, { "kind": 1, @@ -157,7 +157,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:*out-of-scope -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:andreamah -author:bamurtaugh -author:bpasero -author:chrisdias -author:chrmarti -author:Chuxel -author:claudiaregio -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:DonJayamanne -author:egamma -author:fiveisprime -author:gregvanl -author:hediet -author:isidorn -author:joaomoreno -author:joyceerhl -author:jrieken -author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:roblourens -author:rzhao271 -author:sandy081 -author:sbatten -author:stevencl -author:tanhakabir -author:TylerLeonhardt -author:Tyriar -author:weinand -author:amunger -author:karthiknadig -author:eleanorjboyd -author:Yoyokrazy -author:paulacamargo25 -author:ulugbekna -author:aiday-mar -author:daviddossett -author:bhavyaus -author:justschen -author:benibenj -author:luabud -author:anthonykim1 -author:joshspicer" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:*out-of-scope -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:andreamah -author:bamurtaugh -author:bpasero -author:chrisdias -author:chrmarti -author:Chuxel -author:claudiaregio -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:DonJayamanne -author:egamma -author:fiveisprime -author:ntrogh -author:hediet -author:isidorn -author:joaomoreno -author:joyceerhl -author:jrieken -author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:roblourens -author:rzhao271 -author:sandy081 -author:sbatten -author:stevencl -author:tanhakabir -author:TylerLeonhardt -author:Tyriar -author:weinand -author:amunger -author:karthiknadig -author:eleanorjboyd -author:Yoyokrazy -author:paulacamargo25 -author:ulugbekna -author:aiday-mar -author:daviddossett -author:bhavyaus -author:justschen -author:benibenj -author:luabud -author:anthonykim1 -author:joshspicer -author:osortega -author:legomushroom" }, { "kind": 1, @@ -187,6 +187,6 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request -label:on-release-notes\n$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:engineering -label:on-release-notes\n$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:plan-item -label:on-release-notes" + "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request -label:on-release-notes\r\n$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:engineering -label:on-release-notes\r\n$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:plan-item -label:on-release-notes" } ] \ No newline at end of file diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index c58ceae0d010..c7674cef414a 100644 --- a/.vscode/notebooks/my-work.github-issues +++ b/.vscode/notebooks/my-work.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "// list of repos we work in\n$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n// current milestone name\n$MILESTONE=milestone:\"October 2024\"\n" + "value": "// list of repos we work in\n$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n// current milestone name\n$MILESTONE=milestone:\"February 2025\"\n" }, { "kind": 1, @@ -102,7 +102,7 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode assignee:@me is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:code-lens -label:command-center -label:comments -label:config -label:context-keys -label:custom-editors -label:debug -label:debug-console -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-sticky-scroll-decorations -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet-parse -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extension-signature -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:icon-brand -label:icons-product -label:icons-widget -label:inlay-hints -label:inline-chat -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:javascript -label:json -label:json-sorting -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:L10N -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:multi-monitor -label:native-file-dialog -label:network -label:notebook -label:notebook-accessibility -label:notebook-api -label:notebook-builtin-renderers -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-code-actions -label:notebook-commands -label:notebook-commenting -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-format -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-minimap -label:notebook-multiselect -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-rendering -label:notebook-serialization -label:notebook-statusbar -label:notebook-sticky-scroll -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:panel-chat -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:quickpick-chat -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-desktop -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-search -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:system-context-menu -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-tabs -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:unc -label:undo-redo -label:unicode-highlight -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-documents -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-auxwindow -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-voice -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom -label:error-list -label:winget" + "value": "repo:microsoft/vscode assignee:@me is:open type:issue -label:\"info-needed\" -label:api -label:api-finalization -label:api-proposal -label:authentication -label:bisect-ext -label:bracket-pair-colorization -label:bracket-pair-guides -label:breadcrumbs -label:callhierarchy -label:chrome-devtools -label:code-lens -label:command-center -label:comments -label:config -label:context-keys -label:custom-editors -label:debug -label:debug-console -label:debug-disassembly -label:dialogs -label:diff-editor -label:dropdown -label:editor-api -label:editor-autoclosing -label:editor-autoindent -label:editor-bracket-matching -label:editor-clipboard -label:editor-code-actions -label:editor-color-picker -label:editor-columnselect -label:editor-commands -label:editor-comments -label:editor-contrib -label:editor-core -label:editor-drag-and-drop -label:editor-error-widget -label:editor-find -label:editor-folding -label:editor-highlight -label:editor-hover -label:editor-indent-detection -label:editor-indent-guides -label:editor-input -label:editor-input-IME -label:editor-insets -label:editor-minimap -label:editor-multicursor -label:editor-parameter-hints -label:editor-render-whitespace -label:editor-rendering -label:editor-RTL -label:editor-scrollbar -label:editor-sorting -label:editor-sticky-scroll -label:editor-sticky-scroll-decorations -label:editor-symbols -label:editor-synced-region -label:editor-textbuffer -label:editor-theming -label:editor-wordnav -label:editor-wrapping -label:emmet-parse -label:extension-activation -label:extension-host -label:extension-prerelease -label:extension-recommendations -label:extension-signature -label:extensions -label:extensions-development -label:file-decorations -label:file-encoding -label:file-explorer -label:file-glob -label:file-io -label:file-nesting -label:file-watcher -label:font-rendering -label:formatting -label:getting-started -label:ghost-text -label:git -label:github -label:github-repositories -label:gpu -label:grammar -label:grid-widget -label:icon-brand -label:icons-product -label:icons-widget -label:inlay-hints -label:inline-chat -label:inline-completions -label:install-update -label:intellisense-config -label:interactive-playground -label:interactive-window -label:javascript -label:json -label:json-sorting -label:keybindings -label:keybindings-editor -label:keyboard-layout -label:L10N -label:l10n-platform -label:label-provider -label:languages-basic -label:languages-diagnostics -label:languages-guessing -label:layout -label:lcd-text-rendering -label:list-widget -label:live-preview -label:log -label:markdown -label:marketplace -label:menus -label:merge-conflict -label:merge-editor -label:merge-editor-workbench -label:monaco-editor -label:multi-monitor -label:native-file-dialog -label:network -label:notebook -label:notebook-accessibility -label:notebook-api -label:notebook-cell-editor -label:notebook-celltoolbar -label:notebook-clipboard -label:notebook-code-actions -label:notebook-commands -label:notebook-debugging -label:notebook-diff -label:notebook-dnd -label:notebook-execution -label:notebook-find -label:notebook-folding -label:notebook-format -label:notebook-getting-started -label:notebook-globaltoolbar -label:notebook-ipynb -label:notebook-kernel -label:notebook-kernel-picker -label:notebook-language -label:notebook-layout -label:notebook-markdown -label:notebook-output -label:notebook-perf -label:notebook-remote -label:notebook-serialization -label:notebook-statusbar -label:notebook-sticky-scroll -label:notebook-toc-outline -label:notebook-undo-redo -label:notebook-variables -label:notebook-workbench-integration -label:notebook-workflow -label:open-editors -label:opener -label:outline -label:output -label:packaging -label:panel-chat -label:perf -label:perf-bloat -label:perf-startup -label:php -label:portable-mode -label:proxy -label:quick-open -label:quick-pick -label:quickpick-chat -label:references-viewlet -label:release-notes -label:remote -label:remote-connection -label:remote-desktop -label:remote-explorer -label:remote-tunnel -label:rename -label:runCommands -label:sandbox -label:sash-widget -label:scm -label:screencast-mode -label:search -label:search-api -label:search-editor -label:search-replace -label:semantic-tokens -label:server -label:settings-editor -label:settings-search -label:settings-sync -label:settings-sync-server -label:shared-process -label:simple-file-dialog -label:smart-select -label:snap -label:snippets -label:splitview-widget -label:ssh -label:suggest -label:system-context-menu -label:table-widget -label:tasks -label:telemetry -label:terminal -label:terminal-accessibility -label:terminal-conpty -label:terminal-editors -label:terminal-external -label:terminal-find -label:terminal-input -label:terminal-layout -label:terminal-links -label:terminal-local-echo -label:terminal-persistence -label:terminal-process -label:terminal-profiles -label:terminal-quick-fix -label:terminal-rendering -label:terminal-shell-bash -label:terminal-shell-cmd -label:terminal-shell-fish -label:terminal-shell-git-bash -label:terminal-shell-integration -label:terminal-shell-pwsh -label:terminal-shell-zsh -label:terminal-tabs -label:testing -label:themes -label:timeline -label:timeline-git -label:timeline-local-history -label:titlebar -label:tokenization -label:touch/pointer -label:trackpad/scroll -label:tree-views -label:tree-widget -label:typescript -label:unc -label:undo-redo -label:unicode-highlight -label:uri -label:user-profiles -label:ux -label:variable-resolving -label:VIM -label:virtual-documents -label:virtual-workspaces -label:vscode-website -label:vscode.dev -label:web -label:webview -label:webview-views -label:workbench-actions -label:workbench-auxwindow -label:workbench-banner -label:workbench-cli -label:workbench-diagnostics -label:workbench-dnd -label:workbench-editor-grid -label:workbench-editor-groups -label:workbench-editor-resolver -label:workbench-editors -label:workbench-electron -label:workbench-fonts -label:workbench-history -label:workbench-hot-exit -label:workbench-hover -label:workbench-launch -label:workbench-link -label:workbench-multiroot -label:workbench-notifications -label:workbench-os-integration -label:workbench-rapid-render -label:workbench-run-as-admin -label:workbench-state -label:workbench-status -label:workbench-tabs -label:workbench-touchbar -label:workbench-untitled-editors -label:workbench-views -label:workbench-voice -label:workbench-welcome -label:workbench-window -label:workbench-workspace -label:workbench-zen -label:workspace-edit -label:workspace-symbols -label:workspace-trust -label:zoom -label:error-list -label:winget -label:cross-file-editing -label:editor-refactor-preview" }, { "kind": 1, diff --git a/.vscode/settings.json b/.vscode/settings.json index 75cbca16260b..87e51525722b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -49,6 +49,7 @@ "out-vscode-reh/**": true, "extensions/**/dist/**": true, "extensions/**/out/**": true, + "extensions/terminal-suggest/src/completions/upstream/**": true, "test/smoke/out/**": true, "test/automation/out/**": true, "test/integration/browser/out/**": true @@ -76,6 +77,7 @@ "npm.exclude": "**/extensions/**", "emmet.excludeLanguages": [], "typescript.preferences.importModuleSpecifier": "relative", + "typescript.preferences.importModuleSpecifierEnding": "js", "typescript.preferences.quoteStyle": "single", "json.schemas": [ { @@ -156,6 +158,7 @@ "application.experimental.rendererProfiling": true, "editor.experimental.asyncTokenization": true, "editor.experimental.asyncTokenizationVerification": true, + "terminal.integrated.suggest.enabled": true, "typescript.preferences.autoImportFileExcludePatterns": [ "@xterm/xterm", "@xterm/headless", @@ -168,6 +171,13 @@ }, "css.format.spaceAroundSelectorSeparator": true, "typescript.enablePromptUseWorkspaceTsdk": true, - "chat.commandCenter.enabled": true, - "eslint.useFlatConfig": true + "eslint.useFlatConfig": true, + "editor.occurrencesHighlightDelay": 0, + "typescript.experimental.expandableHover": true, + "git.diagnosticsCommitHook.Enabled": true, + "git.diagnosticsCommitHook.Sources": { + "*": "error", + "ts": "warning", + "eslint": "warning" + } } diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index da3989dd371c..0f17c5ed11bd 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -4,6 +4,34 @@ This repository incorporates material as listed below or described in the code. +--------------------------------------------------------- + +@fig/autocomplete-shared 1.1.2 +https://github.com/withfig/autocomplete-tools + +MIT License + +Copyright (c) 2021 Hercules Labs Inc. (Fig) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + --------------------------------------------------------- @iktakahiro/markdown-it-katex 4.0.2 - MIT @@ -58,6 +86,34 @@ SOFTWARE. --------------------------------------------------------- +amazon-q-developer-cli f66e0b0e917ab185eef528dc36eca56b78ca8b5d +https://github.com/aws/amazon-q-developer-cli + +MIT License + +Copyright (c) 2024 Amazon.com, Inc. or its affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + atom/language-clojure 0.22.8 - MIT https://github.com/atom/language-clojure @@ -263,6 +319,34 @@ suitability for any purpose. --------------------------------------------------------- +autocomplete 2.684.0 - MIT +https://github.com/withfig/autocomplete + +MIT License + +Copyright (c) 2021 Hercules Labs Inc. (Fig) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + cacheable-request 7.0.4 - MIT @@ -440,11 +524,11 @@ Title to copyright in this work will at all times remain with copyright holders. --------------------------------------------------------- -dompurify 3.0.5 - Apache 2.0 +dompurify 3.1.7 - Apache 2.0 https://github.com/cure53/DOMPurify DOMPurify -Copyright 2024 Dr.-Ing. Mario Heiderich, Cure53 +Copyright 2025 Dr.-Ing. Mario Heiderich, Cure53 DOMPurify is free software; you can redistribute it and/or modify it under the terms of either: @@ -1119,7 +1203,7 @@ to the base-name name of the original file, and an extension of txt, html, or si --------------------------------------------------------- -go-syntax 0.7.6 - MIT +go-syntax 0.7.9 - MIT https://github.com/worlpaker/go-syntax MIT License @@ -1167,7 +1251,7 @@ without specific, written prior permission. Title to copyright in this document --------------------------------------------------------- -Ionic documentation 1.2.4 - Apache2 +Ionic documentation 1.2.4 - Apache-2.0 https://github.com/ionic-team/ionic-site Copyright Drifty Co. http://drifty.com/. @@ -1435,7 +1519,7 @@ SOFTWARE. --------------------------------------------------------- -jlelong/vscode-latex-basics 1.9.0 - MIT +jlelong/vscode-latex-basics 1.10.0 - MIT https://github.com/jlelong/vscode-latex-basics Copyright (c) vscode-latex-basics authors @@ -1551,7 +1635,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SO --------------------------------------------------------- -language-docker 0.0.0 - Apache2 +language-docker 0.0.0 - Apache-2.0 https://github.com/moby/moby Apache License @@ -2108,7 +2192,7 @@ SOFTWARE. --------------------------------------------------------- -RedCMD/YAML-Syntax-Highlighter 1.1.2 - MIT +RedCMD/YAML-Syntax-Highlighter 1.3.2 - MIT https://github.com/RedCMD/YAML-Syntax-Highlighter MIT License @@ -2164,7 +2248,7 @@ suitability for any purpose. --------------------------------------------------------- -REditorSupport/vscode-R 2.8.1 - MIT +REditorSupport/vscode-R 2.8.4 - MIT https://github.com/REditorSupport/vscode-R MIT License @@ -2297,6 +2381,55 @@ SOFTWARE. --------------------------------------------------------- +Shopify/ruby-lsp 0.0.0 - MIT License +https://github.com/Shopify/ruby-lsp + +The MIT License (MIT) + +Copyright (c) 2022-present, Shopify Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +================================================================================ +The following files and related configuration in package.json are based on a +sequence of adaptions: grammars/ruby.cson.json, grammars/erb.cson.json, +languages/erb.json. + +Copyright (c) 2016 Peng Lv +Copyright (c) 2017-2019 Stafford Brunk +https://github.com/rubyide/vscode-ruby + + Released under the MIT license + https://github.com/rubyide/vscode-ruby/blob/main/LICENSE.txt + +Copyright (c) 2014 GitHub Inc. +https://github.com/atom/language-ruby + + Released under the MIT license + https://github.com/atom/language-ruby/blob/master/LICENSE.md + +https://github.com/textmate/ruby.tmbundle + https://github.com/textmate/ruby.tmbundle#license +--------------------------------------------------------- + +--------------------------------------------------------- + sumneko/lua.tmbundle 1.0.0 - TextMate Bundle License https://github.com/sumneko/lua.tmbundle @@ -2526,26 +2659,6 @@ to the base-name name of the original file, and an extension of txt, html, or si --------------------------------------------------------- -textmate/ruby.tmbundle 0.0.0 - TextMate Bundle License -https://github.com/textmate/ruby.tmbundle - -Copyright (c) textmate-ruby.tmbundle project authors - -If not otherwise specified (see below), files in this folder fall under the following license: - -Permission to copy, use, modify, sell and distribute this -software is granted. This software is provided "as is" without -express or implied warranty, and with no claim as to its -suitability for any purpose. - -An exception is made for files in readable text which contain their own license information, -or files where an accompanying file exists (in the same directory) with a "-license" suffix added -to the base-name name of the original file, and an extension of txt, html, or similar. For example -"tidy" is accompanied by "tidy-license.txt". ---------------------------------------------------------- - ---------------------------------------------------------- - trond-snekvik/vscode-rst 1.5.3 - MIT https://github.com/trond-snekvik/vscode-rst @@ -3056,7 +3169,7 @@ Creative Commons may be contacted at creativecommons.org. --------------------------------------------------------- -vscode-logfile-highlighter 2.17.0 - MIT +vscode-logfile-highlighter 3.3.4 - MIT https://github.com/emilast/vscode-logfile-highlighter The MIT License (MIT) @@ -3168,7 +3281,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -Web Background Synchronization - Apache2 +Web Background Synchronization - Apache-2.0 https://github.com/WICG/background-sync Apache License diff --git a/build/.cachesalt b/build/.cachesalt index 3c6029dc2f4b..5a294f353967 100644 --- a/build/.cachesalt +++ b/build/.cachesalt @@ -1 +1 @@ -2024-09-04T10:21:29.952Z +2024-12-11T00:28:56.838Z diff --git a/build/.moduleignore b/build/.moduleignore index 97b504d85227..6b7f365730fe 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -55,6 +55,11 @@ fsevents/test/** @vscode/windows-registry/build/** !@vscode/windows-registry/build/Release/*.node +@vscode/tree-sitter-wasm/wasm/tree-sitter-*.wasm +!@vscode/tree-sitter-wasm/wasm/tree-sitter-typescript.wasm +!@vscode/tree-sitter-wasm/wasm/tree-sitter-regex.wasm +!@vscode/tree-sitter-wasm/wasm/tree-sitter-ini.wasm + native-keymap/binding.gyp native-keymap/build/** native-keymap/src/** @@ -110,15 +115,13 @@ node-pty/third_party/** @parcel/watcher/src/** !@parcel/watcher/build/Release/*.node -vsda/build/** -vsda/ci/** -vsda/src/** -vsda/.gitignore -vsda/binding.gyp -vsda/README.md -vsda/SECURITY.md -vsda/targets +vsda/** +!vsda/index.js +!vsda/index.d.ts +!vsda/package.json !vsda/build/Release/vsda.node +!vsda/rust/web/** +!vsda/rust/bundler/** @vscode/policy-watcher/build/** @vscode/policy-watcher/.husky/** @@ -132,6 +135,7 @@ vsda/targets !@vscode/windows-ca-certs/package.json !@vscode/windows-ca-certs/**/*.node +@vscode/node-addon-api/**/* node-addon-api/**/* prebuild-install/**/* diff --git a/build/.npmrc b/build/.npmrc index 1b073e71a832..551822f79cd6 100644 --- a/build/.npmrc +++ b/build/.npmrc @@ -2,4 +2,5 @@ disturl="https://nodejs.org/dist" runtime="node" build_from_source="true" legacy-peer-deps="true" +force_process_config="true" timeout=180000 diff --git a/build/.webignore b/build/.webignore index d42f9775ba9a..31c366cc1faf 100644 --- a/build/.webignore +++ b/build/.webignore @@ -26,6 +26,9 @@ vscode-textmate/webpack.config.js @xterm/addon-image/src/** @xterm/addon-image/out/** +@xterm/addon-ligatures/src/** +@xterm/addon-ligatures/out/** + @xterm/addon-search/src/** @xterm/addon-search/out/** @xterm/addon-search/fixtures/** diff --git a/build/azure-pipelines/alpine/cli-build-alpine.yml b/build/azure-pipelines/alpine/cli-build-alpine.yml index 07321ebcd97b..145133481f26 100644 --- a/build/azure-pipelines/alpine/cli-build-alpine.yml +++ b/build/azure-pipelines/alpine/cli-build-alpine.yml @@ -19,13 +19,15 @@ steps: nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - template: ../cli/cli-apply-patches.yml@self + - script: | set -e npm ci workingDirectory: build - displayName: Install pipeline build - - - template: ../cli/cli-apply-patches.yml@self + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" + displayName: Install build dependencies - task: Npm@1 displayName: Download openssl prebuilt diff --git a/build/azure-pipelines/alpine/product-build-alpine.yml b/build/azure-pipelines/alpine/product-build-alpine.yml index 890bc68c7380..d6fe74a9d610 100644 --- a/build/azure-pipelines/alpine/product-build-alpine.yml +++ b/build/azure-pipelines/alpine/product-build-alpine.yml @@ -10,7 +10,7 @@ steps: - task: AzureKeyVault@2 displayName: "Azure Key Vault: Get Secrets" inputs: - azureSubscription: "vscode-builds-subscription" + azureSubscription: vscode KeyVaultName: vscode-build-secrets SecretsFilter: "github-distro-mixin-password" @@ -59,7 +59,7 @@ steps: - task: Docker@1 inputs: - azureSubscriptionEndpoint: "vscode-builds-subscription" + azureSubscriptionEndpoint: vscode azureContainerRegistry: vscodehub.azurecr.io command: "Run an image" imageName: "vscode-linux-build-agent:alpine-$(VSCODE_ARCH)" diff --git a/build/azure-pipelines/cli/cli-compile.yml b/build/azure-pipelines/cli/cli-compile.yml index e77ba78a999f..a5d8bdc1a2c3 100644 --- a/build/azure-pipelines/cli/cli-compile.yml +++ b/build/azure-pipelines/cli/cli-compile.yml @@ -42,7 +42,6 @@ steps: - script: | set -e if [ -n "$SYSROOT_ARCH" ]; then - export VSCODE_SYSROOT_PREFIX='-glibc-2.17' export VSCODE_SYSROOT_DIR=$(Build.SourcesDirectory)/.build/sysroots node -e '(async () => { const { getVSCodeSysroot } = require("../build/linux/debian/install-sysroot.js"); await getVSCodeSysroot(process.env["SYSROOT_ARCH"]); })()' if [ "$SYSROOT_ARCH" == "arm64" ]; then @@ -73,7 +72,7 @@ steps: # verify glibc requirement if [ -n "$SYSROOT_ARCH" ]; then - glibc_version="2.17" + glibc_version="2.28" while IFS= read -r line; do if [[ $line == *"GLIBC_"* ]]; then version=$(echo "$line" | awk '{print $5}' | tr -d '()') @@ -83,8 +82,8 @@ steps: fi fi done < <("$OBJDUMP" -T "$PWD/target/${{ parameters.VSCODE_CLI_TARGET }}/release/code") - if [[ "$glibc_version" != "2.17" ]]; then - echo "Error: binary has dependency on GLIBC > 2.17, found $glibc_version" + if [[ "$glibc_version" != "2.28" ]]; then + echo "Error: binary has dependency on GLIBC > 2.28, found $glibc_version" exit 1 fi fi @@ -108,6 +107,18 @@ steps: ${{ pair.key }}: ${{ pair.value }} - ${{ if contains(parameters.VSCODE_CLI_TARGET, '-windows-') }}: + - task: PublishSymbols@2 + inputs: + IndexSources: false + SymbolsFolder: $(Build.SourcesDirectory)/cli/target/${{ parameters.VSCODE_CLI_TARGET }}/release + SearchPattern: 'code.pdb' + SymbolServerType: TeamServices + SymbolsProduct: 'code' + ArtifactServices.Symbol.AccountName: microsoft + ArtifactServices.Symbol.PAT: $(System.AccessToken) + ArtifactServices.Symbol.UseAAD: false + displayName: Publish Symbols + - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" diff --git a/build/azure-pipelines/cli/cli-darwin-sign.yml b/build/azure-pipelines/cli/cli-darwin-sign.yml index b6dc424d690b..ba8150651a78 100644 --- a/build/azure-pipelines/cli/cli-darwin-sign.yml +++ b/build/azure-pipelines/cli/cli-darwin-sign.yml @@ -4,20 +4,21 @@ parameters: default: [] steps: - - task: AzureKeyVault@2 - displayName: "Azure Key Vault: Get Secrets" - inputs: - azureSubscription: "vscode-builds-subscription" - KeyVaultName: vscode-build-secrets - SecretsFilter: "ESRP-PKI,esrp-aad-username,esrp-aad-password" - - task: UseDotNet@2 inputs: version: 6.x - - task: EsrpClientTool@1 - continueOnError: true - displayName: Download ESRPClient + - task: EsrpCodeSigning@5 + inputs: + UseMSIAuthentication: true + ConnectedServiceName: vscode-esrp + AppRegistrationClientId: $(ESRP_CLIENT_ID) + AppRegistrationTenantId: $(ESRP_TENANT_ID) + AuthAKVName: vscode-esrp + AuthSignCertName: esrp-sign + FolderPath: . + Pattern: noop + displayName: 'Install ESRP Tooling' - ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}: - task: DownloadPipelineArtifact@2 @@ -32,10 +33,14 @@ steps: archiveFilePatterns: $(Build.ArtifactStagingDirectory)/pkg/${{ target }}/*.zip destinationFolder: $(Build.ArtifactStagingDirectory)/sign/${{ target }} - - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll sign-darwin $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Build.ArtifactStagingDirectory)/pkg "*.zip" + - script: node build/azure-pipelines/common/sign $(Agent.RootDirectory)/_tasks/EsrpCodeSigning_*/*/net6.0/esrpcli.dll sign-darwin $(Build.ArtifactStagingDirectory)/pkg "*.zip" + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) displayName: Codesign - - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll notarize-darwin $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Build.ArtifactStagingDirectory)/pkg "*.zip" + - script: node build/azure-pipelines/common/sign $(Agent.RootDirectory)/_tasks/EsrpCodeSigning_*/*/net6.0/esrpcli.dll notarize-darwin $(Build.ArtifactStagingDirectory)/pkg "*.zip" + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) displayName: Notarize - ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}: diff --git a/build/azure-pipelines/cli/cli-win32-sign.yml b/build/azure-pipelines/cli/cli-win32-sign.yml index e39df13c998e..bc711bec4a73 100644 --- a/build/azure-pipelines/cli/cli-win32-sign.yml +++ b/build/azure-pipelines/cli/cli-win32-sign.yml @@ -4,19 +4,29 @@ parameters: default: [] steps: - - task: AzureKeyVault@2 - displayName: "Azure Key Vault: Get Secrets" - inputs: - azureSubscription: "vscode-builds-subscription" - KeyVaultName: vscode-build-secrets - SecretsFilter: "ESRP-PKI,esrp-aad-username,esrp-aad-password" - - task: UseDotNet@2 inputs: version: 6.x - - task: EsrpClientTool@1 - displayName: "Use ESRP client" + - task: EsrpCodeSigning@5 + inputs: + UseMSIAuthentication: true + ConnectedServiceName: vscode-esrp + AppRegistrationClientId: $(ESRP_CLIENT_ID) + AppRegistrationTenantId: $(ESRP_TENANT_ID) + AuthAKVName: vscode-esrp + AuthSignCertName: esrp-sign + FolderPath: . + Pattern: noop + displayName: 'Install ESRP Tooling' + + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $EsrpCodeSigningTool = (gci -directory -filter EsrpCodeSigning_* $(Agent.RootDirectory)\_tasks | Select-Object -last 1).FullName + $Version = (gci -directory $EsrpCodeSigningTool | Select-Object -last 1).FullName + echo "##vso[task.setvariable variable=EsrpCliDllPath]$Version\net6.0\esrpcli.dll" + displayName: Find ESRP CLI - ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}: - task: DownloadPipelineArtifact@2 @@ -31,18 +41,9 @@ steps: archiveFilePatterns: $(Build.ArtifactStagingDirectory)/pkg/${{ target }}/*.zip destinationFolder: $(Build.ArtifactStagingDirectory)/sign/${{ target }} - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $EsrpClientTool = (gci -directory -filter EsrpClientTool_* $(Agent.RootDirectory)\_tasks | Select-Object -last 1).FullName - $EsrpCliZip = (gci -recurse -filter esrpcli.*.zip $EsrpClientTool | Select-Object -last 1).FullName - mkdir -p $(Agent.TempDirectory)\esrpcli - Expand-Archive -Path $EsrpCliZip -DestinationPath $(Agent.TempDirectory)\esrpcli - $EsrpCliDllPath = (gci -recurse -filter esrpcli.dll $(Agent.TempDirectory)\esrpcli | Select-Object -last 1).FullName - echo "##vso[task.setvariable variable=EsrpCliDllPath]$EsrpCliDllPath" - displayName: Find ESRP CLI - - - powershell: node build\azure-pipelines\common\sign $env:EsrpCliDllPath sign-windows $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Build.ArtifactStagingDirectory)/sign "*.exe" + - powershell: node build\azure-pipelines\common\sign $env:EsrpCliDllPath sign-windows $(Build.ArtifactStagingDirectory)/sign "*.exe" + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) displayName: Codesign - ${{ each target in parameters.VSCODE_CLI_ARTIFACTS }}: diff --git a/build/azure-pipelines/cli/install-rust-posix.yml b/build/azure-pipelines/cli/install-rust-posix.yml index fee56e028f72..0607cde33e56 100644 --- a/build/azure-pipelines/cli/install-rust-posix.yml +++ b/build/azure-pipelines/cli/install-rust-posix.yml @@ -1,7 +1,7 @@ parameters: - name: channel type: string - default: 1.81 + default: 1.85 - name: targets default: [] type: object diff --git a/build/azure-pipelines/cli/install-rust-win32.yml b/build/azure-pipelines/cli/install-rust-win32.yml index 45a1cfd188e1..bff114fccd07 100644 --- a/build/azure-pipelines/cli/install-rust-win32.yml +++ b/build/azure-pipelines/cli/install-rust-win32.yml @@ -1,7 +1,7 @@ parameters: - name: channel type: string - default: 1.81 + default: 1.85 - name: targets default: [] type: object diff --git a/build/azure-pipelines/common/computeBuiltInDepsCacheKey.js b/build/azure-pipelines/common/computeBuiltInDepsCacheKey.js index 2d747f56cc73..10fa9087454f 100644 --- a/build/azure-pipelines/common/computeBuiltInDepsCacheKey.js +++ b/build/azure-pipelines/common/computeBuiltInDepsCacheKey.js @@ -3,12 +3,15 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = require("fs"); -const path = require("path"); -const crypto = require("crypto"); -const productjson = JSON.parse(fs.readFileSync(path.join(__dirname, '../../../product.json'), 'utf8')); -const shasum = crypto.createHash('sha256'); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const crypto_1 = __importDefault(require("crypto")); +const productjson = JSON.parse(fs_1.default.readFileSync(path_1.default.join(__dirname, '../../../product.json'), 'utf8')); +const shasum = crypto_1.default.createHash('sha256'); for (const ext of productjson.builtInExtensions) { shasum.update(`${ext.name}@${ext.version}`); } diff --git a/build/azure-pipelines/common/computeBuiltInDepsCacheKey.ts b/build/azure-pipelines/common/computeBuiltInDepsCacheKey.ts index 53d6c501ea9a..8abaaccb6543 100644 --- a/build/azure-pipelines/common/computeBuiltInDepsCacheKey.ts +++ b/build/azure-pipelines/common/computeBuiltInDepsCacheKey.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; -import * as crypto from 'crypto'; +import fs from 'fs'; +import path from 'path'; +import crypto from 'crypto'; const productjson = JSON.parse(fs.readFileSync(path.join(__dirname, '../../../product.json'), 'utf8')); const shasum = crypto.createHash('sha256'); diff --git a/build/azure-pipelines/common/computeNodeModulesCacheKey.js b/build/azure-pipelines/common/computeNodeModulesCacheKey.js index 976e096fad26..c09c13be9d42 100644 --- a/build/azure-pipelines/common/computeNodeModulesCacheKey.js +++ b/build/azure-pipelines/common/computeNodeModulesCacheKey.js @@ -3,21 +3,24 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = require("fs"); -const path = require("path"); -const crypto = require("crypto"); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const crypto_1 = __importDefault(require("crypto")); const { dirs } = require('../../npm/dirs'); -const ROOT = path.join(__dirname, '../../../'); -const shasum = crypto.createHash('sha256'); -shasum.update(fs.readFileSync(path.join(ROOT, 'build/.cachesalt'))); -shasum.update(fs.readFileSync(path.join(ROOT, '.npmrc'))); -shasum.update(fs.readFileSync(path.join(ROOT, 'build', '.npmrc'))); -shasum.update(fs.readFileSync(path.join(ROOT, 'remote', '.npmrc'))); +const ROOT = path_1.default.join(__dirname, '../../../'); +const shasum = crypto_1.default.createHash('sha256'); +shasum.update(fs_1.default.readFileSync(path_1.default.join(ROOT, 'build/.cachesalt'))); +shasum.update(fs_1.default.readFileSync(path_1.default.join(ROOT, '.npmrc'))); +shasum.update(fs_1.default.readFileSync(path_1.default.join(ROOT, 'build', '.npmrc'))); +shasum.update(fs_1.default.readFileSync(path_1.default.join(ROOT, 'remote', '.npmrc'))); // Add `package.json` and `package-lock.json` files for (const dir of dirs) { - const packageJsonPath = path.join(ROOT, dir, 'package.json'); - const packageJson = JSON.parse(fs.readFileSync(packageJsonPath).toString()); + const packageJsonPath = path_1.default.join(ROOT, dir, 'package.json'); + const packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath).toString()); const relevantPackageJsonSections = { dependencies: packageJson.dependencies, devDependencies: packageJson.devDependencies, @@ -26,8 +29,8 @@ for (const dir of dirs) { distro: packageJson.distro }; shasum.update(JSON.stringify(relevantPackageJsonSections)); - const packageLockPath = path.join(ROOT, dir, 'package-lock.json'); - shasum.update(fs.readFileSync(packageLockPath)); + const packageLockPath = path_1.default.join(ROOT, dir, 'package-lock.json'); + shasum.update(fs_1.default.readFileSync(packageLockPath)); } // Add any other command line arguments for (let i = 2; i < process.argv.length; i++) { diff --git a/build/azure-pipelines/common/computeNodeModulesCacheKey.ts b/build/azure-pipelines/common/computeNodeModulesCacheKey.ts index 0940c929b540..57b35dc78de5 100644 --- a/build/azure-pipelines/common/computeNodeModulesCacheKey.ts +++ b/build/azure-pipelines/common/computeNodeModulesCacheKey.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; -import * as crypto from 'crypto'; +import fs from 'fs'; +import path from 'path'; +import crypto from 'crypto'; const { dirs } = require('../../npm/dirs'); const ROOT = path.join(__dirname, '../../../'); diff --git a/build/azure-pipelines/common/createBuild.js b/build/azure-pipelines/common/createBuild.js index 29c3448edc19..c605ed6218ef 100644 --- a/build/azure-pipelines/common/createBuild.js +++ b/build/azure-pipelines/common/createBuild.js @@ -40,7 +40,7 @@ async function main() { assets: [], updates: {} }; - const aadCredentials = new identity_1.ClientSecretCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_CLIENT_SECRET']); + const aadCredentials = new identity_1.ClientAssertionCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], () => Promise.resolve(process.env['AZURE_ID_TOKEN'])); const client = new cosmos_1.CosmosClient({ endpoint: process.env['AZURE_DOCUMENTDB_ENDPOINT'], aadCredentials }); const scripts = client.database('builds').container(quality).scripts; await (0, retry_1.retry)(() => scripts.storedProcedure('createBuild').execute('', [{ ...build, _partitionKey: '' }])); diff --git a/build/azure-pipelines/common/createBuild.ts b/build/azure-pipelines/common/createBuild.ts index afc5f59003a2..6afeb01e6cc7 100644 --- a/build/azure-pipelines/common/createBuild.ts +++ b/build/azure-pipelines/common/createBuild.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ClientSecretCredential } from '@azure/identity'; +import { ClientAssertionCredential } from '@azure/identity'; import { CosmosClient } from '@azure/cosmos'; import { retry } from './retry'; @@ -47,7 +47,7 @@ async function main(): Promise { updates: {} }; - const aadCredentials = new ClientSecretCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, process.env['AZURE_CLIENT_SECRET']!); + const aadCredentials = new ClientAssertionCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, () => Promise.resolve(process.env['AZURE_ID_TOKEN']!)); const client = new CosmosClient({ endpoint: process.env['AZURE_DOCUMENTDB_ENDPOINT']!, aadCredentials }); const scripts = client.database('builds').container(quality).scripts; await retry(() => scripts.storedProcedure('createBuild').execute('', [{ ...build, _partitionKey: '' }])); diff --git a/build/azure-pipelines/common/getPublishAuthTokens.js b/build/azure-pipelines/common/getPublishAuthTokens.js new file mode 100644 index 000000000000..9c22e9ad94bc --- /dev/null +++ b/build/azure-pipelines/common/getPublishAuthTokens.js @@ -0,0 +1,47 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getAccessToken = getAccessToken; +const msal_node_1 = require("@azure/msal-node"); +function e(name) { + const result = process.env[name]; + if (typeof result !== 'string') { + throw new Error(`Missing env: ${name}`); + } + return result; +} +async function getAccessToken(endpoint, tenantId, clientId, idToken) { + const app = new msal_node_1.ConfidentialClientApplication({ + auth: { + clientId, + authority: `https://login.microsoftonline.com/${tenantId}`, + clientAssertion: idToken + } + }); + const result = await app.acquireTokenByClientCredential({ scopes: [`${endpoint}.default`] }); + if (!result) { + throw new Error('Failed to get access token'); + } + return { + token: result.accessToken, + expiresOnTimestamp: result.expiresOn.getTime(), + refreshAfterTimestamp: result.refreshOn?.getTime() + }; +} +async function main() { + const cosmosDBAccessToken = await getAccessToken(e('AZURE_DOCUMENTDB_ENDPOINT'), e('AZURE_TENANT_ID'), e('AZURE_CLIENT_ID'), e('AZURE_ID_TOKEN')); + const blobServiceAccessToken = await getAccessToken(`https://${e('VSCODE_STAGING_BLOB_STORAGE_ACCOUNT_NAME')}.blob.core.windows.net/`, process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_ID_TOKEN']); + console.log(JSON.stringify({ cosmosDBAccessToken, blobServiceAccessToken })); +} +if (require.main === module) { + main().then(() => { + process.exit(0); + }, err => { + console.error(err); + process.exit(1); + }); +} +//# sourceMappingURL=getPublishAuthTokens.js.map \ No newline at end of file diff --git a/build/azure-pipelines/common/getPublishAuthTokens.ts b/build/azure-pipelines/common/getPublishAuthTokens.ts new file mode 100644 index 000000000000..68e76de1a832 --- /dev/null +++ b/build/azure-pipelines/common/getPublishAuthTokens.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { AccessToken } from '@azure/core-auth'; +import { ConfidentialClientApplication } from '@azure/msal-node'; + +function e(name: string): string { + const result = process.env[name]; + + if (typeof result !== 'string') { + throw new Error(`Missing env: ${name}`); + } + + return result; +} + +export async function getAccessToken(endpoint: string, tenantId: string, clientId: string, idToken: string): Promise { + const app = new ConfidentialClientApplication({ + auth: { + clientId, + authority: `https://login.microsoftonline.com/${tenantId}`, + clientAssertion: idToken + } + }); + + const result = await app.acquireTokenByClientCredential({ scopes: [`${endpoint}.default`] }); + + if (!result) { + throw new Error('Failed to get access token'); + } + + return { + token: result.accessToken, + expiresOnTimestamp: result.expiresOn!.getTime(), + refreshAfterTimestamp: result.refreshOn?.getTime() + }; +} + +async function main() { + const cosmosDBAccessToken = await getAccessToken(e('AZURE_DOCUMENTDB_ENDPOINT')!, e('AZURE_TENANT_ID')!, e('AZURE_CLIENT_ID')!, e('AZURE_ID_TOKEN')!); + const blobServiceAccessToken = await getAccessToken(`https://${e('VSCODE_STAGING_BLOB_STORAGE_ACCOUNT_NAME')}.blob.core.windows.net/`, process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, process.env['AZURE_ID_TOKEN']!); + console.log(JSON.stringify({ cosmosDBAccessToken, blobServiceAccessToken })); +} + +if (require.main === module) { + main().then(() => { + process.exit(0); + }, err => { + console.error(err); + process.exit(1); + }); +} diff --git a/build/azure-pipelines/common/listNodeModules.js b/build/azure-pipelines/common/listNodeModules.js index aaa44c51a12d..301b5f930b61 100644 --- a/build/azure-pipelines/common/listNodeModules.js +++ b/build/azure-pipelines/common/listNodeModules.js @@ -3,16 +3,19 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = require("fs"); -const path = require("path"); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); if (process.argv.length !== 3) { console.error('Usage: node listNodeModules.js OUTPUT_FILE'); process.exit(-1); } -const ROOT = path.join(__dirname, '../../../'); +const ROOT = path_1.default.join(__dirname, '../../../'); function findNodeModulesFiles(location, inNodeModules, result) { - const entries = fs.readdirSync(path.join(ROOT, location)); + const entries = fs_1.default.readdirSync(path_1.default.join(ROOT, location)); for (const entry of entries) { const entryPath = `${location}/${entry}`; if (/(^\/out)|(^\/src$)|(^\/.git$)|(^\/.build$)/.test(entryPath)) { @@ -20,7 +23,7 @@ function findNodeModulesFiles(location, inNodeModules, result) { } let stat; try { - stat = fs.statSync(path.join(ROOT, entryPath)); + stat = fs_1.default.statSync(path_1.default.join(ROOT, entryPath)); } catch (err) { continue; @@ -37,5 +40,5 @@ function findNodeModulesFiles(location, inNodeModules, result) { } const result = []; findNodeModulesFiles('', false, result); -fs.writeFileSync(process.argv[2], result.join('\n') + '\n'); +fs_1.default.writeFileSync(process.argv[2], result.join('\n') + '\n'); //# sourceMappingURL=listNodeModules.js.map \ No newline at end of file diff --git a/build/azure-pipelines/common/listNodeModules.ts b/build/azure-pipelines/common/listNodeModules.ts index aca461f8b5f2..fb85b25cfd1b 100644 --- a/build/azure-pipelines/common/listNodeModules.ts +++ b/build/azure-pipelines/common/listNodeModules.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; +import fs from 'fs'; +import path from 'path'; if (process.argv.length !== 3) { console.error('Usage: node listNodeModules.js OUTPUT_FILE'); diff --git a/build/azure-pipelines/common/publish.js b/build/azure-pipelines/common/publish.js index aa185ed83699..8e052881b2ca 100644 --- a/build/azure-pipelines/common/publish.js +++ b/build/azure-pipelines/common/publish.js @@ -3,19 +3,25 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = require("fs"); -const path = require("path"); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); const stream_1 = require("stream"); const promises_1 = require("node:stream/promises"); -const yauzl = require("yauzl"); -const crypto = require("crypto"); +const yauzl_1 = __importDefault(require("yauzl")); +const crypto_1 = __importDefault(require("crypto")); const retry_1 = require("./retry"); const cosmos_1 = require("@azure/cosmos"); -const identity_1 = require("@azure/identity"); -const cp = require("child_process"); -const os = require("os"); +const child_process_1 = __importDefault(require("child_process")); +const os_1 = __importDefault(require("os")); const node_worker_threads_1 = require("node:worker_threads"); +const msal_node_1 = require("@azure/msal-node"); +const storage_blob_1 = require("@azure/storage-blob"); +const jws_1 = __importDefault(require("jws")); +const node_timers_1 = require("node:timers"); function e(name) { const result = process.env[name]; if (typeof result !== 'string') { @@ -23,260 +29,264 @@ function e(name) { } return result; } -class Temp { - _files = []; - tmpNameSync() { - const file = path.join(os.tmpdir(), crypto.randomBytes(20).toString('hex')); - this._files.push(file); - return file; - } - dispose() { - for (const file of this._files) { - try { - fs.unlinkSync(file); - } - catch (err) { - // noop - } - } +function hashStream(hashName, stream) { + return new Promise((c, e) => { + const shasum = crypto_1.default.createHash(hashName); + stream + .on('data', shasum.update.bind(shasum)) + .on('error', e) + .on('close', () => c(shasum.digest())); + }); +} +var StatusCode; +(function (StatusCode) { + StatusCode["Pass"] = "pass"; + StatusCode["Aborted"] = "aborted"; + StatusCode["Inprogress"] = "inprogress"; + StatusCode["FailCanRetry"] = "failCanRetry"; + StatusCode["FailDoNotRetry"] = "failDoNotRetry"; + StatusCode["PendingAnalysis"] = "pendingAnalysis"; + StatusCode["Cancelled"] = "cancelled"; +})(StatusCode || (StatusCode = {})); +function getCertificateBuffer(input) { + return Buffer.from(input.replace(/-----BEGIN CERTIFICATE-----|-----END CERTIFICATE-----|\n/g, ''), 'base64'); +} +function getThumbprint(input, algorithm) { + const buffer = getCertificateBuffer(input); + return crypto_1.default.createHash(algorithm).update(buffer).digest(); +} +function getKeyFromPFX(pfx) { + const pfxCertificatePath = path_1.default.join(os_1.default.tmpdir(), 'cert.pfx'); + const pemKeyPath = path_1.default.join(os_1.default.tmpdir(), 'key.pem'); + try { + const pfxCertificate = Buffer.from(pfx, 'base64'); + fs_1.default.writeFileSync(pfxCertificatePath, pfxCertificate); + child_process_1.default.execSync(`openssl pkcs12 -in "${pfxCertificatePath}" -nocerts -nodes -out "${pemKeyPath}" -passin pass:`); + const raw = fs_1.default.readFileSync(pemKeyPath, 'utf-8'); + const result = raw.match(/-----BEGIN PRIVATE KEY-----[\s\S]+?-----END PRIVATE KEY-----/g)[0]; + return result; + } + finally { + fs_1.default.rmSync(pfxCertificatePath, { force: true }); + fs_1.default.rmSync(pemKeyPath, { force: true }); } } -function isCreateProvisionedFilesErrorResponse(response) { - return response?.ErrorDetails?.Code !== undefined; +function getCertificatesFromPFX(pfx) { + const pfxCertificatePath = path_1.default.join(os_1.default.tmpdir(), 'cert.pfx'); + const pemCertificatePath = path_1.default.join(os_1.default.tmpdir(), 'cert.pem'); + try { + const pfxCertificate = Buffer.from(pfx, 'base64'); + fs_1.default.writeFileSync(pfxCertificatePath, pfxCertificate); + child_process_1.default.execSync(`openssl pkcs12 -in "${pfxCertificatePath}" -nokeys -out "${pemCertificatePath}" -passin pass:`); + const raw = fs_1.default.readFileSync(pemCertificatePath, 'utf-8'); + const matches = raw.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g); + return matches ? matches.reverse() : []; + } + finally { + fs_1.default.rmSync(pfxCertificatePath, { force: true }); + fs_1.default.rmSync(pemCertificatePath, { force: true }); + } } -class ProvisionService { +class ESRPReleaseService { log; + clientId; accessToken; - constructor(log, accessToken) { + requestSigningCertificates; + requestSigningKey; + containerClient; + stagingSasToken; + static async create(log, tenantId, clientId, authCertificatePfx, requestSigningCertificatePfx, containerClient, stagingSasToken) { + const authKey = getKeyFromPFX(authCertificatePfx); + const authCertificate = getCertificatesFromPFX(authCertificatePfx)[0]; + const requestSigningKey = getKeyFromPFX(requestSigningCertificatePfx); + const requestSigningCertificates = getCertificatesFromPFX(requestSigningCertificatePfx); + const app = new msal_node_1.ConfidentialClientApplication({ + auth: { + clientId, + authority: `https://login.microsoftonline.com/${tenantId}`, + clientCertificate: { + thumbprintSha256: getThumbprint(authCertificate, 'sha256').toString('hex'), + privateKey: authKey, + x5c: authCertificate + } + } + }); + const response = await app.acquireTokenByClientCredential({ + scopes: ['https://api.esrp.microsoft.com/.default'] + }); + return new ESRPReleaseService(log, clientId, response.accessToken, requestSigningCertificates, requestSigningKey, containerClient, stagingSasToken); + } + static API_URL = 'https://api.esrp.microsoft.com/api/v3/releaseservices/clients/'; + constructor(log, clientId, accessToken, requestSigningCertificates, requestSigningKey, containerClient, stagingSasToken) { this.log = log; + this.clientId = clientId; this.accessToken = accessToken; + this.requestSigningCertificates = requestSigningCertificates; + this.requestSigningKey = requestSigningKey; + this.containerClient = containerClient; + this.stagingSasToken = stagingSasToken; + } + async createRelease(version, filePath, friendlyFileName) { + const correlationId = crypto_1.default.randomUUID(); + const blobClient = this.containerClient.getBlockBlobClient(correlationId); + this.log(`Uploading ${filePath} to ${blobClient.url}`); + await blobClient.uploadFile(filePath); + this.log('Uploaded blob successfully'); + try { + this.log(`Submitting release for ${version}: ${filePath}`); + const submitReleaseResult = await this.submitRelease(version, filePath, friendlyFileName, correlationId, blobClient); + this.log(`Successfully submitted release ${submitReleaseResult.operationId}. Polling for completion...`); + // Poll every 5 seconds, wait 60 minutes max -> poll 60/5*60=720 times + for (let i = 0; i < 720; i++) { + await new Promise(c => setTimeout(c, 5000)); + const releaseStatus = await this.getReleaseStatus(submitReleaseResult.operationId); + if (releaseStatus.status === 'pass') { + break; + } + else if (releaseStatus.status === 'aborted') { + this.log(JSON.stringify(releaseStatus)); + throw new Error(`Release was aborted`); + } + else if (releaseStatus.status !== 'inprogress') { + this.log(JSON.stringify(releaseStatus)); + throw new Error(`Unknown error when polling for release`); + } + } + const releaseDetails = await this.getReleaseDetails(submitReleaseResult.operationId); + if (releaseDetails.status !== 'pass') { + throw new Error(`Timed out waiting for release: ${JSON.stringify(releaseDetails)}`); + } + this.log('Successfully created release:', releaseDetails.files[0].fileDownloadDetails[0].downloadUrl); + return releaseDetails.files[0].fileDownloadDetails[0].downloadUrl; + } + finally { + this.log(`Deleting blob ${blobClient.url}`); + await blobClient.delete(); + this.log('Deleted blob successfully'); + } } - async provision(releaseId, fileId, fileName) { - const body = JSON.stringify({ - ReleaseId: releaseId, - PortalName: 'VSCode', - PublisherCode: 'VSCode', - ProvisionedFilesCollection: [{ - PublisherKey: fileId, - IsStaticFriendlyFileName: true, - FriendlyFileName: fileName, - MaxTTL: '1440', - CdnMappings: ['ECN'] + async submitRelease(version, filePath, friendlyFileName, correlationId, blobClient) { + const size = fs_1.default.statSync(filePath).size; + const hash = await hashStream('sha256', fs_1.default.createReadStream(filePath)); + const blobUrl = `${blobClient.url}?${this.stagingSasToken}`; + const message = { + customerCorrelationId: correlationId, + esrpCorrelationId: correlationId, + driEmail: ['joao.moreno@microsoft.com'], + createdBy: { userPrincipalName: 'jomo@microsoft.com' }, + owners: [{ owner: { userPrincipalName: 'jomo@microsoft.com' } }], + approvers: [{ approver: { userPrincipalName: 'jomo@microsoft.com' }, isAutoApproved: true, isMandatory: false }], + releaseInfo: { + title: 'VS Code', + properties: { + 'ReleaseContentType': 'InstallPackage' + }, + minimumNumberOfApprovers: 1 + }, + productInfo: { + name: 'VS Code', + version, + description: 'VS Code' + }, + accessPermissionsInfo: { + mainPublisher: 'VSCode', + channelDownloadEntityDetails: { + AllDownloadEntities: ['VSCode'] + } + }, + routingInfo: { + intent: 'filedownloadlinkgeneration' + }, + files: [{ + name: path_1.default.basename(filePath), + friendlyFileName, + tenantFileLocation: blobUrl, + tenantFileLocationType: 'AzureBlob', + sourceLocation: { + type: 'azureBlob', + blobUrl + }, + hashType: 'sha256', + hash: Array.from(hash), + sizeInBytes: size }] + }; + message.jwsToken = await this.generateJwsToken(message); + const res = await fetch(`${ESRPReleaseService.API_URL}${this.clientId}/workflows/release/operations`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${this.accessToken}` + }, + body: JSON.stringify(message) }); - this.log(`Provisioning ${fileName} (releaseId: ${releaseId}, fileId: ${fileId})...`); - const res = await (0, retry_1.retry)(() => this.request('POST', '/api/v2/ProvisionedFiles/CreateProvisionedFiles', { body })); - if (isCreateProvisionedFilesErrorResponse(res) && res.ErrorDetails.Code === 'FriendlyFileNameAlreadyProvisioned') { - this.log(`File already provisioned (most likley due to a re-run), skipping: ${fileName}`); - return; - } - if (!res.IsSuccess) { - throw new Error(`Failed to submit provisioning request: ${JSON.stringify(res.ErrorDetails)}`); + if (!res.ok) { + const text = await res.text(); + throw new Error(`Failed to submit release: ${res.statusText}\n${text}`); } - this.log(`Successfully provisioned ${fileName}`); + return await res.json(); } - async request(method, url, options) { - const opts = { - method, - body: options?.body, + async getReleaseStatus(releaseId) { + const url = `${ESRPReleaseService.API_URL}${this.clientId}/workflows/release/operations/grs/${releaseId}`; + const res = await fetch(url, { headers: { - Authorization: `Bearer ${this.accessToken}`, - 'Content-Type': 'application/json' + 'Authorization': `Bearer ${this.accessToken}` } - }; - const res = await fetch(`https://dsprovisionapi.microsoft.com${url}`, opts); - // 400 normally means the request is bad or something is already provisioned, so we will return as retries are useless - // Otherwise log the text body and headers. We do text because some responses are not JSON. - if ((!res.ok || res.status < 200 || res.status >= 500) && res.status !== 400) { - throw new Error(`Unexpected status code: ${res.status}\nResponse Headers: ${JSON.stringify(res.headers)}\nBody Text: ${await res.text()}`); + }); + if (!res.ok) { + const text = await res.text(); + throw new Error(`Failed to get release status: ${res.statusText}\n${text}`); } return await res.json(); } -} -function hashStream(hashName, stream) { - return new Promise((c, e) => { - const shasum = crypto.createHash(hashName); - stream - .on('data', shasum.update.bind(shasum)) - .on('error', e) - .on('close', () => c(shasum.digest('hex'))); - }); -} -class ESRPClient { - log; - tmp; - authPath; - constructor(log, tmp, tenantId, clientId, authCertSubjectName, requestSigningCertSubjectName) { - this.log = log; - this.tmp = tmp; - this.authPath = this.tmp.tmpNameSync(); - fs.writeFileSync(this.authPath, JSON.stringify({ - Version: '1.0.0', - AuthenticationType: 'AAD_CERT', - TenantId: tenantId, - ClientId: clientId, - AuthCert: { - SubjectName: authCertSubjectName, - StoreLocation: 'LocalMachine', - StoreName: 'My', - SendX5c: 'true' - }, - RequestSigningCert: { - SubjectName: requestSigningCertSubjectName, - StoreLocation: 'LocalMachine', - StoreName: 'My' - } - })); - } - async release(version, filePath) { - this.log(`Submitting release for ${version}: ${filePath}`); - const submitReleaseResult = await this.SubmitRelease(version, filePath); - if (submitReleaseResult.submissionResponse.statusCode !== 'pass') { - throw new Error(`Unexpected status code: ${submitReleaseResult.submissionResponse.statusCode}`); - } - const releaseId = submitReleaseResult.submissionResponse.operationId; - this.log(`Successfully submitted release ${releaseId}. Polling for completion...`); - let details; - // Poll every 5 seconds, wait 60 minutes max -> poll 60/5*60=720 times - for (let i = 0; i < 720; i++) { - details = await this.ReleaseDetails(releaseId); - if (details.releaseDetails[0].statusCode === 'pass') { - break; - } - else if (details.releaseDetails[0].statusCode !== 'inprogress') { - throw new Error(`Failed to submit release: ${JSON.stringify(details)}`); + async getReleaseDetails(releaseId) { + const url = `${ESRPReleaseService.API_URL}${this.clientId}/workflows/release/operations/grd/${releaseId}`; + const res = await fetch(url, { + headers: { + 'Authorization': `Bearer ${this.accessToken}` } - await new Promise(c => setTimeout(c, 5000)); - } - if (details.releaseDetails[0].statusCode !== 'pass') { - throw new Error(`Timed out waiting for release ${releaseId}: ${JSON.stringify(details)}`); + }); + if (!res.ok) { + const text = await res.text(); + throw new Error(`Failed to get release status: ${res.statusText}\n${text}`); } - const fileId = details.releaseDetails[0].fileDetails[0].publisherKey; - this.log('Release completed successfully with fileId: ', fileId); - return { releaseId, fileId }; - } - async SubmitRelease(version, filePath) { - const policyPath = this.tmp.tmpNameSync(); - fs.writeFileSync(policyPath, JSON.stringify({ - Version: '1.0.0', - Audience: 'InternalLimited', - Intent: 'distribution', - ContentType: 'InstallPackage' - })); - const inputPath = this.tmp.tmpNameSync(); - const size = fs.statSync(filePath).size; - const istream = fs.createReadStream(filePath); - const sha256 = await hashStream('sha256', istream); - fs.writeFileSync(inputPath, JSON.stringify({ - Version: '1.0.0', - ReleaseInfo: { - ReleaseMetadata: { - Title: 'VS Code', - Properties: { - ReleaseContentType: 'InstallPackage' - }, - MinimumNumberOfApprovers: 1 - }, - ProductInfo: { - Name: 'VS Code', - Version: version, - Description: path.basename(filePath, path.extname(filePath)), - }, - Owners: [ - { - Owner: { - UserPrincipalName: 'jomo@microsoft.com' - } - } - ], - Approvers: [ - { - Approver: { - UserPrincipalName: 'jomo@microsoft.com' - }, - IsAutoApproved: true, - IsMandatory: false - } - ], - AccessPermissions: { - MainPublisher: 'VSCode', - ChannelDownloadEntityDetails: { - Consumer: ['VSCode'] - } - }, - CreatedBy: { - UserPrincipalName: 'jomo@microsoft.com' - } - }, - ReleaseBatches: [ - { - ReleaseRequestFiles: [ - { - SizeInBytes: size, - SourceHash: sha256, - HashType: 'SHA256', - SourceLocation: path.basename(filePath) - } - ], - SourceLocationType: 'UNC', - SourceRootDirectory: path.dirname(filePath), - DestinationLocationType: 'AzureBlob' - } - ] - })); - const outputPath = this.tmp.tmpNameSync(); - cp.execSync(`ESRPClient SubmitRelease -a ${this.authPath} -p ${policyPath} -i ${inputPath} -o ${outputPath}`, { stdio: 'inherit' }); - const output = fs.readFileSync(outputPath, 'utf8'); - return JSON.parse(output); - } - async ReleaseDetails(releaseId) { - const inputPath = this.tmp.tmpNameSync(); - fs.writeFileSync(inputPath, JSON.stringify({ - Version: '1.0.0', - OperationIds: [releaseId] - })); - const outputPath = this.tmp.tmpNameSync(); - cp.execSync(`ESRPClient ReleaseDetails -a ${this.authPath} -i ${inputPath} -o ${outputPath}`, { stdio: 'inherit' }); - const output = fs.readFileSync(outputPath, 'utf8'); - return JSON.parse(output); + return await res.json(); } -} -async function releaseAndProvision(log, releaseTenantId, releaseClientId, releaseAuthCertSubjectName, releaseRequestSigningCertSubjectName, provisionTenantId, provisionAADUsername, provisionAADPassword, version, quality, filePath) { - const fileName = `${quality}/${version}/${path.basename(filePath)}`; - const result = `${e('PRSS_CDN_URL')}/${fileName}`; - const res = await (0, retry_1.retry)(() => fetch(result)); - if (res.status === 200) { - log(`Already released and provisioned: ${result}`); - return result; + async generateJwsToken(message) { + return jws_1.default.sign({ + header: { + alg: 'RS256', + crit: ['exp', 'x5t'], + // Release service uses ticks, not seconds :roll_eyes: (https://stackoverflow.com/a/7968483) + exp: ((Date.now() + (6 * 60 * 1000)) * 10000) + 621355968000000000, + // Release service uses hex format, not base64url :roll_eyes: + x5t: getThumbprint(this.requestSigningCertificates[0], 'sha1').toString('hex'), + // Release service uses a '.' separated string, not an array of strings :roll_eyes: + x5c: this.requestSigningCertificates.map(c => getCertificateBuffer(c).toString('base64url')).join('.'), + }, + payload: message, + privateKey: this.requestSigningKey, + }); } - const tmp = new Temp(); - process.on('exit', () => tmp.dispose()); - const esrpclient = new ESRPClient(log, tmp, releaseTenantId, releaseClientId, releaseAuthCertSubjectName, releaseRequestSigningCertSubjectName); - const release = await esrpclient.release(version, filePath); - const credential = new identity_1.ClientSecretCredential(provisionTenantId, provisionAADUsername, provisionAADPassword); - const accessToken = await credential.getToken(['https://microsoft.onmicrosoft.com/DS.Provisioning.WebApi/.default']); - const service = new ProvisionService(log, accessToken.token); - await service.provision(release.releaseId, release.fileId, fileName); - return result; } class State { statePath; set = new Set(); constructor() { const pipelineWorkspacePath = e('PIPELINE_WORKSPACE'); - const previousState = fs.readdirSync(pipelineWorkspacePath) + const previousState = fs_1.default.readdirSync(pipelineWorkspacePath) .map(name => /^artifacts_processed_(\d+)$/.exec(name)) .filter((match) => !!match) .map(match => ({ name: match[0], attempt: Number(match[1]) })) .sort((a, b) => b.attempt - a.attempt)[0]; if (previousState) { - const previousStatePath = path.join(pipelineWorkspacePath, previousState.name, previousState.name + '.txt'); - fs.readFileSync(previousStatePath, 'utf8').split(/\n/).filter(name => !!name).forEach(name => this.set.add(name)); + const previousStatePath = path_1.default.join(pipelineWorkspacePath, previousState.name, previousState.name + '.txt'); + fs_1.default.readFileSync(previousStatePath, 'utf8').split(/\n/).filter(name => !!name).forEach(name => this.set.add(name)); } const stageAttempt = e('SYSTEM_STAGEATTEMPT'); - this.statePath = path.join(pipelineWorkspacePath, `artifacts_processed_${stageAttempt}`, `artifacts_processed_${stageAttempt}.txt`); - fs.mkdirSync(path.dirname(this.statePath), { recursive: true }); - fs.writeFileSync(this.statePath, [...this.set.values()].map(name => `${name}\n`).join('')); + this.statePath = path_1.default.join(pipelineWorkspacePath, `artifacts_processed_${stageAttempt}`, `artifacts_processed_${stageAttempt}.txt`); + fs_1.default.mkdirSync(path_1.default.dirname(this.statePath), { recursive: true }); + fs_1.default.writeFileSync(this.statePath, [...this.set.values()].map(name => `${name}\n`).join('')); } get size() { return this.set.size; @@ -286,7 +296,7 @@ class State { } add(name) { this.set.add(name); - fs.appendFileSync(this.statePath, `${name}\n`); + fs_1.default.appendFileSync(this.statePath, `${name}\n`); } [Symbol.iterator]() { return this.set[Symbol.iterator](); @@ -332,7 +342,7 @@ async function downloadArtifact(artifact, downloadPath) { if (!res.ok) { throw new Error(`Unexpected status code: ${res.status}`); } - await (0, promises_1.pipeline)(stream_1.Readable.fromWeb(res.body), fs.createWriteStream(downloadPath)); + await (0, promises_1.pipeline)(stream_1.Readable.fromWeb(res.body), fs_1.default.createWriteStream(downloadPath)); } finally { clearTimeout(timeout); @@ -340,7 +350,7 @@ async function downloadArtifact(artifact, downloadPath) { } async function unzip(packagePath, outputPath) { return new Promise((resolve, reject) => { - yauzl.open(packagePath, { lazyEntries: true, autoClose: true }, (err, zipfile) => { + yauzl_1.default.open(packagePath, { lazyEntries: true, autoClose: true }, (err, zipfile) => { if (err) { return reject(err); } @@ -354,9 +364,9 @@ async function unzip(packagePath, outputPath) { if (err) { return reject(err); } - const filePath = path.join(outputPath, entry.fileName); - fs.mkdirSync(path.dirname(filePath), { recursive: true }); - const ostream = fs.createWriteStream(filePath); + const filePath = path_1.default.join(outputPath, entry.fileName); + fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true }); + const ostream = fs_1.default.createWriteStream(filePath); ostream.on('finish', () => { result.push(filePath); zipfile.readEntry(); @@ -372,7 +382,7 @@ async function unzip(packagePath, outputPath) { }); } // Contains all of the logic for mapping details to our actual product names in CosmosDB -function getPlatform(product, os, arch, type, isLegacy) { +function getPlatform(product, os, arch, type) { switch (os) { case 'win32': switch (product) { @@ -417,12 +427,12 @@ function getPlatform(product, os, arch, type, isLegacy) { case 'client': return `linux-${arch}`; case 'server': - return isLegacy ? `server-linux-legacy-${arch}` : `server-linux-${arch}`; + return `server-linux-${arch}`; case 'web': if (arch === 'standalone') { return 'web-standalone'; } - return isLegacy ? `server-linux-legacy-${arch}-web` : `server-linux-${arch}-web`; + return `server-linux-${arch}-web`; default: throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); } @@ -473,33 +483,101 @@ function getRealType(type) { return type; } } -async function processArtifact(artifact, artifactFilePath) { +async function withLease(client, fn) { + const lease = client.getBlobLeaseClient(); + for (let i = 0; i < 360; i++) { // Try to get lease for 30 minutes + try { + await client.uploadData(new ArrayBuffer()); // blob needs to exist for lease to be acquired + await lease.acquireLease(60); + try { + const abortController = new AbortController(); + const refresher = new Promise((c, e) => { + abortController.signal.onabort = () => { + (0, node_timers_1.clearInterval)(interval); + c(); + }; + const interval = (0, node_timers_1.setInterval)(() => { + lease.renewLease().catch(err => { + (0, node_timers_1.clearInterval)(interval); + e(new Error('Failed to renew lease ' + err)); + }); + }, 30_000); + }); + const result = await Promise.race([fn(), refresher]); + abortController.abort(); + return result; + } + finally { + await lease.releaseLease(); + } + } + catch (err) { + if (err.statusCode !== 409 && err.statusCode !== 412) { + throw err; + } + await new Promise(c => setTimeout(c, 5000)); + } + } + throw new Error('Failed to acquire lease on blob after 30 minutes'); +} +async function processArtifact(artifact, filePath) { const log = (...args) => console.log(`[${artifact.name}]`, ...args); const match = /^vscode_(?[^_]+)_(?[^_]+)(?:_legacy)?_(?[^_]+)_(?[^_]+)$/.exec(artifact.name); if (!match) { throw new Error(`Invalid artifact name: ${artifact.name}`); } - // getPlatform needs the unprocessedType + const { cosmosDBAccessToken, blobServiceAccessToken } = JSON.parse(e('PUBLISH_AUTH_TOKENS')); const quality = e('VSCODE_QUALITY'); - const commit = e('BUILD_SOURCEVERSION'); - const { product, os, arch, unprocessedType } = match.groups; - const isLegacy = artifact.name.includes('_legacy'); - const platform = getPlatform(product, os, arch, unprocessedType, isLegacy); - const type = getRealType(unprocessedType); - const size = fs.statSync(artifactFilePath).size; - const stream = fs.createReadStream(artifactFilePath); - const [hash, sha256hash] = await Promise.all([hashStream('sha1', stream), hashStream('sha256', stream)]); // CodeQL [SM04514] Using SHA1 only for legacy reasons, we are actually only respecting SHA256 - const url = await releaseAndProvision(log, e('RELEASE_TENANT_ID'), e('RELEASE_CLIENT_ID'), e('RELEASE_AUTH_CERT_SUBJECT_NAME'), e('RELEASE_REQUEST_SIGNING_CERT_SUBJECT_NAME'), e('PROVISION_TENANT_ID'), e('PROVISION_AAD_USERNAME'), e('PROVISION_AAD_PASSWORD'), commit, quality, artifactFilePath); - const asset = { platform, type, url, hash, sha256hash, size, supportsFastUpdate: true }; - log('Creating asset...', JSON.stringify(asset, undefined, 2)); - await (0, retry_1.retry)(async (attempt) => { - log(`Creating asset in Cosmos DB (attempt ${attempt})...`); - const aadCredentials = new identity_1.ClientSecretCredential(e('AZURE_TENANT_ID'), e('AZURE_CLIENT_ID'), e('AZURE_CLIENT_SECRET')); - const client = new cosmos_1.CosmosClient({ endpoint: e('AZURE_DOCUMENTDB_ENDPOINT'), aadCredentials }); - const scripts = client.database('builds').container(quality).scripts; - await scripts.storedProcedure('createAsset').execute('', [commit, asset, true]); + const version = e('BUILD_SOURCEVERSION'); + const friendlyFileName = `${quality}/${version}/${path_1.default.basename(filePath)}`; + const blobServiceClient = new storage_blob_1.BlobServiceClient(`https://${e('VSCODE_STAGING_BLOB_STORAGE_ACCOUNT_NAME')}.blob.core.windows.net/`, { getToken: async () => blobServiceAccessToken }); + const leasesContainerClient = blobServiceClient.getContainerClient('leases'); + await leasesContainerClient.createIfNotExists(); + const leaseBlobClient = leasesContainerClient.getBlockBlobClient(friendlyFileName); + log(`Acquiring lease for: ${friendlyFileName}`); + await withLease(leaseBlobClient, async () => { + log(`Successfully acquired lease for: ${friendlyFileName}`); + const url = `${e('PRSS_CDN_URL')}/${friendlyFileName}`; + const res = await (0, retry_1.retry)(() => fetch(url)); + if (res.status === 200) { + log(`Already released and provisioned: ${url}`); + } + else { + const stagingContainerClient = blobServiceClient.getContainerClient('staging'); + await stagingContainerClient.createIfNotExists(); + const now = new Date().valueOf(); + const oneHour = 60 * 60 * 1000; + const oneHourAgo = new Date(now - oneHour); + const oneHourFromNow = new Date(now + oneHour); + const userDelegationKey = await blobServiceClient.getUserDelegationKey(oneHourAgo, oneHourFromNow); + const sasOptions = { containerName: 'staging', permissions: storage_blob_1.ContainerSASPermissions.from({ read: true }), startsOn: oneHourAgo, expiresOn: oneHourFromNow }; + const stagingSasToken = (0, storage_blob_1.generateBlobSASQueryParameters)(sasOptions, userDelegationKey, e('VSCODE_STAGING_BLOB_STORAGE_ACCOUNT_NAME')).toString(); + const releaseService = await ESRPReleaseService.create(log, e('RELEASE_TENANT_ID'), e('RELEASE_CLIENT_ID'), e('RELEASE_AUTH_CERT'), e('RELEASE_REQUEST_SIGNING_CERT'), stagingContainerClient, stagingSasToken); + await releaseService.createRelease(version, filePath, friendlyFileName); + } + const { product, os, arch, unprocessedType } = match.groups; + const platform = getPlatform(product, os, arch, unprocessedType); + const type = getRealType(unprocessedType); + const size = fs_1.default.statSync(filePath).size; + const stream = fs_1.default.createReadStream(filePath); + const [hash, sha256hash] = await Promise.all([hashStream('sha1', stream), hashStream('sha256', stream)]); // CodeQL [SM04514] Using SHA1 only for legacy reasons, we are actually only respecting SHA256 + const asset = { platform, type, url, hash: hash.toString('hex'), sha256hash: sha256hash.toString('hex'), size, supportsFastUpdate: true }; + log('Creating asset...'); + const result = await (0, retry_1.retry)(async (attempt) => { + log(`Creating asset in Cosmos DB (attempt ${attempt})...`); + const client = new cosmos_1.CosmosClient({ endpoint: e('AZURE_DOCUMENTDB_ENDPOINT'), tokenProvider: () => Promise.resolve(`type=aad&ver=1.0&sig=${cosmosDBAccessToken.token}`) }); + const scripts = client.database('builds').container(quality).scripts; + const { resource: result } = await scripts.storedProcedure('createAsset').execute('', [version, asset, true]); + return result; + }); + if (result === 'already exists') { + log('Asset already exists!'); + } + else { + log('Asset successfully created: ', JSON.stringify(asset, undefined, 2)); + } }); - log('Asset successfully created'); + log(`Successfully released lease for: ${friendlyFileName}`); } // It is VERY important that we don't download artifacts too much too fast from AZDO. // AZDO throttles us SEVERELY if we do. Not just that, but they also close open @@ -518,16 +596,19 @@ async function main() { for (const name of done) { console.log(`\u2705 ${name}`); } - const stages = new Set(['Compile', 'CompileCLI']); + const stages = new Set(['Compile']); + if (e('VSCODE_BUILD_STAGE_LINUX') === 'True' || + e('VSCODE_BUILD_STAGE_ALPINE') === 'True' || + e('VSCODE_BUILD_STAGE_MACOS') === 'True' || + e('VSCODE_BUILD_STAGE_WINDOWS') === 'True') { + stages.add('CompileCLI'); + } if (e('VSCODE_BUILD_STAGE_WINDOWS') === 'True') { stages.add('Windows'); } if (e('VSCODE_BUILD_STAGE_LINUX') === 'True') { stages.add('Linux'); } - if (e('VSCODE_BUILD_STAGE_LINUX_LEGACY_SERVER') === 'True') { - stages.add('LinuxLegacyServer'); - } if (e('VSCODE_BUILD_STAGE_ALPINE') === 'True') { stages.add('Alpine'); } @@ -561,12 +642,12 @@ async function main() { continue; } console.log(`[${artifact.name}] Found new artifact`); - const artifactZipPath = path.join(e('AGENT_TEMPDIRECTORY'), `${artifact.name}.zip`); + const artifactZipPath = path_1.default.join(e('AGENT_TEMPDIRECTORY'), `${artifact.name}.zip`); await (0, retry_1.retry)(async (attempt) => { const start = Date.now(); console.log(`[${artifact.name}] Downloading (attempt ${attempt})...`); await downloadArtifact(artifact, artifactZipPath); - const archiveSize = fs.statSync(artifactZipPath).size; + const archiveSize = fs_1.default.statSync(artifactZipPath).size; const downloadDurationS = (Date.now() - start) / 1000; const downloadSpeedKBS = Math.round((archiveSize / 1024) / downloadDurationS); console.log(`[${artifact.name}] Successfully downloaded after ${Math.floor(downloadDurationS)} seconds(${downloadSpeedKBS} KB/s).`); diff --git a/build/azure-pipelines/common/publish.ts b/build/azure-pipelines/common/publish.ts index 652cd1683351..37bf5ccc6cd3 100644 --- a/build/azure-pipelines/common/publish.ts +++ b/build/azure-pipelines/common/publish.ts @@ -3,19 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; +import fs from 'fs'; +import path from 'path'; import { Readable } from 'stream'; import type { ReadableStream } from 'stream/web'; import { pipeline } from 'node:stream/promises'; -import * as yauzl from 'yauzl'; -import * as crypto from 'crypto'; +import yauzl from 'yauzl'; +import crypto from 'crypto'; import { retry } from './retry'; import { CosmosClient } from '@azure/cosmos'; -import { ClientSecretCredential } from '@azure/identity'; -import * as cp from 'child_process'; -import * as os from 'os'; +import cp from 'child_process'; +import os from 'os'; import { Worker, isMainThread, workerData } from 'node:worker_threads'; +import { ConfidentialClientApplication } from '@azure/msal-node'; +import { BlobClient, BlobServiceClient, BlockBlobClient, ContainerClient, ContainerSASPermissions, generateBlobSASQueryParameters } from '@azure/storage-blob'; +import jws from 'jws'; +import { clearInterval, setInterval } from 'node:timers'; function e(name: string): string { const result = process.env[name]; @@ -27,345 +30,503 @@ function e(name: string): string { return result; } -class Temp { - private _files: string[] = []; +function hashStream(hashName: string, stream: Readable): Promise { + return new Promise((c, e) => { + const shasum = crypto.createHash(hashName); - tmpNameSync(): string { - const file = path.join(os.tmpdir(), crypto.randomBytes(20).toString('hex')); - this._files.push(file); - return file; - } + stream + .on('data', shasum.update.bind(shasum)) + .on('error', e) + .on('close', () => c(shasum.digest())); + }); +} - dispose(): void { - for (const file of this._files) { - try { - fs.unlinkSync(file); - } catch (err) { - // noop - } - } - } +interface ReleaseSubmitResponse { + operationId: string; + esrpCorrelationId: string; + code?: string; + message?: string; + target?: string; + innerError?: any; } -interface RequestOptions { - readonly body?: string; +interface ReleaseActivityInfo { + activityId: string; + activityType: string; + name: string; + status: string; + errorCode: number; + errorMessages: string[]; + beginTime?: Date; + endTime?: Date; + lastModifiedAt?: Date; } -interface CreateProvisionedFilesSuccessResponse { - IsSuccess: true; - ErrorDetails: null; +interface InnerServiceError { + code: string; + details: { [key: string]: string }; + innerError?: InnerServiceError; } -interface CreateProvisionedFilesErrorResponse { - IsSuccess: false; - ErrorDetails: { - Code: string; - Category: string; - Message: string; - CanRetry: boolean; - AdditionalProperties: Record; - }; +interface ReleaseError { + errorCode: number; + errorMessages: string[]; } -type CreateProvisionedFilesResponse = CreateProvisionedFilesSuccessResponse | CreateProvisionedFilesErrorResponse; +const enum StatusCode { + Pass = 'pass', + Aborted = 'aborted', + Inprogress = 'inprogress', + FailCanRetry = 'failCanRetry', + FailDoNotRetry = 'failDoNotRetry', + PendingAnalysis = 'pendingAnalysis', + Cancelled = 'cancelled' +} -function isCreateProvisionedFilesErrorResponse(response: unknown): response is CreateProvisionedFilesErrorResponse { - return (response as CreateProvisionedFilesErrorResponse)?.ErrorDetails?.Code !== undefined; +interface ReleaseResultMessage { + activities: ReleaseActivityInfo[]; + childWorkflowType: string; + clientId: string; + customerCorrelationId: string; + errorInfo: InnerServiceError; + groupId: string; + lastModifiedAt: Date; + operationId: string; + releaseError: ReleaseError; + requestSubmittedAt: Date; + routedRegion: string; + status: StatusCode; + totalFileCount: number; + totalReleaseSize: number; + version: string; } -class ProvisionService { +interface ReleaseFileInfo { + name?: string; + hash?: number[]; + sourceLocation?: FileLocation; + sizeInBytes?: number; + hashType?: FileHashType; + fileId?: any; + distributionRelativePath?: string; + partNumber?: string; + friendlyFileName?: string; + tenantFileLocationType?: string; + tenantFileLocation?: string; + signedEngineeringCopyLocation?: string; + encryptedDistributionBlobLocation?: string; + preEncryptedDistributionBlobLocation?: string; + secondaryDistributionHashRequired?: boolean; + secondaryDistributionHashType?: FileHashType; + lastModifiedAt?: Date; + cultureCodes?: string[]; + displayFileInDownloadCenter?: boolean; + isPrimaryFileInDownloadCenter?: boolean; + fileDownloadDetails?: FileDownloadDetails[]; +} - constructor( - private readonly log: (...args: any[]) => void, - private readonly accessToken: string - ) { } +interface ReleaseDetailsFileInfo extends ReleaseFileInfo { } + +interface ReleaseDetailsMessage extends ReleaseResultMessage { + clusterRegion: string; + correlationVector: string; + releaseCompletedAt?: Date; + releaseInfo: ReleaseInfo; + productInfo: ProductInfo; + createdBy: UserInfo; + owners: OwnerInfo[]; + accessPermissionsInfo: AccessPermissionsInfo; + files: ReleaseDetailsFileInfo[]; + comments: string[]; + cancellationReason: string; + downloadCenterInfo: DownloadCenterInfo; +} - async provision(releaseId: string, fileId: string, fileName: string) { - const body = JSON.stringify({ - ReleaseId: releaseId, - PortalName: 'VSCode', - PublisherCode: 'VSCode', - ProvisionedFilesCollection: [{ - PublisherKey: fileId, - IsStaticFriendlyFileName: true, - FriendlyFileName: fileName, - MaxTTL: '1440', - CdnMappings: ['ECN'] - }] - }); - this.log(`Provisioning ${fileName} (releaseId: ${releaseId}, fileId: ${fileId})...`); - const res = await retry(() => this.request('POST', '/api/v2/ProvisionedFiles/CreateProvisionedFiles', { body })); +interface ProductInfo { + name?: string; + version?: string; + description?: string; +} - if (isCreateProvisionedFilesErrorResponse(res) && res.ErrorDetails.Code === 'FriendlyFileNameAlreadyProvisioned') { - this.log(`File already provisioned (most likley due to a re-run), skipping: ${fileName}`); - return; - } +interface ReleaseInfo { + title?: string; + minimumNumberOfApprovers: number; + properties?: { [key: string]: string }; + isRevision?: boolean; + revisionNumber?: string; +} - if (!res.IsSuccess) { - throw new Error(`Failed to submit provisioning request: ${JSON.stringify(res.ErrorDetails)}`); - } +type FileLocationType = 'azureBlob'; - this.log(`Successfully provisioned ${fileName}`); - } +interface FileLocation { + type: FileLocationType; + blobUrl: string; + uncPath?: string; + url?: string; +} - private async request(method: string, url: string, options?: RequestOptions): Promise { - const opts: RequestInit = { - method, - body: options?.body, - headers: { - Authorization: `Bearer ${this.accessToken}`, - 'Content-Type': 'application/json' - } - }; +type FileHashType = 'sha256' | 'sha1'; - const res = await fetch(`https://dsprovisionapi.microsoft.com${url}`, opts); +interface FileDownloadDetails { + portalName: string; + downloadUrl: string; +} +interface RoutingInfo { + intent?: string; + contentType?: string; + contentOrigin?: string; + productState?: string; + audience?: string; +} - // 400 normally means the request is bad or something is already provisioned, so we will return as retries are useless - // Otherwise log the text body and headers. We do text because some responses are not JSON. - if ((!res.ok || res.status < 200 || res.status >= 500) && res.status !== 400) { - throw new Error(`Unexpected status code: ${res.status}\nResponse Headers: ${JSON.stringify(res.headers)}\nBody Text: ${await res.text()}`); - } +interface ReleaseFileInfo { + name?: string; + hash?: number[]; + sourceLocation?: FileLocation; + sizeInBytes?: number; + hashType?: FileHashType; + fileId?: any; + distributionRelativePath?: string; + partNumber?: string; + friendlyFileName?: string; + tenantFileLocationType?: string; + tenantFileLocation?: string; + signedEngineeringCopyLocation?: string; + encryptedDistributionBlobLocation?: string; + preEncryptedDistributionBlobLocation?: string; + secondaryDistributionHashRequired?: boolean; + secondaryDistributionHashType?: FileHashType; + lastModifiedAt?: Date; + cultureCodes?: string[]; + displayFileInDownloadCenter?: boolean; + isPrimaryFileInDownloadCenter?: boolean; + fileDownloadDetails?: FileDownloadDetails[]; +} - return await res.json(); - } +interface UserInfo { + userPrincipalName?: string; } -function hashStream(hashName: string, stream: Readable): Promise { - return new Promise((c, e) => { - const shasum = crypto.createHash(hashName); +interface OwnerInfo { + owner: UserInfo; +} - stream - .on('data', shasum.update.bind(shasum)) - .on('error', e) - .on('close', () => c(shasum.digest('hex'))); - }); +interface ApproverInfo { + approver: UserInfo; + isAutoApproved: boolean; + isMandatory: boolean; } -interface Release { - readonly releaseId: string; - readonly fileId: string; +interface AccessPermissionsInfo { + mainPublisher?: string; + releasePublishers?: string[]; + channelDownloadEntityDetails?: { [key: string]: string[] }; } -interface SubmitReleaseResult { - submissionResponse: { - operationId: string; - statusCode: string; - }; +interface DownloadCenterLocaleInfo { + cultureCode?: string; + downloadTitle?: string; + shortName?: string; + shortDescription?: string; + longDescription?: string; + instructions?: string; + additionalInfo?: string; + keywords?: string[]; + version?: string; + relatedLinks?: { [key: string]: URL }; +} + +interface DownloadCenterInfo { + downloadCenterId: number; + publishToDownloadCenter?: boolean; + publishingGroup?: string; + operatingSystems?: string[]; + relatedReleases?: string[]; + kbNumbers?: string[]; + sbNumbers?: string[]; + locales?: DownloadCenterLocaleInfo[]; + additionalProperties?: { [key: string]: string }; } -interface ReleaseDetailsResult { - releaseDetails: [{ - fileDetails: [{ publisherKey: string }]; - statusCode: 'inprogress' | 'pass'; - }]; +interface ReleaseRequestMessage { + driEmail: string[]; + groupId?: string; + customerCorrelationId: string; + esrpCorrelationId: string; + contextData?: { [key: string]: string }; + releaseInfo: ReleaseInfo; + productInfo: ProductInfo; + files: ReleaseFileInfo[]; + routingInfo?: RoutingInfo; + createdBy: UserInfo; + owners: OwnerInfo[]; + approvers: ApproverInfo[]; + accessPermissionsInfo: AccessPermissionsInfo; + jwsToken?: string; + publisherId?: string; + downloadCenterInfo?: DownloadCenterInfo; } -class ESRPClient { +function getCertificateBuffer(input: string) { + return Buffer.from(input.replace(/-----BEGIN CERTIFICATE-----|-----END CERTIFICATE-----|\n/g, ''), 'base64'); +} - private readonly authPath: string; +function getThumbprint(input: string, algorithm: string): Buffer { + const buffer = getCertificateBuffer(input); + return crypto.createHash(algorithm).update(buffer).digest(); +} - constructor( - private readonly log: (...args: any[]) => void, - private readonly tmp: Temp, +function getKeyFromPFX(pfx: string): string { + const pfxCertificatePath = path.join(os.tmpdir(), 'cert.pfx'); + const pemKeyPath = path.join(os.tmpdir(), 'key.pem'); + + try { + const pfxCertificate = Buffer.from(pfx, 'base64'); + fs.writeFileSync(pfxCertificatePath, pfxCertificate); + cp.execSync(`openssl pkcs12 -in "${pfxCertificatePath}" -nocerts -nodes -out "${pemKeyPath}" -passin pass:`); + const raw = fs.readFileSync(pemKeyPath, 'utf-8'); + const result = raw.match(/-----BEGIN PRIVATE KEY-----[\s\S]+?-----END PRIVATE KEY-----/g)![0]; + return result; + } finally { + fs.rmSync(pfxCertificatePath, { force: true }); + fs.rmSync(pemKeyPath, { force: true }); + } +} + +function getCertificatesFromPFX(pfx: string): string[] { + const pfxCertificatePath = path.join(os.tmpdir(), 'cert.pfx'); + const pemCertificatePath = path.join(os.tmpdir(), 'cert.pem'); + + try { + const pfxCertificate = Buffer.from(pfx, 'base64'); + fs.writeFileSync(pfxCertificatePath, pfxCertificate); + cp.execSync(`openssl pkcs12 -in "${pfxCertificatePath}" -nokeys -out "${pemCertificatePath}" -passin pass:`); + const raw = fs.readFileSync(pemCertificatePath, 'utf-8'); + const matches = raw.match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g); + return matches ? matches.reverse() : []; + } finally { + fs.rmSync(pfxCertificatePath, { force: true }); + fs.rmSync(pemCertificatePath, { force: true }); + } +} + +class ESRPReleaseService { + + static async create( + log: (...args: any[]) => void, tenantId: string, clientId: string, - authCertSubjectName: string, - requestSigningCertSubjectName: string, + authCertificatePfx: string, + requestSigningCertificatePfx: string, + containerClient: ContainerClient, + stagingSasToken: string ) { - this.authPath = this.tmp.tmpNameSync(); - fs.writeFileSync(this.authPath, JSON.stringify({ - Version: '1.0.0', - AuthenticationType: 'AAD_CERT', - TenantId: tenantId, - ClientId: clientId, - AuthCert: { - SubjectName: authCertSubjectName, - StoreLocation: 'LocalMachine', - StoreName: 'My', - SendX5c: 'true' - }, - RequestSigningCert: { - SubjectName: requestSigningCertSubjectName, - StoreLocation: 'LocalMachine', - StoreName: 'My' + const authKey = getKeyFromPFX(authCertificatePfx); + const authCertificate = getCertificatesFromPFX(authCertificatePfx)[0]; + const requestSigningKey = getKeyFromPFX(requestSigningCertificatePfx); + const requestSigningCertificates = getCertificatesFromPFX(requestSigningCertificatePfx); + + const app = new ConfidentialClientApplication({ + auth: { + clientId, + authority: `https://login.microsoftonline.com/${tenantId}`, + clientCertificate: { + thumbprintSha256: getThumbprint(authCertificate, 'sha256').toString('hex'), + privateKey: authKey, + x5c: authCertificate + } } - })); - } - - async release( - version: string, - filePath: string - ): Promise { - this.log(`Submitting release for ${version}: ${filePath}`); - const submitReleaseResult = await this.SubmitRelease(version, filePath); + }); - if (submitReleaseResult.submissionResponse.statusCode !== 'pass') { - throw new Error(`Unexpected status code: ${submitReleaseResult.submissionResponse.statusCode}`); - } + const response = await app.acquireTokenByClientCredential({ + scopes: ['https://api.esrp.microsoft.com/.default'] + }); - const releaseId = submitReleaseResult.submissionResponse.operationId; - this.log(`Successfully submitted release ${releaseId}. Polling for completion...`); + return new ESRPReleaseService(log, clientId, response!.accessToken, requestSigningCertificates, requestSigningKey, containerClient, stagingSasToken); + } - let details!: ReleaseDetailsResult; + private static API_URL = 'https://api.esrp.microsoft.com/api/v3/releaseservices/clients/'; - // Poll every 5 seconds, wait 60 minutes max -> poll 60/5*60=720 times - for (let i = 0; i < 720; i++) { - details = await this.ReleaseDetails(releaseId); + private constructor( + private readonly log: (...args: any[]) => void, + private readonly clientId: string, + private readonly accessToken: string, + private readonly requestSigningCertificates: string[], + private readonly requestSigningKey: string, + private readonly containerClient: ContainerClient, + private readonly stagingSasToken: string + ) { } - if (details.releaseDetails[0].statusCode === 'pass') { - break; - } else if (details.releaseDetails[0].statusCode !== 'inprogress') { - throw new Error(`Failed to submit release: ${JSON.stringify(details)}`); + async createRelease(version: string, filePath: string, friendlyFileName: string) { + const correlationId = crypto.randomUUID(); + const blobClient = this.containerClient.getBlockBlobClient(correlationId); + + this.log(`Uploading ${filePath} to ${blobClient.url}`); + await blobClient.uploadFile(filePath); + this.log('Uploaded blob successfully'); + + try { + this.log(`Submitting release for ${version}: ${filePath}`); + const submitReleaseResult = await this.submitRelease(version, filePath, friendlyFileName, correlationId, blobClient); + + this.log(`Successfully submitted release ${submitReleaseResult.operationId}. Polling for completion...`); + + // Poll every 5 seconds, wait 60 minutes max -> poll 60/5*60=720 times + for (let i = 0; i < 720; i++) { + await new Promise(c => setTimeout(c, 5000)); + const releaseStatus = await this.getReleaseStatus(submitReleaseResult.operationId); + + if (releaseStatus.status === 'pass') { + break; + } else if (releaseStatus.status === 'aborted') { + this.log(JSON.stringify(releaseStatus)); + throw new Error(`Release was aborted`); + } else if (releaseStatus.status !== 'inprogress') { + this.log(JSON.stringify(releaseStatus)); + throw new Error(`Unknown error when polling for release`); + } } - await new Promise(c => setTimeout(c, 5000)); - } - - if (details.releaseDetails[0].statusCode !== 'pass') { - throw new Error(`Timed out waiting for release ${releaseId}: ${JSON.stringify(details)}`); - } + const releaseDetails = await this.getReleaseDetails(submitReleaseResult.operationId); - const fileId = details.releaseDetails[0].fileDetails[0].publisherKey; - this.log('Release completed successfully with fileId: ', fileId); + if (releaseDetails.status !== 'pass') { + throw new Error(`Timed out waiting for release: ${JSON.stringify(releaseDetails)}`); + } - return { releaseId, fileId }; + this.log('Successfully created release:', releaseDetails.files[0].fileDownloadDetails![0].downloadUrl); + return releaseDetails.files[0].fileDownloadDetails![0].downloadUrl; + } finally { + this.log(`Deleting blob ${blobClient.url}`); + await blobClient.delete(); + this.log('Deleted blob successfully'); + } } - private async SubmitRelease( + private async submitRelease( version: string, - filePath: string - ): Promise { - const policyPath = this.tmp.tmpNameSync(); - fs.writeFileSync(policyPath, JSON.stringify({ - Version: '1.0.0', - Audience: 'InternalLimited', - Intent: 'distribution', - ContentType: 'InstallPackage' - })); - - const inputPath = this.tmp.tmpNameSync(); + filePath: string, + friendlyFileName: string, + correlationId: string, + blobClient: BlobClient + ): Promise { const size = fs.statSync(filePath).size; - const istream = fs.createReadStream(filePath); - const sha256 = await hashStream('sha256', istream); - fs.writeFileSync(inputPath, JSON.stringify({ - Version: '1.0.0', - ReleaseInfo: { - ReleaseMetadata: { - Title: 'VS Code', - Properties: { - ReleaseContentType: 'InstallPackage' - }, - MinimumNumberOfApprovers: 1 - }, - ProductInfo: { - Name: 'VS Code', - Version: version, - Description: path.basename(filePath, path.extname(filePath)), - }, - Owners: [ - { - Owner: { - UserPrincipalName: 'jomo@microsoft.com' - } - } - ], - Approvers: [ - { - Approver: { - UserPrincipalName: 'jomo@microsoft.com' - }, - IsAutoApproved: true, - IsMandatory: false - } - ], - AccessPermissions: { - MainPublisher: 'VSCode', - ChannelDownloadEntityDetails: { - Consumer: ['VSCode'] - } + const hash = await hashStream('sha256', fs.createReadStream(filePath)); + const blobUrl = `${blobClient.url}?${this.stagingSasToken}`; + + const message: ReleaseRequestMessage = { + customerCorrelationId: correlationId, + esrpCorrelationId: correlationId, + driEmail: ['joao.moreno@microsoft.com'], + createdBy: { userPrincipalName: 'jomo@microsoft.com' }, + owners: [{ owner: { userPrincipalName: 'jomo@microsoft.com' } }], + approvers: [{ approver: { userPrincipalName: 'jomo@microsoft.com' }, isAutoApproved: true, isMandatory: false }], + releaseInfo: { + title: 'VS Code', + properties: { + 'ReleaseContentType': 'InstallPackage' }, - CreatedBy: { - UserPrincipalName: 'jomo@microsoft.com' - } + minimumNumberOfApprovers: 1 }, - ReleaseBatches: [ - { - ReleaseRequestFiles: [ - { - SizeInBytes: size, - SourceHash: sha256, - HashType: 'SHA256', - SourceLocation: path.basename(filePath) - } - ], - SourceLocationType: 'UNC', - SourceRootDirectory: path.dirname(filePath), - DestinationLocationType: 'AzureBlob' + productInfo: { + name: 'VS Code', + version, + description: 'VS Code' + }, + accessPermissionsInfo: { + mainPublisher: 'VSCode', + channelDownloadEntityDetails: { + AllDownloadEntities: ['VSCode'] } - ] - })); - - const outputPath = this.tmp.tmpNameSync(); - cp.execSync(`ESRPClient SubmitRelease -a ${this.authPath} -p ${policyPath} -i ${inputPath} -o ${outputPath}`, { stdio: 'inherit' }); + }, + routingInfo: { + intent: 'filedownloadlinkgeneration' + }, + files: [{ + name: path.basename(filePath), + friendlyFileName, + tenantFileLocation: blobUrl, + tenantFileLocationType: 'AzureBlob', + sourceLocation: { + type: 'azureBlob', + blobUrl + }, + hashType: 'sha256', + hash: Array.from(hash), + sizeInBytes: size + }] + }; - const output = fs.readFileSync(outputPath, 'utf8'); - return JSON.parse(output) as SubmitReleaseResult; - } + message.jwsToken = await this.generateJwsToken(message); - private async ReleaseDetails( - releaseId: string - ): Promise { - const inputPath = this.tmp.tmpNameSync(); - fs.writeFileSync(inputPath, JSON.stringify({ - Version: '1.0.0', - OperationIds: [releaseId] - })); + const res = await fetch(`${ESRPReleaseService.API_URL}${this.clientId}/workflows/release/operations`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${this.accessToken}` + }, + body: JSON.stringify(message) + }); - const outputPath = this.tmp.tmpNameSync(); - cp.execSync(`ESRPClient ReleaseDetails -a ${this.authPath} -i ${inputPath} -o ${outputPath}`, { stdio: 'inherit' }); + if (!res.ok) { + const text = await res.text(); + throw new Error(`Failed to submit release: ${res.statusText}\n${text}`); + } - const output = fs.readFileSync(outputPath, 'utf8'); - return JSON.parse(output) as ReleaseDetailsResult; + return await res.json() as ReleaseSubmitResponse; } -} -async function releaseAndProvision( - log: (...args: any[]) => void, - releaseTenantId: string, - releaseClientId: string, - releaseAuthCertSubjectName: string, - releaseRequestSigningCertSubjectName: string, - provisionTenantId: string, - provisionAADUsername: string, - provisionAADPassword: string, - version: string, - quality: string, - filePath: string -): Promise { - const fileName = `${quality}/${version}/${path.basename(filePath)}`; - const result = `${e('PRSS_CDN_URL')}/${fileName}`; + private async getReleaseStatus(releaseId: string): Promise { + const url = `${ESRPReleaseService.API_URL}${this.clientId}/workflows/release/operations/grs/${releaseId}`; - const res = await retry(() => fetch(result)); + const res = await fetch(url, { + headers: { + 'Authorization': `Bearer ${this.accessToken}` + } + }); - if (res.status === 200) { - log(`Already released and provisioned: ${result}`); - return result; + if (!res.ok) { + const text = await res.text(); + throw new Error(`Failed to get release status: ${res.statusText}\n${text}`); + } + + return await res.json() as ReleaseResultMessage; } - const tmp = new Temp(); - process.on('exit', () => tmp.dispose()); + private async getReleaseDetails(releaseId: string): Promise { + const url = `${ESRPReleaseService.API_URL}${this.clientId}/workflows/release/operations/grd/${releaseId}`; - const esrpclient = new ESRPClient(log, tmp, releaseTenantId, releaseClientId, releaseAuthCertSubjectName, releaseRequestSigningCertSubjectName); - const release = await esrpclient.release(version, filePath); + const res = await fetch(url, { + headers: { + 'Authorization': `Bearer ${this.accessToken}` + } + }); - const credential = new ClientSecretCredential(provisionTenantId, provisionAADUsername, provisionAADPassword); - const accessToken = await credential.getToken(['https://microsoft.onmicrosoft.com/DS.Provisioning.WebApi/.default']); - const service = new ProvisionService(log, accessToken.token); - await service.provision(release.releaseId, release.fileId, fileName); + if (!res.ok) { + const text = await res.text(); + throw new Error(`Failed to get release status: ${res.statusText}\n${text}`); + } - return result; + return await res.json() as ReleaseDetailsMessage; + } + + private async generateJwsToken(message: ReleaseRequestMessage): Promise { + return jws.sign({ + header: { + alg: 'RS256', + crit: ['exp', 'x5t'], + // Release service uses ticks, not seconds :roll_eyes: (https://stackoverflow.com/a/7968483) + exp: ((Date.now() + (6 * 60 * 1000)) * 10000) + 621355968000000000, + // Release service uses hex format, not base64url :roll_eyes: + x5t: getThumbprint(this.requestSigningCertificates[0], 'sha1').toString('hex'), + // Release service uses a '.' separated string, not an array of strings :roll_eyes: + x5c: this.requestSigningCertificates.map(c => getCertificateBuffer(c).toString('base64url')).join('.') as any, + }, + payload: message, + privateKey: this.requestSigningKey, + }); + } } class State { @@ -533,7 +694,7 @@ interface Asset { } // Contains all of the logic for mapping details to our actual product names in CosmosDB -function getPlatform(product: string, os: string, arch: string, type: string, isLegacy: boolean): string { +function getPlatform(product: string, os: string, arch: string, type: string): string { switch (os) { case 'win32': switch (product) { @@ -578,12 +739,12 @@ function getPlatform(product: string, os: string, arch: string, type: string, is case 'client': return `linux-${arch}`; case 'server': - return isLegacy ? `server-linux-legacy-${arch}` : `server-linux-${arch}`; + return `server-linux-${arch}`; case 'web': if (arch === 'standalone') { return 'web-standalone'; } - return isLegacy ? `server-linux-legacy-${arch}-web` : `server-linux-${arch}-web`; + return `server-linux-${arch}-web`; default: throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); } @@ -636,7 +797,52 @@ function getRealType(type: string) { } } -async function processArtifact(artifact: Artifact, artifactFilePath: string): Promise { +async function withLease(client: BlockBlobClient, fn: () => Promise) { + const lease = client.getBlobLeaseClient(); + + for (let i = 0; i < 360; i++) { // Try to get lease for 30 minutes + try { + await client.uploadData(new ArrayBuffer()); // blob needs to exist for lease to be acquired + await lease.acquireLease(60); + + try { + const abortController = new AbortController(); + const refresher = new Promise((c, e) => { + abortController.signal.onabort = () => { + clearInterval(interval); + c(); + }; + + const interval = setInterval(() => { + lease.renewLease().catch(err => { + clearInterval(interval); + e(new Error('Failed to renew lease ' + err)); + }); + }, 30_000); + }); + + const result = await Promise.race([fn(), refresher]); + abortController.abort(); + return result; + } finally { + await lease.releaseLease(); + } + } catch (err) { + if (err.statusCode !== 409 && err.statusCode !== 412) { + throw err; + } + + await new Promise(c => setTimeout(c, 5000)); + } + } + + throw new Error('Failed to acquire lease on blob after 30 minutes'); +} + +async function processArtifact( + artifact: Artifact, + filePath: string +) { const log = (...args: any[]) => console.log(`[${artifact.name}]`, ...args); const match = /^vscode_(?[^_]+)_(?[^_]+)(?:_legacy)?_(?[^_]+)_(?[^_]+)$/.exec(artifact.name); @@ -644,43 +850,76 @@ async function processArtifact(artifact: Artifact, artifactFilePath: string): Pr throw new Error(`Invalid artifact name: ${artifact.name}`); } - // getPlatform needs the unprocessedType + const { cosmosDBAccessToken, blobServiceAccessToken } = JSON.parse(e('PUBLISH_AUTH_TOKENS')); const quality = e('VSCODE_QUALITY'); - const commit = e('BUILD_SOURCEVERSION'); - const { product, os, arch, unprocessedType } = match.groups!; - const isLegacy = artifact.name.includes('_legacy'); - const platform = getPlatform(product, os, arch, unprocessedType, isLegacy); - const type = getRealType(unprocessedType); - const size = fs.statSync(artifactFilePath).size; - const stream = fs.createReadStream(artifactFilePath); - const [hash, sha256hash] = await Promise.all([hashStream('sha1', stream), hashStream('sha256', stream)]); // CodeQL [SM04514] Using SHA1 only for legacy reasons, we are actually only respecting SHA256 - - const url = await releaseAndProvision( - log, - e('RELEASE_TENANT_ID'), - e('RELEASE_CLIENT_ID'), - e('RELEASE_AUTH_CERT_SUBJECT_NAME'), - e('RELEASE_REQUEST_SIGNING_CERT_SUBJECT_NAME'), - e('PROVISION_TENANT_ID'), - e('PROVISION_AAD_USERNAME'), - e('PROVISION_AAD_PASSWORD'), - commit, - quality, - artifactFilePath - ); - - const asset: Asset = { platform, type, url, hash, sha256hash, size, supportsFastUpdate: true }; - log('Creating asset...', JSON.stringify(asset, undefined, 2)); - - await retry(async (attempt) => { - log(`Creating asset in Cosmos DB (attempt ${attempt})...`); - const aadCredentials = new ClientSecretCredential(e('AZURE_TENANT_ID'), e('AZURE_CLIENT_ID'), e('AZURE_CLIENT_SECRET')); - const client = new CosmosClient({ endpoint: e('AZURE_DOCUMENTDB_ENDPOINT'), aadCredentials }); - const scripts = client.database('builds').container(quality).scripts; - await scripts.storedProcedure('createAsset').execute('', [commit, asset, true]); + const version = e('BUILD_SOURCEVERSION'); + const friendlyFileName = `${quality}/${version}/${path.basename(filePath)}`; + + const blobServiceClient = new BlobServiceClient(`https://${e('VSCODE_STAGING_BLOB_STORAGE_ACCOUNT_NAME')}.blob.core.windows.net/`, { getToken: async () => blobServiceAccessToken }); + const leasesContainerClient = blobServiceClient.getContainerClient('leases'); + await leasesContainerClient.createIfNotExists(); + const leaseBlobClient = leasesContainerClient.getBlockBlobClient(friendlyFileName); + + log(`Acquiring lease for: ${friendlyFileName}`); + + await withLease(leaseBlobClient, async () => { + log(`Successfully acquired lease for: ${friendlyFileName}`); + + const url = `${e('PRSS_CDN_URL')}/${friendlyFileName}`; + const res = await retry(() => fetch(url)); + + if (res.status === 200) { + log(`Already released and provisioned: ${url}`); + } else { + const stagingContainerClient = blobServiceClient.getContainerClient('staging'); + await stagingContainerClient.createIfNotExists(); + + const now = new Date().valueOf(); + const oneHour = 60 * 60 * 1000; + const oneHourAgo = new Date(now - oneHour); + const oneHourFromNow = new Date(now + oneHour); + const userDelegationKey = await blobServiceClient.getUserDelegationKey(oneHourAgo, oneHourFromNow); + const sasOptions = { containerName: 'staging', permissions: ContainerSASPermissions.from({ read: true }), startsOn: oneHourAgo, expiresOn: oneHourFromNow }; + const stagingSasToken = generateBlobSASQueryParameters(sasOptions, userDelegationKey, e('VSCODE_STAGING_BLOB_STORAGE_ACCOUNT_NAME')).toString(); + + const releaseService = await ESRPReleaseService.create( + log, + e('RELEASE_TENANT_ID'), + e('RELEASE_CLIENT_ID'), + e('RELEASE_AUTH_CERT'), + e('RELEASE_REQUEST_SIGNING_CERT'), + stagingContainerClient, + stagingSasToken + ); + + await releaseService.createRelease(version, filePath, friendlyFileName); + } + + const { product, os, arch, unprocessedType } = match.groups!; + const platform = getPlatform(product, os, arch, unprocessedType); + const type = getRealType(unprocessedType); + const size = fs.statSync(filePath).size; + const stream = fs.createReadStream(filePath); + const [hash, sha256hash] = await Promise.all([hashStream('sha1', stream), hashStream('sha256', stream)]); // CodeQL [SM04514] Using SHA1 only for legacy reasons, we are actually only respecting SHA256 + const asset: Asset = { platform, type, url, hash: hash.toString('hex'), sha256hash: sha256hash.toString('hex'), size, supportsFastUpdate: true }; + log('Creating asset...'); + + const result = await retry(async (attempt) => { + log(`Creating asset in Cosmos DB (attempt ${attempt})...`); + const client = new CosmosClient({ endpoint: e('AZURE_DOCUMENTDB_ENDPOINT')!, tokenProvider: () => Promise.resolve(`type=aad&ver=1.0&sig=${cosmosDBAccessToken.token}`) }); + const scripts = client.database('builds').container(quality).scripts; + const { resource: result } = await scripts.storedProcedure('createAsset').execute<'ok' | 'already exists'>('', [version, asset, true]); + return result; + }); + + if (result === 'already exists') { + log('Asset already exists!'); + } else { + log('Asset successfully created: ', JSON.stringify(asset, undefined, 2)); + } }); - log('Asset successfully created'); + log(`Successfully released lease for: ${friendlyFileName}`); } // It is VERY important that we don't download artifacts too much too fast from AZDO. @@ -703,10 +942,19 @@ async function main() { console.log(`\u2705 ${name}`); } - const stages = new Set(['Compile', 'CompileCLI']); + const stages = new Set(['Compile']); + + if ( + e('VSCODE_BUILD_STAGE_LINUX') === 'True' || + e('VSCODE_BUILD_STAGE_ALPINE') === 'True' || + e('VSCODE_BUILD_STAGE_MACOS') === 'True' || + e('VSCODE_BUILD_STAGE_WINDOWS') === 'True' + ) { + stages.add('CompileCLI'); + } + if (e('VSCODE_BUILD_STAGE_WINDOWS') === 'True') { stages.add('Windows'); } if (e('VSCODE_BUILD_STAGE_LINUX') === 'True') { stages.add('Linux'); } - if (e('VSCODE_BUILD_STAGE_LINUX_LEGACY_SERVER') === 'True') { stages.add('LinuxLegacyServer'); } if (e('VSCODE_BUILD_STAGE_ALPINE') === 'True') { stages.add('Alpine'); } if (e('VSCODE_BUILD_STAGE_MACOS') === 'True') { stages.add('macOS'); } if (e('VSCODE_BUILD_STAGE_WEB') === 'True') { stages.add('Web'); } diff --git a/build/azure-pipelines/common/releaseBuild.js b/build/azure-pipelines/common/releaseBuild.js index c2aab2075d0e..fa69cb4e258a 100644 --- a/build/azure-pipelines/common/releaseBuild.js +++ b/build/azure-pipelines/common/releaseBuild.js @@ -31,7 +31,7 @@ async function getConfig(client, quality) { async function main(force) { const commit = getEnv('BUILD_SOURCEVERSION'); const quality = getEnv('VSCODE_QUALITY'); - const aadCredentials = new identity_1.ClientSecretCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_CLIENT_SECRET']); + const aadCredentials = new identity_1.ClientAssertionCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], () => Promise.resolve(process.env['AZURE_ID_TOKEN'])); const client = new cosmos_1.CosmosClient({ endpoint: process.env['AZURE_DOCUMENTDB_ENDPOINT'], aadCredentials }); if (!force) { const config = await getConfig(client, quality); diff --git a/build/azure-pipelines/common/releaseBuild.ts b/build/azure-pipelines/common/releaseBuild.ts index 2e8fff04fb40..b7762de7df60 100644 --- a/build/azure-pipelines/common/releaseBuild.ts +++ b/build/azure-pipelines/common/releaseBuild.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ClientSecretCredential } from '@azure/identity'; +import { ClientAssertionCredential } from '@azure/identity'; import { CosmosClient } from '@azure/cosmos'; import { retry } from './retry'; @@ -45,7 +45,7 @@ async function main(force: boolean): Promise { const commit = getEnv('BUILD_SOURCEVERSION'); const quality = getEnv('VSCODE_QUALITY'); - const aadCredentials = new ClientSecretCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, process.env['AZURE_CLIENT_SECRET']!); + const aadCredentials = new ClientAssertionCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, () => Promise.resolve(process.env['AZURE_ID_TOKEN']!)); const client = new CosmosClient({ endpoint: process.env['AZURE_DOCUMENTDB_ENDPOINT']!, aadCredentials }); if (!force) { diff --git a/build/azure-pipelines/common/sign-win32.js b/build/azure-pipelines/common/sign-win32.js index da899cd3fc09..f4e3f27c1f25 100644 --- a/build/azure-pipelines/common/sign-win32.js +++ b/build/azure-pipelines/common/sign-win32.js @@ -3,16 +3,16 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); const sign_1 = require("./sign"); -const path = require("path"); +const path_1 = __importDefault(require("path")); (0, sign_1.main)([ process.env['EsrpCliDllPath'], 'sign-windows', - process.env['ESRPPKI'], - process.env['ESRPAADUsername'], - process.env['ESRPAADPassword'], - path.dirname(process.argv[2]), - path.basename(process.argv[2]) + path_1.default.dirname(process.argv[2]), + path_1.default.basename(process.argv[2]) ]); //# sourceMappingURL=sign-win32.js.map \ No newline at end of file diff --git a/build/azure-pipelines/common/sign-win32.ts b/build/azure-pipelines/common/sign-win32.ts index 76828b42e1eb..ad88435b5a38 100644 --- a/build/azure-pipelines/common/sign-win32.ts +++ b/build/azure-pipelines/common/sign-win32.ts @@ -4,14 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { main } from './sign'; -import * as path from 'path'; +import path from 'path'; main([ process.env['EsrpCliDllPath']!, 'sign-windows', - process.env['ESRPPKI']!, - process.env['ESRPAADUsername']!, - process.env['ESRPAADPassword']!, path.dirname(process.argv[2]), path.basename(process.argv[2]) ]); diff --git a/build/azure-pipelines/common/sign.js b/build/azure-pipelines/common/sign.js index 32996a7db030..fd87772b3b87 100644 --- a/build/azure-pipelines/common/sign.js +++ b/build/azure-pipelines/common/sign.js @@ -3,25 +3,28 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.Temp = void 0; exports.main = main; -const cp = require("child_process"); -const fs = require("fs"); -const crypto = require("crypto"); -const path = require("path"); -const os = require("os"); +const child_process_1 = __importDefault(require("child_process")); +const fs_1 = __importDefault(require("fs")); +const crypto_1 = __importDefault(require("crypto")); +const path_1 = __importDefault(require("path")); +const os_1 = __importDefault(require("os")); class Temp { _files = []; tmpNameSync() { - const file = path.join(os.tmpdir(), crypto.randomBytes(20).toString('hex')); + const file = path_1.default.join(os_1.default.tmpdir(), crypto_1.default.randomBytes(20).toString('hex')); this._files.push(file); return file; } dispose() { for (const file of this._files) { try { - fs.unlinkSync(file); + fs_1.default.unlinkSync(file); } catch (err) { // noop @@ -105,37 +108,61 @@ function getParams(type) { toolName: 'sign', toolVersion: '1.0' }]; + case 'nuget': + return [{ + keyCode: 'CP-401405', + operationSetCode: 'NuGetSign', + parameters: [], + toolName: 'sign', + toolVersion: '1.0' + }, { + keyCode: 'CP-401405', + operationSetCode: 'NuGetVerify', + parameters: [], + toolName: 'sign', + toolVersion: '1.0' + }]; default: throw new Error(`Sign type ${type} not found`); } } -function main([esrpCliPath, type, cert, username, password, folderPath, pattern]) { +function main([esrpCliPath, type, folderPath, pattern]) { const tmp = new Temp(); process.on('exit', () => tmp.dispose()); + const key = crypto_1.default.randomBytes(32); + const iv = crypto_1.default.randomBytes(16); + const cipher = crypto_1.default.createCipheriv('aes-256-cbc', key, iv); + const encryptedToken = cipher.update(process.env['SYSTEM_ACCESSTOKEN'].trim(), 'utf8', 'hex') + cipher.final('hex'); + const encryptionDetailsPath = tmp.tmpNameSync(); + fs_1.default.writeFileSync(encryptionDetailsPath, JSON.stringify({ key: key.toString('hex'), iv: iv.toString('hex') })); + const encryptedTokenPath = tmp.tmpNameSync(); + fs_1.default.writeFileSync(encryptedTokenPath, encryptedToken); const patternPath = tmp.tmpNameSync(); - fs.writeFileSync(patternPath, pattern); + fs_1.default.writeFileSync(patternPath, pattern); const paramsPath = tmp.tmpNameSync(); - fs.writeFileSync(paramsPath, JSON.stringify(getParams(type))); - const keyFile = tmp.tmpNameSync(); - const key = crypto.randomBytes(32); - const iv = crypto.randomBytes(16); - fs.writeFileSync(keyFile, JSON.stringify({ key: key.toString('hex'), iv: iv.toString('hex') })); - const clientkeyPath = tmp.tmpNameSync(); - const clientkeyCypher = crypto.createCipheriv('aes-256-cbc', key, iv); - let clientkey = clientkeyCypher.update(password, 'utf8', 'hex'); - clientkey += clientkeyCypher.final('hex'); - fs.writeFileSync(clientkeyPath, clientkey); - const clientcertPath = tmp.tmpNameSync(); - const clientcertCypher = crypto.createCipheriv('aes-256-cbc', key, iv); - let clientcert = clientcertCypher.update(cert, 'utf8', 'hex'); - clientcert += clientcertCypher.final('hex'); - fs.writeFileSync(clientcertPath, clientcert); + fs_1.default.writeFileSync(paramsPath, JSON.stringify(getParams(type))); + const dotnetVersion = child_process_1.default.execSync('dotnet --version', { encoding: 'utf8' }).trim(); + const adoTaskVersion = path_1.default.basename(path_1.default.dirname(path_1.default.dirname(esrpCliPath))); + const federatedTokenData = { + jobId: process.env['SYSTEM_JOBID'], + planId: process.env['SYSTEM_PLANID'], + projectId: process.env['SYSTEM_TEAMPROJECTID'], + hub: process.env['SYSTEM_HOSTTYPE'], + uri: process.env['SYSTEM_COLLECTIONURI'], + managedIdentityId: process.env['VSCODE_ESRP_CLIENT_ID'], + managedIdentityTenantId: process.env['VSCODE_ESRP_TENANT_ID'], + serviceConnectionId: process.env['VSCODE_ESRP_SERVICE_CONNECTION_ID'], + tempDirectory: os_1.default.tmpdir(), + systemAccessToken: encryptedTokenPath, + encryptionKey: encryptionDetailsPath + }; const args = [ esrpCliPath, 'vsts.sign', - '-a', username, - '-k', clientkeyPath, - '-z', clientcertPath, + '-a', process.env['ESRP_CLIENT_ID'], + '-d', process.env['ESRP_TENANT_ID'], + '-k', JSON.stringify({ akv: 'vscode-esrp' }), + '-z', JSON.stringify({ akv: 'vscode-esrp', cert: 'esrp-sign' }), '-f', folderPath, '-p', patternPath, '-u', 'false', @@ -154,10 +181,17 @@ function main([esrpCliPath, type, cert, username, password, folderPath, pattern] '-i', 'https://www.microsoft.com', '-n', '5', '-r', 'true', - '-e', keyFile, + '-w', dotnetVersion, + '-skipAdoReportAttachment', 'false', + '-pendingAnalysisWaitTimeoutMinutes', '5', + '-adoTaskVersion', adoTaskVersion, + '-resourceUri', 'https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com', + '-esrpClientId', process.env['ESRP_CLIENT_ID'], + '-useMSIAuthentication', 'true', + '-federatedTokenData', JSON.stringify(federatedTokenData) ]; try { - cp.execFileSync('dotnet', args, { stdio: 'inherit' }); + child_process_1.default.execFileSync('dotnet', args, { stdio: 'inherit' }); } catch (err) { console.error('ESRP failed'); diff --git a/build/azure-pipelines/common/sign.ts b/build/azure-pipelines/common/sign.ts index 28fca31205e9..19a288483c85 100644 --- a/build/azure-pipelines/common/sign.ts +++ b/build/azure-pipelines/common/sign.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as cp from 'child_process'; -import * as fs from 'fs'; -import * as crypto from 'crypto'; -import * as path from 'path'; -import * as os from 'os'; +import cp from 'child_process'; +import fs from 'fs'; +import crypto from 'crypto'; +import path from 'path'; +import os from 'os'; export class Temp { private _files: string[] = []; @@ -115,44 +115,70 @@ function getParams(type: string): Params[] { toolName: 'sign', toolVersion: '1.0' }]; + case 'nuget': + return [{ + keyCode: 'CP-401405', + operationSetCode: 'NuGetSign', + parameters: [], + toolName: 'sign', + toolVersion: '1.0' + }, { + keyCode: 'CP-401405', + operationSetCode: 'NuGetVerify', + parameters: [], + toolName: 'sign', + toolVersion: '1.0' + }]; default: throw new Error(`Sign type ${type} not found`); } } -export function main([esrpCliPath, type, cert, username, password, folderPath, pattern]: string[]) { +export function main([esrpCliPath, type, folderPath, pattern]: string[]) { const tmp = new Temp(); process.on('exit', () => tmp.dispose()); + const key = crypto.randomBytes(32); + const iv = crypto.randomBytes(16); + const cipher = crypto.createCipheriv('aes-256-cbc', key, iv); + const encryptedToken = cipher.update(process.env['SYSTEM_ACCESSTOKEN']!.trim(), 'utf8', 'hex') + cipher.final('hex'); + + const encryptionDetailsPath = tmp.tmpNameSync(); + fs.writeFileSync(encryptionDetailsPath, JSON.stringify({ key: key.toString('hex'), iv: iv.toString('hex') })); + + const encryptedTokenPath = tmp.tmpNameSync(); + fs.writeFileSync(encryptedTokenPath, encryptedToken); + const patternPath = tmp.tmpNameSync(); fs.writeFileSync(patternPath, pattern); const paramsPath = tmp.tmpNameSync(); fs.writeFileSync(paramsPath, JSON.stringify(getParams(type))); - const keyFile = tmp.tmpNameSync(); - const key = crypto.randomBytes(32); - const iv = crypto.randomBytes(16); - fs.writeFileSync(keyFile, JSON.stringify({ key: key.toString('hex'), iv: iv.toString('hex') })); - - const clientkeyPath = tmp.tmpNameSync(); - const clientkeyCypher = crypto.createCipheriv('aes-256-cbc', key, iv); - let clientkey = clientkeyCypher.update(password, 'utf8', 'hex'); - clientkey += clientkeyCypher.final('hex'); - fs.writeFileSync(clientkeyPath, clientkey); + const dotnetVersion = cp.execSync('dotnet --version', { encoding: 'utf8' }).trim(); + const adoTaskVersion = path.basename(path.dirname(path.dirname(esrpCliPath))); - const clientcertPath = tmp.tmpNameSync(); - const clientcertCypher = crypto.createCipheriv('aes-256-cbc', key, iv); - let clientcert = clientcertCypher.update(cert, 'utf8', 'hex'); - clientcert += clientcertCypher.final('hex'); - fs.writeFileSync(clientcertPath, clientcert); + const federatedTokenData = { + jobId: process.env['SYSTEM_JOBID'], + planId: process.env['SYSTEM_PLANID'], + projectId: process.env['SYSTEM_TEAMPROJECTID'], + hub: process.env['SYSTEM_HOSTTYPE'], + uri: process.env['SYSTEM_COLLECTIONURI'], + managedIdentityId: process.env['VSCODE_ESRP_CLIENT_ID'], + managedIdentityTenantId: process.env['VSCODE_ESRP_TENANT_ID'], + serviceConnectionId: process.env['VSCODE_ESRP_SERVICE_CONNECTION_ID'], + tempDirectory: os.tmpdir(), + systemAccessToken: encryptedTokenPath, + encryptionKey: encryptionDetailsPath + }; const args = [ esrpCliPath, 'vsts.sign', - '-a', username, - '-k', clientkeyPath, - '-z', clientcertPath, + '-a', process.env['ESRP_CLIENT_ID']!, + '-d', process.env['ESRP_TENANT_ID']!, + '-k', JSON.stringify({ akv: 'vscode-esrp' }), + '-z', JSON.stringify({ akv: 'vscode-esrp', cert: 'esrp-sign' }), '-f', folderPath, '-p', patternPath, '-u', 'false', @@ -171,7 +197,14 @@ export function main([esrpCliPath, type, cert, username, password, folderPath, p '-i', 'https://www.microsoft.com', '-n', '5', '-r', 'true', - '-e', keyFile, + '-w', dotnetVersion, + '-skipAdoReportAttachment', 'false', + '-pendingAnalysisWaitTimeoutMinutes', '5', + '-adoTaskVersion', adoTaskVersion, + '-resourceUri', 'https://msazurecloud.onmicrosoft.com/api.esrp.microsoft.com', + '-esrpClientId', process.env['ESRP_CLIENT_ID']!, + '-useMSIAuthentication', 'true', + '-federatedTokenData', JSON.stringify(federatedTokenData) ]; try { diff --git a/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml b/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml index 32615c584637..b3d01ca7ff16 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml @@ -3,6 +3,8 @@ parameters: type: boolean - name: VSCODE_BUILD_MACOS_ARM64 type: boolean + - name: VSCODE_QUALITY + type: string steps: - task: NodeTool@0 @@ -11,6 +13,14 @@ steps: versionFilePath: .nvmrc nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - task: AzureKeyVault@2 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: vscode + KeyVaultName: vscode-build-secrets + SecretsFilter: "github-distro-mixin-password" + - script: node build/setup-npm-registry.js $NPM_REGISTRY build condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) displayName: Setup NPM Registry @@ -43,6 +53,8 @@ steps: echo "Npm install failed $i, trying again..." done workingDirectory: build + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Install build dependencies - template: ../cli/cli-darwin-sign.yml@self diff --git a/build/azure-pipelines/darwin/product-build-darwin-sign.yml b/build/azure-pipelines/darwin/product-build-darwin-sign.yml index 82a1e89f2ab1..dffb6665d999 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-sign.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-sign.yml @@ -9,25 +9,49 @@ steps: inputs: version: 6.x - - task: EsrpClientTool@1 - continueOnError: true - displayName: Download ESRPClient - - - task: AzureKeyVault@2 - displayName: "Azure Key Vault: Get Secrets" + - task: EsrpCodeSigning@5 inputs: - azureSubscription: "vscode-builds-subscription" - KeyVaultName: vscode-build-secrets - SecretsFilter: "ESRP-PKI,esrp-aad-username,esrp-aad-password" + UseMSIAuthentication: true + ConnectedServiceName: vscode-esrp + AppRegistrationClientId: $(ESRP_CLIENT_ID) + AppRegistrationTenantId: $(ESRP_TENANT_ID) + AuthAKVName: vscode-esrp + AuthSignCertName: esrp-sign + FolderPath: . + Pattern: noop + displayName: 'Install ESRP Tooling' + + - script: | + # For legacy purposes, arch for x64 is just 'darwin' + case $VSCODE_ARCH in + x64) ASSET_ID="darwin" ;; + arm64) ASSET_ID="darwin-arm64" ;; + universal) ASSET_ID="darwin-universal" ;; + esac + echo "##vso[task.setvariable variable=ASSET_ID]$ASSET_ID" + displayName: Set asset id variable + + - script: | + if [ -z "$(ASSET_ID)" ]; then + echo "ASSET_ID is empty" + exit 1 + else + echo "ASSET_ID is set to $(ASSET_ID)" + fi + displayName: Check ASSET_ID variable - download: current artifact: unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive displayName: Download $(VSCODE_ARCH) artifact - - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll sign-darwin $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive VSCode-darwin-$(VSCODE_ARCH).zip + - script: node build/azure-pipelines/common/sign $(Agent.RootDirectory)/_tasks/EsrpCodeSigning_*/*/net6.0/esrpcli.dll sign-darwin $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive VSCode-darwin-$(VSCODE_ARCH).zip + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) displayName: Codesign - - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll notarize-darwin $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive VSCode-darwin-$(VSCODE_ARCH).zip + - script: node build/azure-pipelines/common/sign $(Agent.RootDirectory)/_tasks/EsrpCodeSigning_*/*/net6.0/esrpcli.dll notarize-darwin $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive VSCode-darwin-$(VSCODE_ARCH).zip + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) displayName: Notarize - script: unzip $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive/VSCode-darwin-$(VSCODE_ARCH).zip -d $(Agent.BuildDirectory)/VSCode-darwin-$(VSCODE_ARCH) @@ -43,16 +67,6 @@ steps: displayName: Verify signature condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'arm64')) - - script: | - # For legacy purposes, arch for x64 is just 'darwin' - case $VSCODE_ARCH in - x64) ASSET_ID="darwin" ;; - arm64) ASSET_ID="darwin-arm64" ;; - universal) ASSET_ID="darwin-universal" ;; - esac - echo "##vso[task.setvariable variable=ASSET_ID]$ASSET_ID" - displayName: Set asset id variable - - script: mv $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive/VSCode-darwin-x64.zip $(Pipeline.Workspace)/unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive/VSCode-darwin.zip displayName: Rename x64 build to its legacy name condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64')) diff --git a/build/azure-pipelines/darwin/product-build-darwin-test.yml b/build/azure-pipelines/darwin/product-build-darwin-test.yml index 359aa460a590..9e054574c81c 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-test.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-test.yml @@ -61,6 +61,7 @@ steps: compile-extension:typescript-language-features \ compile-extension:vscode-api-tests \ compile-extension:vscode-colorize-tests \ + compile-extension:vscode-colorize-perf-tests \ compile-extension:vscode-test-resolver displayName: Build integration tests diff --git a/build/azure-pipelines/darwin/product-build-darwin-universal.yml b/build/azure-pipelines/darwin/product-build-darwin-universal.yml index e9c532857955..3bb62e154034 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-universal.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-universal.yml @@ -10,7 +10,7 @@ steps: - task: AzureKeyVault@2 displayName: "Azure Key Vault: Get Secrets" inputs: - azureSubscription: "vscode-builds-subscription" + azureSubscription: vscode KeyVaultName: vscode-build-secrets SecretsFilter: "github-distro-mixin-password,macos-developer-certificate,macos-developer-certificate-key" @@ -46,6 +46,8 @@ steps: echo "Npm install failed $i, trying again..." done workingDirectory: build + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Install build dependencies - download: current diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 6709b9149c3f..bd37d675aa2f 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -28,7 +28,7 @@ steps: - task: AzureKeyVault@2 displayName: "Azure Key Vault: Get Secrets" inputs: - azureSubscription: "vscode-builds-subscription" + azureSubscription: vscode KeyVaultName: vscode-build-secrets SecretsFilter: "github-distro-mixin-password,macos-developer-certificate,macos-developer-certificate-key" @@ -121,6 +121,11 @@ steps: - template: ../common/install-builtin-extensions.yml@self + - ${{ if and(ne(parameters.VSCODE_CIBUILD, true), ne(parameters.VSCODE_QUALITY, 'oss')) }}: + - script: node build/lib/policies darwin + displayName: Generate policy definitions + retryCountOnTaskFailure: 3 + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - script: | set -e diff --git a/build/azure-pipelines/distro/download-distro.yml b/build/azure-pipelines/distro/download-distro.yml index 92325762a60c..5c9ed0e56cd3 100644 --- a/build/azure-pipelines/distro/download-distro.yml +++ b/build/azure-pipelines/distro/download-distro.yml @@ -2,7 +2,7 @@ steps: - task: AzureKeyVault@2 displayName: "Azure Key Vault: Get Secrets" inputs: - azureSubscription: "vscode-builds-subscription" + azureSubscription: vscode KeyVaultName: vscode-build-secrets SecretsFilter: "github-distro-mixin-password" diff --git a/build/azure-pipelines/distro/mixin-npm.js b/build/azure-pipelines/distro/mixin-npm.js index 0c61bb3dcf41..87958a5d4490 100644 --- a/build/azure-pipelines/distro/mixin-npm.js +++ b/build/azure-pipelines/distro/mixin-npm.js @@ -3,24 +3,27 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = require("fs"); -const path = require("path"); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); const { dirs } = require('../../npm/dirs'); function log(...args) { console.log(`[${new Date().toLocaleTimeString('en', { hour12: false })}]`, '[distro]', ...args); } function mixin(mixinPath) { - if (!fs.existsSync(`${mixinPath}/node_modules`)) { + if (!fs_1.default.existsSync(`${mixinPath}/node_modules`)) { log(`Skipping distro npm dependencies: ${mixinPath} (no node_modules)`); return; } log(`Mixing in distro npm dependencies: ${mixinPath}`); - const distroPackageJson = JSON.parse(fs.readFileSync(`${mixinPath}/package.json`, 'utf8')); - const targetPath = path.relative('.build/distro/npm', mixinPath); + const distroPackageJson = JSON.parse(fs_1.default.readFileSync(`${mixinPath}/package.json`, 'utf8')); + const targetPath = path_1.default.relative('.build/distro/npm', mixinPath); for (const dependency of Object.keys(distroPackageJson.dependencies)) { - fs.rmSync(`./${targetPath}/node_modules/${dependency}`, { recursive: true, force: true }); - fs.cpSync(`${mixinPath}/node_modules/${dependency}`, `./${targetPath}/node_modules/${dependency}`, { recursive: true, force: true, dereference: true }); + fs_1.default.rmSync(`./${targetPath}/node_modules/${dependency}`, { recursive: true, force: true }); + fs_1.default.cpSync(`${mixinPath}/node_modules/${dependency}`, `./${targetPath}/node_modules/${dependency}`, { recursive: true, force: true, dereference: true }); } log(`Mixed in distro npm dependencies: ${mixinPath} ✔︎`); } diff --git a/build/azure-pipelines/distro/mixin-npm.ts b/build/azure-pipelines/distro/mixin-npm.ts index da5eb24ca28b..6e32f10db508 100644 --- a/build/azure-pipelines/distro/mixin-npm.ts +++ b/build/azure-pipelines/distro/mixin-npm.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; +import fs from 'fs'; +import path from 'path'; const { dirs } = require('../../npm/dirs') as { dirs: string[] }; function log(...args: any[]): void { diff --git a/build/azure-pipelines/distro/mixin-quality.js b/build/azure-pipelines/distro/mixin-quality.js index 6e011b5a1e94..335f63ca1fc3 100644 --- a/build/azure-pipelines/distro/mixin-quality.js +++ b/build/azure-pipelines/distro/mixin-quality.js @@ -3,9 +3,12 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = require("fs"); -const path = require("path"); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); function log(...args) { console.log(`[${new Date().toLocaleTimeString('en', { hour12: false })}]`, '[distro]', ...args); } @@ -16,12 +19,12 @@ function main() { } log(`Mixing in distro quality...`); const basePath = `.build/distro/mixin/${quality}`; - for (const name of fs.readdirSync(basePath)) { - const distroPath = path.join(basePath, name); - const ossPath = path.relative(basePath, distroPath); + for (const name of fs_1.default.readdirSync(basePath)) { + const distroPath = path_1.default.join(basePath, name); + const ossPath = path_1.default.relative(basePath, distroPath); if (ossPath === 'product.json') { - const distro = JSON.parse(fs.readFileSync(distroPath, 'utf8')); - const oss = JSON.parse(fs.readFileSync(ossPath, 'utf8')); + const distro = JSON.parse(fs_1.default.readFileSync(distroPath, 'utf8')); + const oss = JSON.parse(fs_1.default.readFileSync(ossPath, 'utf8')); let builtInExtensions = oss.builtInExtensions; if (Array.isArray(distro.builtInExtensions)) { log('Overwriting built-in extensions:', distro.builtInExtensions.map(e => e.name)); @@ -41,10 +44,10 @@ function main() { log('Inheriting OSS built-in extensions', builtInExtensions.map(e => e.name)); } const result = { webBuiltInExtensions: oss.webBuiltInExtensions, ...distro, builtInExtensions }; - fs.writeFileSync(ossPath, JSON.stringify(result, null, '\t'), 'utf8'); + fs_1.default.writeFileSync(ossPath, JSON.stringify(result, null, '\t'), 'utf8'); } else { - fs.cpSync(distroPath, ossPath, { force: true, recursive: true }); + fs_1.default.cpSync(distroPath, ossPath, { force: true, recursive: true }); } log(distroPath, '✔︎'); } diff --git a/build/azure-pipelines/distro/mixin-quality.ts b/build/azure-pipelines/distro/mixin-quality.ts index b9b3c4f6c42b..29c90f00a65d 100644 --- a/build/azure-pipelines/distro/mixin-quality.ts +++ b/build/azure-pipelines/distro/mixin-quality.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; +import fs from 'fs'; +import path from 'path'; interface IBuiltInExtension { readonly name: string; diff --git a/build/azure-pipelines/linux/cli-build-linux.yml b/build/azure-pipelines/linux/cli-build-linux.yml index 89bc8a39e24f..dba949395de3 100644 --- a/build/azure-pipelines/linux/cli-build-linux.yml +++ b/build/azure-pipelines/linux/cli-build-linux.yml @@ -72,6 +72,8 @@ steps: echo "Npm install failed $i, trying again..." done workingDirectory: build + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Install build dependencies - script: | diff --git a/build/azure-pipelines/linux/product-build-linux-legacy-server.yml b/build/azure-pipelines/linux/product-build-linux-legacy-server.yml deleted file mode 100644 index 63f71890f199..000000000000 --- a/build/azure-pipelines/linux/product-build-linux-legacy-server.yml +++ /dev/null @@ -1,249 +0,0 @@ -parameters: - - name: VSCODE_QUALITY - type: string - - name: VSCODE_RUN_INTEGRATION_TESTS - type: boolean - - name: VSCODE_ARCH - type: string - -steps: - - task: NodeTool@0 - inputs: - versionSource: fromFile - versionFilePath: .nvmrc - nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download - - - template: ../distro/download-distro.yml - - - task: AzureKeyVault@2 - displayName: "Azure Key Vault: Get Secrets" - inputs: - azureSubscription: "vscode-builds-subscription" - KeyVaultName: vscode-build-secrets - SecretsFilter: "github-distro-mixin-password" - - - task: DownloadPipelineArtifact@2 - inputs: - artifact: Compilation - path: $(Build.ArtifactStagingDirectory) - displayName: Download compilation output - - - script: tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz - displayName: Extract compilation output - - - script: | - set -e - # Start X server - ./build/azure-pipelines/linux/apt-retry.sh sudo apt-get update - ./build/azure-pipelines/linux/apt-retry.sh sudo apt-get install -y pkg-config \ - dbus \ - xvfb \ - libgtk-3-0 \ - libxkbfile-dev \ - libkrb5-dev \ - libgbm1 \ - rpm - sudo cp build/azure-pipelines/linux/xvfb.init /etc/init.d/xvfb - sudo chmod +x /etc/init.d/xvfb - sudo update-rc.d xvfb defaults - sudo service xvfb start - # Start dbus session - sudo mkdir -p /var/run/dbus - DBUS_LAUNCH_RESULT=$(sudo dbus-daemon --config-file=/usr/share/dbus-1/system.conf --print-address) - echo "##vso[task.setvariable variable=DBUS_SESSION_BUS_ADDRESS]$DBUS_LAUNCH_RESULT" - displayName: Setup system services - - - script: node build/setup-npm-registry.js $NPM_REGISTRY - condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) - displayName: Setup NPM Registry - - - script: | - set -e - # Set the private NPM registry to the global npmrc file - # so that authentication works for subfolders like build/, remote/, extensions/ etc - # which does not have their own .npmrc file - npm config set registry "$NPM_REGISTRY" - echo "##vso[task.setvariable variable=NPMRC_PATH]$(npm config get userconfig)" - condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) - displayName: Setup NPM - - - task: npmAuthenticate@0 - inputs: - workingFile: $(NPMRC_PATH) - condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) - displayName: Setup NPM Authentication - - - ${{ if or(eq(parameters.VSCODE_ARCH, 'x64'), eq(parameters.VSCODE_ARCH, 'arm64')) }}: - - task: Docker@1 - displayName: "Pull Docker image" - inputs: - azureSubscriptionEndpoint: "vscode-builds-subscription" - azureContainerRegistry: vscodehub.azurecr.io - command: "Run an image" - imageName: vscode-linux-build-agent:centos7-devtoolset8-$(VSCODE_ARCH) - containerCommand: uname - - - ${{ if or(eq(parameters.VSCODE_ARCH, 'arm64'), eq(parameters.VSCODE_ARCH, 'armhf')) }}: - - script: | - set -e - includes=$(cat << 'EOF' - { - "target_defaults": { - "conditions": [ - ["OS=='linux'", { - 'cflags_cc!': [ '-std=gnu++20' ], - 'cflags_cc': [ '-std=gnu++2a' ], - }] - ] - } - } - EOF - ) - if [ ! -d "$HOME/.gyp" ]; then - mkdir -p "$HOME/.gyp" - fi - echo "$includes" > "$HOME/.gyp/include.gypi" - displayName: Override gnu target for arm64 and arm - - - script: | - set -e - - for i in {1..5}; do # try 5 times - npm ci && break - if [ $i -eq 5 ]; then - echo "Npm install failed too many times" >&2 - exit 1 - fi - echo "Npm install failed $i, trying again..." - done - workingDirectory: build - displayName: Install build dependencies - - - script: | - set -e - - export VSCODE_SYSROOT_PREFIX='-glibc-2.17' - source ./build/azure-pipelines/linux/setup-env.sh --skip-sysroot - - for i in {1..5}; do # try 5 times - npm ci && break - if [ $i -eq 5 ]; then - echo "Npm install failed too many times" >&2 - exit 1 - fi - echo "Npm install failed $i, trying again..." - done - env: - npm_config_arch: $(NPM_ARCH) - VSCODE_ARCH: $(VSCODE_ARCH) - NPM_REGISTRY: "$(NPM_REGISTRY)" - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - GITHUB_TOKEN: "$(github-distro-mixin-password)" - VSCODE_HOST_MOUNT: "/mnt/vss/_work/1/s" - ${{ if or(eq(parameters.VSCODE_ARCH, 'x64'), eq(parameters.VSCODE_ARCH, 'arm64')) }}: - VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME: vscodehub.azurecr.io/vscode-linux-build-agent:centos7-devtoolset8-$(VSCODE_ARCH) - displayName: Install dependencies - - - script: node build/azure-pipelines/distro/mixin-npm - displayName: Mixin distro node modules - - - script: node build/azure-pipelines/distro/mixin-quality - displayName: Mixin distro quality - - - template: ../common/install-builtin-extensions.yml - - - script: | - set -e - npm run gulp vscode-linux-$(VSCODE_ARCH)-min-ci - ARCHIVE_PATH=".build/linux/client/code-${{ parameters.VSCODE_QUALITY }}-$(VSCODE_ARCH)-$(date +%s).tar.gz" - mkdir -p $(dirname $ARCHIVE_PATH) - echo "##vso[task.setvariable variable=CLIENT_PATH]$ARCHIVE_PATH" - env: - GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Build client - - - script: | - set -e - tar -czf $CLIENT_PATH -C .. VSCode-linux-$(VSCODE_ARCH) - env: - GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Archive client - - - script: | - set -e - export VSCODE_NODE_GLIBC="-glibc-2.17" - npm run gulp vscode-reh-linux-$(VSCODE_ARCH)-min-ci - mv ../vscode-reh-linux-$(VSCODE_ARCH) ../vscode-server-linux-$(VSCODE_ARCH) # TODO@joaomoreno - ARCHIVE_PATH=".build/linux/server/vscode-server-linux-legacy-$(VSCODE_ARCH).tar.gz" - UNARCHIVE_PATH="`pwd`/../vscode-server-linux-$(VSCODE_ARCH)" - mkdir -p $(dirname $ARCHIVE_PATH) - tar --owner=0 --group=0 -czf $ARCHIVE_PATH -C .. vscode-server-linux-$(VSCODE_ARCH) - echo "##vso[task.setvariable variable=SERVER_PATH]$ARCHIVE_PATH" - echo "##vso[task.setvariable variable=SERVER_UNARCHIVE_PATH]$UNARCHIVE_PATH" - env: - GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Build server - - - script: | - set -e - export VSCODE_NODE_GLIBC="-glibc-2.17" - npm run gulp vscode-reh-web-linux-$(VSCODE_ARCH)-min-ci - mv ../vscode-reh-web-linux-$(VSCODE_ARCH) ../vscode-server-linux-$(VSCODE_ARCH)-web # TODO@joaomoreno - ARCHIVE_PATH=".build/linux/web/vscode-server-linux-legacy-$(VSCODE_ARCH)-web.tar.gz" - mkdir -p $(dirname $ARCHIVE_PATH) - tar --owner=0 --group=0 -czf $ARCHIVE_PATH -C .. vscode-server-linux-$(VSCODE_ARCH)-web - echo "##vso[task.setvariable variable=WEB_PATH]$ARCHIVE_PATH" - env: - GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Build server (web) - - - ${{ if or(eq(parameters.VSCODE_ARCH, 'x64'), eq(parameters.VSCODE_ARCH, 'arm64')) }}: - - script: | - set -e - EXPECTED_GLIBC_VERSION="2.17" \ - EXPECTED_GLIBCXX_VERSION="3.4.19" \ - ./build/azure-pipelines/linux/verify-glibc-requirements.sh - env: - SEARCH_PATH: $(SERVER_UNARCHIVE_PATH) - displayName: Check GLIBC and GLIBCXX dependencies in server archive - - - ${{ else }}: - - script: | - set -e - EXPECTED_GLIBC_VERSION="2.17" \ - EXPECTED_GLIBCXX_VERSION="3.4.22" \ - ./build/azure-pipelines/linux/verify-glibc-requirements.sh - env: - SEARCH_PATH: $(SERVER_UNARCHIVE_PATH) - displayName: Check GLIBC and GLIBCXX dependencies in server archive - - - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: - - template: product-build-linux-test.yml - parameters: - VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} - VSCODE_RUN_UNIT_TESTS: false - VSCODE_RUN_INTEGRATION_TESTS: ${{ parameters.VSCODE_RUN_INTEGRATION_TESTS }} - VSCODE_RUN_SMOKE_TESTS: false - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - PUBLISH_TASK_NAME: 1ES.PublishPipelineArtifact@1 - - - task: 1ES.PublishPipelineArtifact@1 - inputs: - targetPath: $(SERVER_PATH) - artifactName: $(ARTIFACT_PREFIX)vscode_server_linux_legacy_$(VSCODE_ARCH)_archive-unsigned - sbomBuildDropPath: $(Agent.BuildDirectory)/vscode-server-linux-$(VSCODE_ARCH) - sbomPackageName: "VS Code Linux $(VSCODE_ARCH) Legacy Server" - sbomPackageVersion: $(Build.SourceVersion) - condition: and(succeededOrFailed(), ne(variables['SERVER_PATH'], '')) - displayName: Publish server archive - - - task: 1ES.PublishPipelineArtifact@1 - inputs: - targetPath: $(WEB_PATH) - artifactName: $(ARTIFACT_PREFIX)vscode_web_linux_legacy_$(VSCODE_ARCH)_archive-unsigned - sbomBuildDropPath: $(Agent.BuildDirectory)/vscode-server-linux-$(VSCODE_ARCH)-web - sbomPackageName: "VS Code Linux $(VSCODE_ARCH) Legacy Web" - sbomPackageVersion: $(Build.SourceVersion) - condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], '')) - displayName: Publish web server archive diff --git a/build/azure-pipelines/linux/product-build-linux-test.yml b/build/azure-pipelines/linux/product-build-linux-test.yml index 5f7381d487c4..6796339c738f 100644 --- a/build/azure-pipelines/linux/product-build-linux-test.yml +++ b/build/azure-pipelines/linux/product-build-linux-test.yml @@ -80,6 +80,7 @@ steps: compile-extension:typescript-language-features \ compile-extension:vscode-api-tests \ compile-extension:vscode-colorize-tests \ + compile-extension:vscode-colorize-perf-tests \ compile-extension:vscode-test-resolver displayName: Build integration tests diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index fe3951c9e5fc..b9300b1ccba0 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -30,9 +30,9 @@ steps: - task: AzureKeyVault@2 displayName: "Azure Key Vault: Get Secrets" inputs: - azureSubscription: "vscode-builds-subscription" + azureSubscription: vscode KeyVaultName: vscode-build-secrets - SecretsFilter: "github-distro-mixin-password,ESRP-PKI,esrp-aad-username,esrp-aad-password" + SecretsFilter: "github-distro-mixin-password" - task: DownloadPipelineArtifact@2 inputs: @@ -111,6 +111,8 @@ steps: echo "Npm install failed $i, trying again..." done workingDirectory: build + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Install build dependencies condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) @@ -349,14 +351,26 @@ steps: inputs: version: 6.x - - task: EsrpClientTool@1 - continueOnError: true - displayName: Download ESRPClient - - - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll sign-pgp $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/deb '*.deb' + - task: EsrpCodeSigning@5 + inputs: + UseMSIAuthentication: true + ConnectedServiceName: vscode-esrp + AppRegistrationClientId: $(ESRP_CLIENT_ID) + AppRegistrationTenantId: $(ESRP_TENANT_ID) + AuthAKVName: vscode-esrp + AuthSignCertName: esrp-sign + FolderPath: . + Pattern: noop + displayName: 'Install ESRP Tooling' + + - script: node build/azure-pipelines/common/sign $(Agent.RootDirectory)/_tasks/EsrpCodeSigning_*/*/net6.0/esrpcli.dll sign-pgp .build/linux/deb '*.deb' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) displayName: Codesign deb - - script: node build/azure-pipelines/common/sign $(Agent.ToolsDirectory)/esrpclient/*/*/net6.0/esrpcli.dll sign-pgp $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/rpm '*.rpm' + - script: node build/azure-pipelines/common/sign $(Agent.RootDirectory)/_tasks/EsrpCodeSigning_*/*/net6.0/esrpcli.dll sign-pgp .build/linux/rpm '*.rpm' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) displayName: Codesign rpm - script: echo "##vso[task.setvariable variable=ARTIFACT_PREFIX]attempt$(System.JobAttempt)_" diff --git a/build/azure-pipelines/linux/setup-env.sh b/build/azure-pipelines/linux/setup-env.sh index 03e5b49d5b1a..94105642b190 100755 --- a/build/azure-pipelines/linux/setup-env.sh +++ b/build/azure-pipelines/linux/setup-env.sh @@ -16,56 +16,50 @@ else fi if [ "$npm_config_arch" == "x64" ]; then - # Download clang based on chromium revision used by vscode - curl -s https://raw.githubusercontent.com/chromium/chromium/128.0.6613.162/tools/clang/scripts/update.py | python - --output-dir=$PWD/.build/CR_Clang --host-os=linux + # Download clang based on chromium revision used by vscode + curl -s https://raw.githubusercontent.com/chromium/chromium/132.0.6834.210/tools/clang/scripts/update.py | python - --output-dir=$PWD/.build/CR_Clang --host-os=linux - # Download libcxx headers and objects from upstream electron releases - DEBUG=libcxx-fetcher \ - VSCODE_LIBCXX_OBJECTS_DIR=$PWD/.build/libcxx-objects \ - VSCODE_LIBCXX_HEADERS_DIR=$PWD/.build/libcxx_headers \ - VSCODE_LIBCXXABI_HEADERS_DIR=$PWD/.build/libcxxabi_headers \ - VSCODE_ARCH="$npm_config_arch" \ - node build/linux/libcxx-fetcher.js + # Download libcxx headers and objects from upstream electron releases + DEBUG=libcxx-fetcher \ + VSCODE_LIBCXX_OBJECTS_DIR=$PWD/.build/libcxx-objects \ + VSCODE_LIBCXX_HEADERS_DIR=$PWD/.build/libcxx_headers \ + VSCODE_LIBCXXABI_HEADERS_DIR=$PWD/.build/libcxxabi_headers \ + VSCODE_ARCH="$npm_config_arch" \ + node build/linux/libcxx-fetcher.js - # Set compiler toolchain - # Flags for the client build are based on - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/128.0.6613.162:build/config/arm.gni - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/128.0.6613.162:build/config/compiler/BUILD.gn - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/128.0.6613.162:build/config/c++/BUILD.gn - export CC="$PWD/.build/CR_Clang/bin/clang --gcc-toolchain=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu" - export CXX="$PWD/.build/CR_Clang/bin/clang++ --gcc-toolchain=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu" - export CXXFLAGS="-nostdinc++ -D__NO_INLINE__ -I$PWD/.build/libcxx_headers -isystem$PWD/.build/libcxx_headers/include -isystem$PWD/.build/libcxxabi_headers/include -fPIC -flto=thin -fsplit-lto-unit -D_LIBCPP_ABI_NAMESPACE=Cr -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE --sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot" - export LDFLAGS="-stdlib=libc++ --sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot -fuse-ld=lld -flto=thin -L$PWD/.build/libcxx-objects -lc++abi -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/usr/lib/x86_64-linux-gnu -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/lib/x86_64-linux-gnu -Wl,--lto-O0" + # Set compiler toolchain + # Flags for the client build are based on + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/132.0.6834.210:build/config/arm.gni + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/132.0.6834.210:build/config/compiler/BUILD.gn + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/132.0.6834.210:build/config/c++/BUILD.gn + export CC="$PWD/.build/CR_Clang/bin/clang --gcc-toolchain=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu" + export CXX="$PWD/.build/CR_Clang/bin/clang++ --gcc-toolchain=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu" + export CXXFLAGS="-nostdinc++ -D__NO_INLINE__ -DSPDLOG_USE_STD_FORMAT -I$PWD/.build/libcxx_headers -isystem$PWD/.build/libcxx_headers/include -isystem$PWD/.build/libcxxabi_headers/include -fPIC -flto=thin -fsplit-lto-unit -D_LIBCPP_ABI_NAMESPACE=Cr -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE --sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot" + export LDFLAGS="-stdlib=libc++ --sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot -fuse-ld=lld -flto=thin -L$PWD/.build/libcxx-objects -lc++abi -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/usr/lib/x86_64-linux-gnu -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/lib/x86_64-linux-gnu -Wl,--lto-O0" - if [ "$(echo "$@" | grep -c -- "--skip-sysroot")" -eq 0 ]; then - # Set compiler toolchain for remote server - export VSCODE_REMOTE_CC=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/bin/x86_64-linux-gnu-gcc - export VSCODE_REMOTE_CXX=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/bin/x86_64-linux-gnu-g++ - export VSCODE_REMOTE_CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot" - export VSCODE_REMOTE_LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/usr/lib/x86_64-linux-gnu -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/lib/x86_64-linux-gnu" - fi + # Set compiler toolchain for remote server + export VSCODE_REMOTE_CC=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/bin/x86_64-linux-gnu-gcc + export VSCODE_REMOTE_CXX=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/bin/x86_64-linux-gnu-g++ + export VSCODE_REMOTE_CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot" + export VSCODE_REMOTE_LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/usr/lib/x86_64-linux-gnu -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/lib/x86_64-linux-gnu" elif [ "$npm_config_arch" == "arm64" ]; then - if [ "$(echo "$@" | grep -c -- "--skip-sysroot")" -eq 0 ]; then - # Set compiler toolchain for client native modules - export CC=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc - export CXX=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-g++ - export CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot" - export LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot -L$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/usr/lib/aarch64-linux-gnu -L$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/lib/aarch64-linux-gnu" + # Set compiler toolchain for client native modules + export CC=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc + export CXX=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-g++ + export CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot" + export LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot -L$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/usr/lib/aarch64-linux-gnu -L$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/lib/aarch64-linux-gnu" - # Set compiler toolchain for remote server - export VSCODE_REMOTE_CC=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc - export VSCODE_REMOTE_CXX=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-g++ - export VSCODE_REMOTE_CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot" - export VSCODE_REMOTE_LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot -L$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/usr/lib/aarch64-linux-gnu -L$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/lib/aarch64-linux-gnu" - fi + # Set compiler toolchain for remote server + export VSCODE_REMOTE_CC=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc + export VSCODE_REMOTE_CXX=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-g++ + export VSCODE_REMOTE_CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot" + export VSCODE_REMOTE_LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot -L$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/usr/lib/aarch64-linux-gnu -L$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/lib/aarch64-linux-gnu" elif [ "$npm_config_arch" == "arm" ]; then - if [ "$(echo "$@" | grep -c -- "--skip-sysroot")" -eq 0 ]; then - # Set compiler toolchain for client native modules - export CC=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/bin/arm-rpi-linux-gnueabihf-gcc - export CXX=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/bin/arm-rpi-linux-gnueabihf-g++ - export CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot" - export LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot -L$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot/usr/lib/arm-linux-gnueabihf -L$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot/lib/arm-linux-gnueabihf" - fi + # Set compiler toolchain for client native modules + export CC=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/bin/arm-rpi-linux-gnueabihf-gcc + export CXX=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/bin/arm-rpi-linux-gnueabihf-g++ + export CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot" + export LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot -L$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot/usr/lib/arm-linux-gnueabihf -L$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot/lib/arm-linux-gnueabihf" # Set compiler toolchain for remote server export VSCODE_REMOTE_CC=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/bin/arm-rpi-linux-gnueabihf-gcc diff --git a/build/azure-pipelines/linux/snap-build-linux.yml b/build/azure-pipelines/linux/snap-build-linux.yml index 3582be07ccaf..4d0d26411c33 100644 --- a/build/azure-pipelines/linux/snap-build-linux.yml +++ b/build/azure-pipelines/linux/snap-build-linux.yml @@ -25,9 +25,6 @@ steps: # Define variables SNAP_ROOT="$(pwd)/.build/linux/snap/$(VSCODE_ARCH)" - # Install build dependencies - (cd build && npm ci) - # Unpack snap tarball artifact, in order to preserve file perms (cd .build/linux && tar -xzf snap-tarball/snap-$(VSCODE_ARCH).tar.gz) diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index a196a1117f83..8ec24be3892b 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -8,6 +8,7 @@ schedules: - main trigger: + batch: true branches: include: ["main", "release/*"] @@ -40,26 +41,14 @@ parameters: displayName: "🎯 Linux x64" type: boolean default: true - - name: VSCODE_BUILD_LINUX_X64_LEGACY_SERVER - displayName: "🎯 Linux x64 Legacy Server" - type: boolean - default: true - name: VSCODE_BUILD_LINUX_ARM64 displayName: "🎯 Linux arm64" type: boolean default: true - - name: VSCODE_BUILD_LINUX_ARM64_LEGACY_SERVER - displayName: "🎯 Linux arm64 Legacy Server" - type: boolean - default: true - name: VSCODE_BUILD_LINUX_ARMHF displayName: "🎯 Linux armhf" type: boolean default: true - - name: VSCODE_BUILD_LINUX_ARMHF_LEGACY_SERVER - displayName: "🎯 Linux armhf Legacy Server" - type: boolean - default: true - name: VSCODE_BUILD_ALPINE displayName: "🎯 Alpine x64" type: boolean @@ -105,7 +94,10 @@ variables: - name: VSCODE_PRIVATE_BUILD value: ${{ ne(variables['Build.Repository.Uri'], 'https://github.com/microsoft/vscode.git') }} - name: NPM_REGISTRY - value: ${{ parameters.NPM_REGISTRY }} + ${{ if in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI') }}: # disable terrapin when in VSCODE_CIBUILD + value: none + ${{ else }}: + value: ${{ parameters.NPM_REGISTRY }} - name: CARGO_REGISTRY value: ${{ parameters.CARGO_REGISTRY }} - name: VSCODE_QUALITY @@ -114,8 +106,6 @@ variables: value: ${{ or(eq(parameters.VSCODE_BUILD_WIN32, true), eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }} - name: VSCODE_BUILD_STAGE_LINUX value: ${{ or(eq(parameters.VSCODE_BUILD_LINUX, true), eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true)) }} - - name: VSCODE_BUILD_STAGE_LINUX_LEGACY_SERVER - value: ${{ or(eq(parameters.VSCODE_BUILD_LINUX_X64_LEGACY_SERVER, true), eq(parameters.VSCODE_BUILD_LINUX_ARMHF_LEGACY_SERVER, true), eq(parameters.VSCODE_BUILD_LINUX_ARM64_LEGACY_SERVER, true)) }} - name: VSCODE_BUILD_STAGE_ALPINE value: ${{ or(eq(parameters.VSCODE_BUILD_ALPINE, true), eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true)) }} - name: VSCODE_BUILD_STAGE_MACOS @@ -128,22 +118,26 @@ variables: value: ${{ and(eq(parameters.VSCODE_PUBLISH, true), eq(variables.VSCODE_CIBUILD, false), eq(parameters.VSCODE_COMPILE_ONLY, false)) }} - name: VSCODE_SCHEDULEDBUILD value: ${{ eq(variables['Build.Reason'], 'Schedule') }} - - name: VSCODE_7PM_BUILD - value: ${{ in(variables['Build.Reason'], 'BuildCompletion', 'ResourceTrigger') }} - name: VSCODE_STEP_ON_IT value: ${{ eq(parameters.VSCODE_STEP_ON_IT, true) }} - name: VSCODE_BUILD_MACOS_UNIVERSAL value: ${{ and(eq(parameters.VSCODE_BUILD_MACOS, true), eq(parameters.VSCODE_BUILD_MACOS_ARM64, true), eq(parameters.VSCODE_BUILD_MACOS_UNIVERSAL, true)) }} + - name: VSCODE_STAGING_BLOB_STORAGE_ACCOUNT_NAME + value: vscodeesrp - name: PRSS_CDN_URL value: https://vscode.download.prss.microsoft.com/dbazure/download - - name: PRSS_RELEASE_TENANT_ID + - name: VSCODE_ESRP_SERVICE_CONNECTION_ID + value: fe07e6ce-6ffb-4df9-8d27-d129523a3f3e + - name: VSCODE_ESRP_TENANT_ID value: 975f013f-7f24-47e8-a7d3-abc4752bf346 - - name: PRSS_RELEASE_CLIENT_ID + - name: VSCODE_ESRP_CLIENT_ID + value: 4ac7ed59-b5e9-4f66-9c30-8d1afa72d32d + - name: ESRP_TENANT_ID + value: 975f013f-7f24-47e8-a7d3-abc4752bf346 + - name: ESRP_CLIENT_ID value: c24324f7-e65f-4c45-8702-ed2d4c35df99 - - name: PRSS_PROVISION_TENANT_ID - value: 72f988bf-86f1-41af-91ab-2d7cd011db47 - name: AZURE_DOCUMENTDB_ENDPOINT - value: https://vscode.documents.azure.com:443/ + value: https://vscode.documents.azure.com/ - name: VSCODE_MIXIN_REPO value: microsoft/vscode-distro - name: skipComponentGovernanceDetection @@ -175,6 +169,8 @@ extends: tsa: enabled: true configFile: $(Build.SourcesDirectory)/build/azure-pipelines/config/tsaoptions.json + binskim: + analyzeTargetGlob: '+:file|$(Agent.BuildDirectory)/VSCode-*/**/*.exe;+:file|$(Agent.BuildDirectory)/VSCode-*/**/*.node;+:file|$(Agent.BuildDirectory)/VSCode-*/**/*.dll;-:file|$(Build.SourcesDirectory)/.build/**/system-setup/VSCodeSetup*.exe;-:file|$(Build.SourcesDirectory)/.build/**/user-setup/VSCodeUserSetup*.exe' codeql: runSourceLanguagesInSourceAnalysis: true compiled: @@ -193,20 +189,21 @@ extends: image: vscodehub.azurecr.io/vscode-linux-build-agent:snapcraft-x64 ubuntu-2004-arm64: image: onebranch.azurecr.io/linux/ubuntu-2004-arm64:latest - authenticatedContainerRegistries: - - registry: onebranch.azurecr.io - tenant: AME - identity: 1ESPipelineIdentity + featureFlags: + EnableDefenderForLinux: true stages: - stage: Compile jobs: - job: Compile timeoutInMinutes: 90 pool: - name: 1es-ubuntu-22.04-x64 - os: linux + name: AcesShared + os: macOS + # name: 1es-ubuntu-22.04-x64 + # os: linux variables: - VSCODE_ARCH: x64 + # VSCODE_ARCH: x64 + VSCODE_ARCH: arm64 steps: - template: build/azure-pipelines/product-compile.yml@self parameters: @@ -251,25 +248,30 @@ extends: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} VSCODE_BUILD_ALPINE: ${{ parameters.VSCODE_BUILD_ALPINE }} - - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true)) }}: - - job: CLIAlpineARM64 - pool: - name: 1es-mariner-2.0-arm64 - os: linux - hostArchitecture: arm64 - container: ubuntu-2004-arm64 - steps: - - template: build/azure-pipelines/alpine/cli-build-alpine.yml@self - parameters: - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_ALPINE_ARM64: ${{ parameters.VSCODE_BUILD_ALPINE_ARM64 }} + # - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true)) }}: + # - job: CLIAlpineARM64 + # pool: + # name: 1es-mariner-2.0-arm64 + # os: linux + # hostArchitecture: arm64 + # container: ubuntu-2004-arm64 + # steps: + # - template: build/azure-pipelines/alpine/cli-build-alpine.yml@self + # parameters: + # VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + # VSCODE_BUILD_ALPINE_ARM64: ${{ parameters.VSCODE_BUILD_ALPINE_ARM64 }} - ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}: - job: CLIMacOSX64 pool: - name: Azure Pipelines - image: macOS-13 + name: AcesShared + # name: Azure Pipelines + # image: macOS-13 os: macOS + variables: + # todo@connor4312 to diagnose build flakes + - name: MSRUSTUP_LOG + value: debug steps: - template: build/azure-pipelines/darwin/cli-build-darwin.yml@self parameters: @@ -280,9 +282,14 @@ extends: - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_MACOS_ARM64, true)) }}: - job: CLIMacOSARM64 pool: - name: Azure Pipelines - image: macOS-13 + name: AcesShared + # name: Azure Pipelines + # image: macOS-13 os: macOS + variables: + # todo@connor4312 to diagnose build flakes + - name: MSRUSTUP_LOG + value: debug steps: - template: build/azure-pipelines/darwin/cli-build-darwin.yml@self parameters: @@ -313,15 +320,13 @@ extends: VSCODE_BUILD_WIN32_ARM64: ${{ parameters.VSCODE_BUILD_WIN32_ARM64 }} - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_COMPILE_ONLY, false)) }}: - - stage: CustomSDL + - stage: APIScan dependsOn: [] pool: name: 1es-windows-2019-x64 os: windows jobs: - - job: WindowsSDL - variables: - - group: 'API Scan' + - job: WindowsAPIScan steps: - template: build/azure-pipelines/win32/sdl-scan-win32.yml@self parameters: @@ -402,6 +407,7 @@ extends: steps: - template: build/azure-pipelines/win32/product-build-win32-cli-sign.yml@self parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} VSCODE_BUILD_WIN32: ${{ parameters.VSCODE_BUILD_WIN32 }} VSCODE_BUILD_WIN32_ARM64: ${{ parameters.VSCODE_BUILD_WIN32_ARM64 }} @@ -494,15 +500,15 @@ extends: VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} VSCODE_RUN_SMOKE_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} - - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX, true)) }}: - - job: LinuxSnap - dependsOn: - - Linuxx64 - container: snapcraft - variables: - VSCODE_ARCH: x64 - steps: - - template: build/azure-pipelines/linux/snap-build-linux.yml@self + # - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX, true)) }}: + # - job: LinuxSnap + # dependsOn: + # - Linuxx64 + # container: snapcraft + # variables: + # VSCODE_ARCH: x64 + # steps: + # - template: build/azure-pipelines/linux/snap-build-linux.yml@self - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true)) }}: - job: LinuxArmhf @@ -534,51 +540,6 @@ extends: VSCODE_RUN_INTEGRATION_TESTS: false VSCODE_RUN_SMOKE_TESTS: false - - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_LINUX_LEGACY_SERVER'], true)) }}: - - stage: LinuxLegacyServer - dependsOn: - - Compile - pool: - name: 1es-ubuntu-20.04-x64 - os: linux - jobs: - - ${{ if eq(parameters.VSCODE_BUILD_LINUX_X64_LEGACY_SERVER, true) }}: - - job: Linuxx64LegacyServer - variables: - VSCODE_ARCH: x64 - NPM_ARCH: x64 - DISPLAY: ":10" - steps: - - template: build/azure-pipelines/linux/product-build-linux-legacy-server.yml@self - parameters: - VSCODE_ARCH: x64 - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} - - - ${{ if eq(parameters.VSCODE_BUILD_LINUX_ARMHF_LEGACY_SERVER, true) }}: - - job: LinuxArmhfLegacyServer - variables: - VSCODE_ARCH: armhf - NPM_ARCH: arm - steps: - - template: build/azure-pipelines/linux/product-build-linux-legacy-server.yml@self - parameters: - VSCODE_ARCH: armhf - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_RUN_INTEGRATION_TESTS: false - - - ${{ if eq(parameters.VSCODE_BUILD_LINUX_ARM64_LEGACY_SERVER, true) }}: - - job: LinuxArm64LegacyServer - variables: - VSCODE_ARCH: arm64 - NPM_ARCH: arm64 - steps: - - template: build/azure-pipelines/linux/product-build-linux-legacy-server.yml@self - parameters: - VSCODE_ARCH: arm64 - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_RUN_INTEGRATION_TESTS: false - - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_ALPINE'], true)) }}: - stage: Alpine dependsOn: @@ -613,8 +574,7 @@ extends: - ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true),eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true),eq(parameters.VSCODE_BUILD_LINUX_ARM64, true),eq(parameters.VSCODE_BUILD_ALPINE, true),eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true),eq(parameters.VSCODE_BUILD_MACOS, true),eq(parameters.VSCODE_BUILD_MACOS_ARM64, true),eq(parameters.VSCODE_BUILD_WIN32, true),eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}: - CompileCLI pool: - name: Azure Pipelines - image: macOS-13 + name: AcesShared os: macOS variables: BUILDSECMON_OPT_IN: true @@ -678,7 +638,7 @@ extends: - job: macOSTest timeoutInMinutes: 90 variables: - VSCODE_ARCH: x64 + VSCODE_ARCH: arm64 steps: - template: build/azure-pipelines/darwin/product-build-darwin.yml@self parameters: @@ -702,6 +662,7 @@ extends: steps: - template: build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml@self parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} VSCODE_BUILD_MACOS: ${{ parameters.VSCODE_BUILD_MACOS }} VSCODE_BUILD_MACOS_ARM64: ${{ parameters.VSCODE_BUILD_MACOS_ARM64 }} @@ -786,16 +747,12 @@ extends: name: 1es-ubuntu-22.04-x64 os: linux jobs: - - deployment: ApproveRelease + - job: ApproveRelease displayName: "Approve Release" - environment: "vscode" variables: - skipComponentGovernanceDetection: true - strategy: - runOnce: - deploy: - steps: - - checkout: none + - group: VSCodePeerApproval + - name: skipComponentGovernanceDetection + value: true - ${{ if or(and(parameters.VSCODE_RELEASE, eq(variables['VSCODE_PRIVATE_BUILD'], false)), and(in(parameters.VSCODE_QUALITY, 'insider', 'exploration'), eq(variables['VSCODE_SCHEDULEDBUILD'], true))) }}: - stage: Release diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index facc7af4bc28..e3e0e4227412 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -15,7 +15,7 @@ steps: - task: AzureKeyVault@2 displayName: "Azure Key Vault: Get Secrets" inputs: - azureSubscription: "vscode-builds-subscription" + azureSubscription: vscode KeyVaultName: vscode-build-secrets SecretsFilter: "github-distro-mixin-password" @@ -23,7 +23,7 @@ steps: condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) displayName: Setup NPM Registry - - script: mkdir -p .build && node build/azure-pipelines/common/computeNodeModulesCacheKey.js compile > .build/packagelockhash + - script: mkdir -p .build && node build/azure-pipelines/common/computeNodeModulesCacheKey.js compile $VSCODE_ARCH > .build/packagelockhash displayName: Prepare node_modules cache key - task: Cache@2 @@ -53,9 +53,9 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), ne(variables['NPM_REGISTRY'], 'none')) displayName: Setup NPM Authentication - - script: sudo apt update -y && sudo apt install -y build-essential pkg-config libx11-dev libx11-xcb-dev libxkbfile-dev libnotify-bin libkrb5-dev - displayName: Install build tools - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + # - script: sudo apt update -y && sudo apt install -y build-essential pkg-config libx11-dev libx11-xcb-dev libxkbfile-dev libnotify-bin libkrb5-dev + # displayName: Install build tools + # condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - script: | set -e @@ -103,12 +103,12 @@ steps: - template: common/install-builtin-extensions.yml@self - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: - - script: npm exec -- npm-run-all -lp core-ci-pr extensions-ci-pr hygiene eslint valid-layers-check vscode-dts-compile-check tsec-compile-check + - script: npm exec -- npm-run-all -lp core-ci-pr extensions-ci-pr hygiene eslint valid-layers-check property-init-order-check vscode-dts-compile-check tsec-compile-check env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Compile & Hygiene (OSS) - ${{ else }}: - - script: npm exec -- npm-run-all -lp core-ci extensions-ci hygiene eslint valid-layers-check vscode-dts-compile-check tsec-compile-check + - script: npm exec -- npm-run-all -lp core-ci extensions-ci hygiene eslint valid-layers-check property-init-order-check vscode-dts-compile-check tsec-compile-check env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Compile & Hygiene (non-OSS) @@ -131,37 +131,28 @@ steps: - task: AzureCLI@2 displayName: Fetch secrets inputs: - azureSubscription: "vscode-builds-subscription" + azureSubscription: vscode scriptType: pscore scriptLocation: inlineScript addSpnToEnvironment: true inlineScript: | Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId" Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" - Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" + Write-Host "##vso[task.setvariable variable=AZURE_ID_TOKEN;issecret=true]$env:idToken" - script: | set -e AZURE_STORAGE_ACCOUNT="vscodeweb" \ AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ - AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ + AZURE_ID_TOKEN="$(AZURE_ID_TOKEN)" \ node build/azure-pipelines/upload-sourcemaps displayName: Upload sourcemaps to Azure - - script: | - set -e - AZURE_STORAGE_ACCOUNT="ticino" \ - AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ - AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ - AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ - node build/azure-pipelines/upload-sourcemaps - displayName: Upload sourcemaps to Azure (Deprecated) - - script: ./build/azure-pipelines/common/extract-telemetry.sh displayName: Generate lists of telemetry events - - script: tar -cz --ignore-failed-read --exclude='.build/node_modules_cache' --exclude='.build/node_modules_list.txt' --exclude='.build/distro' -f $(Build.ArtifactStagingDirectory)/compilation.tar.gz .build out-* test/integration/browser/out test/smoke/out test/automation/out + - script: tar -cz --exclude='.build/node_modules_cache' --exclude='.build/node_modules_list.txt' --exclude='.build/distro' -f $(Build.ArtifactStagingDirectory)/compilation.tar.gz $(ls -d .build out-* test/integration/browser/out test/smoke/out test/automation/out 2>/dev/null) displayName: Compress compilation artifact - task: 1ES.PublishPipelineArtifact@1 diff --git a/build/azure-pipelines/product-publish.yml b/build/azure-pipelines/product-publish.yml index 59012a938acc..1127e5212e9b 100644 --- a/build/azure-pipelines/product-publish.yml +++ b/build/azure-pipelines/product-publish.yml @@ -5,22 +5,19 @@ steps: versionFilePath: .nvmrc nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download - - task: SFP.build-tasks.esrpclient-tools-task.EsrpClientTool@2 - displayName: "Use EsrpClient" - - task: AzureKeyVault@2 displayName: "Azure Key Vault: Get Secrets" inputs: - azureSubscription: "vscode-builds-subscription" + azureSubscription: vscode KeyVaultName: vscode-build-secrets - SecretsFilter: "github-distro-mixin-password,esrp-aad-username,esrp-aad-password" + SecretsFilter: "github-distro-mixin-password" - task: AzureKeyVault@2 - displayName: "Azure Key Vault: Get Secrets" + displayName: "Azure Key Vault: Get ESRP Secrets" inputs: - azureSubscription: "vscode-builds-subscription" - KeyVaultName: vscode-build-packages - SecretsFilter: "vscode-esrp,c24324f7-e65f-4c45-8702-ed2d4c35df99" + azureSubscription: vscode-esrp + KeyVaultName: vscode-esrp + SecretsFilter: esrp-auth,esrp-sign # allow-any-unicode-next-line - pwsh: Write-Host "##vso[build.addbuildtag]🚀" @@ -29,6 +26,8 @@ steps: - pwsh: | npm ci workingDirectory: build + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Install build dependencies - download: current @@ -38,14 +37,14 @@ steps: - task: AzureCLI@2 displayName: Fetch secrets inputs: - azureSubscription: "vscode-builds-subscription" + azureSubscription: vscode scriptType: pscore scriptLocation: inlineScript addSpnToEnvironment: true inlineScript: | Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId" Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" - Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" + Write-Host "##vso[task.setvariable variable=AZURE_ID_TOKEN;issecret=true]$env:idToken" - pwsh: | . build/azure-pipelines/win32/exec.ps1 @@ -61,41 +60,30 @@ steps: env: AZURE_TENANT_ID: "$(AZURE_TENANT_ID)" AZURE_CLIENT_ID: "$(AZURE_CLIENT_ID)" - AZURE_CLIENT_SECRET: "$(AZURE_CLIENT_SECRET)" + AZURE_ID_TOKEN: "$(AZURE_ID_TOKEN)" displayName: Create build if it hasn't been created before - pwsh: | - $ErrorActionPreference = "Stop" - $CertCollection = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2Collection - $AuthCertBytes = [System.Convert]::FromBase64String("$(vscode-esrp)") - $CertCollection.Import($AuthCertBytes, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable -bxor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet) - $RequestSigningCertIndex = $CertCollection.Count - $RequestSigningCertBytes = [System.Convert]::FromBase64String("$(c24324f7-e65f-4c45-8702-ed2d4c35df99)") - $CertCollection.Import($RequestSigningCertBytes, $null, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable -bxor [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet) - $CertStore = New-Object System.Security.Cryptography.X509Certificates.X509Store("My","LocalMachine") - $CertStore.Open("ReadWrite") - $CertStore.AddRange($CertCollection) - $CertStore.Close() - $AuthCertSubjectName = $CertCollection[0].Subject - $RequestSigningCertSubjectName = $CertCollection[$RequestSigningCertIndex].Subject - Write-Host "##vso[task.setvariable variable=RELEASE_AUTH_CERT_SUBJECT_NAME]$AuthCertSubjectName" - Write-Host "##vso[task.setvariable variable=RELEASE_REQUEST_SIGNING_CERT_SUBJECT_NAME]$RequestSigningCertSubjectName" - displayName: Import certificates + $publishAuthTokens = (node build/azure-pipelines/common/getPublishAuthTokens) + Write-Host "##vso[task.setvariable variable=PUBLISH_AUTH_TOKENS;issecret=true]$publishAuthTokens" + env: + AZURE_TENANT_ID: "$(AZURE_TENANT_ID)" + AZURE_CLIENT_ID: "$(AZURE_CLIENT_ID)" + AZURE_ID_TOKEN: "$(AZURE_ID_TOKEN)" + displayName: Get publish auth tokens - pwsh: node build/azure-pipelines/common/publish.js env: GITHUB_TOKEN: "$(github-distro-mixin-password)" AZURE_TENANT_ID: "$(AZURE_TENANT_ID)" AZURE_CLIENT_ID: "$(AZURE_CLIENT_ID)" - AZURE_CLIENT_SECRET: "$(AZURE_CLIENT_SECRET)" + AZURE_ID_TOKEN: "$(AZURE_ID_TOKEN)" SYSTEM_ACCESSTOKEN: $(System.AccessToken) - RELEASE_TENANT_ID: "$(PRSS_RELEASE_TENANT_ID)" - RELEASE_CLIENT_ID: "$(PRSS_RELEASE_CLIENT_ID)" - RELEASE_AUTH_CERT_SUBJECT_NAME: "$(RELEASE_AUTH_CERT_SUBJECT_NAME)" - RELEASE_REQUEST_SIGNING_CERT_SUBJECT_NAME: "$(RELEASE_REQUEST_SIGNING_CERT_SUBJECT_NAME)" - PROVISION_TENANT_ID: "$(PRSS_PROVISION_TENANT_ID)" - PROVISION_AAD_USERNAME: "$(esrp-aad-username)" - PROVISION_AAD_PASSWORD: "$(esrp-aad-password)" + PUBLISH_AUTH_TOKENS: "$(PUBLISH_AUTH_TOKENS)" + RELEASE_TENANT_ID: "$(ESRP_TENANT_ID)" + RELEASE_CLIENT_ID: "$(ESRP_CLIENT_ID)" + RELEASE_AUTH_CERT: "$(esrp-auth)" + RELEASE_REQUEST_SIGNING_CERT: "$(esrp-sign)" displayName: Process artifacts retryCountOnTaskFailure: 3 @@ -114,7 +102,6 @@ steps: $stages = @( if ($env:VSCODE_BUILD_STAGE_WINDOWS -eq 'True') { 'Windows' } if ($env:VSCODE_BUILD_STAGE_LINUX -eq 'True') { 'Linux' } - if ($env:VSCODE_BUILD_STAGE_LINUX_LEGACY_SERVER -eq 'True') { 'LinuxLegacyServer' } if ($env:VSCODE_BUILD_STAGE_ALPINE -eq 'True') { 'Alpine' } if ($env:VSCODE_BUILD_STAGE_MACOS -eq 'True') { 'macOS' } if ($env:VSCODE_BUILD_STAGE_WEB -eq 'True') { 'Web' } diff --git a/build/azure-pipelines/product-release.yml b/build/azure-pipelines/product-release.yml index 8afdcf10053e..87896f9340b8 100644 --- a/build/azure-pipelines/product-release.yml +++ b/build/azure-pipelines/product-release.yml @@ -12,18 +12,16 @@ steps: - task: AzureCLI@2 displayName: Fetch secrets inputs: - azureSubscription: "vscode-builds-subscription" + azureSubscription: vscode scriptType: pscore scriptLocation: inlineScript addSpnToEnvironment: true inlineScript: | Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId" Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" - Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" + Write-Host "##vso[task.setvariable variable=AZURE_ID_TOKEN;issecret=true]$env:idToken" - - script: | - set -e - npm ci + - script: npm ci workingDirectory: build displayName: Install /build dependencies @@ -31,6 +29,6 @@ steps: set -e AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ - AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ - node build/azure-pipelines/common/releaseBuild.js ${{ parameters.VSCODE_RELEASE }} + AZURE_ID_TOKEN="$(AZURE_ID_TOKEN)" \ + node build/azure-pipelines/common/releaseBuild.js ${{ parameters.VSCODE_RELEASE }} displayName: Release build diff --git a/build/azure-pipelines/publish-types/check-version.js b/build/azure-pipelines/publish-types/check-version.js index 9e93a7fa4c97..5bd80a69bbfc 100644 --- a/build/azure-pipelines/publish-types/check-version.js +++ b/build/azure-pipelines/publish-types/check-version.js @@ -3,11 +3,14 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const cp = require("child_process"); +const child_process_1 = __importDefault(require("child_process")); let tag = ''; try { - tag = cp + tag = child_process_1.default .execSync('git describe --tags `git rev-list --tags --max-count=1`') .toString() .trim(); diff --git a/build/azure-pipelines/publish-types/check-version.ts b/build/azure-pipelines/publish-types/check-version.ts index 35c5a5115939..4496ed93af11 100644 --- a/build/azure-pipelines/publish-types/check-version.ts +++ b/build/azure-pipelines/publish-types/check-version.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as cp from 'child_process'; +import cp from 'child_process'; let tag = ''; try { diff --git a/build/azure-pipelines/publish-types/update-types.js b/build/azure-pipelines/publish-types/update-types.js index ed2deded3fce..29f9bfcf66eb 100644 --- a/build/azure-pipelines/publish-types/update-types.js +++ b/build/azure-pipelines/publish-types/update-types.js @@ -3,19 +3,22 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = require("fs"); -const cp = require("child_process"); -const path = require("path"); +const fs_1 = __importDefault(require("fs")); +const child_process_1 = __importDefault(require("child_process")); +const path_1 = __importDefault(require("path")); let tag = ''; try { - tag = cp + tag = child_process_1.default .execSync('git describe --tags `git rev-list --tags --max-count=1`') .toString() .trim(); const dtsUri = `https://raw.githubusercontent.com/microsoft/vscode/${tag}/src/vscode-dts/vscode.d.ts`; - const outPath = path.resolve(process.cwd(), 'DefinitelyTyped/types/vscode/index.d.ts'); - cp.execSync(`curl ${dtsUri} --output ${outPath}`); + const outPath = path_1.default.resolve(process.cwd(), 'DefinitelyTyped/types/vscode/index.d.ts'); + child_process_1.default.execSync(`curl ${dtsUri} --output ${outPath}`); updateDTSFile(outPath, tag); console.log(`Done updating vscode.d.ts at ${outPath}`); } @@ -25,9 +28,9 @@ catch (err) { process.exit(1); } function updateDTSFile(outPath, tag) { - const oldContent = fs.readFileSync(outPath, 'utf-8'); + const oldContent = fs_1.default.readFileSync(outPath, 'utf-8'); const newContent = getNewFileContent(oldContent, tag); - fs.writeFileSync(outPath, newContent); + fs_1.default.writeFileSync(outPath, newContent); } function repeat(str, times) { const result = new Array(times); diff --git a/build/azure-pipelines/publish-types/update-types.ts b/build/azure-pipelines/publish-types/update-types.ts index a727647e64a2..3bb02b7adabd 100644 --- a/build/azure-pipelines/publish-types/update-types.ts +++ b/build/azure-pipelines/publish-types/update-types.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as cp from 'child_process'; -import * as path from 'path'; +import fs from 'fs'; +import cp from 'child_process'; +import path from 'path'; let tag = ''; try { @@ -16,7 +16,7 @@ try { const dtsUri = `https://raw.githubusercontent.com/microsoft/vscode/${tag}/src/vscode-dts/vscode.d.ts`; const outPath = path.resolve(process.cwd(), 'DefinitelyTyped/types/vscode/index.d.ts'); - cp.execSync(`curl ${dtsUri} --output ${outPath}`); + cp.execFileSync('curl', [dtsUri, '--output', outPath]); updateDTSFile(outPath, tag); diff --git a/build/azure-pipelines/upload-cdn.js b/build/azure-pipelines/upload-cdn.js index 62247de06bfd..f8247450f258 100644 --- a/build/azure-pipelines/upload-cdn.js +++ b/build/azure-pipelines/upload-cdn.js @@ -3,18 +3,21 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const es = require("event-stream"); -const Vinyl = require("vinyl"); -const vfs = require("vinyl-fs"); -const filter = require("gulp-filter"); -const gzip = require("gulp-gzip"); -const mime = require("mime"); +const event_stream_1 = __importDefault(require("event-stream")); +const vinyl_1 = __importDefault(require("vinyl")); +const vinyl_fs_1 = __importDefault(require("vinyl-fs")); +const gulp_filter_1 = __importDefault(require("gulp-filter")); +const gulp_gzip_1 = __importDefault(require("gulp-gzip")); +const mime_1 = __importDefault(require("mime")); const identity_1 = require("@azure/identity"); const azure = require('gulp-azure-storage'); const commit = process.env['BUILD_SOURCEVERSION']; -const credential = new identity_1.ClientSecretCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_CLIENT_SECRET']); -mime.define({ +const credential = new identity_1.ClientAssertionCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], () => Promise.resolve(process.env['AZURE_ID_TOKEN'])); +mime_1.default.define({ 'application/typescript': ['ts'], 'application/json': ['code-snippets'], }); @@ -75,37 +78,37 @@ async function main() { const options = (compressed) => ({ account: process.env.AZURE_STORAGE_ACCOUNT, credential, - container: process.env.VSCODE_QUALITY, - prefix: commit + '/', + container: '$web', + prefix: `${process.env.VSCODE_QUALITY}/${commit}/`, contentSettings: { contentEncoding: compressed ? 'gzip' : undefined, cacheControl: 'max-age=31536000, public' } }); - const all = vfs.src('**', { cwd: '../vscode-web', base: '../vscode-web', dot: true }) - .pipe(filter(f => !f.isDirectory())); + const all = vinyl_fs_1.default.src('**', { cwd: '../vscode-web', base: '../vscode-web', dot: true }) + .pipe((0, gulp_filter_1.default)(f => !f.isDirectory())); const compressed = all - .pipe(filter(f => MimeTypesToCompress.has(mime.lookup(f.path)))) - .pipe(gzip({ append: false })) + .pipe((0, gulp_filter_1.default)(f => MimeTypesToCompress.has(mime_1.default.lookup(f.path)))) + .pipe((0, gulp_gzip_1.default)({ append: false })) .pipe(azure.upload(options(true))); const uncompressed = all - .pipe(filter(f => !MimeTypesToCompress.has(mime.lookup(f.path)))) + .pipe((0, gulp_filter_1.default)(f => !MimeTypesToCompress.has(mime_1.default.lookup(f.path)))) .pipe(azure.upload(options(false))); - const out = es.merge(compressed, uncompressed) - .pipe(es.through(function (f) { + const out = event_stream_1.default.merge(compressed, uncompressed) + .pipe(event_stream_1.default.through(function (f) { console.log('Uploaded:', f.relative); files.push(f.relative); this.emit('data', f); })); console.log(`Uploading files to CDN...`); // debug await wait(out); - const listing = new Vinyl({ + const listing = new vinyl_1.default({ path: 'files.txt', contents: Buffer.from(files.join('\n')), stat: { mode: 0o666 } }); - const filesOut = es.readArray([listing]) - .pipe(gzip({ append: false })) + const filesOut = event_stream_1.default.readArray([listing]) + .pipe((0, gulp_gzip_1.default)({ append: false })) .pipe(azure.upload(options(true))); console.log(`Uploading: files.txt (${files.length} files)`); // debug await wait(filesOut); diff --git a/build/azure-pipelines/upload-cdn.ts b/build/azure-pipelines/upload-cdn.ts index 81a4ac14eabb..61d7cea523ca 100644 --- a/build/azure-pipelines/upload-cdn.ts +++ b/build/azure-pipelines/upload-cdn.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as es from 'event-stream'; -import * as Vinyl from 'vinyl'; -import * as vfs from 'vinyl-fs'; -import * as filter from 'gulp-filter'; -import * as gzip from 'gulp-gzip'; -import * as mime from 'mime'; -import { ClientSecretCredential } from '@azure/identity'; +import es from 'event-stream'; +import Vinyl from 'vinyl'; +import vfs from 'vinyl-fs'; +import filter from 'gulp-filter'; +import gzip from 'gulp-gzip'; +import mime from 'mime'; +import { ClientAssertionCredential } from '@azure/identity'; const azure = require('gulp-azure-storage'); const commit = process.env['BUILD_SOURCEVERSION']; -const credential = new ClientSecretCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, process.env['AZURE_CLIENT_SECRET']!); +const credential = new ClientAssertionCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, () => Promise.resolve(process.env['AZURE_ID_TOKEN']!)); mime.define({ 'application/typescript': ['ts'], @@ -79,8 +79,8 @@ async function main(): Promise { const options = (compressed: boolean) => ({ account: process.env.AZURE_STORAGE_ACCOUNT, credential, - container: process.env.VSCODE_QUALITY, - prefix: commit + '/', + container: '$web', + prefix: `${process.env.VSCODE_QUALITY}/${commit}/`, contentSettings: { contentEncoding: compressed ? 'gzip' : undefined, cacheControl: 'max-age=31536000, public' diff --git a/build/azure-pipelines/upload-nlsmetadata.js b/build/azure-pipelines/upload-nlsmetadata.js index 5b6cd3ed1fd5..e89a6497d704 100644 --- a/build/azure-pipelines/upload-nlsmetadata.js +++ b/build/azure-pipelines/upload-nlsmetadata.js @@ -3,25 +3,28 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const es = require("event-stream"); -const vfs = require("vinyl-fs"); -const merge = require("gulp-merge-json"); -const gzip = require("gulp-gzip"); +const event_stream_1 = __importDefault(require("event-stream")); +const vinyl_fs_1 = __importDefault(require("vinyl-fs")); +const gulp_merge_json_1 = __importDefault(require("gulp-merge-json")); +const gulp_gzip_1 = __importDefault(require("gulp-gzip")); const identity_1 = require("@azure/identity"); const path = require("path"); const fs_1 = require("fs"); const azure = require('gulp-azure-storage'); const commit = process.env['BUILD_SOURCEVERSION']; -const credential = new identity_1.ClientSecretCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_CLIENT_SECRET']); +const credential = new identity_1.ClientAssertionCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], () => Promise.resolve(process.env['AZURE_ID_TOKEN'])); function main() { return new Promise((c, e) => { - const combinedMetadataJson = es.merge( + const combinedMetadataJson = event_stream_1.default.merge( // vscode: we are not using `out-build/nls.metadata.json` here because // it includes metadata for translators for `keys`. but for our purpose // we want only the `keys` and `messages` as `string`. - es.merge(vfs.src('out-build/nls.keys.json', { base: 'out-build' }), vfs.src('out-build/nls.messages.json', { base: 'out-build' })) - .pipe(merge({ + event_stream_1.default.merge(vinyl_fs_1.default.src('out-build/nls.keys.json', { base: 'out-build' }), vinyl_fs_1.default.src('out-build/nls.messages.json', { base: 'out-build' })) + .pipe((0, gulp_merge_json_1.default)({ fileName: 'vscode.json', jsonSpace: '', concatArrays: true, @@ -37,7 +40,7 @@ function main() { } })), // extensions - vfs.src('.build/extensions/**/nls.metadata.json', { base: '.build/extensions' }), vfs.src('.build/extensions/**/nls.metadata.header.json', { base: '.build/extensions' }), vfs.src('.build/extensions/**/package.nls.json', { base: '.build/extensions' })).pipe(merge({ + vinyl_fs_1.default.src('.build/extensions/**/nls.metadata.json', { base: '.build/extensions' }), vinyl_fs_1.default.src('.build/extensions/**/nls.metadata.header.json', { base: '.build/extensions' }), vinyl_fs_1.default.src('.build/extensions/**/package.nls.json', { base: '.build/extensions' })).pipe((0, gulp_merge_json_1.default)({ fileName: 'combined.nls.metadata.json', jsonSpace: '', concatArrays: true, @@ -93,11 +96,11 @@ function main() { return { [key]: parsedJson }; }, })); - const nlsMessagesJs = vfs.src('out-build/nls.messages.js', { base: 'out-build' }); - es.merge(combinedMetadataJson, nlsMessagesJs) - .pipe(gzip({ append: false })) - .pipe(vfs.dest('./nlsMetadata')) - .pipe(es.through(function (data) { + const nlsMessagesJs = vinyl_fs_1.default.src('out-build/nls.messages.js', { base: 'out-build' }); + event_stream_1.default.merge(combinedMetadataJson, nlsMessagesJs) + .pipe((0, gulp_gzip_1.default)({ append: false })) + .pipe(vinyl_fs_1.default.dest('./nlsMetadata')) + .pipe(event_stream_1.default.through(function (data) { console.log(`Uploading ${data.path}`); // trigger artifact upload console.log(`##vso[artifact.upload containerfolder=nlsmetadata;artifactname=${data.basename}]${data.path}`); @@ -106,8 +109,8 @@ function main() { .pipe(azure.upload({ account: process.env.AZURE_STORAGE_ACCOUNT, credential, - container: 'nlsmetadata', - prefix: commit + '/', + container: '$web', + prefix: `nlsmetadata/${commit}/`, contentSettings: { contentEncoding: 'gzip', cacheControl: 'max-age=31536000, public' diff --git a/build/azure-pipelines/upload-nlsmetadata.ts b/build/azure-pipelines/upload-nlsmetadata.ts index 030cc8f0e5a6..1a4f2665617d 100644 --- a/build/azure-pipelines/upload-nlsmetadata.ts +++ b/build/azure-pipelines/upload-nlsmetadata.ts @@ -3,18 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as es from 'event-stream'; -import * as Vinyl from 'vinyl'; -import * as vfs from 'vinyl-fs'; -import * as merge from 'gulp-merge-json'; -import * as gzip from 'gulp-gzip'; -import { ClientSecretCredential } from '@azure/identity'; +import es from 'event-stream'; +import Vinyl from 'vinyl'; +import vfs from 'vinyl-fs'; +import merge from 'gulp-merge-json'; +import gzip from 'gulp-gzip'; +import { ClientAssertionCredential } from '@azure/identity'; import path = require('path'); import { readFileSync } from 'fs'; const azure = require('gulp-azure-storage'); const commit = process.env['BUILD_SOURCEVERSION']; -const credential = new ClientSecretCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, process.env['AZURE_CLIENT_SECRET']!); +const credential = new ClientAssertionCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, () => Promise.resolve(process.env['AZURE_ID_TOKEN']!)); interface NlsMetadata { keys: { [module: string]: string }; @@ -126,8 +126,8 @@ function main(): Promise { .pipe(azure.upload({ account: process.env.AZURE_STORAGE_ACCOUNT, credential, - container: 'nlsmetadata', - prefix: commit + '/', + container: '$web', + prefix: `nlsmetadata/${commit}/`, contentSettings: { contentEncoding: 'gzip', cacheControl: 'max-age=31536000, public' diff --git a/build/azure-pipelines/upload-sourcemaps.js b/build/azure-pipelines/upload-sourcemaps.js index 83c1cae596d5..cac1ae3caf20 100644 --- a/build/azure-pipelines/upload-sourcemaps.js +++ b/build/azure-pipelines/upload-sourcemaps.js @@ -3,23 +3,58 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const path = require("path"); -const es = require("event-stream"); -const vfs = require("vinyl-fs"); -const util = require("../lib/util"); -// @ts-ignore -const deps = require("../lib/dependencies"); +const path_1 = __importDefault(require("path")); +const event_stream_1 = __importDefault(require("event-stream")); +const vinyl_fs_1 = __importDefault(require("vinyl-fs")); +const util = __importStar(require("../lib/util")); +const dependencies_1 = require("../lib/dependencies"); const identity_1 = require("@azure/identity"); const azure = require('gulp-azure-storage'); -const root = path.dirname(path.dirname(__dirname)); +const root = path_1.default.dirname(path_1.default.dirname(__dirname)); const commit = process.env['BUILD_SOURCEVERSION']; -const credential = new identity_1.ClientSecretCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_CLIENT_SECRET']); +const credential = new identity_1.ClientAssertionCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], () => Promise.resolve(process.env['AZURE_ID_TOKEN'])); // optionally allow to pass in explicit base/maps to upload const [, , base, maps] = process.argv; function src(base, maps = `${base}/**/*.map`) { - return vfs.src(maps, { base }) - .pipe(es.mapSync((f) => { + return vinyl_fs_1.default.src(maps, { base }) + .pipe(event_stream_1.default.mapSync((f) => { f.path = `${f.base}/core/${f.relative}`; return f; })); @@ -30,13 +65,13 @@ function main() { if (!base) { const vs = src('out-vscode-min'); // client source-maps only sources.push(vs); - const productionDependencies = deps.getProductionDependencies(root); - const productionDependenciesSrc = productionDependencies.map(d => path.relative(root, d)).map(d => `./${d}/**/*.map`); - const nodeModules = vfs.src(productionDependenciesSrc, { base: '.' }) - .pipe(util.cleanNodeModules(path.join(root, 'build', '.moduleignore'))) - .pipe(util.cleanNodeModules(path.join(root, 'build', `.moduleignore.${process.platform}`))); + const productionDependencies = (0, dependencies_1.getProductionDependencies)(root); + const productionDependenciesSrc = productionDependencies.map((d) => path_1.default.relative(root, d)).map((d) => `./${d}/**/*.map`); + const nodeModules = vinyl_fs_1.default.src(productionDependenciesSrc, { base: '.' }) + .pipe(util.cleanNodeModules(path_1.default.join(root, 'build', '.moduleignore'))) + .pipe(util.cleanNodeModules(path_1.default.join(root, 'build', `.moduleignore.${process.platform}`))); sources.push(nodeModules); - const extensionsOut = vfs.src(['.build/extensions/**/*.js.map', '!**/node_modules/**'], { base: '.build' }); + const extensionsOut = vinyl_fs_1.default.src(['.build/extensions/**/*.js.map', '!**/node_modules/**'], { base: '.build' }); sources.push(extensionsOut); } // specific client base/maps @@ -44,16 +79,16 @@ function main() { sources.push(src(base, maps)); } return new Promise((c, e) => { - es.merge(...sources) - .pipe(es.through(function (data) { + event_stream_1.default.merge(...sources) + .pipe(event_stream_1.default.through(function (data) { console.log('Uploading Sourcemap', data.relative); // debug this.emit('data', data); })) .pipe(azure.upload({ account: process.env.AZURE_STORAGE_ACCOUNT, credential, - container: 'sourcemaps', - prefix: commit + '/' + container: '$web', + prefix: `sourcemaps/${commit}/` })) .on('end', () => c()) .on('error', (err) => e(err)); diff --git a/build/azure-pipelines/upload-sourcemaps.ts b/build/azure-pipelines/upload-sourcemaps.ts index 8e148c6095f2..0c51827fef41 100644 --- a/build/azure-pipelines/upload-sourcemaps.ts +++ b/build/azure-pipelines/upload-sourcemaps.ts @@ -3,19 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; -import * as es from 'event-stream'; -import * as Vinyl from 'vinyl'; -import * as vfs from 'vinyl-fs'; +import path from 'path'; +import es from 'event-stream'; +import Vinyl from 'vinyl'; +import vfs from 'vinyl-fs'; import * as util from '../lib/util'; -// @ts-ignore -import * as deps from '../lib/dependencies'; -import { ClientSecretCredential } from '@azure/identity'; +import { getProductionDependencies } from '../lib/dependencies'; +import { ClientAssertionCredential } from '@azure/identity'; const azure = require('gulp-azure-storage'); const root = path.dirname(path.dirname(__dirname)); const commit = process.env['BUILD_SOURCEVERSION']; -const credential = new ClientSecretCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, process.env['AZURE_CLIENT_SECRET']!); +const credential = new ClientAssertionCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, () => Promise.resolve(process.env['AZURE_ID_TOKEN']!)); // optionally allow to pass in explicit base/maps to upload const [, , base, maps] = process.argv; @@ -36,8 +35,8 @@ function main(): Promise { const vs = src('out-vscode-min'); // client source-maps only sources.push(vs); - const productionDependencies = deps.getProductionDependencies(root); - const productionDependenciesSrc = productionDependencies.map(d => path.relative(root, d)).map(d => `./${d}/**/*.map`); + const productionDependencies = getProductionDependencies(root); + const productionDependenciesSrc = productionDependencies.map((d: string) => path.relative(root, d)).map((d: string) => `./${d}/**/*.map`); const nodeModules = vfs.src(productionDependenciesSrc, { base: '.' }) .pipe(util.cleanNodeModules(path.join(root, 'build', '.moduleignore'))) .pipe(util.cleanNodeModules(path.join(root, 'build', `.moduleignore.${process.platform}`))); @@ -61,8 +60,8 @@ function main(): Promise { .pipe(azure.upload({ account: process.env.AZURE_STORAGE_ACCOUNT, credential, - container: 'sourcemaps', - prefix: commit + '/' + container: '$web', + prefix: `sourcemaps/${commit}/` })) .on('end', () => c()) .on('error', (err: any) => e(err)); diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index 3866dc5cf812..e0e91c1c5898 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -10,7 +10,7 @@ steps: - task: AzureKeyVault@2 displayName: "Azure Key Vault: Get Secrets" inputs: - azureSubscription: "vscode-builds-subscription" + azureSubscription: vscode KeyVaultName: vscode-build-secrets SecretsFilter: "github-distro-mixin-password" @@ -113,21 +113,21 @@ steps: - task: AzureCLI@2 displayName: Fetch secrets from Azure inputs: - azureSubscription: "vscode-builds-subscription" + azureSubscription: vscode scriptType: pscore scriptLocation: inlineScript addSpnToEnvironment: true inlineScript: | Write-Host "##vso[task.setvariable variable=AZURE_TENANT_ID]$env:tenantId" Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_ID]$env:servicePrincipalId" - Write-Host "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]$env:servicePrincipalKey" + Write-Host "##vso[task.setvariable variable=AZURE_ID_TOKEN;issecret=true]$env:idToken" - script: | set -e AZURE_STORAGE_ACCOUNT="vscodeweb" \ AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ - AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ + AZURE_ID_TOKEN="$(AZURE_ID_TOKEN)" \ node build/azure-pipelines/upload-cdn displayName: Upload to CDN @@ -136,7 +136,7 @@ steps: AZURE_STORAGE_ACCOUNT="vscodeweb" \ AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ - AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ + AZURE_ID_TOKEN="$(AZURE_ID_TOKEN)" \ node build/azure-pipelines/upload-sourcemaps out-vscode-web-min out-vscode-web-min/vs/workbench/workbench.web.main.js.map displayName: Upload sourcemaps (Web Main) @@ -145,28 +145,16 @@ steps: AZURE_STORAGE_ACCOUNT="vscodeweb" \ AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ - AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ + AZURE_ID_TOKEN="$(AZURE_ID_TOKEN)" \ node build/azure-pipelines/upload-sourcemaps out-vscode-web-min out-vscode-web-min/vs/workbench/workbench.web.main.internal.js.map displayName: Upload sourcemaps (Web Internal) - # upload only the workbench.web.main.js source maps because - # we just compiled these bits in the previous step and the - # general task to upload source maps has already been run - - script: | - set -e - AZURE_STORAGE_ACCOUNT="ticino" \ - AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ - AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ - AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ - node build/azure-pipelines/upload-sourcemaps out-vscode-web-min out-vscode-web-min/vs/workbench/workbench.web.main.js.map - displayName: Upload sourcemaps (Deprecated) - - script: | set -e AZURE_STORAGE_ACCOUNT="vscodeweb" \ AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ - AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ + AZURE_ID_TOKEN="$(AZURE_ID_TOKEN)" \ node build/azure-pipelines/upload-nlsmetadata displayName: Upload NLS Metadata diff --git a/build/azure-pipelines/win32/cli-build-win32.yml b/build/azure-pipelines/win32/cli-build-win32.yml index 19409272ff07..d61f0e722f5f 100644 --- a/build/azure-pipelines/win32/cli-build-win32.yml +++ b/build/azure-pipelines/win32/cli-build-win32.yml @@ -53,7 +53,8 @@ steps: VSCODE_CLI_ENV: OPENSSL_LIB_DIR: $(Build.ArtifactStagingDirectory)/openssl/x64-windows-static/lib OPENSSL_INCLUDE_DIR: $(Build.ArtifactStagingDirectory)/openssl/x64-windows-static/include - RUSTFLAGS: "-C target-feature=+crt-static" + RUSTFLAGS: "-Ctarget-feature=+crt-static -Clink-args=/guard:cf -Clink-args=/CETCOMPAT" + CFLAGS: "/guard:cf /Qspectre" - ${{ if eq(parameters.VSCODE_BUILD_WIN32_ARM64, true) }}: - template: ../cli/cli-compile.yml@self @@ -65,7 +66,8 @@ steps: VSCODE_CLI_ENV: OPENSSL_LIB_DIR: $(Build.ArtifactStagingDirectory)/openssl/arm64-windows-static/lib OPENSSL_INCLUDE_DIR: $(Build.ArtifactStagingDirectory)/openssl/arm64-windows-static/include - RUSTFLAGS: "-C target-feature=+crt-static" + RUSTFLAGS: "-C target-feature=+crt-static -Clink-args=/guard:cf -Clink-args=/CETCOMPAT:NO" + CFLAGS: "/guard:cf /Qspectre" - ${{ if not(parameters.VSCODE_CHECK_ONLY) }}: - ${{ if eq(parameters.VSCODE_BUILD_WIN32_ARM64, true) }}: diff --git a/build/azure-pipelines/win32/product-build-win32-cli-sign.yml b/build/azure-pipelines/win32/product-build-win32-cli-sign.yml index 9d9af45d4747..c7f4b0a0a127 100644 --- a/build/azure-pipelines/win32/product-build-win32-cli-sign.yml +++ b/build/azure-pipelines/win32/product-build-win32-cli-sign.yml @@ -3,6 +3,8 @@ parameters: type: boolean - name: VSCODE_BUILD_WIN32_ARM64 type: boolean + - name: VSCODE_QUALITY + type: string steps: - task: NodeTool@0 @@ -12,6 +14,14 @@ steps: versionFilePath: .nvmrc nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: + - task: AzureKeyVault@2 + displayName: "Azure Key Vault: Get Secrets" + inputs: + azureSubscription: vscode + KeyVaultName: vscode-build-secrets + SecretsFilter: "github-distro-mixin-password" + - powershell: node build/setup-npm-registry.js $env:NPM_REGISTRY build condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) displayName: Setup NPM Registry @@ -39,6 +49,8 @@ steps: $ErrorActionPreference = "Stop" exec { npm ci } workingDirectory: build + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" retryCountOnTaskFailure: 5 displayName: Install build dependencies diff --git a/build/azure-pipelines/win32/product-build-win32-test.yml b/build/azure-pipelines/win32/product-build-win32-test.yml index cdcccd7684bc..09db30d1914a 100644 --- a/build/azure-pipelines/win32/product-build-win32-test.yml +++ b/build/azure-pipelines/win32/product-build-win32-test.yml @@ -63,6 +63,7 @@ steps: compile-extension:typescript-language-features ` compile-extension:vscode-api-tests ` compile-extension:vscode-colorize-tests ` + compile-extension:vscode-colorize-perf-tests ` compile-extension:vscode-test-resolver ` } displayName: Build integration tests diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index f52bf07de084..ab0b6cbb4dff 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -35,9 +35,9 @@ steps: - task: AzureKeyVault@2 displayName: "Azure Key Vault: Get Secrets" inputs: - azureSubscription: "vscode-builds-subscription" + azureSubscription: vscode KeyVaultName: vscode-build-secrets - SecretsFilter: "github-distro-mixin-password,ESRP-PKI,esrp-aad-username,esrp-aad-password" + SecretsFilter: "github-distro-mixin-password" - task: DownloadPipelineArtifact@2 inputs: @@ -124,7 +124,7 @@ steps: - template: ../common/install-builtin-extensions.yml@self - ${{ if and(ne(parameters.VSCODE_CIBUILD, true), ne(parameters.VSCODE_QUALITY, 'oss')) }}: - - powershell: node build\lib\policies + - powershell: node build\lib\policies win32 displayName: Generate Group Policy definitions retryCountOnTaskFailure: 3 @@ -145,7 +145,7 @@ steps: exec { npm run gulp "vscode-win32-$(VSCODE_ARCH)-min-ci" } exec { npm run gulp "vscode-win32-$(VSCODE_ARCH)-inno-updater" } echo "##vso[task.setvariable variable=BUILT_CLIENT]true" - echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)" + echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(Agent.BuildDirectory)/VSCode-win32-$(VSCODE_ARCH)" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build client @@ -156,7 +156,7 @@ steps: exec { npm run gulp "vscode-reh-win32-$(VSCODE_ARCH)-min-ci" } mv ..\vscode-reh-win32-$(VSCODE_ARCH) ..\vscode-server-win32-$(VSCODE_ARCH) # TODO@joaomoreno echo "##vso[task.setvariable variable=BUILT_SERVER]true" - echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(CodeSigningFolderPath),$(agent.builddirectory)/vscode-server-win32-$(VSCODE_ARCH)" + echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(CodeSigningFolderPath),$(Agent.BuildDirectory)/vscode-server-win32-$(VSCODE_ARCH)" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build server @@ -196,52 +196,67 @@ steps: $ErrorActionPreference = "Stop" $ArtifactName = (gci -Path "$(Build.ArtifactStagingDirectory)/cli" | Select-Object -last 1).FullName Expand-Archive -Path $ArtifactName -DestinationPath "$(Build.ArtifactStagingDirectory)/cli" - $AppProductJson = Get-Content -Raw -Path "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)\resources\app\product.json" | ConvertFrom-Json + $AppProductJson = Get-Content -Raw -Path "$(Agent.BuildDirectory)\VSCode-win32-$(VSCODE_ARCH)\resources\app\product.json" | ConvertFrom-Json $CliAppName = $AppProductJson.tunnelApplicationName $AppName = $AppProductJson.applicationName - Move-Item -Path "$(Build.ArtifactStagingDirectory)/cli/$AppName.exe" -Destination "$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)/bin/$CliAppName.exe" + Move-Item -Path "$(Build.ArtifactStagingDirectory)/cli/$AppName.exe" -Destination "$(Agent.BuildDirectory)/VSCode-win32-$(VSCODE_ARCH)/bin/$CliAppName.exe" displayName: Move VS Code CLI - task: UseDotNet@2 inputs: version: 6.x - - task: EsrpClientTool@1 - displayName: Download ESRPClient + - task: EsrpCodeSigning@5 + inputs: + UseMSIAuthentication: true + ConnectedServiceName: vscode-esrp + AppRegistrationClientId: $(ESRP_CLIENT_ID) + AppRegistrationTenantId: $(ESRP_TENANT_ID) + AuthAKVName: vscode-esrp + AuthSignCertName: esrp-sign + FolderPath: . + Pattern: noop + displayName: 'Install ESRP Tooling' - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - $EsrpClientTool = (gci -directory -filter EsrpClientTool_* $(Agent.RootDirectory)\_tasks | Select-Object -last 1).FullName - $EsrpCliZip = (gci -recurse -filter esrpcli.*.zip $EsrpClientTool | Select-Object -last 1).FullName - mkdir -p $(Agent.TempDirectory)\esrpcli - Expand-Archive -Path $EsrpCliZip -DestinationPath $(Agent.TempDirectory)\esrpcli - $EsrpCliDllPath = (gci -recurse -filter esrpcli.dll $(Agent.TempDirectory)\esrpcli | Select-Object -last 1).FullName - echo "##vso[task.setvariable variable=EsrpCliDllPath]$EsrpCliDllPath" + $EsrpCodeSigningTool = (gci -directory -filter EsrpCodeSigning_* $(Agent.RootDirectory)\_tasks | Select-Object -last 1).FullName + $Version = (gci -directory $EsrpCodeSigningTool | Select-Object -last 1).FullName + echo "##vso[task.setvariable variable=EsrpCliDllPath]$Version\net6.0\esrpcli.dll" displayName: Find ESRP CLI - - powershell: node build\azure-pipelines\common\sign $env:EsrpCliDllPath sign-windows $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(CodeSigningFolderPath) '*.dll,*.exe,*.node' + - powershell: node build\azure-pipelines\common\sign $env:EsrpCliDllPath sign-windows $(CodeSigningFolderPath) '*.dll,*.exe,*.node' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) displayName: Codesign executables and shared libraries - ${{ if eq(parameters.VSCODE_QUALITY, 'insider') }}: - - powershell: node build\azure-pipelines\common\sign $env:EsrpCliDllPath sign-windows-appx $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) $(CodeSigningFolderPath) '*.appx' + - powershell: node build\azure-pipelines\common\sign $env:EsrpCliDllPath sign-windows-appx $(CodeSigningFolderPath) '*.appx' + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) displayName: Codesign context menu appx package - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" $PackageJson = Get-Content -Raw -Path ..\VSCode-win32-$(VSCODE_ARCH)\resources\app\package.json | ConvertFrom-Json $Version = $PackageJson.version echo "##vso[task.setvariable variable=VSCODE_VERSION]$Version" condition: succeededOrFailed() displayName: Get product version - - powershell: | + - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" $ArchivePath = ".build\win32-$(VSCODE_ARCH)\VSCode-win32-$(VSCODE_ARCH)-$(VSCODE_VERSION).zip" New-Item -ItemType Directory -Path .build\win32-$(VSCODE_ARCH) -Force - exec { 7z.exe a -tzip $ArchivePath -x!CodeSignSummary*.md ..\VSCode-win32-$(VSCODE_ARCH)\* -r } + exec { 7z.exe a -tzip $ArchivePath ..\VSCode-win32-$(VSCODE_ARCH)\* "-xr!CodeSignSummary*.md" } echo "##vso[task.setvariable variable=CLIENT_PATH]$ArchivePath" + + echo "Listing archive contents" + 7z.exe l $ArchivePath condition: and(succeededOrFailed(), eq(variables['BUILT_CLIENT'], 'true')) displayName: Package client @@ -250,8 +265,11 @@ steps: $ErrorActionPreference = "Stop" $ArchivePath = ".build\win32-$(VSCODE_ARCH)\vscode-server-win32-$(VSCODE_ARCH).zip" New-Item -ItemType Directory -Path .build\win32-$(VSCODE_ARCH) -Force - exec { 7z.exe a -tzip $ArchivePath ..\vscode-server-win32-$(VSCODE_ARCH) -r } + exec { 7z.exe a -tzip $ArchivePath ..\vscode-server-win32-$(VSCODE_ARCH) } echo "##vso[task.setvariable variable=SERVER_PATH]$ArchivePath" + + echo "Listing archive contents" + 7z.exe l $ArchivePath condition: and(succeededOrFailed(), eq(variables['BUILT_SERVER'], 'true')) displayName: Package server @@ -260,33 +278,34 @@ steps: $ErrorActionPreference = "Stop" $ArchivePath = ".build\win32-$(VSCODE_ARCH)\vscode-server-win32-$(VSCODE_ARCH)-web.zip" New-Item -ItemType Directory -Path .build\win32-$(VSCODE_ARCH) -Force - exec { 7z.exe a -tzip $ArchivePath ..\vscode-server-win32-$(VSCODE_ARCH)-web -r } + exec { 7z.exe a -tzip $ArchivePath ..\vscode-server-win32-$(VSCODE_ARCH)-web } echo "##vso[task.setvariable variable=WEB_PATH]$ArchivePath" + + echo "Listing archive contents" + 7z.exe l $ArchivePath condition: and(succeededOrFailed(), eq(variables['BUILT_WEB'], 'true')) displayName: Package server (web) - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - $env:ESRPPKI = "$(ESRP-PKI)" - $env:ESRPAADUsername = "$(esrp-aad-username)" - $env:ESRPAADPassword = "$(esrp-aad-password)" exec { npm run -- gulp "vscode-win32-$(VSCODE_ARCH)-system-setup" --sign } $SetupPath = ".build\win32-$(VSCODE_ARCH)\system-setup\VSCodeSetup-$(VSCODE_ARCH)-$(VSCODE_VERSION).exe" mv .build\win32-$(VSCODE_ARCH)\system-setup\VSCodeSetup.exe $SetupPath echo "##vso[task.setvariable variable=SYSTEM_SETUP_PATH]$SetupPath" + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) displayName: Build system setup - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - $env:ESRPPKI = "$(ESRP-PKI)" - $env:ESRPAADUsername = "$(esrp-aad-username)" - $env:ESRPAADPassword = "$(esrp-aad-password)" exec { npm run -- gulp "vscode-win32-$(VSCODE_ARCH)-user-setup" --sign } $SetupPath = ".build\win32-$(VSCODE_ARCH)\user-setup\VSCodeUserSetup-$(VSCODE_ARCH)-$(VSCODE_VERSION).exe" mv .build\win32-$(VSCODE_ARCH)\user-setup\VSCodeSetup.exe $SetupPath echo "##vso[task.setvariable variable=USER_SETUP_PATH]$SetupPath" + env: + SYSTEM_ACCESSTOKEN: $(System.AccessToken) displayName: Build user setup - powershell: echo "##vso[task.setvariable variable=ARTIFACT_PREFIX]attempt$(System.JobAttempt)_" diff --git a/build/azure-pipelines/win32/sdl-scan-win32.yml b/build/azure-pipelines/win32/sdl-scan-win32.yml index 4b9d8bc9badc..bf6819a4b479 100644 --- a/build/azure-pipelines/win32/sdl-scan-win32.yml +++ b/build/azure-pipelines/win32/sdl-scan-win32.yml @@ -21,7 +21,7 @@ steps: - task: AzureKeyVault@2 displayName: "Azure Key Vault: Get Secrets" inputs: - azureSubscription: "vscode-builds-subscription" + azureSubscription: vscode KeyVaultName: vscode-build-secrets SecretsFilter: "github-distro-mixin-password" @@ -102,13 +102,6 @@ steps: - powershell: npm run compile displayName: Compile - - powershell: | - Get-ChildItem '$(Build.SourcesDirectory)' -Recurse -Filter "*.exe" - Get-ChildItem '$(Build.SourcesDirectory)' -Recurse -Filter "*.dll" - Get-ChildItem '$(Build.SourcesDirectory)' -Recurse -Filter "*.node" - Get-ChildItem '$(Build.SourcesDirectory)' -Recurse -Filter "*.pdb" - displayName: List files - - powershell: npm run gulp "vscode-symbols-win32-${{ parameters.VSCODE_ARCH }}" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" @@ -119,16 +112,7 @@ steps: Get-ChildItem '$(Agent.BuildDirectory)\scanbin' -Recurse -Filter "*.dll" Get-ChildItem '$(Agent.BuildDirectory)\scanbin' -Recurse -Filter "*.node" Get-ChildItem '$(Agent.BuildDirectory)\scanbin' -Recurse -Filter "*.pdb" - displayName: List files again - - - task: BinSkim@4 - inputs: - InputType: "Basic" - Function: "analyze" - TargetPattern: "guardianGlob" - AnalyzeIgnorePdbLoadError: true - AnalyzeTargetGlob: '$(Agent.BuildDirectory)\scanbin\**.dll;$(Agent.BuildDirectory)\scanbin\**.exe;$(Agent.BuildDirectory)\scanbin\**.node' - AnalyzeLocalSymbolDirectories: '$(Agent.BuildDirectory)\scanbin\VSCode-win32-${{ parameters.VSCODE_ARCH }}\pdb' + displayName: List files - task: CopyFiles@2 displayName: 'Collect Symbols for API Scan' @@ -151,7 +135,7 @@ steps: displayName: Run ApiScan condition: succeeded() env: - AzureServicesAuthConnectionString: $(apiscan-connectionstring) + AzureServicesAuthConnectionString: RunAs=App;AppId=c0940da5-8bd3-4dd3-8af1-40774b50edbd;TenantId=72f988bf-86f1-41af-91ab-2d7cd011db47;ServiceConnectionId=3e55d992-b60d-414d-9071-e4fad359c748; SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: PublishSecurityAnalysisLogs@3 diff --git a/build/buildfile.js b/build/buildfile.js index 607e79e13d78..683e20fc46b7 100644 --- a/build/buildfile.js +++ b/build/buildfile.js @@ -36,7 +36,6 @@ exports.workbenchDesktop = [ createModuleDescription('vs/platform/files/node/watcher/watcherMain'), createModuleDescription('vs/platform/terminal/node/ptyHostMain'), createModuleDescription('vs/workbench/api/node/extensionHostProcess'), - createModuleDescription('vs/workbench/contrib/issue/electron-sandbox/issueReporterMain'), createModuleDescription('vs/workbench/workbench.desktop.main') ]; @@ -55,8 +54,6 @@ exports.code = [ createModuleDescription('vs/code/electron-utility/sharedProcess/sharedProcessMain'), createModuleDescription('vs/code/electron-sandbox/processExplorer/processExplorerMain'), createModuleDescription('vs/code/electron-sandbox/workbench/workbench'), - // TODO: @justchen https://github.com/microsoft/vscode/issues/213332 make sure to remove when we use window.open on desktop. - createModuleDescription('vs/workbench/contrib/issue/electron-sandbox/issueReporter'), createModuleDescription('vs/code/electron-sandbox/processExplorer/processExplorer') ]; diff --git a/build/checksums/electron.txt b/build/checksums/electron.txt index 1653fbaa134f..d390d7a233ca 100644 --- a/build/checksums/electron.txt +++ b/build/checksums/electron.txt @@ -1,75 +1,75 @@ -7fb7b8736fcd9dbde92628e4aa951fd2c54d3136bf20c1821ce2a1b85fef3046 *chromedriver-v32.1.2-darwin-arm64.zip -a9e43916cbe91c9a905f2bf38d326d5dc462c3d8a7d88f52c25c1e7c1b9ce7cc *chromedriver-v32.1.2-darwin-x64.zip -405664c1b6529cd6c2af65778f2c84bfe262ad0b8d9d044a39d2eff2ed218828 *chromedriver-v32.1.2-linux-arm64.zip -32682f487ed9307d0f40d35c4b998cee75272effd228093be3e008ca7b39e16e *chromedriver-v32.1.2-linux-armv7l.zip -d98aa7a90ebfe519700e47fa6bcb94a157483fac615920c5ee467dcc41c46702 *chromedriver-v32.1.2-linux-x64.zip -046737617a523278ebe1786f64b723c3bb3fcc9811b98b224029d7fa730485dd *chromedriver-v32.1.2-mas-arm64.zip -e05d44d95ebd0bd897499a1012980d9a7d87f63f4da8ce82dc15a6f82008d3ea *chromedriver-v32.1.2-mas-x64.zip -9940246d4017db30673c9e77db590ff11a6616b24ad630ec33600129694c6570 *chromedriver-v32.1.2-win32-arm64.zip -c8580f7596b54400a6018ab5a43046c99d8370f61127b414f048200a8f7f303b *chromedriver-v32.1.2-win32-ia32.zip -ab40ffd5a1cc5e9c854f02d11773f1dfab8a78a89f5f31b79cf229489a672a09 *chromedriver-v32.1.2-win32-x64.zip -649584a630e9e4b21b8f42a06ed01d9d2e12444ed39500c78f0b76388dd704c7 *electron-api.json -7edc15b6c0d1cfb05f641e091186041f557fc23be7e8517e132f0ab0026a7cfd *electron-v32.1.2-darwin-arm64-dsym-snapshot.zip -b879508f26eec70407c79724a69d3a98901ba388491128ed3a2952b010cb9da8 *electron-v32.1.2-darwin-arm64-dsym.zip -218791e68ce0177ff994b446f67f60e028edaf1c6a39bb490602847ceb77e9da *electron-v32.1.2-darwin-arm64-symbols.zip -b5f6db900997ba931c98addaef28744a0a6af0f2ec2e8e5755f7f50db2fe8bbc *electron-v32.1.2-darwin-arm64.zip -e52950f675119c4fe67de927a2a8b20507f8496b07dab72be82b341a4c96227b *electron-v32.1.2-darwin-x64-dsym-snapshot.zip -a3c64201aa41cf6ebe32dbc92296c91b11dd321be9584b3c1a46ea0bbacc7201 *electron-v32.1.2-darwin-x64-dsym.zip -711736bee5238aecb1b424d2c8c7ffc3007a0de1d32f784db97c8e44e92daf83 *electron-v32.1.2-darwin-x64-symbols.zip -150ac6a59e31ad516685bdbb9cee67c7e927b872ad94ffc900fbf6616433f8ab *electron-v32.1.2-darwin-x64.zip -1bc88c381f835d21a4cb95d81429d6bc2f01105276ef02fde562cb03eb493729 *electron-v32.1.2-linux-arm64-debug.zip -11853479e086ec020ac85cc438c74c61473808382408614bd8c8afb6b496a9b9 *electron-v32.1.2-linux-arm64-symbols.zip -702326c51679ed705bc22d7e4049b29cef2d66366d3387c401836aaae0fa450c *electron-v32.1.2-linux-arm64.zip -1bc88c381f835d21a4cb95d81429d6bc2f01105276ef02fde562cb03eb493729 *electron-v32.1.2-linux-armv7l-debug.zip -bd99cb482559ba3c24152a3563bf8f4dd2650c62585bcced0e4b1bb4f58c2d33 *electron-v32.1.2-linux-armv7l-symbols.zip -d9511449c328f90f47e499f44c6d84c6204d4a3a2caec5c5d52f176cfc77f50d *electron-v32.1.2-linux-armv7l.zip -8177e8084aef855cd5c5a5b72210fc948bd27b362f73801cf0d99ee28d5aa7a6 *electron-v32.1.2-linux-x64-debug.zip -38269028f07a405b4012df80f7fa9a65099e325cea67a4c0e654af890d2dca7f *electron-v32.1.2-linux-x64-symbols.zip -b51e5f1296f8971d7eb4ca86606b6f5d31fb3dab8caa91dcfbfa522be5679691 *electron-v32.1.2-linux-x64.zip -ae2e09fe44b8bf7ed6ca5c3ee2fae6a25415469b83125f0c370e819d208e1ca8 *electron-v32.1.2-mas-arm64-dsym-snapshot.zip -b882dd1adc0da2444e220ea213eb4fde838958f637040f11c62f986cb64532aa *electron-v32.1.2-mas-arm64-dsym.zip -6559e93a3e9e4c3f828ca0c457827c9dbade9124d6d6b92f6ca7a56943032e0c *electron-v32.1.2-mas-arm64-symbols.zip -5235c3b5f417c8a196a00732ee6ef79a55610465ef51c32b8ea481393a8a2214 *electron-v32.1.2-mas-arm64.zip -db68c078b17d5bfbca5dfd3ebf7b9f9eec9c84ec4a257b248b14ac9080e83e34 *electron-v32.1.2-mas-x64-dsym-snapshot.zip -b5f552ca41c4b7f197b0d584ec7fd5c027ae28c9162a80e8be7899bc59003a35 *electron-v32.1.2-mas-x64-dsym.zip -c7347605cb44bce675f2b08efd9a2c3dd4ae85ac85fd9444afe529379ad1c160 *electron-v32.1.2-mas-x64-symbols.zip -f27569c1c20e3f982b526e5e4ae3c091417e0cada77b772804ffeb94bc0a6744 *electron-v32.1.2-mas-x64.zip -4615c90817f137568e93208cabd3c3b90cfb03658fa12eda5d47060c921121b4 *electron-v32.1.2-win32-arm64-pdb.zip -7075092d2888d761f45978935b56dcdc4f08272a0454ea46cca4c0b21f617220 *electron-v32.1.2-win32-arm64-symbols.zip -7351fa5cb892853a7d4b67d8858d0f9cc6506c554a2e42c9ad7e8d5e29ae2743 *electron-v32.1.2-win32-arm64-toolchain-profile.zip -7eabd415f53b3180ce7ef73a6d12d67f8db62cac53db5edc9393acd4a1bb6312 *electron-v32.1.2-win32-arm64.zip -1524a0e4acc46c7f4d2a9361e60e1ef91bb2f3fdf14b8f58ee413219e4e1ed4c *electron-v32.1.2-win32-ia32-pdb.zip -58fd0edf38918f01d509750aad735fbbd811f603bfa67da1d5566a0ff3fc930d *electron-v32.1.2-win32-ia32-symbols.zip -7351fa5cb892853a7d4b67d8858d0f9cc6506c554a2e42c9ad7e8d5e29ae2743 *electron-v32.1.2-win32-ia32-toolchain-profile.zip -72cf49d2d54c474df4322c37ac2c1cee05db8e2cfd5066735bb9395271a05b6b *electron-v32.1.2-win32-ia32.zip -259b155478b26a69884683a2eb248819871c42ac777033e568068b7dcb668049 *electron-v32.1.2-win32-x64-pdb.zip -0246c4461414505ddcec690a48c77298446b96c5de61aba565840e0f8fb11167 *electron-v32.1.2-win32-x64-symbols.zip -7351fa5cb892853a7d4b67d8858d0f9cc6506c554a2e42c9ad7e8d5e29ae2743 *electron-v32.1.2-win32-x64-toolchain-profile.zip -02dadb152d508ba7ac4371ac84d9e1166cc96a63567022df155b4f1859707b8e *electron-v32.1.2-win32-x64.zip -6fd6176fd0f3214ef81b4a318663503fdcb0931f949eb7f98902fc7eee1502ee *electron.d.ts -4050fdcb3a783d67481653a7edf051d9df9b374a03a31c6f312d9b5a71b0dca7 *ffmpeg-v32.1.2-darwin-arm64.zip -55d758ad33095ef332ec528445f70499437b8da06b40b48ea8451e82caf87963 *ffmpeg-v32.1.2-darwin-x64.zip -b75410296a741951fab0a26a90c0dc986fdc0ffed090a18db511567cf572b02a *ffmpeg-v32.1.2-linux-arm64.zip -e70efbd03d587f2b772c0b40e92ea066fc4cb880a6df3a389552140cd70ad15c *ffmpeg-v32.1.2-linux-armv7l.zip -7b502f35be711a3d983754a4096751e5cb7c8df7cae349678f68407d51bc2123 *ffmpeg-v32.1.2-linux-x64.zip -7e4a3d9d32a9da96a3b4efbe413152c9da7a0a005a3e348e9afd5b2799b642f7 *ffmpeg-v32.1.2-mas-arm64.zip -153e20e4daaa9b5242d5260b3fafbb8d1f477e01d8bff4d5813c85e617ef7ac7 *ffmpeg-v32.1.2-mas-x64.zip -31061f9b762697c5bc85fb47b5838a1d391711c6efa58784c6f43e1e5347844e *ffmpeg-v32.1.2-win32-arm64.zip -726def466970a97c8001d311ae2ce044b4c7676e47037906f95d0bd70d3843ba *ffmpeg-v32.1.2-win32-ia32.zip -c1a6ab3a4d9f640a6cff7f287be7ee2c60c93b01ebba30cfd30851daeca06020 *ffmpeg-v32.1.2-win32-x64.zip -1a33ccc8d7c2327b578370c0bc55e44e93f3b2802a59b40621b5f595643c53d7 *hunspell_dictionaries.zip -c26dce5871a613b2f21fe5ba5c580e56e6444103c5f557cf2e026dc415e8a910 *libcxx-objects-v32.1.2-linux-arm64.zip -44f3c2ef522261187b0e919ea366be8489829580f3c2a593c0b971c382ff36db *libcxx-objects-v32.1.2-linux-armv7l.zip -2b544a48375d5bc24996ca6660bd09b55502982573d13a5d35ebccfa8d9d3433 *libcxx-objects-v32.1.2-linux-x64.zip -a69a52bc6cb45f39b1d441c03fc03b17665849e29db4c4a848b70e8b4dc8601a *libcxx_headers.zip -4da13f2694e3e07044d445492a28838f310e8082470c0b79cc341de9de6af9d4 *libcxxabi_headers.zip -7e0601c5b34eff190993a4e3e56978e5775e2449099a6c64b170605a7c05725d *mksnapshot-v32.1.2-darwin-arm64.zip -0c3e09d57fcf1401dc124c30b987e96dc0e97595fe9af23e49091d1ebbd1b6cb *mksnapshot-v32.1.2-darwin-x64.zip -a3897c6892a64d1ef3251e51d6711630ba5e8d4cc16d80f5941326ef089fd9d8 *mksnapshot-v32.1.2-linux-arm64-x64.zip -e30e6399ccf0a5207da201d5d8d32b83b7270a19e558a72936ea24eaf3f1772f *mksnapshot-v32.1.2-linux-armv7l-x64.zip -5ec39a59a1a2c2dd2a833406cf7ff0f155129e7f345a7d0c8b4efaa37825464a *mksnapshot-v32.1.2-linux-x64.zip -f9e493fc4a75012dcfb34bde465540fa0661275dec1c2aaee5a39cb957d89935 *mksnapshot-v32.1.2-mas-arm64.zip -1c32fd1892b77c58f3176046a2f60b6bdfc2f6f9b277e534e83da6b760b6aee8 *mksnapshot-v32.1.2-mas-x64.zip -286fbd00deb8e0c806230633703aeab692848235a2881fa746b6db0a5f425f97 *mksnapshot-v32.1.2-win32-arm64-x64.zip -a49e4c843e8f3e49fd8b8f35ab36d75eae56644d725eba3a9c68c2fc2a6aed17 *mksnapshot-v32.1.2-win32-ia32.zip -aaf24b934dddc724a171a550d18c2236fd660a0ef5b5b72385e4997519caf605 *mksnapshot-v32.1.2-win32-x64.zip +c9b82c9f381742e839fea00aeb14f24519bcaf38a0f4eed25532191701f9535b *chromedriver-v34.3.2-darwin-arm64.zip +d556c1e2b06f1bf131e83c2fb981de755c28e1083a884d257eb964815be16b0c *chromedriver-v34.3.2-darwin-x64.zip +1cabad4f3303ac2ff172a9f22185f64944dbaa6fc68271609077158eaefdee35 *chromedriver-v34.3.2-linux-arm64.zip +4213ce52c72ef414179b5c5c22ae8423847ff030d438296bd6c2aac763930a7b *chromedriver-v34.3.2-linux-armv7l.zip +3c64c08221fdfc0f4be60ea8b1b126f2ecca45f60001b63778522f711022c6ea *chromedriver-v34.3.2-linux-x64.zip +e8388734d88e011cb6cd79795431de9206820749219d80565ee49d90501d2bf3 *chromedriver-v34.3.2-mas-arm64.zip +3ad1dd37bd6e0bb37e8503898db7aedd56bd5213e6d6760b05c3d11f4625062b *chromedriver-v34.3.2-mas-x64.zip +d567b481a0f5d88e84bba7718f89fb08f56363bfc4cb5914e1c2086358a5c252 *chromedriver-v34.3.2-win32-arm64.zip +df6732e9dc61cb20a3c0b2a2de453aac7e2bd54e7cbff43512afa614852c15fa *chromedriver-v34.3.2-win32-ia32.zip +dda0765c8d064924632e18cd152014ecd767f3808fc51c8249a053bfb7ca70a2 *chromedriver-v34.3.2-win32-x64.zip +1945f15caff98f2e0f1ee539c483d352fb8d4d0c13f342caa7abe247676d828c *electron-api.json +c078bbf727b3c3026f60e07a0f4643b85c06c581b54be017d0a6c284ba6772d3 *electron-v34.3.2-darwin-arm64-dsym-snapshot.zip +35f587754d6a3272606258386bf73688d63dd53c7e572d3a7cbaae6f3f60bdae *electron-v34.3.2-darwin-arm64-dsym.zip +08b14ee02c98353de3c738120dfd017322666e82b914a7f6de9b9888dcc5c0f0 *electron-v34.3.2-darwin-arm64-symbols.zip +2a4aa7e8fa30f229e465ebd18d3e4722e2b41529dc51a68a954d333a7e556ffe *electron-v34.3.2-darwin-arm64.zip +1509ccdeb80024f5e3edd5ecf804b4cef4e47ea2bd74e33ef0b39044b0ccf892 *electron-v34.3.2-darwin-x64-dsym-snapshot.zip +3bbe5d587c3f582ed8c126b0fb635cc02ad9a14d077b04892fe6f862092445b0 *electron-v34.3.2-darwin-x64-dsym.zip +fa7ece82e6ecaf1c94ed341e8ebff98e64687c68fe113f52cd9a21400302e22f *electron-v34.3.2-darwin-x64-symbols.zip +23938c62257a65a863ed7aa7c7966ba5f257a7d3dc16c78293e826962cc39c5c *electron-v34.3.2-darwin-x64.zip +0547eecf8ab538d74fa854f591ce8b888a3dbb339256d2db3038e7bb2c6dd929 *electron-v34.3.2-linux-arm64-debug.zip +676d0dc2b1c1c85c8b2abbb8cd5376ee22ecdb910493b910d9ae5a998532136a *electron-v34.3.2-linux-arm64-symbols.zip +774e4ccb39d553e5487994a9f8c60774a90f08cdb049ff65f3963fc27c969ff2 *electron-v34.3.2-linux-arm64.zip +0547eecf8ab538d74fa854f591ce8b888a3dbb339256d2db3038e7bb2c6dd929 *electron-v34.3.2-linux-armv7l-debug.zip +ba33bf53fcb35dea568a2795f5b23ecf46c218abe8258946611c72a1f42f716c *electron-v34.3.2-linux-armv7l-symbols.zip +73ae92c8fffb351d3a455569cf57ce9a3f676f42bf61939c613c607fe0fc3bfb *electron-v34.3.2-linux-armv7l.zip +e61a9a69dd7ea6f2687708a8e83516670cdea53c728226e598e2f6f1fad5b77b *electron-v34.3.2-linux-x64-debug.zip +f1a04df7fe67dd1cd29e7b87871525458d2eb24c0cf3b5835a1c56974707562a *electron-v34.3.2-linux-x64-symbols.zip +7b74c0c4fae82e27c7e9cbca13e9763e046113dba8737d3e27de9a0b300ac87e *electron-v34.3.2-linux-x64.zip +8571a6aa83e00925ceb39fdc5a45a9f6b9aa3d92fd84951c6f252ed723aea4ae *electron-v34.3.2-mas-arm64-dsym-snapshot.zip +477410c6f9a6c5eeaedf376058a02c2996fc0a334aa40eeec7d3734c09347f4d *electron-v34.3.2-mas-arm64-dsym.zip +c2e62dcd6630cb51b2d8e2869e74e47d29bda785521cea6e82e546d0fc58aabb *electron-v34.3.2-mas-arm64-symbols.zip +a1698e8546a062fd59b7f8e5507a7f3220fb00b347f2377de83fc9a07f7f3507 *electron-v34.3.2-mas-arm64.zip +741a24ac230a3651dca81d211f9f00b835c428a5ed0c5f67d370d4e88b62f8d6 *electron-v34.3.2-mas-x64-dsym-snapshot.zip +aeff97ec9e5c9e173ac89e38acd94476025c5640d5f27be1e8c2abd54398bab3 *electron-v34.3.2-mas-x64-dsym.zip +9f14b66b1d612ac66697288e8763171c388f7f200854871a5f0ab464a6a921c2 *electron-v34.3.2-mas-x64-symbols.zip +c979d7e7175f1e8e03ca187997d4c156b878189fc3611b347fadebcb16f3e027 *electron-v34.3.2-mas-x64.zip +f43c700641e8220205dd356952e32718d113cf530520c4ed7209b59851eac266 *electron-v34.3.2-win32-arm64-pdb.zip +3ba6e01c99bffac6b5dd3fd6f122ecdb571cf6f675dc5498c65050bd7a382ef8 *electron-v34.3.2-win32-arm64-symbols.zip +c23f84aabb09c24cd2ae759a547fdba4206af19a3bb0f4554a91cd9528648ad0 *electron-v34.3.2-win32-arm64-toolchain-profile.zip +9b9cb65d75a16782088b492f9ef3bb4d27525012b819c12bf29bd27e159d749b *electron-v34.3.2-win32-arm64.zip +1006e7af4c149114b5ebc3497617aaa6cd1bb0b131e0a225fd73709ff308f9c5 *electron-v34.3.2-win32-ia32-pdb.zip +1ecb6430cd04454f08f557c9579163f3552144bfcc0b67b768dad8868b5b891d *electron-v34.3.2-win32-ia32-symbols.zip +c23f84aabb09c24cd2ae759a547fdba4206af19a3bb0f4554a91cd9528648ad0 *electron-v34.3.2-win32-ia32-toolchain-profile.zip +d004fd5f853754001fafaec33e383d1950b30c935ee71b297ec1c9e084355e9b *electron-v34.3.2-win32-ia32.zip +4e0721552fd2f09e9466e88089af8b965f1bfbc4ae00a59aaf6245b1d1efabfd *electron-v34.3.2-win32-x64-pdb.zip +9dea812a7e7cd0fb18e5fed9a99db5531959a068c24d3c0ecedceb644cd3ffa0 *electron-v34.3.2-win32-x64-symbols.zip +c23f84aabb09c24cd2ae759a547fdba4206af19a3bb0f4554a91cd9528648ad0 *electron-v34.3.2-win32-x64-toolchain-profile.zip +1785e161420fb90d2331c26e50bba3413cae9625b7db3c8524ea02ade631efba *electron-v34.3.2-win32-x64.zip +722b304c31ddac58b0083d94a241c5276464f04bd8ea4fcbfd33051d197be103 *electron.d.ts +31ce159b2e47d1de5bc907d8e1c89726b0f2ba530ec2e9d7a8e5c723b1ccf6e0 *ffmpeg-v34.3.2-darwin-arm64.zip +565539bac64a6ee9cf6f188070f520210a1507341718f5dc388ac7c454b1e1d5 *ffmpeg-v34.3.2-darwin-x64.zip +6006ea0f46ab229feb2685be086b0fafd65981e2939dd2218a078459c75ab527 *ffmpeg-v34.3.2-linux-arm64.zip +9404ce2e85df7c40f809f2cf62c7af607de299839fe6b7ae978c3015500abcc8 *ffmpeg-v34.3.2-linux-armv7l.zip +79aec96898b7e2462826780ee0b52b9ab299dc662af333e128a34fd5ddae87f1 *ffmpeg-v34.3.2-linux-x64.zip +9190743c78210574faf5d5ecb82a00f8fa15e5f2253378cb925a99ca9d39961b *ffmpeg-v34.3.2-mas-arm64.zip +48915adcb1a6342efeda896035101300f0432c0304cfb38f2378e98c6309ebae *ffmpeg-v34.3.2-mas-x64.zip +745d5ef786de6d4a720475079836e2fda7b501cfcd255819485a47de5b24b74e *ffmpeg-v34.3.2-win32-arm64.zip +d0d86d60978439dc8ae4a723d4e4c1f853891d596bfd84033440a232fa762e2f *ffmpeg-v34.3.2-win32-ia32.zip +4441539fd8c9cbe79880ff1bade9bdc0c3744c33d7409130af6404e57ee401ff *ffmpeg-v34.3.2-win32-x64.zip +39edd1eeefe881aa75af0e438204e0b1c6e6724e34fa5819109276331c0c2c9a *hunspell_dictionaries.zip +20dd417536e5f4ebc01f480221284c0673729c27b082bc04e2922f16cd571719 *libcxx-objects-v34.3.2-linux-arm64.zip +7e53c5779c04f895f8282c0450ec4a63074d15a0e910e41006cfea133d0288af *libcxx-objects-v34.3.2-linux-armv7l.zip +92e2283c924ab523ffec3ea22513beaab6417f7fc3d570f57d51a1e1ceb7f510 *libcxx-objects-v34.3.2-linux-x64.zip +9bf3c6e8ad68f523fe086fada4148dd04e5bb0b9290d104873e66f2584a5cf50 *libcxx_headers.zip +34e4b44f9c5e08b557a2caed55456ce7690abab910196a783a2a47b58d2b9ac9 *libcxxabi_headers.zip +11f67635e6659f9188198e4086c51b89890b61a22f6c17c99eff35595ee8f51d *mksnapshot-v34.3.2-darwin-arm64.zip +c0add9ef4ac27c73fa043d04b4c9635fd3fd9f5c16d7a03e961864ba05252813 *mksnapshot-v34.3.2-darwin-x64.zip +6262adf86a340d8d28059937b01ef1117b93212e945fddbceea5c18d7e7c64f0 *mksnapshot-v34.3.2-linux-arm64-x64.zip +f7db8ebe91a1cc8d24ef6aad12949a18d8e4975ac296e3e5e9ecd88c9bccb143 *mksnapshot-v34.3.2-linux-armv7l-x64.zip +6642038e86bda362980ff1c8973a404e2b02efdd87de9e35b650fc1e743833da *mksnapshot-v34.3.2-linux-x64.zip +15883bf8e8cd737c3682d1e719d7cbac92f50b525681aac324dca876861dfc7d *mksnapshot-v34.3.2-mas-arm64.zip +4da23a950bfcc377ef21c37d496017ab4c36da03f3b41049ac114042c42608ce *mksnapshot-v34.3.2-mas-x64.zip +fab59573d3c2f9bdf31146a1896d24ac0c51f736aad86d2f3c7ecef13c05a7fd *mksnapshot-v34.3.2-win32-arm64-x64.zip +66f25e07c6f8d5d2009577a129440255a3baf63c929a5b60b2e77cd52e46105b *mksnapshot-v34.3.2-win32-ia32.zip +8168bfbf61882cfac80aed1e71e364e1c7f2fccd11eac298e6abade8b46894ea *mksnapshot-v34.3.2-win32-x64.zip diff --git a/build/checksums/nodejs.txt b/build/checksums/nodejs.txt index d10698b07301..d394605dda3a 100644 --- a/build/checksums/nodejs.txt +++ b/build/checksums/nodejs.txt @@ -1,7 +1,7 @@ -476324108c4361935465631eec47df1c943ba2c87bc050853385b1d1c71f0b1f node-v20.17.0-darwin-arm64.tar.gz -eefe9447dbb0b5b233d42730989c6c364487de4043145db2f63da94e9623c380 node-v20.17.0-darwin-x64.tar.gz -18afbf2781edfcc9918343f4bf74a8c35d74d778b85d40a0c09b232adc0ea82c node-v20.17.0-linux-arm64.tar.gz -fcc19311817622f2e4693c3fff488c72fa1ce7c2c423c2bd114c86b24fa8c388 node-v20.17.0-linux-armv7l.tar.gz -21e656f6f4e34080ddc5d75fbfe58ce8482fe6e70a76aeae14afdcdc1e23079d node-v20.17.0-linux-x64.tar.gz -6f9118a87189bc101160929a94ddc6b999c30228da7d3d97c2b15eb11b258b95 win-arm64/node.exe -0740b4a681b320f966b57f51c87c11f897e8605064b6ae62d03e177bc66f01b9 win-x64/node.exe +1f15b7ed18a580af31cf32bc126572292d820f547bf55bf9cdce08041a24e1d9 node-v20.18.3-darwin-arm64.tar.gz +ba668f64df9239843fefcef095ee539f5ac5aa1b0fc15a71f1ecca16abedec7a node-v20.18.3-darwin-x64.tar.gz +93a9df19238adfaa289f4784041d03edaf2fdd89fbb247faffca2fe4a1000703 node-v20.18.3-linux-arm64.tar.gz +8a84eb34287db6a273066934d7195e429f57b91686b62fc19497210204a2b3de node-v20.18.3-linux-armv7l.tar.gz +9fc3952da39b20d1fcfdb777b198cc035485afbbb1004b4df93f35245d61151e node-v20.18.3-linux-x64.tar.gz +4258e333f4b95060681d61bffa762542a8068547d3dffebe57c575b38d380dda win-arm64/node.exe +528a9aa64888a2a3ba71c6aea89434dd5ab5cb3caa9f0f31345cf5facf685ab0 win-x64/node.exe diff --git a/build/checksums/vscode-sysroot.txt b/build/checksums/vscode-sysroot.txt index 0b5f38c60ce7..67182b078ed5 100644 --- a/build/checksums/vscode-sysroot.txt +++ b/build/checksums/vscode-sysroot.txt @@ -1,6 +1,3 @@ -68a17006021975ff271a1dd615f9db9eda7c25f2cc65e750c87980dc57a06c94 aarch64-linux-gnu-glibc-2.17.tar.gz 0de422a81683cf9e8cf875dbd1e0c27545ac3c775b2d53015daf3ca2b31d3f15 aarch64-linux-gnu-glibc-2.28.tar.gz -3ced48cb479f2cdba95aa649710fcb7778685551c745bbd76ac706c3c0ead9fb arm-rpi-linux-gnueabihf-glibc-2.17.tar.gz 7aea163f7fad8cc50000c86b5108be880121d35e2f55d016ef8c96bbe54129eb arm-rpi-linux-gnueabihf-glibc-2.28.tar.gz -5aae21115f1d284c3cdf32c83db15771b59bc80793f1423032abf5a823c0d658 x86_64-linux-gnu-glibc-2.17.tar.gz dbb927408393041664a020661f2641c9785741be3d29b050b9dac58980967784 x86_64-linux-gnu-glibc-2.28.tar.gz diff --git a/build/darwin/create-universal-app.js b/build/darwin/create-universal-app.js index 1f53369356b4..7d3f9164d5f1 100644 --- a/build/darwin/create-universal-app.js +++ b/build/darwin/create-universal-app.js @@ -3,27 +3,33 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const path = require("path"); -const fs = require("fs"); -const minimatch = require("minimatch"); +const path_1 = __importDefault(require("path")); +const fs_1 = __importDefault(require("fs")); +const minimatch_1 = __importDefault(require("minimatch")); const vscode_universal_bundler_1 = require("vscode-universal-bundler"); -const root = path.dirname(path.dirname(__dirname)); +const root = path_1.default.dirname(path_1.default.dirname(__dirname)); async function main(buildDir) { const arch = process.env['VSCODE_ARCH']; if (!buildDir) { throw new Error('Build dir not provided'); } - const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8')); + const product = JSON.parse(fs_1.default.readFileSync(path_1.default.join(root, 'product.json'), 'utf8')); const appName = product.nameLong + '.app'; - const x64AppPath = path.join(buildDir, 'VSCode-darwin-x64', appName); - const arm64AppPath = path.join(buildDir, 'VSCode-darwin-arm64', appName); - const asarRelativePath = path.join('Contents', 'Resources', 'app', 'node_modules.asar'); - const outAppPath = path.join(buildDir, `VSCode-darwin-${arch}`, appName); - const productJsonPath = path.resolve(outAppPath, 'Contents', 'Resources', 'app', 'product.json'); + const x64AppPath = path_1.default.join(buildDir, 'VSCode-darwin-x64', appName); + const arm64AppPath = path_1.default.join(buildDir, 'VSCode-darwin-arm64', appName); + const asarRelativePath = path_1.default.join('Contents', 'Resources', 'app', 'node_modules.asar'); + const outAppPath = path_1.default.join(buildDir, `VSCode-darwin-${arch}`, appName); + const productJsonPath = path_1.default.resolve(outAppPath, 'Contents', 'Resources', 'app', 'product.json'); const filesToSkip = [ '**/CodeResources', '**/Credits.rtf', + '**/policies/{*.mobileconfig,**/*.plist}', + // TODO: Should we consider expanding this to other files in this area? + '**/node_modules/@parcel/node-addon-api/nothing.target.mk' ]; await (0, vscode_universal_bundler_1.makeUniversalApp)({ x64AppPath, @@ -35,18 +41,18 @@ async function main(buildDir) { x64ArchFiles: '*/kerberos.node', filesToSkipComparison: (file) => { for (const expected of filesToSkip) { - if (minimatch(file, expected)) { + if ((0, minimatch_1.default)(file, expected)) { return true; } } return false; } }); - const productJson = JSON.parse(fs.readFileSync(productJsonPath, 'utf8')); + const productJson = JSON.parse(fs_1.default.readFileSync(productJsonPath, 'utf8')); Object.assign(productJson, { darwinUniversalAssetId: 'darwin-universal' }); - fs.writeFileSync(productJsonPath, JSON.stringify(productJson, null, '\t')); + fs_1.default.writeFileSync(productJsonPath, JSON.stringify(productJson, null, '\t')); } if (require.main === module) { main(process.argv[2]).catch(err => { diff --git a/build/darwin/create-universal-app.ts b/build/darwin/create-universal-app.ts index 1f19053494d2..7872eccc63e3 100644 --- a/build/darwin/create-universal-app.ts +++ b/build/darwin/create-universal-app.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; -import * as fs from 'fs'; -import * as minimatch from 'minimatch'; +import path from 'path'; +import fs from 'fs'; +import minimatch from 'minimatch'; import { makeUniversalApp } from 'vscode-universal-bundler'; const root = path.dirname(path.dirname(__dirname)); @@ -28,6 +28,9 @@ async function main(buildDir?: string) { const filesToSkip = [ '**/CodeResources', '**/Credits.rtf', + '**/policies/{*.mobileconfig,**/*.plist}', + // TODO: Should we consider expanding this to other files in this area? + '**/node_modules/@parcel/node-addon-api/nothing.target.mk' ]; await makeUniversalApp({ diff --git a/build/darwin/sign.js b/build/darwin/sign.js index feb5834ff85e..dff30fd0e18a 100644 --- a/build/darwin/sign.js +++ b/build/darwin/sign.js @@ -3,14 +3,17 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = require("fs"); -const path = require("path"); -const codesign = require("electron-osx-sign"); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const electron_osx_sign_1 = __importDefault(require("electron-osx-sign")); const cross_spawn_promise_1 = require("@malept/cross-spawn-promise"); -const root = path.dirname(path.dirname(__dirname)); +const root = path_1.default.dirname(path_1.default.dirname(__dirname)); function getElectronVersion() { - const npmrc = fs.readFileSync(path.join(root, '.npmrc'), 'utf8'); + const npmrc = fs_1.default.readFileSync(path_1.default.join(root, '.npmrc'), 'utf8'); const target = /^target="(.*)"$/m.exec(npmrc)[1]; return target; } @@ -24,25 +27,25 @@ async function main(buildDir) { if (!tempDir) { throw new Error('$AGENT_TEMPDIRECTORY not set'); } - const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8')); - const baseDir = path.dirname(__dirname); - const appRoot = path.join(buildDir, `VSCode-darwin-${arch}`); + const product = JSON.parse(fs_1.default.readFileSync(path_1.default.join(root, 'product.json'), 'utf8')); + const baseDir = path_1.default.dirname(__dirname); + const appRoot = path_1.default.join(buildDir, `VSCode-darwin-${arch}`); const appName = product.nameLong + '.app'; - const appFrameworkPath = path.join(appRoot, appName, 'Contents', 'Frameworks'); + const appFrameworkPath = path_1.default.join(appRoot, appName, 'Contents', 'Frameworks'); const helperAppBaseName = product.nameShort; const gpuHelperAppName = helperAppBaseName + ' Helper (GPU).app'; const rendererHelperAppName = helperAppBaseName + ' Helper (Renderer).app'; const pluginHelperAppName = helperAppBaseName + ' Helper (Plugin).app'; - const infoPlistPath = path.resolve(appRoot, appName, 'Contents', 'Info.plist'); + const infoPlistPath = path_1.default.resolve(appRoot, appName, 'Contents', 'Info.plist'); const defaultOpts = { - app: path.join(appRoot, appName), + app: path_1.default.join(appRoot, appName), platform: 'darwin', - entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist'), - 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist'), + entitlements: path_1.default.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist'), + 'entitlements-inherit': path_1.default.join(baseDir, 'azure-pipelines', 'darwin', 'app-entitlements.plist'), hardenedRuntime: true, 'pre-auto-entitlements': false, 'pre-embed-provisioning-profile': false, - keychain: path.join(tempDir, 'buildagent.keychain'), + keychain: path_1.default.join(tempDir, 'buildagent.keychain'), version: getElectronVersion(), identity, 'gatekeeper-assess': false @@ -58,21 +61,21 @@ async function main(buildDir) { }; const gpuHelperOpts = { ...defaultOpts, - app: path.join(appFrameworkPath, gpuHelperAppName), - entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist'), - 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist'), + app: path_1.default.join(appFrameworkPath, gpuHelperAppName), + entitlements: path_1.default.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist'), + 'entitlements-inherit': path_1.default.join(baseDir, 'azure-pipelines', 'darwin', 'helper-gpu-entitlements.plist'), }; const rendererHelperOpts = { ...defaultOpts, - app: path.join(appFrameworkPath, rendererHelperAppName), - entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist'), - 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist'), + app: path_1.default.join(appFrameworkPath, rendererHelperAppName), + entitlements: path_1.default.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist'), + 'entitlements-inherit': path_1.default.join(baseDir, 'azure-pipelines', 'darwin', 'helper-renderer-entitlements.plist'), }; const pluginHelperOpts = { ...defaultOpts, - app: path.join(appFrameworkPath, pluginHelperAppName), - entitlements: path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-plugin-entitlements.plist'), - 'entitlements-inherit': path.join(baseDir, 'azure-pipelines', 'darwin', 'helper-plugin-entitlements.plist'), + app: path_1.default.join(appFrameworkPath, pluginHelperAppName), + entitlements: path_1.default.join(baseDir, 'azure-pipelines', 'darwin', 'helper-plugin-entitlements.plist'), + 'entitlements-inherit': path_1.default.join(baseDir, 'azure-pipelines', 'darwin', 'helper-plugin-entitlements.plist'), }; // Only overwrite plist entries for x64 and arm64 builds, // universal will get its copy from the x64 build. @@ -99,10 +102,10 @@ async function main(buildDir) { `${infoPlistPath}` ]); } - await codesign.signAsync(gpuHelperOpts); - await codesign.signAsync(rendererHelperOpts); - await codesign.signAsync(pluginHelperOpts); - await codesign.signAsync(appOpts); + await electron_osx_sign_1.default.signAsync(gpuHelperOpts); + await electron_osx_sign_1.default.signAsync(rendererHelperOpts); + await electron_osx_sign_1.default.signAsync(pluginHelperOpts); + await electron_osx_sign_1.default.signAsync(appOpts); } if (require.main === module) { main(process.argv[2]).catch(err => { diff --git a/build/darwin/sign.ts b/build/darwin/sign.ts index 5b3413b79e12..ecf162743ef2 100644 --- a/build/darwin/sign.ts +++ b/build/darwin/sign.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; -import * as codesign from 'electron-osx-sign'; +import fs from 'fs'; +import path from 'path'; +import codesign from 'electron-osx-sign'; import { spawn } from '@malept/cross-spawn-promise'; const root = path.dirname(path.dirname(__dirname)); diff --git a/build/darwin/verify-macho.js b/build/darwin/verify-macho.js index 947184324e2c..e7a4eb28d705 100644 --- a/build/darwin/verify-macho.js +++ b/build/darwin/verify-macho.js @@ -3,9 +3,12 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const assert = require("assert"); -const path = require("path"); +const assert_1 = __importDefault(require("assert")); +const path_1 = __importDefault(require("path")); const promises_1 = require("fs/promises"); const cross_spawn_promise_1 = require("@malept/cross-spawn-promise"); const MACHO_PREFIX = 'Mach-O '; @@ -78,7 +81,7 @@ async function checkMachOFiles(appPath, arch) { } else if (header_magic === MACHO_UNIVERSAL_MAGIC_LE) { const num_binaries = header.readUInt32BE(4); - assert.equal(num_binaries, 2); + assert_1.default.equal(num_binaries, 2); const file_entries_size = file_header_entry_size * num_binaries; const file_entries = Buffer.alloc(file_entries_size); read(p, file_entries, 0, file_entries_size, 8).then(_ => { @@ -95,7 +98,7 @@ async function checkMachOFiles(appPath, arch) { } if (info.isDirectory()) { for (const child of await (0, promises_1.readdir)(p)) { - await traverse(path.resolve(p, child)); + await traverse(path_1.default.resolve(p, child)); } } }; @@ -103,8 +106,8 @@ async function checkMachOFiles(appPath, arch) { return invalidFiles; } const archToCheck = process.argv[2]; -assert(process.env['APP_PATH'], 'APP_PATH not set'); -assert(archToCheck === 'x64' || archToCheck === 'arm64' || archToCheck === 'universal', `Invalid architecture ${archToCheck} to check`); +(0, assert_1.default)(process.env['APP_PATH'], 'APP_PATH not set'); +(0, assert_1.default)(archToCheck === 'x64' || archToCheck === 'arm64' || archToCheck === 'universal', `Invalid architecture ${archToCheck} to check`); checkMachOFiles(process.env['APP_PATH'], archToCheck).then(invalidFiles => { if (invalidFiles.length > 0) { console.error('\x1b[31mThe following files are built for the wrong architecture:\x1b[0m'); diff --git a/build/darwin/verify-macho.ts b/build/darwin/verify-macho.ts index f418c44a2301..61ebc71aba24 100644 --- a/build/darwin/verify-macho.ts +++ b/build/darwin/verify-macho.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as assert from 'assert'; -import * as path from 'path'; +import assert from 'assert'; +import path from 'path'; import { open, stat, readdir, realpath } from 'fs/promises'; import { spawn, ExitCodeError } from '@malept/cross-spawn-promise'; diff --git a/build/filters.js b/build/filters.js index 1705d4b32c33..ece41209baf8 100644 --- a/build/filters.js +++ b/build/filters.js @@ -49,6 +49,7 @@ module.exports.unicodeFilter = [ '!extensions/ipynb/notebook-out/**', '!extensions/notebook-renderers/renderer-out/**', '!extensions/php-language-features/src/features/phpGlobalFunctions.ts', + '!extensions/terminal-suggest/src/completions/upstream/**', '!extensions/typescript-language-features/test-workspace/**', '!extensions/vscode-api-tests/testWorkspace/**', '!extensions/vscode-api-tests/testWorkspace2/**', @@ -56,6 +57,7 @@ module.exports.unicodeFilter = [ '!extensions/**/out/**', '!extensions/**/snippets/**', '!extensions/**/colorize-fixtures/**', + '!extensions/terminal-suggest/src/shell/fishBuiltinsCache.ts', '!src/vs/base/browser/dompurify/**', '!src/vs/workbench/services/keybinding/browser/keyboardLayouts/**', @@ -88,6 +90,9 @@ module.exports.indentationFilter = [ '!test/automation/out/**', '!test/monaco/out/**', '!test/smoke/out/**', + '!extensions/terminal-suggest/src/shell/zshBuiltinsCache.ts', + '!extensions/terminal-suggest/src/shell/fishBuiltinsCache.ts', + '!extensions/terminal-suggest/src/completions/upstream/**', '!extensions/typescript-language-features/test-workspace/**', '!extensions/typescript-language-features/resources/walkthroughs/**', '!extensions/typescript-language-features/package-manager/node-maintainer/**', @@ -170,10 +175,10 @@ module.exports.copyrightFilter = [ '!extensions/markdown-math/notebook-out/**', '!extensions/ipynb/notebook-out/**', '!extensions/simple-browser/media/codicon.css', + '!extensions/terminal-suggest/src/completions/upstream/**', '!extensions/typescript-language-features/node-maintainer/**', '!extensions/html-language-features/server/src/modes/typescript/*', '!extensions/*/server/bin/*', - '!src/vs/editor/test/node/classification/typescript-test.ts', ]; module.exports.tsFormattingFilter = [ @@ -191,6 +196,8 @@ module.exports.tsFormattingFilter = [ '!extensions/vscode-api-tests/testWorkspace2/**', '!extensions/**/*.test.ts', '!extensions/html-language-features/server/lib/jquery.d.ts', + '!extensions/terminal-suggest/src/shell/zshBuiltinsCache.ts', + '!extensions/terminal-suggest/src/shell/fishBuiltinsCache.ts', ]; module.exports.eslintFilter = [ diff --git a/build/gulpfile.compile.js b/build/gulpfile.compile.js index e40b05f8d39e..d9584c2b54fb 100644 --- a/build/gulpfile.compile.js +++ b/build/gulpfile.compile.js @@ -13,14 +13,15 @@ const task = require('./lib/task'); const compilation = require('./lib/compilation'); /** - * @param {boolean} disableMangle + * @param {boolean} _disableMangle */ -function makeCompileBuildTask(disableMangle) { +function makeCompileBuildTask(_disableMangle) { return task.series( util.rimraf('out-build'), date.writeISODate('out-build'), compilation.compileApiProposalNamesTask, - compilation.compileTask('src', 'out-build', true, { disableMangle }) + compilation.compileTask(isAMDBuild ? 'src2' : 'src', 'out-build', true, { disableMangle: true }), + optimize.optimizeLoaderTask('out-build', 'out-build', true) ); } diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 739c37fc677b..9f7ea57465ba 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -80,7 +80,7 @@ const extractEditorSrcTask = task.define('extract-editor-src', () => { importIgnorePattern: /\.css$/, destRoot: path.join(root, 'out-editor-src'), redirects: { - '@vscode/tree-sitter-wasm': '../node_modules/@vscode/tree-sitter-wasm/wasm/tree-sitter-web', + '@vscode/tree-sitter-wasm': '../node_modules/@vscode/tree-sitter-wasm/wasm/web-tree-sitter', } }); }); diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 4f745aebdc76..f05738faa620 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -52,6 +52,7 @@ const compilations = [ 'extensions/markdown-math/tsconfig.json', 'extensions/media-preview/tsconfig.json', 'extensions/merge-conflict/tsconfig.json', + 'extensions/terminal-suggest/tsconfig.json', 'extensions/microsoft-authentication/tsconfig.json', 'extensions/notebook-renderers/tsconfig.json', 'extensions/npm/tsconfig.json', @@ -65,6 +66,7 @@ const compilations = [ 'extensions/typescript-language-features/tsconfig.json', 'extensions/vscode-api-tests/tsconfig.json', 'extensions/vscode-colorize-tests/tsconfig.json', + 'extensions/vscode-colorize-perf-tests/tsconfig.json', 'extensions/vscode-test-resolver/tsconfig.json', '.vscode/extensions/vscode-selfhost-test-provider/tsconfig.json', @@ -229,27 +231,61 @@ exports.compileExtensionMediaBuildTask = compileExtensionMediaBuildTask; //#region Azure Pipelines +/** + * Cleans the build directory for extensions + */ const cleanExtensionsBuildTask = task.define('clean-extensions-build', util.rimraf('.build/extensions')); -const compileExtensionsBuildTask = task.define('compile-extensions-build', task.series( +exports.cleanExtensionsBuildTask = cleanExtensionsBuildTask; + +/** + * brings in the marketplace extensions for the build + */ +const bundleMarketplaceExtensionsBuildTask = task.define('bundle-marketplace-extensions-build', () => ext.packageMarketplaceExtensionsStream(false).pipe(gulp.dest('.build'))); + +/** + * Compiles the non-native extensions for the build + * @note this does not clean the directory ahead of it. See {@link cleanExtensionsBuildTask} for that. + */ +const compileNonNativeExtensionsBuildTask = task.define('compile-non-native-extensions-build', task.series( + bundleMarketplaceExtensionsBuildTask, + task.define('bundle-non-native-extensions-build', () => ext.packageNonNativeLocalExtensionsStream().pipe(gulp.dest('.build'))) +)); +gulp.task(compileNonNativeExtensionsBuildTask); +exports.compileNonNativeExtensionsBuildTask = compileNonNativeExtensionsBuildTask; + +/** + * Compiles the native extensions for the build + * @note this does not clean the directory ahead of it. See {@link cleanExtensionsBuildTask} for that. + */ +const compileNativeExtensionsBuildTask = task.define('compile-native-extensions-build', () => ext.packageNativeLocalExtensionsStream().pipe(gulp.dest('.build'))); +gulp.task(compileNativeExtensionsBuildTask); +exports.compileNativeExtensionsBuildTask = compileNativeExtensionsBuildTask; + +/** + * Compiles the extensions for the build. + * This is essentially a helper task that combines {@link cleanExtensionsBuildTask}, {@link compileNonNativeExtensionsBuildTask} and {@link compileNativeExtensionsBuildTask} + */ +const compileAllExtensionsBuildTask = task.define('compile-extensions-build', task.series( cleanExtensionsBuildTask, - task.define('bundle-marketplace-extensions-build', () => ext.packageMarketplaceExtensionsStream(false).pipe(gulp.dest('.build'))), - task.define('bundle-extensions-build', () => ext.packageLocalExtensionsStream(false, false).pipe(gulp.dest('.build'))), + bundleMarketplaceExtensionsBuildTask, + task.define('bundle-extensions-build', () => ext.packageAllLocalExtensionsStream(false, false).pipe(gulp.dest('.build'))), )); +gulp.task(compileAllExtensionsBuildTask); +exports.compileAllExtensionsBuildTask = compileAllExtensionsBuildTask; -gulp.task(compileExtensionsBuildTask); -gulp.task(task.define('extensions-ci', task.series(compileExtensionsBuildTask, compileExtensionMediaBuildTask))); +// This task is run in the compilation stage of the CI pipeline. We only compile the non-native extensions since those can be fully built regardless of platform. +// This defers the native extensions to the platform specific stage of the CI pipeline. +gulp.task(task.define('extensions-ci', task.series(compileNonNativeExtensionsBuildTask, compileExtensionMediaBuildTask))); const compileExtensionsBuildPullRequestTask = task.define('compile-extensions-build-pr', task.series( cleanExtensionsBuildTask, - task.define('bundle-marketplace-extensions-build', () => ext.packageMarketplaceExtensionsStream(false).pipe(gulp.dest('.build'))), - task.define('bundle-extensions-build-pr', () => ext.packageLocalExtensionsStream(false, true).pipe(gulp.dest('.build'))), + bundleMarketplaceExtensionsBuildTask, + task.define('bundle-extensions-build-pr', () => ext.packageAllLocalExtensionsStream(false, true).pipe(gulp.dest('.build'))), )); - gulp.task(compileExtensionsBuildPullRequestTask); -gulp.task(task.define('extensions-ci-pr', task.series(compileExtensionsBuildPullRequestTask, compileExtensionMediaBuildTask))); - -exports.compileExtensionsBuildTask = compileExtensionsBuildTask; +// This task is run in the compilation stage of the PR pipeline. We compile all extensions in it to verify compilation. +gulp.task(task.define('extensions-ci-pr', task.series(compileExtensionsBuildPullRequestTask, compileExtensionMediaBuildTask))); //#endregion diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index 53ef6f38b02c..44add792d14c 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -27,7 +27,7 @@ const File = require('vinyl'); const fs = require('fs'); const glob = require('glob'); const { compileBuildTask } = require('./gulpfile.compile'); -const { compileExtensionsBuildTask, compileExtensionMediaBuildTask } = require('./gulpfile.extensions'); +const { cleanExtensionsBuildTask, compileNonNativeExtensionsBuildTask, compileNativeExtensionsBuildTask, compileExtensionMediaBuildTask } = require('./gulpfile.extensions'); const { vscodeWebResourceIncludes, createVSCodeWebFileContentMapper } = require('./gulpfile.vscode.web'); const cp = require('child_process'); const log = require('fancy-log'); @@ -63,6 +63,9 @@ const serverResourceIncludes = [ 'out-build/vs/base/node/cpuUsage.sh', 'out-build/vs/base/node/ps.sh', + // External Terminal + 'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt', + // Terminal shell integration 'out-build/vs/workbench/contrib/terminal/common/scripts/shellIntegration.ps1', 'out-build/vs/workbench/contrib/terminal/common/scripts/CodeTabExpansion.psm1', @@ -72,7 +75,7 @@ const serverResourceIncludes = [ 'out-build/vs/workbench/contrib/terminal/common/scripts/shellIntegration-profile.zsh', 'out-build/vs/workbench/contrib/terminal/common/scripts/shellIntegration-rc.zsh', 'out-build/vs/workbench/contrib/terminal/common/scripts/shellIntegration-login.zsh', - 'out-build/vs/workbench/contrib/terminal/common/scripts/fish_xdg_data/fish/vendor_conf.d/shellIntegration.fish', + 'out-build/vs/workbench/contrib/terminal/common/scripts/shellIntegration.fish', ]; @@ -399,13 +402,7 @@ function packageTask(type, platform, arch, sourceFolderName, destinationFolderNa ); } - if (platform === 'linux' && process.env['VSCODE_NODE_GLIBC'] === '-glibc-2.17') { - result = es.merge(result, - gulp.src(`resources/server/bin/helpers/check-requirements-linux-legacy.sh`, { base: '.' }) - .pipe(rename(`bin/helpers/check-requirements.sh`)) - .pipe(util.setExecutableBit()) - ); - } else if (platform === 'linux' || platform === 'alpine') { + if (platform === 'linux' || platform === 'alpine') { result = es.merge(result, gulp.src(`resources/server/bin/helpers/check-requirements-linux.sh`, { base: '.' }) .pipe(rename(`bin/helpers/check-requirements.sh`)) @@ -468,6 +465,7 @@ function tweakProductForServerWeb(product) { const destinationFolderName = `vscode-${type}${dashed(platform)}${dashed(arch)}`; const serverTaskCI = task.define(`vscode-${type}${dashed(platform)}${dashed(arch)}${dashed(minified)}-ci`, task.series( + compileNativeExtensionsBuildTask, gulp.task(`node-${platform}-${arch}`), util.rimraf(path.join(BUILD_ROOT, destinationFolderName)), packageTask(type, platform, arch, sourceFolderName, destinationFolderName) @@ -476,7 +474,8 @@ function tweakProductForServerWeb(product) { const serverTask = task.define(`vscode-${type}${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series( compileBuildTask, - compileExtensionsBuildTask, + cleanExtensionsBuildTask, + compileNonNativeExtensionsBuildTask, compileExtensionMediaBuildTask, minified ? minifyTask : bundleTask, serverTaskCI diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index d59b42da590e..0624f5f5a673 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -31,7 +31,7 @@ const { config } = require('./lib/electron'); const createAsar = require('./lib/asar').createAsar; const minimist = require('minimist'); const { compileBuildTask } = require('./gulpfile.compile'); -const { compileExtensionsBuildTask, compileExtensionMediaBuildTask } = require('./gulpfile.extensions'); +const { compileNonNativeExtensionsBuildTask, compileNativeExtensionsBuildTask, compileAllExtensionsBuildTask, compileExtensionMediaBuildTask, cleanExtensionsBuildTask } = require('./gulpfile.extensions'); const { promisify } = require('util'); const glob = promisify(require('glob')); const rcedit = promisify(require('rcedit')); @@ -74,7 +74,7 @@ const vscodeResourceIncludes = [ 'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt', // Terminal shell integration - 'out-build/vs/workbench/contrib/terminal/common/scripts/fish_xdg_data/fish/vendor_conf.d/*.fish', + 'out-build/vs/workbench/contrib/terminal/common/scripts/*.fish', 'out-build/vs/workbench/contrib/terminal/common/scripts/*.ps1', 'out-build/vs/workbench/contrib/terminal/common/scripts/*.psm1', 'out-build/vs/workbench/contrib/terminal/common/scripts/*.sh', @@ -101,9 +101,6 @@ const vscodeResourceIncludes = [ // Tree Sitter highlights 'out-build/vs/editor/common/languages/highlights/*.scm', - - // Issue Reporter - 'out-build/vs/workbench/contrib/issue/electron-sandbox/issueReporter.html' ]; const vscodeResources = [ @@ -144,8 +141,6 @@ const bundleVSCodeTask = task.define('bundle-vscode', task.series( fileContentMapper: filePath => { if ( filePath.endsWith('vs/code/electron-sandbox/workbench/workbench.js') || - // TODO: @justchen https://github.com/microsoft/vscode/issues/213332 make sure to remove when we use window.open on desktop - filePath.endsWith('vs/workbench/contrib/issue/electron-sandbox/issueReporter.js') || filePath.endsWith('vs/code/electron-sandbox/processExplorer/processExplorer.js')) { return async (content) => { const bootstrapWindowContent = await fs.promises.readFile(path.join(root, 'out-build', 'bootstrap-window.js'), 'utf-8'); @@ -156,8 +151,6 @@ const bundleVSCodeTask = task.define('bundle-vscode', task.series( }, skipTSBoilerplateRemoval: entryPoint => entryPoint === 'vs/code/electron-sandbox/workbench/workbench' || - // TODO: @justchen https://github.com/microsoft/vscode/issues/213332 make sure to remove when we use window.open on desktop - entryPoint === 'vs/workbench/contrib/issue/electron-sandbox/issueReporter' || entryPoint === 'vs/code/electron-sandbox/processExplorer/processExplorer', } } @@ -276,9 +269,8 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op const name = product.nameShort; const packageJsonUpdates = { name, version }; - // for linux url handling if (platform === 'linux') { - packageJsonUpdates.desktopName = `${product.applicationName}-url-handler.desktop`; + packageJsonUpdates.desktopName = `${product.applicationName}.desktop`; } let packageJsonContents; @@ -307,7 +299,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op const jsFilter = util.filter(data => !data.isDirectory() && /\.js$/.test(data.path)); const root = path.resolve(path.join(__dirname, '..')); const productionDependencies = getProductionDependencies(root); - const dependenciesSrc = productionDependencies.map(d => path.relative(root, d)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`, `!**/*.mk`]).flat(); + const dependenciesSrc = productionDependencies.map(d => path.relative(root, d)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`]).flat().concat('!**/*.mk'); const deps = gulp.src(dependenciesSrc, { base: '.', dot: true }) .pipe(filter(['**', `!**/${config.version}/**`, '!**/bin/darwin-arm64-87/**', '!**/package-lock.json', '!**/yarn.lock', '!**/*.js.map'])) @@ -380,8 +372,9 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op const shortcut = gulp.src('resources/darwin/bin/code.sh') .pipe(replace('@@APPNAME@@', product.applicationName)) .pipe(rename('bin/code')); - - all = es.merge(all, shortcut); + const policyDest = gulp.src('.build/policies/darwin/**', { base: '.build/policies/darwin' }) + .pipe(rename(f => f.dirname = `policies/${f.dirname}`)); + all = es.merge(all, shortcut, policyDest); } let result = all @@ -495,6 +488,7 @@ BUILD_TARGETS.forEach(buildTarget => { const destinationFolderName = `VSCode${dashed(platform)}${dashed(arch)}`; const tasks = [ + compileNativeExtensionsBuildTask, util.rimraf(path.join(buildRoot, destinationFolderName)), packageTask(platform, arch, sourceFolderName, destinationFolderName, opts) ]; @@ -508,7 +502,8 @@ BUILD_TARGETS.forEach(buildTarget => { const vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series( compileBuildTask, - compileExtensionsBuildTask, + cleanExtensionsBuildTask, + compileNonNativeExtensionsBuildTask, compileExtensionMediaBuildTask, minified ? minifyVSCodeTask : bundleVSCodeTask, vscodeTaskCI @@ -545,7 +540,7 @@ gulp.task(task.define( 'vscode-translations-export', task.series( core, - compileExtensionsBuildTask, + compileAllExtensionsBuildTask, function () { const pathToMetadata = './out-build/nls.metadata.json'; const pathToExtensions = '.build/extensions/*'; diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index 1b498cea8371..02b17022fa88 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -202,7 +202,7 @@ function packageTask(sourceFolderName, destinationFolderName) { const compileWebExtensionsBuildTask = task.define('compile-web-extensions-build', task.series( task.define('clean-web-extensions-build', util.rimraf('.build/web/extensions')), - task.define('bundle-web-extensions-build', () => extensions.packageLocalExtensionsStream(true, false).pipe(gulp.dest('.build/web'))), + task.define('bundle-web-extensions-build', () => extensions.packageAllLocalExtensionsStream(true, false).pipe(gulp.dest('.build/web'))), task.define('bundle-marketplace-web-extensions-build', () => extensions.packageMarketplaceExtensionsStream(true).pipe(gulp.dest('.build/web'))), task.define('bundle-web-extension-media-build', () => extensions.buildExtensionMedia(false, '.build/web/extensions')), )); diff --git a/build/lib/asar.js b/build/lib/asar.js index 19285ef71002..20c982a66217 100644 --- a/build/lib/asar.js +++ b/build/lib/asar.js @@ -3,18 +3,21 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.createAsar = createAsar; -const path = require("path"); -const es = require("event-stream"); +const path_1 = __importDefault(require("path")); +const event_stream_1 = __importDefault(require("event-stream")); const pickle = require('chromium-pickle-js'); const Filesystem = require('asar/lib/filesystem'); -const VinylFile = require("vinyl"); -const minimatch = require("minimatch"); +const vinyl_1 = __importDefault(require("vinyl")); +const minimatch_1 = __importDefault(require("minimatch")); function createAsar(folderPath, unpackGlobs, skipGlobs, duplicateGlobs, destFilename) { const shouldUnpackFile = (file) => { for (let i = 0; i < unpackGlobs.length; i++) { - if (minimatch(file.relative, unpackGlobs[i])) { + if ((0, minimatch_1.default)(file.relative, unpackGlobs[i])) { return true; } } @@ -22,7 +25,7 @@ function createAsar(folderPath, unpackGlobs, skipGlobs, duplicateGlobs, destFile }; const shouldSkipFile = (file) => { for (const skipGlob of skipGlobs) { - if (minimatch(file.relative, skipGlob)) { + if ((0, minimatch_1.default)(file.relative, skipGlob)) { return true; } } @@ -32,7 +35,7 @@ function createAsar(folderPath, unpackGlobs, skipGlobs, duplicateGlobs, destFile // node_modules.asar and node_modules const shouldDuplicateFile = (file) => { for (const duplicateGlob of duplicateGlobs) { - if (minimatch(file.relative, duplicateGlob)) { + if ((0, minimatch_1.default)(file.relative, duplicateGlob)) { return true; } } @@ -75,7 +78,7 @@ function createAsar(folderPath, unpackGlobs, skipGlobs, duplicateGlobs, destFile // Create a closure capturing `onFileInserted`. filesystem.insertFile(relativePath, shouldUnpack, { stat: stat }, {}).then(() => onFileInserted(), () => onFileInserted()); }; - return es.through(function (file) { + return event_stream_1.default.through(function (file) { if (file.stat.isDirectory()) { return; } @@ -83,7 +86,7 @@ function createAsar(folderPath, unpackGlobs, skipGlobs, duplicateGlobs, destFile throw new Error(`unknown item in stream!`); } if (shouldSkipFile(file)) { - this.queue(new VinylFile({ + this.queue(new vinyl_1.default({ base: '.', path: file.path, stat: file.stat, @@ -92,7 +95,7 @@ function createAsar(folderPath, unpackGlobs, skipGlobs, duplicateGlobs, destFile return; } if (shouldDuplicateFile(file)) { - this.queue(new VinylFile({ + this.queue(new vinyl_1.default({ base: '.', path: file.path, stat: file.stat, @@ -103,10 +106,10 @@ function createAsar(folderPath, unpackGlobs, skipGlobs, duplicateGlobs, destFile insertFile(file.relative, { size: file.contents.length, mode: file.stat.mode }, shouldUnpack); if (shouldUnpack) { // The file goes outside of xx.asar, in a folder xx.asar.unpacked - const relative = path.relative(folderPath, file.path); - this.queue(new VinylFile({ + const relative = path_1.default.relative(folderPath, file.path); + this.queue(new vinyl_1.default({ base: '.', - path: path.join(destFilename + '.unpacked', relative), + path: path_1.default.join(destFilename + '.unpacked', relative), stat: file.stat, contents: file.contents })); @@ -129,7 +132,7 @@ function createAsar(folderPath, unpackGlobs, skipGlobs, duplicateGlobs, destFile } const contents = Buffer.concat(out); out.length = 0; - this.queue(new VinylFile({ + this.queue(new vinyl_1.default({ base: '.', path: destFilename, contents: contents diff --git a/build/lib/asar.ts b/build/lib/asar.ts index 0b225ab1624f..5f2df925bde9 100644 --- a/build/lib/asar.ts +++ b/build/lib/asar.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; -import * as es from 'event-stream'; +import path from 'path'; +import es from 'event-stream'; const pickle = require('chromium-pickle-js'); const Filesystem = require('asar/lib/filesystem'); -import * as VinylFile from 'vinyl'; -import * as minimatch from 'minimatch'; +import VinylFile from 'vinyl'; +import minimatch from 'minimatch'; declare class AsarFilesystem { readonly header: unknown; diff --git a/build/lib/builtInExtensions.js b/build/lib/builtInExtensions.js index ac784c035060..249777c44588 100644 --- a/build/lib/builtInExtensions.js +++ b/build/lib/builtInExtensions.js @@ -3,39 +3,75 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.getExtensionStream = getExtensionStream; exports.getBuiltInExtensions = getBuiltInExtensions; -const fs = require("fs"); -const path = require("path"); -const os = require("os"); -const rimraf = require("rimraf"); -const es = require("event-stream"); -const rename = require("gulp-rename"); -const vfs = require("vinyl-fs"); -const ext = require("./extensions"); -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); -const root = path.dirname(path.dirname(__dirname)); -const productjson = JSON.parse(fs.readFileSync(path.join(__dirname, '../../product.json'), 'utf8')); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const os_1 = __importDefault(require("os")); +const rimraf_1 = __importDefault(require("rimraf")); +const event_stream_1 = __importDefault(require("event-stream")); +const gulp_rename_1 = __importDefault(require("gulp-rename")); +const vinyl_fs_1 = __importDefault(require("vinyl-fs")); +const ext = __importStar(require("./extensions")); +const fancy_log_1 = __importDefault(require("fancy-log")); +const ansi_colors_1 = __importDefault(require("ansi-colors")); +const root = path_1.default.dirname(path_1.default.dirname(__dirname)); +const productjson = JSON.parse(fs_1.default.readFileSync(path_1.default.join(__dirname, '../../product.json'), 'utf8')); const builtInExtensions = productjson.builtInExtensions || []; const webBuiltInExtensions = productjson.webBuiltInExtensions || []; -const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json'); +const controlFilePath = path_1.default.join(os_1.default.homedir(), '.vscode-oss-dev', 'extensions', 'control.json'); const ENABLE_LOGGING = !process.env['VSCODE_BUILD_BUILTIN_EXTENSIONS_SILENCE_PLEASE']; function log(...messages) { if (ENABLE_LOGGING) { - fancyLog(...messages); + (0, fancy_log_1.default)(...messages); } } function getExtensionPath(extension) { - return path.join(root, '.build', 'builtInExtensions', extension.name); + return path_1.default.join(root, '.build', 'builtInExtensions', extension.name); } function isUpToDate(extension) { - const packagePath = path.join(getExtensionPath(extension), 'package.json'); - if (!fs.existsSync(packagePath)) { + const packagePath = path_1.default.join(getExtensionPath(extension), 'package.json'); + if (!fs_1.default.existsSync(packagePath)) { return false; } - const packageContents = fs.readFileSync(packagePath, { encoding: 'utf8' }); + const packageContents = fs_1.default.readFileSync(packagePath, { encoding: 'utf8' }); try { const diskVersion = JSON.parse(packageContents).version; return (diskVersion === extension.version); @@ -45,73 +81,81 @@ function isUpToDate(extension) { } } function getExtensionDownloadStream(extension) { - const galleryServiceUrl = productjson.extensionsGallery?.serviceUrl; - return (galleryServiceUrl ? ext.fromMarketplace(galleryServiceUrl, extension) : ext.fromGithub(extension)) - .pipe(rename(p => p.dirname = `${extension.name}/${p.dirname}`)); + let input; + if (extension.vsix) { + input = ext.fromVsix(path_1.default.join(root, extension.vsix), extension); + } + else if (productjson.extensionsGallery?.serviceUrl) { + input = ext.fromMarketplace(productjson.extensionsGallery.serviceUrl, extension); + } + else { + input = ext.fromGithub(extension); + } + return input.pipe((0, gulp_rename_1.default)(p => p.dirname = `${extension.name}/${p.dirname}`)); } function getExtensionStream(extension) { // if the extension exists on disk, use those files instead of downloading anew if (isUpToDate(extension)) { - log('[extensions]', `${extension.name}@${extension.version} up to date`, ansiColors.green('✔︎')); - return vfs.src(['**'], { cwd: getExtensionPath(extension), dot: true }) - .pipe(rename(p => p.dirname = `${extension.name}/${p.dirname}`)); + log('[extensions]', `${extension.name}@${extension.version} up to date`, ansi_colors_1.default.green('✔︎')); + return vinyl_fs_1.default.src(['**'], { cwd: getExtensionPath(extension), dot: true }) + .pipe((0, gulp_rename_1.default)(p => p.dirname = `${extension.name}/${p.dirname}`)); } return getExtensionDownloadStream(extension); } function syncMarketplaceExtension(extension) { const galleryServiceUrl = productjson.extensionsGallery?.serviceUrl; - const source = ansiColors.blue(galleryServiceUrl ? '[marketplace]' : '[github]'); + const source = ansi_colors_1.default.blue(galleryServiceUrl ? '[marketplace]' : '[github]'); if (isUpToDate(extension)) { - log(source, `${extension.name}@${extension.version}`, ansiColors.green('✔︎')); - return es.readArray([]); + log(source, `${extension.name}@${extension.version}`, ansi_colors_1.default.green('✔︎')); + return event_stream_1.default.readArray([]); } - rimraf.sync(getExtensionPath(extension)); + rimraf_1.default.sync(getExtensionPath(extension)); return getExtensionDownloadStream(extension) - .pipe(vfs.dest('.build/builtInExtensions')) - .on('end', () => log(source, extension.name, ansiColors.green('✔︎'))); + .pipe(vinyl_fs_1.default.dest('.build/builtInExtensions')) + .on('end', () => log(source, extension.name, ansi_colors_1.default.green('✔︎'))); } function syncExtension(extension, controlState) { if (extension.platforms) { const platforms = new Set(extension.platforms); if (!platforms.has(process.platform)) { - log(ansiColors.gray('[skip]'), `${extension.name}@${extension.version}: Platform '${process.platform}' not supported: [${extension.platforms}]`, ansiColors.green('✔︎')); - return es.readArray([]); + log(ansi_colors_1.default.gray('[skip]'), `${extension.name}@${extension.version}: Platform '${process.platform}' not supported: [${extension.platforms}]`, ansi_colors_1.default.green('✔︎')); + return event_stream_1.default.readArray([]); } } switch (controlState) { case 'disabled': - log(ansiColors.blue('[disabled]'), ansiColors.gray(extension.name)); - return es.readArray([]); + log(ansi_colors_1.default.blue('[disabled]'), ansi_colors_1.default.gray(extension.name)); + return event_stream_1.default.readArray([]); case 'marketplace': return syncMarketplaceExtension(extension); default: - if (!fs.existsSync(controlState)) { - log(ansiColors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but that path does not exist.`)); - return es.readArray([]); + if (!fs_1.default.existsSync(controlState)) { + log(ansi_colors_1.default.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but that path does not exist.`)); + return event_stream_1.default.readArray([]); } - else if (!fs.existsSync(path.join(controlState, 'package.json'))) { - log(ansiColors.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but there is no 'package.json' file in that directory.`)); - return es.readArray([]); + else if (!fs_1.default.existsSync(path_1.default.join(controlState, 'package.json'))) { + log(ansi_colors_1.default.red(`Error: Built-in extension '${extension.name}' is configured to run from '${controlState}' but there is no 'package.json' file in that directory.`)); + return event_stream_1.default.readArray([]); } - log(ansiColors.blue('[local]'), `${extension.name}: ${ansiColors.cyan(controlState)}`, ansiColors.green('✔︎')); - return es.readArray([]); + log(ansi_colors_1.default.blue('[local]'), `${extension.name}: ${ansi_colors_1.default.cyan(controlState)}`, ansi_colors_1.default.green('✔︎')); + return event_stream_1.default.readArray([]); } } function readControlFile() { try { - return JSON.parse(fs.readFileSync(controlFilePath, 'utf8')); + return JSON.parse(fs_1.default.readFileSync(controlFilePath, 'utf8')); } catch (err) { return {}; } } function writeControlFile(control) { - fs.mkdirSync(path.dirname(controlFilePath), { recursive: true }); - fs.writeFileSync(controlFilePath, JSON.stringify(control, null, 2)); + fs_1.default.mkdirSync(path_1.default.dirname(controlFilePath), { recursive: true }); + fs_1.default.writeFileSync(controlFilePath, JSON.stringify(control, null, 2)); } function getBuiltInExtensions() { log('Synchronizing built-in extensions...'); - log(`You can manage built-in extensions with the ${ansiColors.cyan('--builtin')} flag`); + log(`You can manage built-in extensions with the ${ansi_colors_1.default.cyan('--builtin')} flag`); const control = readControlFile(); const streams = []; for (const extension of [...builtInExtensions, ...webBuiltInExtensions]) { @@ -121,7 +165,7 @@ function getBuiltInExtensions() { } writeControlFile(control); return new Promise((resolve, reject) => { - es.merge(streams) + event_stream_1.default.merge(streams) .on('error', reject) .on('end', resolve); }); diff --git a/build/lib/builtInExtensions.ts b/build/lib/builtInExtensions.ts index 8b831d42d44f..e9a1180ce35e 100644 --- a/build/lib/builtInExtensions.ts +++ b/build/lib/builtInExtensions.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; -import * as os from 'os'; -import * as rimraf from 'rimraf'; -import * as es from 'event-stream'; -import * as rename from 'gulp-rename'; -import * as vfs from 'vinyl-fs'; +import fs from 'fs'; +import path from 'path'; +import os from 'os'; +import rimraf from 'rimraf'; +import es from 'event-stream'; +import rename from 'gulp-rename'; +import vfs from 'vinyl-fs'; import * as ext from './extensions'; -import * as fancyLog from 'fancy-log'; -import * as ansiColors from 'ansi-colors'; +import fancyLog from 'fancy-log'; +import ansiColors from 'ansi-colors'; import { Stream } from 'stream'; export interface IExtensionDefinition { @@ -21,6 +21,7 @@ export interface IExtensionDefinition { sha256: string; repo: string; platforms?: string[]; + vsix?: string; metadata: { id: string; publisherId: { @@ -68,9 +69,17 @@ function isUpToDate(extension: IExtensionDefinition): boolean { } function getExtensionDownloadStream(extension: IExtensionDefinition) { - const galleryServiceUrl = productjson.extensionsGallery?.serviceUrl; - return (galleryServiceUrl ? ext.fromMarketplace(galleryServiceUrl, extension) : ext.fromGithub(extension)) - .pipe(rename(p => p.dirname = `${extension.name}/${p.dirname}`)); + let input: Stream; + + if (extension.vsix) { + input = ext.fromVsix(path.join(root, extension.vsix), extension); + } else if (productjson.extensionsGallery?.serviceUrl) { + input = ext.fromMarketplace(productjson.extensionsGallery.serviceUrl, extension); + } else { + input = ext.fromGithub(extension); + } + + return input.pipe(rename(p => p.dirname = `${extension.name}/${p.dirname}`)); } export function getExtensionStream(extension: IExtensionDefinition) { diff --git a/build/lib/builtInExtensionsCG.js b/build/lib/builtInExtensionsCG.js index 6a1e5ea539ec..3dc0ae27f0a8 100644 --- a/build/lib/builtInExtensionsCG.js +++ b/build/lib/builtInExtensionsCG.js @@ -3,14 +3,17 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const fs = require("fs"); -const path = require("path"); -const url = require("url"); -const ansiColors = require("ansi-colors"); -const root = path.dirname(path.dirname(__dirname)); -const rootCG = path.join(root, 'extensionsCG'); -const productjson = JSON.parse(fs.readFileSync(path.join(__dirname, '../../product.json'), 'utf8')); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const url_1 = __importDefault(require("url")); +const ansi_colors_1 = __importDefault(require("ansi-colors")); +const root = path_1.default.dirname(path_1.default.dirname(__dirname)); +const rootCG = path_1.default.join(root, 'extensionsCG'); +const productjson = JSON.parse(fs_1.default.readFileSync(path_1.default.join(__dirname, '../../product.json'), 'utf8')); const builtInExtensions = productjson.builtInExtensions || []; const webBuiltInExtensions = productjson.webBuiltInExtensions || []; const token = process.env['GITHUB_TOKEN']; @@ -18,7 +21,7 @@ const contentBasePath = 'raw.githubusercontent.com'; const contentFileNames = ['package.json', 'package-lock.json']; async function downloadExtensionDetails(extension) { const extensionLabel = `${extension.name}@${extension.version}`; - const repository = url.parse(extension.repo).path.substr(1); + const repository = url_1.default.parse(extension.repo).path.substr(1); const repositoryContentBaseUrl = `https://${token ? `${token}@` : ''}${contentBasePath}/${repository}/v${extension.version}`; async function getContent(fileName) { try { @@ -42,16 +45,16 @@ async function downloadExtensionDetails(extension) { const results = await Promise.all(promises); for (const result of results) { if (result.body) { - const extensionFolder = path.join(rootCG, extension.name); - fs.mkdirSync(extensionFolder, { recursive: true }); - fs.writeFileSync(path.join(extensionFolder, result.fileName), result.body); - console.log(` - ${result.fileName} ${ansiColors.green('✔︎')}`); + const extensionFolder = path_1.default.join(rootCG, extension.name); + fs_1.default.mkdirSync(extensionFolder, { recursive: true }); + fs_1.default.writeFileSync(path_1.default.join(extensionFolder, result.fileName), result.body); + console.log(` - ${result.fileName} ${ansi_colors_1.default.green('✔︎')}`); } else if (result.body === undefined) { - console.log(` - ${result.fileName} ${ansiColors.yellow('⚠️')}`); + console.log(` - ${result.fileName} ${ansi_colors_1.default.yellow('⚠️')}`); } else { - console.log(` - ${result.fileName} ${ansiColors.red('🛑')}`); + console.log(` - ${result.fileName} ${ansi_colors_1.default.red('🛑')}`); } } // Validation @@ -68,10 +71,10 @@ async function main() { } } main().then(() => { - console.log(`Built-in extensions component data downloaded ${ansiColors.green('✔︎')}`); + console.log(`Built-in extensions component data downloaded ${ansi_colors_1.default.green('✔︎')}`); process.exit(0); }, err => { - console.log(`Built-in extensions component data could not be downloaded ${ansiColors.red('🛑')}`); + console.log(`Built-in extensions component data could not be downloaded ${ansi_colors_1.default.red('🛑')}`); console.error(err); process.exit(1); }); diff --git a/build/lib/builtInExtensionsCG.ts b/build/lib/builtInExtensionsCG.ts index 9d11dea3dcae..4628b365a2e1 100644 --- a/build/lib/builtInExtensionsCG.ts +++ b/build/lib/builtInExtensionsCG.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; -import * as url from 'url'; -import ansiColors = require('ansi-colors'); +import fs from 'fs'; +import path from 'path'; +import url from 'url'; +import ansiColors from 'ansi-colors'; import { IExtensionDefinition } from './builtInExtensions'; const root = path.dirname(path.dirname(__dirname)); diff --git a/build/lib/bundle.js b/build/lib/bundle.js index 627b9966700e..f1490f4ad4b7 100644 --- a/build/lib/bundle.js +++ b/build/lib/bundle.js @@ -3,12 +3,15 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.bundle = bundle; exports.removeAllTSBoilerplate = removeAllTSBoilerplate; -const fs = require("fs"); -const path = require("path"); -const vm = require("vm"); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const vm_1 = __importDefault(require("vm")); /** * Bundle `entryPoints` given config `config`. */ @@ -30,8 +33,8 @@ function bundle(entryPoints, config, callback) { allMentionedModulesMap[excludedModule] = true; }); }); - const code = require('fs').readFileSync(path.join(__dirname, '../../src/vs/loader.js')); - const r = vm.runInThisContext('(function(require, module, exports) { ' + code + '\n});'); + const code = require('fs').readFileSync(path_1.default.join(__dirname, '../../src/vs/loader.js')); + const r = vm_1.default.runInThisContext('(function(require, module, exports) { ' + code + '\n});'); const loaderModule = { exports: {} }; r.call({}, require, loaderModule, loaderModule.exports); const loader = loaderModule.exports; @@ -149,7 +152,7 @@ function extractStrings(destFiles) { _path = pieces[0]; } if (/^\.\//.test(_path) || /^\.\.\//.test(_path)) { - const res = path.join(path.dirname(module), _path).replace(/\\/g, '/'); + const res = path_1.default.join(path_1.default.dirname(module), _path).replace(/\\/g, '/'); return prefix + res; } return prefix + _path; @@ -224,7 +227,7 @@ function removeAllDuplicateTSBoilerplate(destFiles) { return destFiles; } function removeAllTSBoilerplate(source) { - const seen = new Array(BOILERPLATE.length).fill(true, 0, 10); + const seen = new Array(BOILERPLATE.length).fill(true, 0, BOILERPLATE.length); return removeDuplicateTSBoilerplate(source, seen); } // Taken from typescript compiler => emitFiles @@ -239,6 +242,8 @@ const BOILERPLATE = [ { start: /^var __createBinding/, end: /^}\)\);$/ }, { start: /^var __setModuleDefault/, end: /^}\);$/ }, { start: /^var __importStar/, end: /^};$/ }, + { start: /^var __addDisposableResource/, end: /^};$/ }, + { start: /^var __disposeResources/, end: /^}\);$/ }, ]; function removeDuplicateTSBoilerplate(source, SEEN_BOILERPLATE = []) { const lines = source.split(/\r\n|\n|\r/); @@ -357,7 +362,7 @@ function emitEntryPoint(modulesMap, deps, entryPoint, includedModules, prepend, } function readFileAndRemoveBOM(path) { const BOM_CHAR_CODE = 65279; - let contents = fs.readFileSync(path, 'utf8'); + let contents = fs_1.default.readFileSync(path, 'utf8'); // Remove BOM if (contents.charCodeAt(0) === BOM_CHAR_CODE) { contents = contents.substring(1); diff --git a/build/lib/bundle.ts b/build/lib/bundle.ts index 58995b7d5d1b..68182e6b85de 100644 --- a/build/lib/bundle.ts +++ b/build/lib/bundle.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; -import * as vm from 'vm'; +import fs from 'fs'; +import path from 'path'; +import vm from 'vm'; interface IPosition { line: number; @@ -358,7 +358,7 @@ function removeAllDuplicateTSBoilerplate(destFiles: IConcatFile[]): IConcatFile[ } export function removeAllTSBoilerplate(source: string) { - const seen = new Array(BOILERPLATE.length).fill(true, 0, 10); + const seen = new Array(BOILERPLATE.length).fill(true, 0, BOILERPLATE.length); return removeDuplicateTSBoilerplate(source, seen); } @@ -374,6 +374,8 @@ const BOILERPLATE = [ { start: /^var __createBinding/, end: /^}\)\);$/ }, { start: /^var __setModuleDefault/, end: /^}\);$/ }, { start: /^var __importStar/, end: /^};$/ }, + { start: /^var __addDisposableResource/, end: /^};$/ }, + { start: /^var __disposeResources/, end: /^}\);$/ }, ]; function removeDuplicateTSBoilerplate(source: string, SEEN_BOILERPLATE: boolean[] = []): string { diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 7b9d73facbbd..841dbe13ecf5 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -3,24 +3,60 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.watchApiProposalNamesTask = exports.compileApiProposalNamesTask = void 0; exports.transpileTask = transpileTask; exports.compileTask = compileTask; exports.watchTask = watchTask; -const es = require("event-stream"); -const fs = require("fs"); -const gulp = require("gulp"); -const path = require("path"); -const monacodts = require("./monaco-api"); -const nls = require("./nls"); +const event_stream_1 = __importDefault(require("event-stream")); +const fs_1 = __importDefault(require("fs")); +const gulp_1 = __importDefault(require("gulp")); +const path_1 = __importDefault(require("path")); +const monacodts = __importStar(require("./monaco-api")); +const nls = __importStar(require("./nls")); const reporter_1 = require("./reporter"); -const util = require("./util"); -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); -const os = require("os"); -const File = require("vinyl"); -const task = require("./task"); +const util = __importStar(require("./util")); +const fancy_log_1 = __importDefault(require("fancy-log")); +const ansi_colors_1 = __importDefault(require("ansi-colors")); +const os_1 = __importDefault(require("os")); +const vinyl_1 = __importDefault(require("vinyl")); +const task = __importStar(require("./task")); const index_1 = require("./mangle/index"); const postcss_1 = require("./postcss"); const ts = require("typescript"); @@ -28,7 +64,7 @@ const watch = require('./watch'); // --- gulp-tsb: compile and transpile -------------------------------- const reporter = (0, reporter_1.createReporter)(); function getTypeScriptCompilerOptions(src) { - const rootDir = path.join(__dirname, `../../${src}`); + const rootDir = path_1.default.join(__dirname, `../../${src}`); const options = {}; options.verbose = false; options.sourceMap = true; @@ -38,13 +74,13 @@ function getTypeScriptCompilerOptions(src) { options.rootDir = rootDir; options.baseUrl = rootDir; options.sourceRoot = util.toFileUri(rootDir); - options.newLine = /\r\n/.test(fs.readFileSync(__filename, 'utf8')) ? 0 : 1; + options.newLine = /\r\n/.test(fs_1.default.readFileSync(__filename, 'utf8')) ? 0 : 1; return options; } function createCompile(src, { build, emitError, transpileOnly, preserveEnglish }) { const tsb = require('./tsb'); const sourcemaps = require('gulp-sourcemaps'); - const projectPath = path.join(__dirname, '../../', src, 'tsconfig.json'); + const projectPath = path_1.default.join(__dirname, '../../', src, 'tsconfig.json'); const overrideOptions = { ...getTypeScriptCompilerOptions(src), inlineSources: Boolean(build) }; if (!build) { overrideOptions.inlineSourceMap = true; @@ -62,7 +98,7 @@ function createCompile(src, { build, emitError, transpileOnly, preserveEnglish } const isCSS = (f) => f.path.endsWith('.css') && !f.path.includes('fixtures'); const noDeclarationsFilter = util.filter(data => !(/\.d\.ts$/.test(data.path))); const postcssNesting = require('postcss-nesting'); - const input = es.through(); + const input = event_stream_1.default.through(); const output = input .pipe(util.$if(isUtf8Test, bom())) // this is required to preserve BOM in test files that loose it otherwise .pipe(util.$if(!build && isRuntimeJs, util.appendOwnPathSourceURL())) @@ -80,7 +116,7 @@ function createCompile(src, { build, emitError, transpileOnly, preserveEnglish } }))) .pipe(tsFilter.restore) .pipe(reporter.end(!!emitError)); - return es.duplex(input, output); + return event_stream_1.default.duplex(input, output); } pipeline.tsProjectSrc = () => { return compilation.src({ base: src }); @@ -91,31 +127,31 @@ function createCompile(src, { build, emitError, transpileOnly, preserveEnglish } function transpileTask(src, out, esbuild) { const task = () => { const transpile = createCompile(src, { build: false, emitError: true, transpileOnly: { esbuild }, preserveEnglish: false }); - const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); + const srcPipe = gulp_1.default.src(`${src}/**`, { base: `${src}` }); return srcPipe .pipe(transpile()) - .pipe(gulp.dest(out)); + .pipe(gulp_1.default.dest(out)); }; - task.taskName = `transpile-${path.basename(src)}`; + task.taskName = `transpile-${path_1.default.basename(src)}`; return task; } function compileTask(src, out, build, options = {}) { const task = () => { - if (os.totalmem() < 4_000_000_000) { + if (os_1.default.totalmem() < 4_000_000_000) { throw new Error('compilation requires 4GB of RAM'); } const compile = createCompile(src, { build, emitError: true, transpileOnly: false, preserveEnglish: !!options.preserveEnglish }); - const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); + const srcPipe = gulp_1.default.src(`${src}/**`, { base: `${src}` }); const generator = new MonacoGenerator(false); if (src === 'src') { generator.execute(); } // mangle: TypeScript to TypeScript - let mangleStream = es.through(); + let mangleStream = event_stream_1.default.through(); if (build && !options.disableMangle) { - let ts2tsMangler = new index_1.Mangler(compile.projectPath, (...data) => fancyLog(ansiColors.blue('[mangler]'), ...data), { mangleExports: true, manglePrivateFields: true }); + let ts2tsMangler = new index_1.Mangler(compile.projectPath, (...data) => (0, fancy_log_1.default)(ansi_colors_1.default.blue('[mangler]'), ...data), { mangleExports: true, manglePrivateFields: true }); const newContentsByFileName = ts2tsMangler.computeNewFileContents(new Set(['saveState'])); - mangleStream = es.through(async function write(data) { + mangleStream = event_stream_1.default.through(async function write(data) { const tsNormalPath = ts.normalizePath(data.path); const newContents = (await newContentsByFileName).get(tsNormalPath); if (newContents !== undefined) { @@ -134,27 +170,27 @@ function compileTask(src, out, build, options = {}) { .pipe(mangleStream) .pipe(generator.stream) .pipe(compile()) - .pipe(gulp.dest(out)); + .pipe(gulp_1.default.dest(out)); }; - task.taskName = `compile-${path.basename(src)}`; + task.taskName = `compile-${path_1.default.basename(src)}`; return task; } function watchTask(out, build, srcPath = 'src') { const task = () => { const compile = createCompile(srcPath, { build, emitError: false, transpileOnly: false, preserveEnglish: false }); - const src = gulp.src(`${srcPath}/**`, { base: srcPath }); + const src = gulp_1.default.src(`${srcPath}/**`, { base: srcPath }); const watchSrc = watch(`${srcPath}/**`, { base: srcPath, readDelay: 200 }); const generator = new MonacoGenerator(true); generator.execute(); return watchSrc .pipe(generator.stream) .pipe(util.incremental(compile, src, true)) - .pipe(gulp.dest(out)); + .pipe(gulp_1.default.dest(out)); }; - task.taskName = `watch-${path.basename(out)}`; + task.taskName = `watch-${path_1.default.basename(out)}`; return task; } -const REPO_SRC_FOLDER = path.join(__dirname, '../../src'); +const REPO_SRC_FOLDER = path_1.default.join(__dirname, '../../src'); class MonacoGenerator { _isWatch; stream; @@ -163,7 +199,7 @@ class MonacoGenerator { _declarationResolver; constructor(isWatch) { this._isWatch = isWatch; - this.stream = es.through(); + this.stream = event_stream_1.default.through(); this._watchedFiles = {}; const onWillReadFile = (moduleId, filePath) => { if (!this._isWatch) { @@ -173,7 +209,7 @@ class MonacoGenerator { return; } this._watchedFiles[filePath] = true; - fs.watchFile(filePath, () => { + fs_1.default.watchFile(filePath, () => { this._declarationResolver.invalidateCache(moduleId); this._executeSoon(); }); @@ -186,7 +222,7 @@ class MonacoGenerator { }; this._declarationResolver = new monacodts.DeclarationResolver(this._fsProvider); if (this._isWatch) { - fs.watchFile(monacodts.RECIPE_PATH, () => { + fs_1.default.watchFile(monacodts.RECIPE_PATH, () => { this._executeSoon(); }); } @@ -211,7 +247,7 @@ class MonacoGenerator { return r; } _log(message, ...rest) { - fancyLog(ansiColors.cyan('[monaco.d.ts]'), message, ...rest); + (0, fancy_log_1.default)(ansi_colors_1.default.cyan('[monaco.d.ts]'), message, ...rest); } execute() { const startTime = Date.now(); @@ -223,8 +259,8 @@ class MonacoGenerator { if (result.isTheSame) { return; } - fs.writeFileSync(result.filePath, result.content); - fs.writeFileSync(path.join(REPO_SRC_FOLDER, 'vs/editor/common/standalone/standaloneEnums.ts'), result.enums); + fs_1.default.writeFileSync(result.filePath, result.content); + fs_1.default.writeFileSync(path_1.default.join(REPO_SRC_FOLDER, 'vs/editor/common/standalone/standaloneEnums.ts'), result.enums); this._log(`monaco.d.ts is changed - total time took ${Date.now() - startTime} ms`); if (!this._isWatch) { this.stream.emit('error', 'monaco.d.ts is no longer up to date. Please run gulp watch and commit the new file.'); @@ -234,21 +270,21 @@ class MonacoGenerator { function generateApiProposalNames() { let eol; try { - const src = fs.readFileSync('src/vs/platform/extensions/common/extensionsApiProposals.ts', 'utf-8'); + const src = fs_1.default.readFileSync('src/vs/platform/extensions/common/extensionsApiProposals.ts', 'utf-8'); const match = /\r?\n/m.exec(src); - eol = match ? match[0] : os.EOL; + eol = match ? match[0] : os_1.default.EOL; } catch { - eol = os.EOL; + eol = os_1.default.EOL; } const pattern = /vscode\.proposed\.([a-zA-Z\d]+)\.d\.ts$/; const versionPattern = /^\s*\/\/\s*version\s*:\s*(\d+)\s*$/mi; const proposals = new Map(); - const input = es.through(); + const input = event_stream_1.default.through(); const output = input .pipe(util.filter((f) => pattern.test(f.path))) - .pipe(es.through((f) => { - const name = path.basename(f.path); + .pipe(event_stream_1.default.through((f) => { + const name = path_1.default.basename(f.path); const match = pattern.exec(name); if (!match) { return; @@ -281,27 +317,27 @@ function generateApiProposalNames() { 'export type ApiProposalName = keyof typeof _allApiProposals;', '', ].join(eol); - this.emit('data', new File({ + this.emit('data', new vinyl_1.default({ path: 'vs/platform/extensions/common/extensionsApiProposals.ts', contents: Buffer.from(contents) })); this.emit('end'); })); - return es.duplex(input, output); + return event_stream_1.default.duplex(input, output); } const apiProposalNamesReporter = (0, reporter_1.createReporter)('api-proposal-names'); exports.compileApiProposalNamesTask = task.define('compile-api-proposal-names', () => { - return gulp.src('src/vscode-dts/**') + return gulp_1.default.src('src/vscode-dts/**') .pipe(generateApiProposalNames()) - .pipe(gulp.dest('src')) + .pipe(gulp_1.default.dest('src')) .pipe(apiProposalNamesReporter.end(true)); }); exports.watchApiProposalNamesTask = task.define('watch-api-proposal-names', () => { - const task = () => gulp.src('src/vscode-dts/**') + const task = () => gulp_1.default.src('src/vscode-dts/**') .pipe(generateApiProposalNames()) .pipe(apiProposalNamesReporter.end(true)); return watch('src/vscode-dts/**', { readDelay: 200 }) .pipe(util.debounce(task)) - .pipe(gulp.dest('src')); + .pipe(gulp_1.default.dest('src')); }); //# sourceMappingURL=compilation.js.map \ No newline at end of file diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 124bcc17c17c..6e1fcab5186f 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -3,18 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as es from 'event-stream'; -import * as fs from 'fs'; -import * as gulp from 'gulp'; -import * as path from 'path'; +import es from 'event-stream'; +import fs from 'fs'; +import gulp from 'gulp'; +import path from 'path'; import * as monacodts from './monaco-api'; import * as nls from './nls'; import { createReporter } from './reporter'; import * as util from './util'; -import * as fancyLog from 'fancy-log'; -import * as ansiColors from 'ansi-colors'; -import * as os from 'os'; -import * as File from 'vinyl'; +import fancyLog from 'fancy-log'; +import ansiColors from 'ansi-colors'; +import os from 'os'; +import File from 'vinyl'; import * as task from './task'; import { Mangler } from './mangle/index'; import { RawSourceMap } from 'source-map'; diff --git a/build/lib/date.js b/build/lib/date.js index 77fff0e5073e..1ed884fb7ee7 100644 --- a/build/lib/date.js +++ b/build/lib/date.js @@ -3,12 +3,15 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.writeISODate = writeISODate; exports.readISODate = readISODate; -const path = require("path"); -const fs = require("fs"); -const root = path.join(__dirname, '..', '..'); +const path_1 = __importDefault(require("path")); +const fs_1 = __importDefault(require("fs")); +const root = path_1.default.join(__dirname, '..', '..'); /** * Writes a `outDir/date` file with the contents of the build * so that other tasks during the build process can use it and @@ -16,17 +19,17 @@ const root = path.join(__dirname, '..', '..'); */ function writeISODate(outDir) { const result = () => new Promise((resolve, _) => { - const outDirectory = path.join(root, outDir); - fs.mkdirSync(outDirectory, { recursive: true }); + const outDirectory = path_1.default.join(root, outDir); + fs_1.default.mkdirSync(outDirectory, { recursive: true }); const date = new Date().toISOString(); - fs.writeFileSync(path.join(outDirectory, 'date'), date, 'utf8'); + fs_1.default.writeFileSync(path_1.default.join(outDirectory, 'date'), date, 'utf8'); resolve(); }); result.taskName = 'build-date-file'; return result; } function readISODate(outDir) { - const outDirectory = path.join(root, outDir); - return fs.readFileSync(path.join(outDirectory, 'date'), 'utf8'); + const outDirectory = path_1.default.join(root, outDir); + return fs_1.default.readFileSync(path_1.default.join(outDirectory, 'date'), 'utf8'); } //# sourceMappingURL=date.js.map \ No newline at end of file diff --git a/build/lib/date.ts b/build/lib/date.ts index 998e89f8e6ab..8a9331789520 100644 --- a/build/lib/date.ts +++ b/build/lib/date.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; -import * as fs from 'fs'; +import path from 'path'; +import fs from 'fs'; const root = path.join(__dirname, '..', '..'); diff --git a/build/lib/dependencies.js b/build/lib/dependencies.js index 9bcd1204eab8..04a09f98708a 100644 --- a/build/lib/dependencies.js +++ b/build/lib/dependencies.js @@ -3,16 +3,19 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.getProductionDependencies = getProductionDependencies; -const fs = require("fs"); -const path = require("path"); -const cp = require("child_process"); -const root = fs.realpathSync(path.dirname(path.dirname(__dirname))); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const child_process_1 = __importDefault(require("child_process")); +const root = fs_1.default.realpathSync(path_1.default.dirname(path_1.default.dirname(__dirname))); function getNpmProductionDependencies(folder) { let raw; try { - raw = cp.execSync('npm ls --all --omit=dev --parseable', { cwd: folder, encoding: 'utf8', env: { ...process.env, NODE_ENV: 'production' }, stdio: [null, null, null] }); + raw = child_process_1.default.execSync('npm ls --all --omit=dev --parseable', { cwd: folder, encoding: 'utf8', env: { ...process.env, NODE_ENV: 'production' }, stdio: [null, null, null] }); } catch (err) { const regex = /^npm ERR! .*$/gm; @@ -34,16 +37,16 @@ function getNpmProductionDependencies(folder) { raw = err.stdout; } return raw.split(/\r?\n/).filter(line => { - return !!line.trim() && path.relative(root, line) !== path.relative(root, folder); + return !!line.trim() && path_1.default.relative(root, line) !== path_1.default.relative(root, folder); }); } function getProductionDependencies(folderPath) { const result = getNpmProductionDependencies(folderPath); // Account for distro npm dependencies - const realFolderPath = fs.realpathSync(folderPath); - const relativeFolderPath = path.relative(root, realFolderPath); + const realFolderPath = fs_1.default.realpathSync(folderPath); + const relativeFolderPath = path_1.default.relative(root, realFolderPath); const distroFolderPath = `${root}/.build/distro/npm/${relativeFolderPath}`; - if (fs.existsSync(distroFolderPath)) { + if (fs_1.default.existsSync(distroFolderPath)) { result.push(...getNpmProductionDependencies(distroFolderPath)); } return [...new Set(result)]; diff --git a/build/lib/dependencies.ts b/build/lib/dependencies.ts index 45368ffd26db..a5bc70088a7f 100644 --- a/build/lib/dependencies.ts +++ b/build/lib/dependencies.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; -import * as cp from 'child_process'; +import fs from 'fs'; +import path from 'path'; +import cp from 'child_process'; const root = fs.realpathSync(path.dirname(path.dirname(__dirname))); function getNpmProductionDependencies(folder: string): string[] { diff --git a/build/lib/electron.js b/build/lib/electron.js index 99252e4e64a2..56992d8a7f71 100644 --- a/build/lib/electron.js +++ b/build/lib/electron.js @@ -3,19 +3,55 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.config = void 0; -const fs = require("fs"); -const path = require("path"); -const vfs = require("vinyl-fs"); -const filter = require("gulp-filter"); -const util = require("./util"); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const vinyl_fs_1 = __importDefault(require("vinyl-fs")); +const gulp_filter_1 = __importDefault(require("gulp-filter")); +const util = __importStar(require("./util")); const getVersion_1 = require("./getVersion"); function isDocumentSuffix(str) { return str === 'document' || str === 'script' || str === 'file' || str === 'source code'; } -const root = path.dirname(path.dirname(__dirname)); -const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8')); +const root = path_1.default.dirname(path_1.default.dirname(__dirname)); +const product = JSON.parse(fs_1.default.readFileSync(path_1.default.join(root, 'product.json'), 'utf8')); const commit = (0, getVersion_1.getVersion)(root); function createTemplate(input) { return (params) => { @@ -24,7 +60,7 @@ function createTemplate(input) { }); }; } -const darwinCreditsTemplate = product.darwinCredits && createTemplate(fs.readFileSync(path.join(root, product.darwinCredits), 'utf8')); +const darwinCreditsTemplate = product.darwinCredits && createTemplate(fs_1.default.readFileSync(path_1.default.join(root, product.darwinCredits), 'utf8')); /** * Generate a `DarwinDocumentType` given a list of file extensions, an icon name, and an optional suffix or file type name. * @param extensions A list of file extensions, such as `['bat', 'cmd']` @@ -159,7 +195,7 @@ exports.config = { 'F# source code': 'fs', 'F# signature file': 'fsi', 'F# script': ['fsx', 'fsscript'], - 'SVG document': ['svg', 'svgz'], + 'SVG document': ['svg'], 'TOML document': 'toml', 'Swift source code': 'swift', }, 'default'), @@ -183,7 +219,7 @@ exports.config = { token: process.env['GITHUB_TOKEN'], repo: product.electronRepository || undefined, validateChecksum: true, - checksumFile: path.join(root, 'build', 'checksums', 'electron.txt'), + checksumFile: path_1.default.join(root, 'build', 'checksums', 'electron.txt'), }; function getElectron(arch) { return () => { @@ -196,18 +232,18 @@ function getElectron(arch) { ffmpegChromium: false, keepDefaultApp: true }; - return vfs.src('package.json') + return vinyl_fs_1.default.src('package.json') .pipe(json({ name: product.nameShort })) .pipe(electron(electronOpts)) - .pipe(filter(['**', '!**/app/package.json'])) - .pipe(vfs.dest('.build/electron')); + .pipe((0, gulp_filter_1.default)(['**', '!**/app/package.json'])) + .pipe(vinyl_fs_1.default.dest('.build/electron')); }; } async function main(arch = process.arch) { const version = electronVersion; - const electronPath = path.join(root, '.build', 'electron'); - const versionFile = path.join(electronPath, 'version'); - const isUpToDate = fs.existsSync(versionFile) && fs.readFileSync(versionFile, 'utf8') === `${version}`; + const electronPath = path_1.default.join(root, '.build', 'electron'); + const versionFile = path_1.default.join(electronPath, 'version'); + const isUpToDate = fs_1.default.existsSync(versionFile) && fs_1.default.readFileSync(versionFile, 'utf8') === `${version}`; if (!isUpToDate) { await util.rimraf(electronPath)(); await util.streamToPromise(getElectron(arch)()); diff --git a/build/lib/electron.ts b/build/lib/electron.ts index da2387f68f63..3bb047dfceeb 100644 --- a/build/lib/electron.ts +++ b/build/lib/electron.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; -import * as vfs from 'vinyl-fs'; -import * as filter from 'gulp-filter'; +import fs from 'fs'; +import path from 'path'; +import vfs from 'vinyl-fs'; +import filter from 'gulp-filter'; import * as util from './util'; import { getVersion } from './getVersion'; @@ -176,7 +176,7 @@ export const config = { 'F# source code': 'fs', 'F# signature file': 'fsi', 'F# script': ['fsx', 'fsscript'], - 'SVG document': ['svg', 'svgz'], + 'SVG document': ['svg'], 'TOML document': 'toml', 'Swift source code': 'swift', }, 'default'), diff --git a/build/lib/extensions.js b/build/lib/extensions.js index fbf11ee1ee4e..79a9507f66c0 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -3,44 +3,84 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.fromMarketplace = fromMarketplace; +exports.fromVsix = fromVsix; exports.fromGithub = fromGithub; -exports.packageLocalExtensionsStream = packageLocalExtensionsStream; +exports.packageNonNativeLocalExtensionsStream = packageNonNativeLocalExtensionsStream; +exports.packageNativeLocalExtensionsStream = packageNativeLocalExtensionsStream; +exports.packageAllLocalExtensionsStream = packageAllLocalExtensionsStream; exports.packageMarketplaceExtensionsStream = packageMarketplaceExtensionsStream; exports.scanBuiltinExtensions = scanBuiltinExtensions; exports.translatePackageJSON = translatePackageJSON; exports.webpackExtensions = webpackExtensions; exports.buildExtensionMedia = buildExtensionMedia; -const es = require("event-stream"); -const fs = require("fs"); -const cp = require("child_process"); -const glob = require("glob"); -const gulp = require("gulp"); -const path = require("path"); -const File = require("vinyl"); +const event_stream_1 = __importDefault(require("event-stream")); +const fs_1 = __importDefault(require("fs")); +const child_process_1 = __importDefault(require("child_process")); +const glob_1 = __importDefault(require("glob")); +const gulp_1 = __importDefault(require("gulp")); +const path_1 = __importDefault(require("path")); +const crypto_1 = __importDefault(require("crypto")); +const vinyl_1 = __importDefault(require("vinyl")); const stats_1 = require("./stats"); -const util2 = require("./util"); +const util2 = __importStar(require("./util")); const vzip = require('gulp-vinyl-zip'); -const filter = require("gulp-filter"); -const rename = require("gulp-rename"); -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); -const buffer = require('gulp-buffer'); -const jsoncParser = require("jsonc-parser"); +const gulp_filter_1 = __importDefault(require("gulp-filter")); +const gulp_rename_1 = __importDefault(require("gulp-rename")); +const fancy_log_1 = __importDefault(require("fancy-log")); +const ansi_colors_1 = __importDefault(require("ansi-colors")); +const gulp_buffer_1 = __importDefault(require("gulp-buffer")); +const jsoncParser = __importStar(require("jsonc-parser")); const dependencies_1 = require("./dependencies"); const builtInExtensions_1 = require("./builtInExtensions"); const getVersion_1 = require("./getVersion"); const fetch_1 = require("./fetch"); -const root = path.dirname(path.dirname(__dirname)); +const root = path_1.default.dirname(path_1.default.dirname(__dirname)); const commit = (0, getVersion_1.getVersion)(root); const sourceMappingURLBase = `https://main.vscode-cdn.net/sourcemaps/${commit}`; function minifyExtensionResources(input) { - const jsonFilter = filter(['**/*.json', '**/*.code-snippets'], { restore: true }); + const jsonFilter = (0, gulp_filter_1.default)(['**/*.json', '**/*.code-snippets'], { restore: true }); return input .pipe(jsonFilter) - .pipe(buffer()) - .pipe(es.mapSync((f) => { + .pipe((0, gulp_buffer_1.default)()) + .pipe(event_stream_1.default.mapSync((f) => { const errors = []; const value = jsoncParser.parse(f.contents.toString('utf8'), errors, { allowTrailingComma: true }); if (errors.length === 0) { @@ -52,11 +92,11 @@ function minifyExtensionResources(input) { .pipe(jsonFilter.restore); } function updateExtensionPackageJSON(input, update) { - const packageJsonFilter = filter('extensions/*/package.json', { restore: true }); + const packageJsonFilter = (0, gulp_filter_1.default)('extensions/*/package.json', { restore: true }); return input .pipe(packageJsonFilter) - .pipe(buffer()) - .pipe(es.mapSync((f) => { + .pipe((0, gulp_buffer_1.default)()) + .pipe(event_stream_1.default.mapSync((f) => { const data = JSON.parse(f.contents.toString('utf8')); f.contents = Buffer.from(JSON.stringify(update(data))); return f; @@ -65,7 +105,7 @@ function updateExtensionPackageJSON(input, update) { } function fromLocal(extensionPath, forWeb, disableMangle) { const webpackConfigFileName = forWeb ? 'extension-browser.webpack.config.js' : 'extension.webpack.config.js'; - const isWebPacked = fs.existsSync(path.join(extensionPath, webpackConfigFileName)); + const isWebPacked = fs_1.default.existsSync(path_1.default.join(extensionPath, webpackConfigFileName)); let input = isWebPacked ? fromLocalWebpack(extensionPath, webpackConfigFileName, disableMangle) : fromLocalNormal(extensionPath); @@ -86,11 +126,11 @@ function fromLocalWebpack(extensionPath, webpackConfigFileName, disableMangle) { const vsce = require('@vscode/vsce'); const webpack = require('webpack'); const webpackGulp = require('webpack-stream'); - const result = es.through(); + const result = event_stream_1.default.through(); const packagedDependencies = []; - const packageJsonConfig = require(path.join(extensionPath, 'package.json')); + const packageJsonConfig = require(path_1.default.join(extensionPath, 'package.json')); if (packageJsonConfig.dependencies) { - const webpackRootConfig = require(path.join(extensionPath, webpackConfigFileName)); + const webpackRootConfig = require(path_1.default.join(extensionPath, webpackConfigFileName)); for (const key in webpackRootConfig.externals) { if (key in packageJsonConfig.dependencies) { packagedDependencies.push(key); @@ -104,19 +144,19 @@ function fromLocalWebpack(extensionPath, webpackConfigFileName, disableMangle) { // as a temporary workaround. vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.None, packagedDependencies }).then(fileNames => { const files = fileNames - .map(fileName => path.join(extensionPath, fileName)) - .map(filePath => new File({ + .map(fileName => path_1.default.join(extensionPath, fileName)) + .map(filePath => new vinyl_1.default({ path: filePath, - stat: fs.statSync(filePath), + stat: fs_1.default.statSync(filePath), base: extensionPath, - contents: fs.createReadStream(filePath) + contents: fs_1.default.createReadStream(filePath) })); // check for a webpack configuration files, then invoke webpack // and merge its output with the files stream. - const webpackConfigLocations = glob.sync(path.join(extensionPath, '**', webpackConfigFileName), { ignore: ['**/node_modules'] }); + const webpackConfigLocations = glob_1.default.sync(path_1.default.join(extensionPath, '**', webpackConfigFileName), { ignore: ['**/node_modules'] }); const webpackStreams = webpackConfigLocations.flatMap(webpackConfigPath => { const webpackDone = (err, stats) => { - fancyLog(`Bundled extension: ${ansiColors.yellow(path.join(path.basename(extensionPath), path.relative(extensionPath, webpackConfigPath)))}...`); + (0, fancy_log_1.default)(`Bundled extension: ${ansi_colors_1.default.yellow(path_1.default.join(path_1.default.basename(extensionPath), path_1.default.relative(extensionPath, webpackConfigPath)))}...`); if (err) { result.emit('error', err); } @@ -147,26 +187,28 @@ function fromLocalWebpack(extensionPath, webpackConfigFileName, disableMangle) { } } } - const relativeOutputPath = path.relative(extensionPath, webpackConfig.output.path); + const relativeOutputPath = path_1.default.relative(extensionPath, webpackConfig.output.path); return webpackGulp(webpackConfig, webpack, webpackDone) - .pipe(es.through(function (data) { + .pipe(event_stream_1.default.through(function (data) { data.stat = data.stat || {}; data.base = extensionPath; this.emit('data', data); })) - .pipe(es.through(function (data) { + .pipe(event_stream_1.default.through(function (data) { // source map handling: // * rewrite sourceMappingURL // * save to disk so that upload-task picks this up - const contents = data.contents.toString('utf8'); - data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) { - return `\n//# sourceMappingURL=${sourceMappingURLBase}/extensions/${path.basename(extensionPath)}/${relativeOutputPath}/${g1}`; - }), 'utf8'); + if (path_1.default.extname(data.basename) === '.js') { + const contents = data.contents.toString('utf8'); + data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) { + return `\n//# sourceMappingURL=${sourceMappingURLBase}/extensions/${path_1.default.basename(extensionPath)}/${relativeOutputPath}/${g1}`; + }), 'utf8'); + } this.emit('data', data); })); }); }); - es.merge(...webpackStreams, es.readArray(files)) + event_stream_1.default.merge(...webpackStreams, event_stream_1.default.readArray(files)) // .pipe(es.through(function (data) { // // debug // console.log('out', data.path, data.contents.length); @@ -178,25 +220,25 @@ function fromLocalWebpack(extensionPath, webpackConfigFileName, disableMangle) { console.error(packagedDependencies); result.emit('error', err); }); - return result.pipe((0, stats_1.createStatsStream)(path.basename(extensionPath))); + return result.pipe((0, stats_1.createStatsStream)(path_1.default.basename(extensionPath))); } function fromLocalNormal(extensionPath) { const vsce = require('@vscode/vsce'); - const result = es.through(); + const result = event_stream_1.default.through(); vsce.listFiles({ cwd: extensionPath, packageManager: vsce.PackageManager.Npm }) .then(fileNames => { const files = fileNames - .map(fileName => path.join(extensionPath, fileName)) - .map(filePath => new File({ + .map(fileName => path_1.default.join(extensionPath, fileName)) + .map(filePath => new vinyl_1.default({ path: filePath, - stat: fs.statSync(filePath), + stat: fs_1.default.statSync(filePath), base: extensionPath, - contents: fs.createReadStream(filePath) + contents: fs_1.default.createReadStream(filePath) })); - es.readArray(files).pipe(result); + event_stream_1.default.readArray(files).pipe(result); }) .catch(err => result.emit('error', err)); - return result.pipe((0, stats_1.createStatsStream)(path.basename(extensionPath))); + return result.pipe((0, stats_1.createStatsStream)(path_1.default.basename(extensionPath))); } const userAgent = 'VSCode Build'; const baseHeaders = { @@ -208,8 +250,8 @@ function fromMarketplace(serviceUrl, { name: extensionName, version, sha256, met const json = require('gulp-json-editor'); const [publisher, name] = extensionName.split('.'); const url = `${serviceUrl}/publishers/${publisher}/vsextensions/${name}/${version}/vspackage`; - fancyLog('Downloading extension:', ansiColors.yellow(`${extensionName}@${version}`), '...'); - const packageJsonFilter = filter('package.json', { restore: true }); + (0, fancy_log_1.default)('Downloading extension:', ansi_colors_1.default.yellow(`${extensionName}@${version}`), '...'); + const packageJsonFilter = (0, gulp_filter_1.default)('package.json', { restore: true }); return (0, fetch_1.fetchUrls)('', { base: url, nodeFetchOptions: { @@ -218,34 +260,65 @@ function fromMarketplace(serviceUrl, { name: extensionName, version, sha256, met checksumSha256: sha256 }) .pipe(vzip.src()) - .pipe(filter('extension/**')) - .pipe(rename(p => p.dirname = p.dirname.replace(/^extension\/?/, ''))) + .pipe((0, gulp_filter_1.default)('extension/**')) + .pipe((0, gulp_rename_1.default)(p => p.dirname = p.dirname.replace(/^extension\/?/, ''))) + .pipe(packageJsonFilter) + .pipe((0, gulp_buffer_1.default)()) + .pipe(json({ __metadata: metadata })) + .pipe(packageJsonFilter.restore); +} +function fromVsix(vsixPath, { name: extensionName, version, sha256, metadata }) { + const json = require('gulp-json-editor'); + (0, fancy_log_1.default)('Using local VSIX for extension:', ansi_colors_1.default.yellow(`${extensionName}@${version}`), '...'); + const packageJsonFilter = (0, gulp_filter_1.default)('package.json', { restore: true }); + return gulp_1.default.src(vsixPath) + .pipe((0, gulp_buffer_1.default)()) + .pipe(event_stream_1.default.mapSync((f) => { + const hash = crypto_1.default.createHash('sha256'); + hash.update(f.contents); + const checksum = hash.digest('hex'); + if (checksum !== sha256) { + throw new Error(`Checksum mismatch for ${vsixPath} (expected ${sha256}, actual ${checksum}))`); + } + return f; + })) + .pipe(vzip.src()) + .pipe((0, gulp_filter_1.default)('extension/**')) + .pipe((0, gulp_rename_1.default)(p => p.dirname = p.dirname.replace(/^extension\/?/, ''))) .pipe(packageJsonFilter) - .pipe(buffer()) + .pipe((0, gulp_buffer_1.default)()) .pipe(json({ __metadata: metadata })) .pipe(packageJsonFilter.restore); } function fromGithub({ name, version, repo, sha256, metadata }) { const json = require('gulp-json-editor'); - fancyLog('Downloading extension from GH:', ansiColors.yellow(`${name}@${version}`), '...'); - const packageJsonFilter = filter('package.json', { restore: true }); + (0, fancy_log_1.default)('Downloading extension from GH:', ansi_colors_1.default.yellow(`${name}@${version}`), '...'); + const packageJsonFilter = (0, gulp_filter_1.default)('package.json', { restore: true }); return (0, fetch_1.fetchGithub)(new URL(repo).pathname, { version, name: name => name.endsWith('.vsix'), checksumSha256: sha256 }) - .pipe(buffer()) + .pipe((0, gulp_buffer_1.default)()) .pipe(vzip.src()) - .pipe(filter('extension/**')) - .pipe(rename(p => p.dirname = p.dirname.replace(/^extension\/?/, ''))) + .pipe((0, gulp_filter_1.default)('extension/**')) + .pipe((0, gulp_rename_1.default)(p => p.dirname = p.dirname.replace(/^extension\/?/, ''))) .pipe(packageJsonFilter) - .pipe(buffer()) + .pipe((0, gulp_buffer_1.default)()) .pipe(json({ __metadata: metadata })) .pipe(packageJsonFilter.restore); } +/** + * All extensions that are known to have some native component and thus must be built on the + * platform that is being built. + */ +const nativeExtensions = [ + 'microsoft-authentication', +]; const excludedExtensions = [ 'vscode-api-tests', 'vscode-colorize-tests', + 'vscode-colorize-perf-tests', 'vscode-test-resolver', 'ms-vscode.node-debug', 'ms-vscode.node-debug2', @@ -257,7 +330,7 @@ const marketplaceWebExtensionsExclude = new Set([ 'ms-vscode.js-debug', 'ms-vscode.vscode-js-profile-table' ]); -const productJson = JSON.parse(fs.readFileSync(path.join(__dirname, '../../product.json'), 'utf8')); +const productJson = JSON.parse(fs_1.default.readFileSync(path_1.default.join(__dirname, '../../product.json'), 'utf8')); const builtInExtensions = productJson.builtInExtensions || []; const webBuiltInExtensions = productJson.webBuiltInExtensions || []; /** @@ -286,20 +359,60 @@ function isWebExtension(manifest) { } return true; } -function packageLocalExtensionsStream(forWeb, disableMangle) { - const localExtensionsDescriptions = (glob.sync('extensions/*/package.json') +/** + * Package local extensions that are known to not have native dependencies. Mutually exclusive to {@link packageNativeLocalExtensionsStream}. + * @param forWeb build the extensions that have web targets + * @param disableMangle disable the mangler + * @returns a stream + */ +function packageNonNativeLocalExtensionsStream(forWeb, disableMangle) { + return doPackageLocalExtensionsStream(forWeb, disableMangle, false); +} +/** + * Package local extensions that are known to have native dependencies. Mutually exclusive to {@link packageNonNativeLocalExtensionsStream}. + * @note it's possible that the extension does not have native dependencies for the current platform, especially if building for the web, + * but we simplify the logic here by having a flat list of extensions (See {@link nativeExtensions}) that are known to have native + * dependencies on some platform and thus should be packaged on the platform that they are building for. + * @param forWeb build the extensions that have web targets + * @param disableMangle disable the mangler + * @returns a stream + */ +function packageNativeLocalExtensionsStream(forWeb, disableMangle) { + return doPackageLocalExtensionsStream(forWeb, disableMangle, true); +} +/** + * Package all the local extensions... both those that are known to have native dependencies and those that are not. + * @param forWeb build the extensions that have web targets + * @param disableMangle disable the mangler + * @returns a stream + */ +function packageAllLocalExtensionsStream(forWeb, disableMangle) { + return event_stream_1.default.merge([ + packageNonNativeLocalExtensionsStream(forWeb, disableMangle), + packageNativeLocalExtensionsStream(forWeb, disableMangle) + ]); +} +/** + * @param forWeb build the extensions that have web targets + * @param disableMangle disable the mangler + * @param native build the extensions that are marked as having native dependencies + */ +function doPackageLocalExtensionsStream(forWeb, disableMangle, native) { + const nativeExtensionsSet = new Set(nativeExtensions); + const localExtensionsDescriptions = (glob_1.default.sync('extensions/*/package.json') .map(manifestPath => { - const absoluteManifestPath = path.join(root, manifestPath); - const extensionPath = path.dirname(path.join(root, manifestPath)); - const extensionName = path.basename(extensionPath); + const absoluteManifestPath = path_1.default.join(root, manifestPath); + const extensionPath = path_1.default.dirname(path_1.default.join(root, manifestPath)); + const extensionName = path_1.default.basename(extensionPath); return { name: extensionName, path: extensionPath, manifestPath: absoluteManifestPath }; }) + .filter(({ name }) => native ? nativeExtensionsSet.has(name) : !nativeExtensionsSet.has(name)) .filter(({ name }) => excludedExtensions.indexOf(name) === -1) .filter(({ name }) => builtInExtensions.every(b => b.name !== name)) .filter(({ manifestPath }) => (forWeb ? isWebExtension(require(manifestPath)) : true))); - const localExtensionsStream = minifyExtensionResources(es.merge(...localExtensionsDescriptions.map(extension => { + const localExtensionsStream = minifyExtensionResources(event_stream_1.default.merge(...localExtensionsDescriptions.map(extension => { return fromLocal(extension.path, forWeb, disableMangle) - .pipe(rename(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); + .pipe((0, gulp_rename_1.default)(p => p.dirname = `extensions/${extension.name}/${p.dirname}`)); }))); let result; if (forWeb) { @@ -308,10 +421,10 @@ function packageLocalExtensionsStream(forWeb, disableMangle) { else { // also include shared production node modules const productionDependencies = (0, dependencies_1.getProductionDependencies)('extensions/'); - const dependenciesSrc = productionDependencies.map(d => path.relative(root, d)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`]).flat(); - result = es.merge(localExtensionsStream, gulp.src(dependenciesSrc, { base: '.' }) - .pipe(util2.cleanNodeModules(path.join(root, 'build', '.moduleignore'))) - .pipe(util2.cleanNodeModules(path.join(root, 'build', `.moduleignore.${process.platform}`)))); + const dependenciesSrc = productionDependencies.map(d => path_1.default.relative(root, d)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`]).flat(); + result = event_stream_1.default.merge(localExtensionsStream, gulp_1.default.src(dependenciesSrc, { base: '.' }) + .pipe(util2.cleanNodeModules(path_1.default.join(root, 'build', '.moduleignore'))) + .pipe(util2.cleanNodeModules(path_1.default.join(root, 'build', `.moduleignore.${process.platform}`)))); } return (result .pipe(util2.setExecutableBit(['**/*.sh']))); @@ -321,9 +434,9 @@ function packageMarketplaceExtensionsStream(forWeb) { ...builtInExtensions.filter(({ name }) => (forWeb ? !marketplaceWebExtensionsExclude.has(name) : true)), ...(forWeb ? webBuiltInExtensions : []) ]; - const marketplaceExtensionsStream = minifyExtensionResources(es.merge(...marketplaceExtensionsDescriptions + const marketplaceExtensionsStream = minifyExtensionResources(event_stream_1.default.merge(...marketplaceExtensionsDescriptions .map(extension => { - const src = (0, builtInExtensions_1.getExtensionStream)(extension).pipe(rename(p => p.dirname = `extensions/${p.dirname}`)); + const src = (0, builtInExtensions_1.getExtensionStream)(extension).pipe((0, gulp_rename_1.default)(p => p.dirname = `extensions/${p.dirname}`)); return updateExtensionPackageJSON(src, (data) => { delete data.scripts; delete data.dependencies; @@ -337,30 +450,30 @@ function packageMarketplaceExtensionsStream(forWeb) { function scanBuiltinExtensions(extensionsRoot, exclude = []) { const scannedExtensions = []; try { - const extensionsFolders = fs.readdirSync(extensionsRoot); + const extensionsFolders = fs_1.default.readdirSync(extensionsRoot); for (const extensionFolder of extensionsFolders) { if (exclude.indexOf(extensionFolder) >= 0) { continue; } - const packageJSONPath = path.join(extensionsRoot, extensionFolder, 'package.json'); - if (!fs.existsSync(packageJSONPath)) { + const packageJSONPath = path_1.default.join(extensionsRoot, extensionFolder, 'package.json'); + if (!fs_1.default.existsSync(packageJSONPath)) { continue; } - const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath).toString('utf8')); + const packageJSON = JSON.parse(fs_1.default.readFileSync(packageJSONPath).toString('utf8')); if (!isWebExtension(packageJSON)) { continue; } - const children = fs.readdirSync(path.join(extensionsRoot, extensionFolder)); + const children = fs_1.default.readdirSync(path_1.default.join(extensionsRoot, extensionFolder)); const packageNLSPath = children.filter(child => child === 'package.nls.json')[0]; - const packageNLS = packageNLSPath ? JSON.parse(fs.readFileSync(path.join(extensionsRoot, extensionFolder, packageNLSPath)).toString()) : undefined; + const packageNLS = packageNLSPath ? JSON.parse(fs_1.default.readFileSync(path_1.default.join(extensionsRoot, extensionFolder, packageNLSPath)).toString()) : undefined; const readme = children.filter(child => /^readme(\.txt|\.md|)$/i.test(child))[0]; const changelog = children.filter(child => /^changelog(\.txt|\.md|)$/i.test(child))[0]; scannedExtensions.push({ extensionPath: extensionFolder, packageJSON, packageNLS, - readmePath: readme ? path.join(extensionFolder, readme) : undefined, - changelogPath: changelog ? path.join(extensionFolder, changelog) : undefined, + readmePath: readme ? path_1.default.join(extensionFolder, readme) : undefined, + changelogPath: changelog ? path_1.default.join(extensionFolder, changelog) : undefined, }); } return scannedExtensions; @@ -371,7 +484,7 @@ function scanBuiltinExtensions(extensionsRoot, exclude = []) { } function translatePackageJSON(packageJSON, packageNLSPath) { const CharCode_PC = '%'.charCodeAt(0); - const packageNls = JSON.parse(fs.readFileSync(packageNLSPath).toString()); + const packageNls = JSON.parse(fs_1.default.readFileSync(packageNLSPath).toString()); const translate = (obj) => { for (const key in obj) { const val = obj[key]; @@ -392,7 +505,7 @@ function translatePackageJSON(packageJSON, packageNLSPath) { translate(packageJSON); return packageJSON; } -const extensionsPath = path.join(root, 'extensions'); +const extensionsPath = path_1.default.join(root, 'extensions'); // Additional projects to run esbuild on. These typically build code for webviews const esbuildMediaScripts = [ 'markdown-language-features/esbuild-notebook.js', @@ -411,7 +524,7 @@ async function webpackExtensions(taskName, isWatch, webpackConfigLocations) { for (const configOrFn of Array.isArray(configOrFnOrArray) ? configOrFnOrArray : [configOrFnOrArray]) { const config = typeof configOrFn === 'function' ? configOrFn({}, {}) : configOrFn; if (outputRoot) { - config.output.path = path.join(outputRoot, path.relative(path.dirname(configPath), config.output.path)); + config.output.path = path_1.default.join(outputRoot, path_1.default.relative(path_1.default.dirname(configPath), config.output.path)); } webpackConfigs.push(config); } @@ -423,18 +536,18 @@ async function webpackExtensions(taskName, isWatch, webpackConfigLocations) { for (const stats of fullStats.children) { const outputPath = stats.outputPath; if (outputPath) { - const relativePath = path.relative(extensionsPath, outputPath).replace(/\\/g, '/'); + const relativePath = path_1.default.relative(extensionsPath, outputPath).replace(/\\/g, '/'); const match = relativePath.match(/[^\/]+(\/server|\/client)?/); - fancyLog(`Finished ${ansiColors.green(taskName)} ${ansiColors.cyan(match[0])} with ${stats.errors.length} errors.`); + (0, fancy_log_1.default)(`Finished ${ansi_colors_1.default.green(taskName)} ${ansi_colors_1.default.cyan(match[0])} with ${stats.errors.length} errors.`); } if (Array.isArray(stats.errors)) { stats.errors.forEach((error) => { - fancyLog.error(error); + fancy_log_1.default.error(error); }); } if (Array.isArray(stats.warnings)) { stats.warnings.forEach((warning) => { - fancyLog.warn(warning); + fancy_log_1.default.warn(warning); }); } } @@ -454,7 +567,7 @@ async function webpackExtensions(taskName, isWatch, webpackConfigLocations) { else { webpack(webpackConfigs).run((err, stats) => { if (err) { - fancyLog.error(err); + fancy_log_1.default.error(err); reject(); } else { @@ -468,9 +581,9 @@ async function webpackExtensions(taskName, isWatch, webpackConfigLocations) { async function esbuildExtensions(taskName, isWatch, scripts) { function reporter(stdError, script) { const matches = (stdError || '').match(/\> (.+): error: (.+)?/g); - fancyLog(`Finished ${ansiColors.green(taskName)} ${script} with ${matches ? matches.length : 0} errors.`); + (0, fancy_log_1.default)(`Finished ${ansi_colors_1.default.green(taskName)} ${script} with ${matches ? matches.length : 0} errors.`); for (const match of matches || []) { - fancyLog.error(match); + fancy_log_1.default.error(match); } } const tasks = scripts.map(({ script, outputRoot }) => { @@ -482,7 +595,7 @@ async function esbuildExtensions(taskName, isWatch, scripts) { if (outputRoot) { args.push('--outputRoot', outputRoot); } - const proc = cp.execFile(process.argv[0], args, {}, (error, _stdout, stderr) => { + const proc = child_process_1.default.execFile(process.argv[0], args, {}, (error, _stdout, stderr) => { if (error) { return reject(error); } @@ -490,7 +603,7 @@ async function esbuildExtensions(taskName, isWatch, scripts) { return resolve(); }); proc.stdout.on('data', (data) => { - fancyLog(`${ansiColors.green(taskName)}: ${data.toString('utf8')}`); + (0, fancy_log_1.default)(`${ansi_colors_1.default.green(taskName)}: ${data.toString('utf8')}`); }); }); }); @@ -498,8 +611,8 @@ async function esbuildExtensions(taskName, isWatch, scripts) { } async function buildExtensionMedia(isWatch, outputRoot) { return esbuildExtensions('esbuilding extension media', isWatch, esbuildMediaScripts.map(p => ({ - script: path.join(extensionsPath, p), - outputRoot: outputRoot ? path.join(root, outputRoot, path.dirname(p)) : undefined + script: path_1.default.join(extensionsPath, p), + outputRoot: outputRoot ? path_1.default.join(root, outputRoot, path_1.default.dirname(p)) : undefined }))); } //# sourceMappingURL=extensions.js.map \ No newline at end of file diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 628cf90c4c91..908480b60776 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -3,24 +3,25 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as es from 'event-stream'; -import * as fs from 'fs'; -import * as cp from 'child_process'; -import * as glob from 'glob'; -import * as gulp from 'gulp'; -import * as path from 'path'; +import es from 'event-stream'; +import fs from 'fs'; +import cp from 'child_process'; +import glob from 'glob'; +import gulp from 'gulp'; +import path from 'path'; +import crypto from 'crypto'; import { Stream } from 'stream'; -import * as File from 'vinyl'; +import File from 'vinyl'; import { createStatsStream } from './stats'; import * as util2 from './util'; const vzip = require('gulp-vinyl-zip'); -import filter = require('gulp-filter'); -import rename = require('gulp-rename'); -import * as fancyLog from 'fancy-log'; -import * as ansiColors from 'ansi-colors'; -const buffer = require('gulp-buffer'); +import filter from 'gulp-filter'; +import rename from 'gulp-rename'; +import fancyLog from 'fancy-log'; +import ansiColors from 'ansi-colors'; +import buffer from 'gulp-buffer'; import * as jsoncParser from 'jsonc-parser'; -import webpack = require('webpack'); +import webpack from 'webpack'; import { getProductionDependencies } from './dependencies'; import { IExtensionDefinition, getExtensionStream } from './builtInExtensions'; import { getVersion } from './getVersion'; @@ -170,10 +171,12 @@ function fromLocalWebpack(extensionPath: string, webpackConfigFileName: string, // source map handling: // * rewrite sourceMappingURL // * save to disk so that upload-task picks this up - const contents = (data.contents).toString('utf8'); - data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) { - return `\n//# sourceMappingURL=${sourceMappingURLBase}/extensions/${path.basename(extensionPath)}/${relativeOutputPath}/${g1}`; - }), 'utf8'); + if (path.extname(data.basename) === '.js') { + const contents = (data.contents).toString('utf8'); + data.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, function (_m, g1) { + return `\n//# sourceMappingURL=${sourceMappingURLBase}/extensions/${path.basename(extensionPath)}/${relativeOutputPath}/${g1}`; + }), 'utf8'); + } this.emit('data', data); })); @@ -252,6 +255,33 @@ export function fromMarketplace(serviceUrl: string, { name: extensionName, versi .pipe(packageJsonFilter.restore); } +export function fromVsix(vsixPath: string, { name: extensionName, version, sha256, metadata }: IExtensionDefinition): Stream { + const json = require('gulp-json-editor') as typeof import('gulp-json-editor'); + + fancyLog('Using local VSIX for extension:', ansiColors.yellow(`${extensionName}@${version}`), '...'); + + const packageJsonFilter = filter('package.json', { restore: true }); + + return gulp.src(vsixPath) + .pipe(buffer()) + .pipe(es.mapSync((f: File) => { + const hash = crypto.createHash('sha256'); + hash.update(f.contents as Buffer); + const checksum = hash.digest('hex'); + if (checksum !== sha256) { + throw new Error(`Checksum mismatch for ${vsixPath} (expected ${sha256}, actual ${checksum}))`); + } + return f; + })) + .pipe(vzip.src()) + .pipe(filter('extension/**')) + .pipe(rename(p => p.dirname = p.dirname!.replace(/^extension\/?/, ''))) + .pipe(packageJsonFilter) + .pipe(buffer()) + .pipe(json({ __metadata: metadata })) + .pipe(packageJsonFilter.restore); +} + export function fromGithub({ name, version, repo, sha256, metadata }: IExtensionDefinition): Stream { const json = require('gulp-json-editor') as typeof import('gulp-json-editor'); @@ -275,9 +305,18 @@ export function fromGithub({ name, version, repo, sha256, metadata }: IExtension .pipe(packageJsonFilter.restore); } +/** + * All extensions that are known to have some native component and thus must be built on the + * platform that is being built. + */ +const nativeExtensions = [ + 'microsoft-authentication', +]; + const excludedExtensions = [ 'vscode-api-tests', 'vscode-colorize-tests', + 'vscode-colorize-perf-tests', 'vscode-test-resolver', 'ms-vscode.node-debug', 'ms-vscode.node-debug2', @@ -331,7 +370,49 @@ function isWebExtension(manifest: IExtensionManifest): boolean { return true; } -export function packageLocalExtensionsStream(forWeb: boolean, disableMangle: boolean): Stream { +/** + * Package local extensions that are known to not have native dependencies. Mutually exclusive to {@link packageNativeLocalExtensionsStream}. + * @param forWeb build the extensions that have web targets + * @param disableMangle disable the mangler + * @returns a stream + */ +export function packageNonNativeLocalExtensionsStream(forWeb: boolean, disableMangle: boolean): Stream { + return doPackageLocalExtensionsStream(forWeb, disableMangle, false); +} + +/** + * Package local extensions that are known to have native dependencies. Mutually exclusive to {@link packageNonNativeLocalExtensionsStream}. + * @note it's possible that the extension does not have native dependencies for the current platform, especially if building for the web, + * but we simplify the logic here by having a flat list of extensions (See {@link nativeExtensions}) that are known to have native + * dependencies on some platform and thus should be packaged on the platform that they are building for. + * @param forWeb build the extensions that have web targets + * @param disableMangle disable the mangler + * @returns a stream + */ +export function packageNativeLocalExtensionsStream(forWeb: boolean, disableMangle: boolean): Stream { + return doPackageLocalExtensionsStream(forWeb, disableMangle, true); +} + +/** + * Package all the local extensions... both those that are known to have native dependencies and those that are not. + * @param forWeb build the extensions that have web targets + * @param disableMangle disable the mangler + * @returns a stream + */ +export function packageAllLocalExtensionsStream(forWeb: boolean, disableMangle: boolean): Stream { + return es.merge([ + packageNonNativeLocalExtensionsStream(forWeb, disableMangle), + packageNativeLocalExtensionsStream(forWeb, disableMangle) + ]); +} + +/** + * @param forWeb build the extensions that have web targets + * @param disableMangle disable the mangler + * @param native build the extensions that are marked as having native dependencies + */ +function doPackageLocalExtensionsStream(forWeb: boolean, disableMangle: boolean, native: boolean): Stream { + const nativeExtensionsSet = new Set(nativeExtensions); const localExtensionsDescriptions = ( (glob.sync('extensions/*/package.json')) .map(manifestPath => { @@ -340,6 +421,7 @@ export function packageLocalExtensionsStream(forWeb: boolean, disableMangle: boo const extensionName = path.basename(extensionPath); return { name: extensionName, path: extensionPath, manifestPath: absoluteManifestPath }; }) + .filter(({ name }) => native ? nativeExtensionsSet.has(name) : !nativeExtensionsSet.has(name)) .filter(({ name }) => excludedExtensions.indexOf(name) === -1) .filter(({ name }) => builtInExtensions.every(b => b.name !== name)) .filter(({ manifestPath }) => (forWeb ? isWebExtension(require(manifestPath)) : true)) diff --git a/build/lib/fetch.js b/build/lib/fetch.js index b7da65f4af24..078706cdd000 100644 --- a/build/lib/fetch.js +++ b/build/lib/fetch.js @@ -3,16 +3,19 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.fetchUrls = fetchUrls; exports.fetchUrl = fetchUrl; exports.fetchGithub = fetchGithub; -const es = require("event-stream"); -const VinylFile = require("vinyl"); -const log = require("fancy-log"); -const ansiColors = require("ansi-colors"); -const crypto = require("crypto"); -const through2 = require("through2"); +const event_stream_1 = __importDefault(require("event-stream")); +const vinyl_1 = __importDefault(require("vinyl")); +const fancy_log_1 = __importDefault(require("fancy-log")); +const ansi_colors_1 = __importDefault(require("ansi-colors")); +const crypto_1 = __importDefault(require("crypto")); +const through2_1 = __importDefault(require("through2")); function fetchUrls(urls, options) { if (options === undefined) { options = {}; @@ -23,7 +26,7 @@ function fetchUrls(urls, options) { if (!Array.isArray(urls)) { urls = [urls]; } - return es.readArray(urls).pipe(es.map((data, cb) => { + return event_stream_1.default.readArray(urls).pipe(event_stream_1.default.map((data, cb) => { const url = [options.base, data].join(''); fetchUrl(url, options).then(file => { cb(undefined, file); @@ -37,7 +40,7 @@ async function fetchUrl(url, options, retries = 10, retryDelay = 1000) { try { let startTime = 0; if (verbose) { - log(`Start fetching ${ansiColors.magenta(url)}${retries !== 10 ? ` (${10 - retries} retry)` : ''}`); + (0, fancy_log_1.default)(`Start fetching ${ansi_colors_1.default.magenta(url)}${retries !== 10 ? ` (${10 - retries} retry)` : ''}`); startTime = new Date().getTime(); } const controller = new AbortController(); @@ -48,33 +51,33 @@ async function fetchUrl(url, options, retries = 10, retryDelay = 1000) { signal: controller.signal /* Typings issue with lib.dom.d.ts */ }); if (verbose) { - log(`Fetch completed: Status ${response.status}. Took ${ansiColors.magenta(`${new Date().getTime() - startTime} ms`)}`); + (0, fancy_log_1.default)(`Fetch completed: Status ${response.status}. Took ${ansi_colors_1.default.magenta(`${new Date().getTime() - startTime} ms`)}`); } if (response.ok && (response.status >= 200 && response.status < 300)) { const contents = Buffer.from(await response.arrayBuffer()); if (options.checksumSha256) { - const actualSHA256Checksum = crypto.createHash('sha256').update(contents).digest('hex'); + const actualSHA256Checksum = crypto_1.default.createHash('sha256').update(contents).digest('hex'); if (actualSHA256Checksum !== options.checksumSha256) { - throw new Error(`Checksum mismatch for ${ansiColors.cyan(url)} (expected ${options.checksumSha256}, actual ${actualSHA256Checksum}))`); + throw new Error(`Checksum mismatch for ${ansi_colors_1.default.cyan(url)} (expected ${options.checksumSha256}, actual ${actualSHA256Checksum}))`); } else if (verbose) { - log(`Verified SHA256 checksums match for ${ansiColors.cyan(url)}`); + (0, fancy_log_1.default)(`Verified SHA256 checksums match for ${ansi_colors_1.default.cyan(url)}`); } } else if (verbose) { - log(`Skipping checksum verification for ${ansiColors.cyan(url)} because no expected checksum was provided`); + (0, fancy_log_1.default)(`Skipping checksum verification for ${ansi_colors_1.default.cyan(url)} because no expected checksum was provided`); } if (verbose) { - log(`Fetched response body buffer: ${ansiColors.magenta(`${contents.byteLength} bytes`)}`); + (0, fancy_log_1.default)(`Fetched response body buffer: ${ansi_colors_1.default.magenta(`${contents.byteLength} bytes`)}`); } - return new VinylFile({ + return new vinyl_1.default({ cwd: '/', base: options.base, path: url, contents }); } - let err = `Request ${ansiColors.magenta(url)} failed with status code: ${response.status}`; + let err = `Request ${ansi_colors_1.default.magenta(url)} failed with status code: ${response.status}`; if (response.status === 403) { err += ' (you may be rate limited)'; } @@ -86,7 +89,7 @@ async function fetchUrl(url, options, retries = 10, retryDelay = 1000) { } catch (e) { if (verbose) { - log(`Fetching ${ansiColors.cyan(url)} failed: ${e}`); + (0, fancy_log_1.default)(`Fetching ${ansi_colors_1.default.cyan(url)} failed: ${e}`); } if (retries > 0) { await new Promise(resolve => setTimeout(resolve, retryDelay)); @@ -117,7 +120,7 @@ function fetchGithub(repo, options) { base: 'https://api.github.com', verbose: options.verbose, nodeFetchOptions: { headers: ghApiHeaders } - }).pipe(through2.obj(async function (file, _enc, callback) { + }).pipe(through2_1.default.obj(async function (file, _enc, callback) { const assetFilter = typeof options.name === 'string' ? (name) => name === options.name : options.name; const asset = JSON.parse(file.contents.toString()).assets.find((a) => assetFilter(a.name)); if (!asset) { diff --git a/build/lib/fetch.ts b/build/lib/fetch.ts index 0c44b8e567f1..47a65b88fb53 100644 --- a/build/lib/fetch.ts +++ b/build/lib/fetch.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as es from 'event-stream'; -import * as VinylFile from 'vinyl'; -import * as log from 'fancy-log'; -import * as ansiColors from 'ansi-colors'; -import * as crypto from 'crypto'; -import * as through2 from 'through2'; +import es from 'event-stream'; +import VinylFile from 'vinyl'; +import log from 'fancy-log'; +import ansiColors from 'ansi-colors'; +import crypto from 'crypto'; +import through2 from 'through2'; import { Stream } from 'stream'; export interface IFetchOptions { diff --git a/build/lib/formatter.js b/build/lib/formatter.js index 29f265c8289d..1085ea8f4889 100644 --- a/build/lib/formatter.js +++ b/build/lib/formatter.js @@ -1,17 +1,20 @@ "use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.format = format; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -const fs = require("fs"); -const path = require("path"); -const ts = require("typescript"); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const typescript_1 = __importDefault(require("typescript")); class LanguageServiceHost { files = {}; addFile(fileName, text) { - this.files[fileName] = ts.ScriptSnapshot.fromString(text); + this.files[fileName] = typescript_1.default.ScriptSnapshot.fromString(text); } fileExists(path) { return !!this.files[path]; @@ -20,18 +23,18 @@ class LanguageServiceHost { return this.files[path]?.getText(0, this.files[path].getLength()); } // for ts.LanguageServiceHost - getCompilationSettings = () => ts.getDefaultCompilerOptions(); + getCompilationSettings = () => typescript_1.default.getDefaultCompilerOptions(); getScriptFileNames = () => Object.keys(this.files); getScriptVersion = (_fileName) => '0'; getScriptSnapshot = (fileName) => this.files[fileName]; getCurrentDirectory = () => process.cwd(); - getDefaultLibFileName = (options) => ts.getDefaultLibFilePath(options); + getDefaultLibFileName = (options) => typescript_1.default.getDefaultLibFilePath(options); } const defaults = { baseIndentSize: 0, indentSize: 4, tabSize: 4, - indentStyle: ts.IndentStyle.Smart, + indentStyle: typescript_1.default.IndentStyle.Smart, newLineCharacter: '\r\n', convertTabsToSpaces: false, insertSpaceAfterCommaDelimiter: true, @@ -54,14 +57,14 @@ const defaults = { const getOverrides = (() => { let value; return () => { - value ??= JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'tsfmt.json'), 'utf8')); + value ??= JSON.parse(fs_1.default.readFileSync(path_1.default.join(__dirname, '..', '..', 'tsfmt.json'), 'utf8')); return value; }; })(); function format(fileName, text) { const host = new LanguageServiceHost(); host.addFile(fileName, text); - const languageService = ts.createLanguageService(host); + const languageService = typescript_1.default.createLanguageService(host); const edits = languageService.getFormattingEditsForDocument(fileName, { ...defaults, ...getOverrides() }); edits .sort((a, b) => a.span.start - b.span.start) diff --git a/build/lib/formatter.ts b/build/lib/formatter.ts index 0d9035b3d87c..993722e5f924 100644 --- a/build/lib/formatter.ts +++ b/build/lib/formatter.ts @@ -2,9 +2,9 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; -import * as ts from 'typescript'; +import fs from 'fs'; +import path from 'path'; +import ts from 'typescript'; class LanguageServiceHost implements ts.LanguageServiceHost { diff --git a/build/lib/getVersion.js b/build/lib/getVersion.js index b50ead538a25..7606c17ab14f 100644 --- a/build/lib/getVersion.js +++ b/build/lib/getVersion.js @@ -3,9 +3,42 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); Object.defineProperty(exports, "__esModule", { value: true }); exports.getVersion = getVersion; -const git = require("./git"); +const git = __importStar(require("./git")); function getVersion(root) { let version = process.env['BUILD_SOURCEVERSION']; if (!version || !/^[0-9a-f]{40}$/i.test(version.trim())) { diff --git a/build/lib/git.js b/build/lib/git.js index 798a408bdb91..30de97ed6e36 100644 --- a/build/lib/git.js +++ b/build/lib/git.js @@ -1,21 +1,24 @@ "use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.getVersion = getVersion; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -const path = require("path"); -const fs = require("fs"); +const path_1 = __importDefault(require("path")); +const fs_1 = __importDefault(require("fs")); /** * Returns the sha1 commit version of a repository or undefined in case of failure. */ function getVersion(repo) { - const git = path.join(repo, '.git'); - const headPath = path.join(git, 'HEAD'); + const git = path_1.default.join(repo, '.git'); + const headPath = path_1.default.join(git, 'HEAD'); let head; try { - head = fs.readFileSync(headPath, 'utf8').trim(); + head = fs_1.default.readFileSync(headPath, 'utf8').trim(); } catch (e) { return undefined; @@ -28,17 +31,17 @@ function getVersion(repo) { return undefined; } const ref = refMatch[1]; - const refPath = path.join(git, ref); + const refPath = path_1.default.join(git, ref); try { - return fs.readFileSync(refPath, 'utf8').trim(); + return fs_1.default.readFileSync(refPath, 'utf8').trim(); } catch (e) { // noop } - const packedRefsPath = path.join(git, 'packed-refs'); + const packedRefsPath = path_1.default.join(git, 'packed-refs'); let refsRaw; try { - refsRaw = fs.readFileSync(packedRefsPath, 'utf8').trim(); + refsRaw = fs_1.default.readFileSync(packedRefsPath, 'utf8').trim(); } catch (e) { return undefined; diff --git a/build/lib/git.ts b/build/lib/git.ts index dbb424f21df7..a3c23d8c29b3 100644 --- a/build/lib/git.ts +++ b/build/lib/git.ts @@ -2,8 +2,8 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; -import * as fs from 'fs'; +import path from 'path'; +import fs from 'fs'; /** * Returns the sha1 commit version of a repository or undefined in case of failure. diff --git a/build/lib/i18n.js b/build/lib/i18n.js index 6964616291b3..9483d319a50b 100644 --- a/build/lib/i18n.js +++ b/build/lib/i18n.js @@ -3,6 +3,9 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.EXTERNAL_EXTENSIONS = exports.XLF = exports.Line = exports.extraLanguages = exports.defaultLanguages = void 0; exports.processNlsFiles = processNlsFiles; @@ -12,20 +15,20 @@ exports.createXlfFilesForExtensions = createXlfFilesForExtensions; exports.createXlfFilesForIsl = createXlfFilesForIsl; exports.prepareI18nPackFiles = prepareI18nPackFiles; exports.prepareIslFiles = prepareIslFiles; -const path = require("path"); -const fs = require("fs"); +const path_1 = __importDefault(require("path")); +const fs_1 = __importDefault(require("fs")); const event_stream_1 = require("event-stream"); -const jsonMerge = require("gulp-merge-json"); -const File = require("vinyl"); -const xml2js = require("xml2js"); -const gulp = require("gulp"); -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); -const iconv = require("@vscode/iconv-lite-umd"); +const gulp_merge_json_1 = __importDefault(require("gulp-merge-json")); +const vinyl_1 = __importDefault(require("vinyl")); +const xml2js_1 = __importDefault(require("xml2js")); +const gulp_1 = __importDefault(require("gulp")); +const fancy_log_1 = __importDefault(require("fancy-log")); +const ansi_colors_1 = __importDefault(require("ansi-colors")); +const iconv_lite_umd_1 = __importDefault(require("@vscode/iconv-lite-umd")); const l10n_dev_1 = require("@vscode/l10n-dev"); -const REPO_ROOT_PATH = path.join(__dirname, '../..'); +const REPO_ROOT_PATH = path_1.default.join(__dirname, '../..'); function log(message, ...rest) { - fancyLog(ansiColors.green('[i18n]'), message, ...rest); + (0, fancy_log_1.default)(ansi_colors_1.default.green('[i18n]'), message, ...rest); } exports.defaultLanguages = [ { id: 'zh-tw', folderName: 'cht', translationId: 'zh-hant' }, @@ -188,7 +191,7 @@ class XLF { } static parse = function (xlfString) { return new Promise((resolve, reject) => { - const parser = new xml2js.Parser(); + const parser = new xml2js_1.default.Parser(); const files = []; parser.parseString(xlfString, function (err, result) { if (err) { @@ -278,8 +281,8 @@ function stripComments(content) { return result; } function processCoreBundleFormat(base, fileHeader, languages, json, emitter) { - const languageDirectory = path.join(REPO_ROOT_PATH, '..', 'vscode-loc', 'i18n'); - if (!fs.existsSync(languageDirectory)) { + const languageDirectory = path_1.default.join(REPO_ROOT_PATH, '..', 'vscode-loc', 'i18n'); + if (!fs_1.default.existsSync(languageDirectory)) { log(`No VS Code localization repository found. Looking at ${languageDirectory}`); log(`To bundle translations please check out the vscode-loc repository as a sibling of the vscode repository.`); } @@ -289,10 +292,10 @@ function processCoreBundleFormat(base, fileHeader, languages, json, emitter) { log(`Generating nls bundles for: ${language.id}`); } const languageFolderName = language.translationId || language.id; - const i18nFile = path.join(languageDirectory, `vscode-language-pack-${languageFolderName}`, 'translations', 'main.i18n.json'); + const i18nFile = path_1.default.join(languageDirectory, `vscode-language-pack-${languageFolderName}`, 'translations', 'main.i18n.json'); let allMessages; - if (fs.existsSync(i18nFile)) { - const content = stripComments(fs.readFileSync(i18nFile, 'utf8')); + if (fs_1.default.existsSync(i18nFile)) { + const content = stripComments(fs_1.default.readFileSync(i18nFile, 'utf8')); allMessages = JSON.parse(content); } let nlsIndex = 0; @@ -304,7 +307,7 @@ function processCoreBundleFormat(base, fileHeader, languages, json, emitter) { nlsIndex++; } } - emitter.queue(new File({ + emitter.queue(new vinyl_1.default({ contents: Buffer.from(`${fileHeader} globalThis._VSCODE_NLS_MESSAGES=${JSON.stringify(nlsResult)}; globalThis._VSCODE_NLS_LANGUAGE=${JSON.stringify(language.id)};`), @@ -315,10 +318,10 @@ globalThis._VSCODE_NLS_LANGUAGE=${JSON.stringify(language.id)};`), } function processNlsFiles(opts) { return (0, event_stream_1.through)(function (file) { - const fileName = path.basename(file.path); + const fileName = path_1.default.basename(file.path); if (fileName === 'bundleInfo.json') { // pick a root level file to put the core bundles (TODO@esm this file is not created anymore, pick another) try { - const json = JSON.parse(fs.readFileSync(path.join(REPO_ROOT_PATH, opts.out, 'nls.keys.json')).toString()); + const json = JSON.parse(fs_1.default.readFileSync(path_1.default.join(REPO_ROOT_PATH, opts.out, 'nls.keys.json')).toString()); if (NLSKeysFormat.is(json)) { processCoreBundleFormat(file.base, opts.fileHeader, opts.languages, json, this); } @@ -366,7 +369,7 @@ function getResource(sourceFile) { } function createXlfFilesForCoreBundle() { return (0, event_stream_1.through)(function (file) { - const basename = path.basename(file.path); + const basename = path_1.default.basename(file.path); if (basename === 'nls.metadata.json') { if (file.isBuffer()) { const xlfs = Object.create(null); @@ -393,7 +396,7 @@ function createXlfFilesForCoreBundle() { for (const resource in xlfs) { const xlf = xlfs[resource]; const filePath = `${xlf.project}/${resource.replace(/\//g, '_')}.xlf`; - const xlfFile = new File({ + const xlfFile = new vinyl_1.default({ path: filePath, contents: Buffer.from(xlf.toString(), 'utf8') }); @@ -413,7 +416,7 @@ function createXlfFilesForCoreBundle() { } function createL10nBundleForExtension(extensionFolderName, prefixWithBuildFolder) { const prefix = prefixWithBuildFolder ? '.build/' : ''; - return gulp + return gulp_1.default .src([ // For source code of extensions `${prefix}extensions/${extensionFolderName}/{src,client,server}/**/*.{ts,tsx}`, @@ -429,12 +432,12 @@ function createL10nBundleForExtension(extensionFolderName, prefixWithBuildFolder callback(); return; } - const extension = path.extname(file.relative); + const extension = path_1.default.extname(file.relative); if (extension !== '.json') { const contents = file.contents.toString('utf8'); (0, l10n_dev_1.getL10nJson)([{ contents, extension }]) .then((json) => { - callback(undefined, new File({ + callback(undefined, new vinyl_1.default({ path: `extensions/${extensionFolderName}/bundle.l10n.json`, contents: Buffer.from(JSON.stringify(json), 'utf8') })); @@ -464,7 +467,7 @@ function createL10nBundleForExtension(extensionFolderName, prefixWithBuildFolder } callback(undefined, file); })) - .pipe(jsonMerge({ + .pipe((0, gulp_merge_json_1.default)({ fileName: `extensions/${extensionFolderName}/bundle.l10n.json`, jsonSpace: '', concatArrays: true @@ -481,16 +484,16 @@ function createXlfFilesForExtensions() { let folderStreamEndEmitted = false; return (0, event_stream_1.through)(function (extensionFolder) { const folderStream = this; - const stat = fs.statSync(extensionFolder.path); + const stat = fs_1.default.statSync(extensionFolder.path); if (!stat.isDirectory()) { return; } - const extensionFolderName = path.basename(extensionFolder.path); + const extensionFolderName = path_1.default.basename(extensionFolder.path); if (extensionFolderName === 'node_modules') { return; } // Get extension id and use that as the id - const manifest = fs.readFileSync(path.join(extensionFolder.path, 'package.json'), 'utf-8'); + const manifest = fs_1.default.readFileSync(path_1.default.join(extensionFolder.path, 'package.json'), 'utf-8'); const manifestJson = JSON.parse(manifest); const extensionId = manifestJson.publisher + '.' + manifestJson.name; counter++; @@ -501,17 +504,17 @@ function createXlfFilesForExtensions() { } return _l10nMap; } - (0, event_stream_1.merge)(gulp.src([`.build/extensions/${extensionFolderName}/package.nls.json`, `.build/extensions/${extensionFolderName}/**/nls.metadata.json`], { allowEmpty: true }), createL10nBundleForExtension(extensionFolderName, exports.EXTERNAL_EXTENSIONS.includes(extensionId))).pipe((0, event_stream_1.through)(function (file) { + (0, event_stream_1.merge)(gulp_1.default.src([`.build/extensions/${extensionFolderName}/package.nls.json`, `.build/extensions/${extensionFolderName}/**/nls.metadata.json`], { allowEmpty: true }), createL10nBundleForExtension(extensionFolderName, exports.EXTERNAL_EXTENSIONS.includes(extensionId))).pipe((0, event_stream_1.through)(function (file) { if (file.isBuffer()) { const buffer = file.contents; - const basename = path.basename(file.path); + const basename = path_1.default.basename(file.path); if (basename === 'package.nls.json') { const json = JSON.parse(buffer.toString('utf8')); getL10nMap().set(`extensions/${extensionId}/package`, json); } else if (basename === 'nls.metadata.json') { const json = JSON.parse(buffer.toString('utf8')); - const relPath = path.relative(`.build/extensions/${extensionFolderName}`, path.dirname(file.path)); + const relPath = path_1.default.relative(`.build/extensions/${extensionFolderName}`, path_1.default.dirname(file.path)); for (const file in json) { const fileContent = json[file]; const info = Object.create(null); @@ -536,8 +539,8 @@ function createXlfFilesForExtensions() { } }, function () { if (_l10nMap?.size > 0) { - const xlfFile = new File({ - path: path.join(extensionsProject, extensionId + '.xlf'), + const xlfFile = new vinyl_1.default({ + path: path_1.default.join(extensionsProject, extensionId + '.xlf'), contents: Buffer.from((0, l10n_dev_1.getL10nXlf)(_l10nMap), 'utf8') }); folderStream.queue(xlfFile); @@ -560,7 +563,7 @@ function createXlfFilesForExtensions() { function createXlfFilesForIsl() { return (0, event_stream_1.through)(function (file) { let projectName, resourceFile; - if (path.basename(file.path) === 'messages.en.isl') { + if (path_1.default.basename(file.path) === 'messages.en.isl') { projectName = setupProject; resourceFile = 'messages.xlf'; } @@ -602,8 +605,8 @@ function createXlfFilesForIsl() { const originalPath = file.path.substring(file.cwd.length + 1, file.path.split('.')[0].length).replace(/\\/g, '/'); xlf.addFile(originalPath, keys, messages); // Emit only upon all ISL files combined into single XLF instance - const newFilePath = path.join(projectName, resourceFile); - const xlfFile = new File({ path: newFilePath, contents: Buffer.from(xlf.toString(), 'utf-8') }); + const newFilePath = path_1.default.join(projectName, resourceFile); + const xlfFile = new vinyl_1.default({ path: newFilePath, contents: Buffer.from(xlf.toString(), 'utf-8') }); this.queue(xlfFile); }); } @@ -623,8 +626,8 @@ function createI18nFile(name, messages) { if (process.platform === 'win32') { content = content.replace(/\n/g, '\r\n'); } - return new File({ - path: path.join(name + '.i18n.json'), + return new vinyl_1.default({ + path: path_1.default.join(name + '.i18n.json'), contents: Buffer.from(content, 'utf8') }); } @@ -643,9 +646,9 @@ function prepareI18nPackFiles(resultingTranslationPaths) { const extensionsPacks = {}; const errors = []; return (0, event_stream_1.through)(function (xlf) { - let project = path.basename(path.dirname(path.dirname(xlf.relative))); + let project = path_1.default.basename(path_1.default.dirname(path_1.default.dirname(xlf.relative))); // strip `-new` since vscode-extensions-loc uses the `-new` suffix to indicate that it's from the new loc pipeline - const resource = path.basename(path.basename(xlf.relative, '.xlf'), '-new'); + const resource = path_1.default.basename(path_1.default.basename(xlf.relative, '.xlf'), '-new'); if (exports.EXTERNAL_EXTENSIONS.find(e => e === resource)) { project = extensionsProject; } @@ -720,11 +723,11 @@ function prepareIslFiles(language, innoSetupConfig) { function createIslFile(name, messages, language, innoSetup) { const content = []; let originalContent; - if (path.basename(name) === 'Default') { - originalContent = new TextModel(fs.readFileSync(name + '.isl', 'utf8')); + if (path_1.default.basename(name) === 'Default') { + originalContent = new TextModel(fs_1.default.readFileSync(name + '.isl', 'utf8')); } else { - originalContent = new TextModel(fs.readFileSync(name + '.en.isl', 'utf8')); + originalContent = new TextModel(fs_1.default.readFileSync(name + '.en.isl', 'utf8')); } originalContent.lines.forEach(line => { if (line.length > 0) { @@ -746,10 +749,10 @@ function createIslFile(name, messages, language, innoSetup) { } } }); - const basename = path.basename(name); + const basename = path_1.default.basename(name); const filePath = `${basename}.${language.id}.isl`; - const encoded = iconv.encode(Buffer.from(content.join('\r\n'), 'utf8').toString(), innoSetup.codePage); - return new File({ + const encoded = iconv_lite_umd_1.default.encode(Buffer.from(content.join('\r\n'), 'utf8').toString(), innoSetup.codePage); + return new vinyl_1.default({ path: filePath, contents: Buffer.from(encoded), }); diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index fb732ae1eaf8..2b5107578554 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -58,10 +58,6 @@ "name": "vs/workbench/contrib/commands", "project": "vscode-workbench" }, - { - "name": "vs/workbench/contrib/mappedEdits", - "project": "vscode-workbench" - }, { "name": "vs/workbench/contrib/markdown", "project": "vscode-workbench" diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts index cd7e522ad361..d2904ccf0fb5 100644 --- a/build/lib/i18n.ts +++ b/build/lib/i18n.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; -import * as fs from 'fs'; +import path from 'path'; +import fs from 'fs'; import { map, merge, through, ThroughStream } from 'event-stream'; -import * as jsonMerge from 'gulp-merge-json'; -import * as File from 'vinyl'; -import * as xml2js from 'xml2js'; -import * as gulp from 'gulp'; -import * as fancyLog from 'fancy-log'; -import * as ansiColors from 'ansi-colors'; -import * as iconv from '@vscode/iconv-lite-umd'; +import jsonMerge from 'gulp-merge-json'; +import File from 'vinyl'; +import xml2js from 'xml2js'; +import gulp from 'gulp'; +import fancyLog from 'fancy-log'; +import ansiColors from 'ansi-colors'; +import iconv from '@vscode/iconv-lite-umd'; import { l10nJsonFormat, getL10nXlf, l10nJsonDetails, getL10nFilesFromXlf, getL10nJson } from '@vscode/l10n-dev'; const REPO_ROOT_PATH = path.join(__dirname, '../..'); diff --git a/build/lib/inlineMeta.js b/build/lib/inlineMeta.js index f1dbfa83a7e1..3b473ae091e1 100644 --- a/build/lib/inlineMeta.js +++ b/build/lib/inlineMeta.js @@ -3,12 +3,15 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.inlineMeta = inlineMeta; -const es = require("event-stream"); +const event_stream_1 = __importDefault(require("event-stream")); const path_1 = require("path"); const packageJsonMarkerId = 'BUILD_INSERT_PACKAGE_CONFIGURATION'; -// TODO@bpasero in order to inline `product.json`, more work is +// TODO in order to inline `product.json`, more work is // needed to ensure that we cover all cases where modifications // are done to the product configuration during build. There are // at least 2 more changes that kick in very late: @@ -16,7 +19,7 @@ const packageJsonMarkerId = 'BUILD_INSERT_PACKAGE_CONFIGURATION'; // - a `target` is added in `gulpfile.vscode.win32.js` // const productJsonMarkerId = 'BUILD_INSERT_PRODUCT_CONFIGURATION'; function inlineMeta(result, ctx) { - return result.pipe(es.through(function (file) { + return result.pipe(event_stream_1.default.through(function (file) { if (matchesFile(file, ctx)) { let content = file.contents.toString(); let markerFound = false; diff --git a/build/lib/inlineMeta.ts b/build/lib/inlineMeta.ts index ef3987fc32ed..2a0db13d06e2 100644 --- a/build/lib/inlineMeta.ts +++ b/build/lib/inlineMeta.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as es from 'event-stream'; +import es from 'event-stream'; import { basename } from 'path'; -import * as File from 'vinyl'; +import File from 'vinyl'; export interface IInlineMetaContext { readonly targetPaths: string[]; @@ -15,7 +15,7 @@ export interface IInlineMetaContext { const packageJsonMarkerId = 'BUILD_INSERT_PACKAGE_CONFIGURATION'; -// TODO@bpasero in order to inline `product.json`, more work is +// TODO in order to inline `product.json`, more work is // needed to ensure that we cover all cases where modifications // are done to the product configuration during build. There are // at least 2 more changes that kick in very late: diff --git a/build/lib/layersChecker.js b/build/lib/layersChecker.js index ffc21be21f5c..2cab7b818328 100644 --- a/build/lib/layersChecker.js +++ b/build/lib/layersChecker.js @@ -3,8 +3,11 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const ts = require("typescript"); +const typescript_1 = __importDefault(require("typescript")); const fs_1 = require("fs"); const path_1 = require("path"); const minimatch_1 = require("minimatch"); @@ -67,14 +70,20 @@ const CORE_TYPES = [ 'fetch', 'RequestInit', 'Headers', + 'Request', 'Response', + 'Body', + '__type', '__global', + 'Performance', 'PerformanceMark', 'PerformanceObserver', 'ImportMeta', // webcrypto has been available since Node.js 19, but still live in dom.d.ts 'Crypto', - 'SubtleCrypto' + 'SubtleCrypto', + 'JsonWebKey', + 'MessageEvent', ]; // Types that are defined in a common layer but are known to be only // available in native environments should not be allowed in browser @@ -291,8 +300,8 @@ let hasErrors = false; function checkFile(program, sourceFile, rule) { checkNode(sourceFile); function checkNode(node) { - if (node.kind !== ts.SyntaxKind.Identifier) { - return ts.forEachChild(node, checkNode); // recurse down + if (node.kind !== typescript_1.default.SyntaxKind.Identifier) { + return typescript_1.default.forEachChild(node, checkNode); // recurse down } const checker = program.getTypeChecker(); const symbol = checker.getSymbolAtLocation(node); @@ -348,11 +357,11 @@ function checkFile(program, sourceFile, rule) { } } function createProgram(tsconfigPath) { - const tsConfig = ts.readConfigFile(tsconfigPath, ts.sys.readFile); - const configHostParser = { fileExists: fs_1.existsSync, readDirectory: ts.sys.readDirectory, readFile: file => (0, fs_1.readFileSync)(file, 'utf8'), useCaseSensitiveFileNames: process.platform === 'linux' }; - const tsConfigParsed = ts.parseJsonConfigFileContent(tsConfig.config, configHostParser, (0, path_1.resolve)((0, path_1.dirname)(tsconfigPath)), { noEmit: true }); - const compilerHost = ts.createCompilerHost(tsConfigParsed.options, true); - return ts.createProgram(tsConfigParsed.fileNames, tsConfigParsed.options, compilerHost); + const tsConfig = typescript_1.default.readConfigFile(tsconfigPath, typescript_1.default.sys.readFile); + const configHostParser = { fileExists: fs_1.existsSync, readDirectory: typescript_1.default.sys.readDirectory, readFile: file => (0, fs_1.readFileSync)(file, 'utf8'), useCaseSensitiveFileNames: process.platform === 'linux' }; + const tsConfigParsed = typescript_1.default.parseJsonConfigFileContent(tsConfig.config, configHostParser, (0, path_1.resolve)((0, path_1.dirname)(tsconfigPath)), { noEmit: true }); + const compilerHost = typescript_1.default.createCompilerHost(tsConfigParsed.options, true); + return typescript_1.default.createProgram(tsConfigParsed.fileNames, tsConfigParsed.options, compilerHost); } // // Create program and start checking diff --git a/build/lib/layersChecker.ts b/build/lib/layersChecker.ts index 3ecde09be21f..685dd59cb36a 100644 --- a/build/lib/layersChecker.ts +++ b/build/lib/layersChecker.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as ts from 'typescript'; +import ts from 'typescript'; import { readFileSync, existsSync } from 'fs'; import { resolve, dirname, join } from 'path'; import { match } from 'minimatch'; @@ -68,15 +68,21 @@ const CORE_TYPES = [ 'fetch', 'RequestInit', 'Headers', + 'Request', 'Response', + 'Body', + '__type', '__global', + 'Performance', 'PerformanceMark', 'PerformanceObserver', 'ImportMeta', // webcrypto has been available since Node.js 19, but still live in dom.d.ts 'Crypto', - 'SubtleCrypto' + 'SubtleCrypto', + 'JsonWebKey', + 'MessageEvent', ]; // Types that are defined in a common layer but are known to be only diff --git a/build/lib/mangle/index.js b/build/lib/mangle/index.js index 1c2c8cc3dd39..ce7446425512 100644 --- a/build/lib/mangle/index.js +++ b/build/lib/mangle/index.js @@ -3,16 +3,19 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.Mangler = void 0; -const v8 = require("node:v8"); -const fs = require("fs"); -const path = require("path"); +const node_v8_1 = __importDefault(require("node:v8")); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); const process_1 = require("process"); const source_map_1 = require("source-map"); -const ts = require("typescript"); +const typescript_1 = __importDefault(require("typescript")); const url_1 = require("url"); -const workerpool = require("workerpool"); +const workerpool_1 = __importDefault(require("workerpool")); const staticLanguageServiceHost_1 = require("./staticLanguageServiceHost"); const buildfile = require('../../buildfile'); class ShortIdent { @@ -66,29 +69,29 @@ class ClassData { this.node = node; const candidates = []; for (const member of node.members) { - if (ts.isMethodDeclaration(member)) { + if (typescript_1.default.isMethodDeclaration(member)) { // method `foo() {}` candidates.push(member); } - else if (ts.isPropertyDeclaration(member)) { + else if (typescript_1.default.isPropertyDeclaration(member)) { // property `foo = 234` candidates.push(member); } - else if (ts.isGetAccessor(member)) { + else if (typescript_1.default.isGetAccessor(member)) { // getter: `get foo() { ... }` candidates.push(member); } - else if (ts.isSetAccessor(member)) { + else if (typescript_1.default.isSetAccessor(member)) { // setter: `set foo() { ... }` candidates.push(member); } - else if (ts.isConstructorDeclaration(member)) { + else if (typescript_1.default.isConstructorDeclaration(member)) { // constructor-prop:`constructor(private foo) {}` for (const param of member.parameters) { - if (hasModifier(param, ts.SyntaxKind.PrivateKeyword) - || hasModifier(param, ts.SyntaxKind.ProtectedKeyword) - || hasModifier(param, ts.SyntaxKind.PublicKeyword) - || hasModifier(param, ts.SyntaxKind.ReadonlyKeyword)) { + if (hasModifier(param, typescript_1.default.SyntaxKind.PrivateKeyword) + || hasModifier(param, typescript_1.default.SyntaxKind.ProtectedKeyword) + || hasModifier(param, typescript_1.default.SyntaxKind.PublicKeyword) + || hasModifier(param, typescript_1.default.SyntaxKind.ReadonlyKeyword)) { candidates.push(param); } } @@ -109,8 +112,8 @@ class ClassData { } const { name } = node; let ident = name.getText(); - if (name.kind === ts.SyntaxKind.ComputedPropertyName) { - if (name.expression.kind !== ts.SyntaxKind.StringLiteral) { + if (name.kind === typescript_1.default.SyntaxKind.ComputedPropertyName) { + if (name.expression.kind !== typescript_1.default.SyntaxKind.StringLiteral) { // unsupported: [Symbol.foo] or [abc + 'field'] return; } @@ -120,10 +123,10 @@ class ClassData { return ident; } static _getFieldType(node) { - if (hasModifier(node, ts.SyntaxKind.PrivateKeyword)) { + if (hasModifier(node, typescript_1.default.SyntaxKind.PrivateKeyword)) { return 2 /* FieldType.Private */; } - else if (hasModifier(node, ts.SyntaxKind.ProtectedKeyword)) { + else if (hasModifier(node, typescript_1.default.SyntaxKind.ProtectedKeyword)) { return 1 /* FieldType.Protected */; } else { @@ -302,7 +305,7 @@ class DeclarationData { this.replacementName = fileIdents.next(); } getLocations(service) { - if (ts.isVariableDeclaration(this.node)) { + if (typescript_1.default.isVariableDeclaration(this.node)) { // If the const aliases any types, we need to rename those too const definitionResult = service.getDefinitionAndBoundSpan(this.fileName, this.node.name.getStart()); if (definitionResult?.definitions && definitionResult.definitions.length > 1) { @@ -350,20 +353,20 @@ class Mangler { this.projectPath = projectPath; this.log = log; this.config = config; - this.renameWorkerPool = workerpool.pool(path.join(__dirname, 'renameWorker.js'), { - maxWorkers: 1, + this.renameWorkerPool = workerpool_1.default.pool(path_1.default.join(__dirname, 'renameWorker.js'), { + maxWorkers: 4, minWorkers: 'max' }); } async computeNewFileContents(strictImplicitPublicHandling) { - const service = ts.createLanguageService(new staticLanguageServiceHost_1.StaticLanguageServiceHost(this.projectPath)); + const service = typescript_1.default.createLanguageService(new staticLanguageServiceHost_1.StaticLanguageServiceHost(this.projectPath)); // STEP: // - Find all classes and their field info. // - Find exported symbols. const fileIdents = new ShortIdent('$'); const visit = (node) => { if (this.config.manglePrivateFields) { - if (ts.isClassDeclaration(node) || ts.isClassExpression(node)) { + if (typescript_1.default.isClassDeclaration(node) || typescript_1.default.isClassExpression(node)) { const anchor = node.name ?? node; const key = `${node.getSourceFile().fileName}|${anchor.getStart()}`; if (this.allClassDataByKey.has(key)) { @@ -376,19 +379,19 @@ class Mangler { // Find exported classes, functions, and vars if (( // Exported class - ts.isClassDeclaration(node) - && hasModifier(node, ts.SyntaxKind.ExportKeyword) + typescript_1.default.isClassDeclaration(node) + && hasModifier(node, typescript_1.default.SyntaxKind.ExportKeyword) && node.name) || ( // Exported function - ts.isFunctionDeclaration(node) - && ts.isSourceFile(node.parent) - && hasModifier(node, ts.SyntaxKind.ExportKeyword) + typescript_1.default.isFunctionDeclaration(node) + && typescript_1.default.isSourceFile(node.parent) + && hasModifier(node, typescript_1.default.SyntaxKind.ExportKeyword) && node.name && node.body // On named function and not on the overload ) || ( // Exported variable - ts.isVariableDeclaration(node) - && hasModifier(node.parent.parent, ts.SyntaxKind.ExportKeyword) // Variable statement is exported - && ts.isSourceFile(node.parent.parent.parent)) + typescript_1.default.isVariableDeclaration(node) + && hasModifier(node.parent.parent, typescript_1.default.SyntaxKind.ExportKeyword) // Variable statement is exported + && typescript_1.default.isSourceFile(node.parent.parent.parent)) // Disabled for now because we need to figure out how to handle // enums that are used in monaco or extHost interfaces. /* || ( @@ -406,17 +409,17 @@ class Mangler { this.allExportedSymbols.add(new DeclarationData(node.getSourceFile().fileName, node, fileIdents)); } } - ts.forEachChild(node, visit); + typescript_1.default.forEachChild(node, visit); }; for (const file of service.getProgram().getSourceFiles()) { if (!file.isDeclarationFile) { - ts.forEachChild(file, visit); + typescript_1.default.forEachChild(file, visit); } } this.log(`Done collecting. Classes: ${this.allClassDataByKey.size}. Exported symbols: ${this.allExportedSymbols.size}`); // STEP: connect sub and super-types const setupParents = (data) => { - const extendsClause = data.node.heritageClauses?.find(h => h.token === ts.SyntaxKind.ExtendsKeyword); + const extendsClause = data.node.heritageClauses?.find(h => h.token === typescript_1.default.SyntaxKind.ExtendsKeyword); if (!extendsClause) { // no EXTENDS-clause return; @@ -497,7 +500,7 @@ class Mangler { .then((locations) => ({ newName, locations }))); }; for (const data of this.allClassDataByKey.values()) { - if (hasModifier(data.node, ts.SyntaxKind.DeclareKeyword)) { + if (hasModifier(data.node, typescript_1.default.SyntaxKind.DeclareKeyword)) { continue; } fields: for (const [name, info] of data.fields) { @@ -545,7 +548,7 @@ class Mangler { let savedBytes = 0; for (const item of service.getProgram().getSourceFiles()) { const { mapRoot, sourceRoot } = service.getProgram().getCompilerOptions(); - const projectDir = path.dirname(this.projectPath); + const projectDir = path_1.default.dirname(this.projectPath); const sourceMapRoot = mapRoot ?? (0, url_1.pathToFileURL)(sourceRoot ?? projectDir).toString(); // source maps let generator; @@ -557,7 +560,7 @@ class Mangler { } else { // source map generator - const relativeFileName = normalize(path.relative(projectDir, item.fileName)); + const relativeFileName = normalize(path_1.default.relative(projectDir, item.fileName)); const mappingsByLine = new Map(); // apply renames edits.sort((a, b) => b.offset - a.offset); @@ -596,7 +599,7 @@ class Mangler { }); } // source map generation, make sure to get mappings per line correct - generator = new source_map_1.SourceMapGenerator({ file: path.basename(item.fileName), sourceRoot: sourceMapRoot }); + generator = new source_map_1.SourceMapGenerator({ file: path_1.default.basename(item.fileName), sourceRoot: sourceMapRoot }); generator.setSourceContent(relativeFileName, item.getFullText()); for (const [, mappings] of mappingsByLine) { let lineDelta = 0; @@ -614,19 +617,19 @@ class Mangler { } service.dispose(); this.renameWorkerPool.terminate(); - this.log(`Done: ${savedBytes / 1000}kb saved, memory-usage: ${JSON.stringify(v8.getHeapStatistics())}`); + this.log(`Done: ${savedBytes / 1000}kb saved, memory-usage: ${JSON.stringify(node_v8_1.default.getHeapStatistics())}`); return result; } } exports.Mangler = Mangler; // --- ast utils function hasModifier(node, kind) { - const modifiers = ts.canHaveModifiers(node) ? ts.getModifiers(node) : undefined; + const modifiers = typescript_1.default.canHaveModifiers(node) ? typescript_1.default.getModifiers(node) : undefined; return Boolean(modifiers?.find(mode => mode.kind === kind)); } function isInAmbientContext(node) { for (let p = node.parent; p; p = p.parent) { - if (ts.isModuleDeclaration(p)) { + if (typescript_1.default.isModuleDeclaration(p)) { return true; } } @@ -636,21 +639,21 @@ function normalize(path) { return path.replace(/\\/g, '/'); } async function _run() { - const root = path.join(__dirname, '..', '..', '..'); - const projectBase = path.join(root, 'src'); - const projectPath = path.join(projectBase, 'tsconfig.json'); - const newProjectBase = path.join(path.dirname(projectBase), path.basename(projectBase) + '2'); - fs.cpSync(projectBase, newProjectBase, { recursive: true }); + const root = path_1.default.join(__dirname, '..', '..', '..'); + const projectBase = path_1.default.join(root, 'src'); + const projectPath = path_1.default.join(projectBase, 'tsconfig.json'); + const newProjectBase = path_1.default.join(path_1.default.dirname(projectBase), path_1.default.basename(projectBase) + '2'); + fs_1.default.cpSync(projectBase, newProjectBase, { recursive: true }); const mangler = new Mangler(projectPath, console.log, { mangleExports: true, manglePrivateFields: true, }); for (const [fileName, contents] of await mangler.computeNewFileContents(new Set(['saveState']))) { - const newFilePath = path.join(newProjectBase, path.relative(projectBase, fileName)); - await fs.promises.mkdir(path.dirname(newFilePath), { recursive: true }); - await fs.promises.writeFile(newFilePath, contents.out); + const newFilePath = path_1.default.join(newProjectBase, path_1.default.relative(projectBase, fileName)); + await fs_1.default.promises.mkdir(path_1.default.dirname(newFilePath), { recursive: true }); + await fs_1.default.promises.writeFile(newFilePath, contents.out); if (contents.sourceMap) { - await fs.promises.writeFile(newFilePath + '.map', contents.sourceMap); + await fs_1.default.promises.writeFile(newFilePath + '.map', contents.sourceMap); } } } diff --git a/build/lib/mangle/index.ts b/build/lib/mangle/index.ts index f291bd63f6b9..4cbbd3cdaddc 100644 --- a/build/lib/mangle/index.ts +++ b/build/lib/mangle/index.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as v8 from 'node:v8'; -import * as fs from 'fs'; -import * as path from 'path'; +import v8 from 'node:v8'; +import fs from 'fs'; +import path from 'path'; import { argv } from 'process'; import { Mapping, SourceMapGenerator } from 'source-map'; -import * as ts from 'typescript'; +import ts from 'typescript'; import { pathToFileURL } from 'url'; -import * as workerpool from 'workerpool'; +import workerpool from 'workerpool'; import { StaticLanguageServiceHost } from './staticLanguageServiceHost'; const buildfile = require('../../buildfile'); @@ -407,7 +407,7 @@ export class Mangler { ) { this.renameWorkerPool = workerpool.pool(path.join(__dirname, 'renameWorker.js'), { - maxWorkers: 1, + maxWorkers: 4, minWorkers: 'max' }); } diff --git a/build/lib/mangle/renameWorker.js b/build/lib/mangle/renameWorker.js index 6cd429b8c9ae..8bd59a4e2d55 100644 --- a/build/lib/mangle/renameWorker.js +++ b/build/lib/mangle/renameWorker.js @@ -3,20 +3,23 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const ts = require("typescript"); -const workerpool = require("workerpool"); +const typescript_1 = __importDefault(require("typescript")); +const workerpool_1 = __importDefault(require("workerpool")); const staticLanguageServiceHost_1 = require("./staticLanguageServiceHost"); let service; function findRenameLocations(projectPath, fileName, position) { if (!service) { - service = ts.createLanguageService(new staticLanguageServiceHost_1.StaticLanguageServiceHost(projectPath)); + service = typescript_1.default.createLanguageService(new staticLanguageServiceHost_1.StaticLanguageServiceHost(projectPath)); } return service.findRenameLocations(fileName, position, false, false, { providePrefixAndSuffixTextForRename: true, }) ?? []; } -workerpool.worker({ +workerpool_1.default.worker({ findRenameLocations }); //# sourceMappingURL=renameWorker.js.map \ No newline at end of file diff --git a/build/lib/mangle/renameWorker.ts b/build/lib/mangle/renameWorker.ts index 29b34e8c5147..0cce5677593c 100644 --- a/build/lib/mangle/renameWorker.ts +++ b/build/lib/mangle/renameWorker.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as ts from 'typescript'; -import * as workerpool from 'workerpool'; +import ts from 'typescript'; +import workerpool from 'workerpool'; import { StaticLanguageServiceHost } from './staticLanguageServiceHost'; let service: ts.LanguageService | undefined; diff --git a/build/lib/mangle/staticLanguageServiceHost.js b/build/lib/mangle/staticLanguageServiceHost.js index 1f338f0e61c5..7777888dd06a 100644 --- a/build/lib/mangle/staticLanguageServiceHost.js +++ b/build/lib/mangle/staticLanguageServiceHost.js @@ -3,10 +3,13 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.StaticLanguageServiceHost = void 0; -const ts = require("typescript"); -const path = require("path"); +const typescript_1 = __importDefault(require("typescript")); +const path_1 = __importDefault(require("path")); class StaticLanguageServiceHost { projectPath; _cmdLine; @@ -14,11 +17,11 @@ class StaticLanguageServiceHost { constructor(projectPath) { this.projectPath = projectPath; const existingOptions = {}; - const parsed = ts.readConfigFile(projectPath, ts.sys.readFile); + const parsed = typescript_1.default.readConfigFile(projectPath, typescript_1.default.sys.readFile); if (parsed.error) { throw parsed.error; } - this._cmdLine = ts.parseJsonConfigFileContent(parsed.config, ts.sys, path.dirname(projectPath), existingOptions); + this._cmdLine = typescript_1.default.parseJsonConfigFileContent(parsed.config, typescript_1.default.sys, path_1.default.dirname(projectPath), existingOptions); if (this._cmdLine.errors.length > 0) { throw parsed.error; } @@ -38,28 +41,28 @@ class StaticLanguageServiceHost { getScriptSnapshot(fileName) { let result = this._scriptSnapshots.get(fileName); if (result === undefined) { - const content = ts.sys.readFile(fileName); + const content = typescript_1.default.sys.readFile(fileName); if (content === undefined) { return undefined; } - result = ts.ScriptSnapshot.fromString(content); + result = typescript_1.default.ScriptSnapshot.fromString(content); this._scriptSnapshots.set(fileName, result); } return result; } getCurrentDirectory() { - return path.dirname(this.projectPath); + return path_1.default.dirname(this.projectPath); } getDefaultLibFileName(options) { - return ts.getDefaultLibFilePath(options); + return typescript_1.default.getDefaultLibFilePath(options); } - directoryExists = ts.sys.directoryExists; - getDirectories = ts.sys.getDirectories; - fileExists = ts.sys.fileExists; - readFile = ts.sys.readFile; - readDirectory = ts.sys.readDirectory; + directoryExists = typescript_1.default.sys.directoryExists; + getDirectories = typescript_1.default.sys.getDirectories; + fileExists = typescript_1.default.sys.fileExists; + readFile = typescript_1.default.sys.readFile; + readDirectory = typescript_1.default.sys.readDirectory; // this is necessary to make source references work. - realpath = ts.sys.realpath; + realpath = typescript_1.default.sys.realpath; } exports.StaticLanguageServiceHost = StaticLanguageServiceHost; //# sourceMappingURL=staticLanguageServiceHost.js.map \ No newline at end of file diff --git a/build/lib/mangle/staticLanguageServiceHost.ts b/build/lib/mangle/staticLanguageServiceHost.ts index c2793342ce34..b41b4e521336 100644 --- a/build/lib/mangle/staticLanguageServiceHost.ts +++ b/build/lib/mangle/staticLanguageServiceHost.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as ts from 'typescript'; -import * as path from 'path'; +import ts from 'typescript'; +import path from 'path'; export class StaticLanguageServiceHost implements ts.LanguageServiceHost { diff --git a/build/lib/monaco-api.js b/build/lib/monaco-api.js index 2052806c46bc..84cc556cb625 100644 --- a/build/lib/monaco-api.js +++ b/build/lib/monaco-api.js @@ -3,21 +3,24 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.DeclarationResolver = exports.FSProvider = exports.RECIPE_PATH = void 0; exports.run3 = run3; exports.execute = execute; -const fs = require("fs"); -const path = require("path"); -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const fancy_log_1 = __importDefault(require("fancy-log")); +const ansi_colors_1 = __importDefault(require("ansi-colors")); const dtsv = '3'; const tsfmt = require('../../tsfmt.json'); -const SRC = path.join(__dirname, '../../src'); -exports.RECIPE_PATH = path.join(__dirname, '../monaco/monaco.d.ts.recipe'); -const DECLARATION_PATH = path.join(__dirname, '../../src/vs/monaco.d.ts'); +const SRC = path_1.default.join(__dirname, '../../src'); +exports.RECIPE_PATH = path_1.default.join(__dirname, '../monaco/monaco.d.ts.recipe'); +const DECLARATION_PATH = path_1.default.join(__dirname, '../../src/vs/monaco.d.ts'); function logErr(message, ...rest) { - fancyLog(ansiColors.yellow(`[monaco.d.ts]`), message, ...rest); + (0, fancy_log_1.default)(ansi_colors_1.default.yellow(`[monaco.d.ts]`), message, ...rest); } function isDeclaration(ts, a) { return (a.kind === ts.SyntaxKind.InterfaceDeclaration @@ -464,7 +467,7 @@ function generateDeclarationFile(ts, recipe, sourceFileGetter) { }; } function _run(ts, sourceFileGetter) { - const recipe = fs.readFileSync(exports.RECIPE_PATH).toString(); + const recipe = fs_1.default.readFileSync(exports.RECIPE_PATH).toString(); const t = generateDeclarationFile(ts, recipe, sourceFileGetter); if (!t) { return null; @@ -472,7 +475,7 @@ function _run(ts, sourceFileGetter) { const result = t.result; const usageContent = t.usageContent; const enums = t.enums; - const currentContent = fs.readFileSync(DECLARATION_PATH).toString(); + const currentContent = fs_1.default.readFileSync(DECLARATION_PATH).toString(); const one = currentContent.replace(/\r\n/gm, '\n'); const other = result.replace(/\r\n/gm, '\n'); const isTheSame = (one === other); @@ -486,13 +489,13 @@ function _run(ts, sourceFileGetter) { } class FSProvider { existsSync(filePath) { - return fs.existsSync(filePath); + return fs_1.default.existsSync(filePath); } statSync(filePath) { - return fs.statSync(filePath); + return fs_1.default.statSync(filePath); } readFileSync(_moduleId, filePath) { - return fs.readFileSync(filePath); + return fs_1.default.readFileSync(filePath); } } exports.FSProvider = FSProvider; @@ -532,9 +535,9 @@ class DeclarationResolver { } _getFileName(moduleId) { if (/\.d\.ts$/.test(moduleId)) { - return path.join(SRC, moduleId); + return path_1.default.join(SRC, moduleId); } - return path.join(SRC, `${moduleId}.ts`); + return path_1.default.join(SRC, `${moduleId}.ts`); } _getDeclarationSourceFile(moduleId) { const fileName = this._getFileName(moduleId); diff --git a/build/lib/monaco-api.ts b/build/lib/monaco-api.ts index 288bec0f858f..5dc9a04266c5 100644 --- a/build/lib/monaco-api.ts +++ b/build/lib/monaco-api.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; +import fs from 'fs'; import type * as ts from 'typescript'; -import * as path from 'path'; -import * as fancyLog from 'fancy-log'; -import * as ansiColors from 'ansi-colors'; +import path from 'path'; +import fancyLog from 'fancy-log'; +import ansiColors from 'ansi-colors'; const dtsv = '3'; diff --git a/build/lib/nls.js b/build/lib/nls.js index 6ddcd46167a9..12e60a36ec99 100644 --- a/build/lib/nls.js +++ b/build/lib/nls.js @@ -3,14 +3,17 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.nls = nls; -const lazy = require("lazy.js"); +const lazy_js_1 = __importDefault(require("lazy.js")); const event_stream_1 = require("event-stream"); -const File = require("vinyl"); -const sm = require("source-map"); -const path = require("path"); -const sort = require("gulp-sort"); +const vinyl_1 = __importDefault(require("vinyl")); +const source_map_1 = __importDefault(require("source-map")); +const path_1 = __importDefault(require("path")); +const gulp_sort_1 = __importDefault(require("gulp-sort")); var CollectStepResult; (function (CollectStepResult) { CollectStepResult[CollectStepResult["Yes"] = 0] = "Yes"; @@ -46,7 +49,7 @@ function nls(options) { let base; const input = (0, event_stream_1.through)(); const output = input - .pipe(sort()) // IMPORTANT: to ensure stable NLS metadata generation, we must sort the files because NLS messages are globally extracted and indexed across all files + .pipe((0, gulp_sort_1.default)()) // IMPORTANT: to ensure stable NLS metadata generation, we must sort the files because NLS messages are globally extracted and indexed across all files .pipe((0, event_stream_1.through)(function (f) { if (!f.sourceMap) { return this.emit('error', new Error(`File ${f.relative} does not have sourcemaps.`)); @@ -57,7 +60,7 @@ function nls(options) { } const root = f.sourceMap.sourceRoot; if (root) { - source = path.join(root, source); + source = path_1.default.join(root, source); } const typescript = f.sourceMap.sourcesContent[0]; if (!typescript) { @@ -67,7 +70,7 @@ function nls(options) { this.emit('data', _nls.patchFile(f, typescript, options)); }, function () { for (const file of [ - new File({ + new vinyl_1.default({ contents: Buffer.from(JSON.stringify({ keys: _nls.moduleToNLSKeys, messages: _nls.moduleToNLSMessages, @@ -75,17 +78,17 @@ function nls(options) { base, path: `${base}/nls.metadata.json` }), - new File({ + new vinyl_1.default({ contents: Buffer.from(JSON.stringify(_nls.allNLSMessages)), base, path: `${base}/nls.messages.json` }), - new File({ + new vinyl_1.default({ contents: Buffer.from(JSON.stringify(_nls.allNLSModulesAndKeys)), base, path: `${base}/nls.keys.json` }), - new File({ + new vinyl_1.default({ contents: Buffer.from(`/*--------------------------------------------------------- * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ @@ -111,7 +114,7 @@ var _nls; _nls.allNLSModulesAndKeys = []; let allNLSMessagesIndex = 0; function fileFrom(file, contents, path = file.path) { - return new File({ + return new vinyl_1.default({ contents: Buffer.from(contents), base: file.base, cwd: file.cwd, @@ -163,7 +166,7 @@ var _nls; const service = ts.createLanguageService(serviceHost); const sourceFile = ts.createSourceFile(filename, contents, ts.ScriptTarget.ES5, true); // all imports - const imports = lazy(collect(ts, sourceFile, n => isImportNode(ts, n) ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse)); + const imports = (0, lazy_js_1.default)(collect(ts, sourceFile, n => isImportNode(ts, n) ? CollectStepResult.YesAndRecurse : CollectStepResult.NoAndRecurse)); // import nls = require('vs/nls'); const importEqualsDeclarations = imports .filter(n => n.kind === ts.SyntaxKind.ImportEqualsDeclaration) @@ -188,7 +191,7 @@ var _nls; .filter(r => !r.isWriteAccess) // find the deepest call expressions AST nodes that contain those references .map(r => collect(ts, sourceFile, n => isCallExpressionWithinTextSpanCollectStep(ts, r.textSpan, n))) - .map(a => lazy(a).last()) + .map(a => (0, lazy_js_1.default)(a).last()) .filter(n => !!n) .map(n => n) // only `localize` calls @@ -214,7 +217,7 @@ var _nls; const localizeCallExpressions = localizeReferences .concat(namedLocalizeReferences) .map(r => collect(ts, sourceFile, n => isCallExpressionWithinTextSpanCollectStep(ts, r.textSpan, n))) - .map(a => lazy(a).last()) + .map(a => (0, lazy_js_1.default)(a).last()) .filter(n => !!n) .map(n => n); // collect everything @@ -281,18 +284,18 @@ var _nls; } } toString() { - return lazy(this.lines).zip(this.lineEndings) + return (0, lazy_js_1.default)(this.lines).zip(this.lineEndings) .flatten().toArray().join(''); } } function patchJavascript(patches, contents) { const model = new TextModel(contents); // patch the localize calls - lazy(patches).reverse().each(p => model.apply(p)); + (0, lazy_js_1.default)(patches).reverse().each(p => model.apply(p)); return model.toString(); } function patchSourcemap(patches, rsm, smc) { - const smg = new sm.SourceMapGenerator({ + const smg = new source_map_1.default.SourceMapGenerator({ file: rsm.file, sourceRoot: rsm.sourceRoot }); @@ -317,10 +320,10 @@ var _nls; generated.column += lengthDiff; patches.pop(); } - source = rsm.sourceRoot ? path.relative(rsm.sourceRoot, m.source) : m.source; + source = rsm.sourceRoot ? path_1.default.relative(rsm.sourceRoot, m.source) : m.source; source = source.replace(/\\/g, '/'); smg.addMapping({ source, name: m.name, original, generated }); - }, null, sm.SourceMapConsumer.GENERATED_ORDER); + }, null, source_map_1.default.SourceMapConsumer.GENERATED_ORDER); if (source) { smg.setSourceContent(source, smc.sourceContentFor(source)); } @@ -341,7 +344,7 @@ var _nls; } const nlsKeys = localizeCalls.map(lc => parseLocalizeKeyOrValue(lc.key)).concat(localize2Calls.map(lc => parseLocalizeKeyOrValue(lc.key))); const nlsMessages = localizeCalls.map(lc => parseLocalizeKeyOrValue(lc.value)).concat(localize2Calls.map(lc => parseLocalizeKeyOrValue(lc.value))); - const smc = new sm.SourceMapConsumer(sourcemap); + const smc = new source_map_1.default.SourceMapConsumer(sourcemap); const positionFrom = mappedPositionFrom.bind(null, sourcemap.sources[0]); // build patches const toPatch = (c) => { @@ -349,7 +352,7 @@ var _nls; const end = lcFrom(smc.generatedPositionFor(positionFrom(c.range.end))); return { span: { start, end }, content: c.content }; }; - const localizePatches = lazy(localizeCalls) + const localizePatches = (0, lazy_js_1.default)(localizeCalls) .map(lc => (options.preserveEnglish ? [ { range: lc.keySpan, content: `${allNLSMessagesIndex++}` } // localize('key', "message") => localize(, "message") ] : [ @@ -358,7 +361,7 @@ var _nls; ])) .flatten() .map(toPatch); - const localize2Patches = lazy(localize2Calls) + const localize2Patches = (0, lazy_js_1.default)(localize2Calls) .map(lc => ({ range: lc.keySpan, content: `${allNLSMessagesIndex++}` } // localize2('key', "message") => localize(, "message") )) .map(toPatch); diff --git a/build/lib/nls.ts b/build/lib/nls.ts index cac832903a3b..ef2afc5d7c8a 100644 --- a/build/lib/nls.ts +++ b/build/lib/nls.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import type * as ts from 'typescript'; -import * as lazy from 'lazy.js'; +import lazy from 'lazy.js'; import { duplex, through } from 'event-stream'; -import * as File from 'vinyl'; -import * as sm from 'source-map'; -import * as path from 'path'; -import * as sort from 'gulp-sort'; +import File from 'vinyl'; +import sm from 'source-map'; +import path from 'path'; +import sort from 'gulp-sort'; declare class FileSourceMap extends File { public sourceMap: sm.RawSourceMap; diff --git a/build/lib/node.js b/build/lib/node.js index 74a54a3c1708..01a381183ff5 100644 --- a/build/lib/node.js +++ b/build/lib/node.js @@ -3,16 +3,19 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const path = require("path"); -const fs = require("fs"); -const root = path.dirname(path.dirname(__dirname)); -const npmrcPath = path.join(root, 'remote', '.npmrc'); -const npmrc = fs.readFileSync(npmrcPath, 'utf8'); +const path_1 = __importDefault(require("path")); +const fs_1 = __importDefault(require("fs")); +const root = path_1.default.dirname(path_1.default.dirname(__dirname)); +const npmrcPath = path_1.default.join(root, 'remote', '.npmrc'); +const npmrc = fs_1.default.readFileSync(npmrcPath, 'utf8'); const version = /^target="(.*)"$/m.exec(npmrc)[1]; const platform = process.platform; const arch = process.arch; const node = platform === 'win32' ? 'node.exe' : 'node'; -const nodePath = path.join(root, '.build', 'node', `v${version}`, `${platform}-${arch}`, node); +const nodePath = path_1.default.join(root, '.build', 'node', `v${version}`, `${platform}-${arch}`, node); console.log(nodePath); //# sourceMappingURL=node.js.map \ No newline at end of file diff --git a/build/lib/node.ts b/build/lib/node.ts index 4beb13ae91bf..a2fdc361aa15 100644 --- a/build/lib/node.ts +++ b/build/lib/node.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; -import * as fs from 'fs'; +import path from 'path'; +import fs from 'fs'; const root = path.dirname(path.dirname(__dirname)); const npmrcPath = path.join(root, 'remote', '.npmrc'); diff --git a/build/lib/optimize.js b/build/lib/optimize.js index 478390c42288..d75a29786976 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -3,34 +3,70 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.bundleTask = bundleTask; exports.minifyTask = minifyTask; -const es = require("event-stream"); -const gulp = require("gulp"); -const filter = require("gulp-filter"); -const path = require("path"); -const fs = require("fs"); -const pump = require("pump"); -const VinylFile = require("vinyl"); -const bundle = require("./bundle"); +const event_stream_1 = __importDefault(require("event-stream")); +const gulp_1 = __importDefault(require("gulp")); +const gulp_filter_1 = __importDefault(require("gulp-filter")); +const path_1 = __importDefault(require("path")); +const fs_1 = __importDefault(require("fs")); +const pump_1 = __importDefault(require("pump")); +const vinyl_1 = __importDefault(require("vinyl")); +const bundle = __importStar(require("./bundle")); const postcss_1 = require("./postcss"); -const esbuild = require("esbuild"); -const sourcemaps = require("gulp-sourcemaps"); -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); -const REPO_ROOT_PATH = path.join(__dirname, '../..'); +const esbuild_1 = __importDefault(require("esbuild")); +const gulp_sourcemaps_1 = __importDefault(require("gulp-sourcemaps")); +const fancy_log_1 = __importDefault(require("fancy-log")); +const ansi_colors_1 = __importDefault(require("ansi-colors")); +const REPO_ROOT_PATH = path_1.default.join(__dirname, '../..'); const DEFAULT_FILE_HEADER = [ '/*!--------------------------------------------------------', ' * Copyright (C) Microsoft Corporation. All rights reserved.', ' *--------------------------------------------------------*/' ].join('\n'); function bundleESMTask(opts) { - const resourcesStream = es.through(); // this stream will contain the resources - const bundlesStream = es.through(); // this stream will contain the bundled files + const resourcesStream = event_stream_1.default.through(); // this stream will contain the resources + const bundlesStream = event_stream_1.default.through(); // this stream will contain the bundled files const entryPoints = opts.entryPoints.map(entryPoint => { if (typeof entryPoint === 'string') { - return { name: path.parse(entryPoint).name }; + return { name: path_1.default.parse(entryPoint).name }; } return entryPoint; }); @@ -44,7 +80,7 @@ function bundleESMTask(opts) { const files = []; const tasks = []; for (const entryPoint of entryPoints) { - fancyLog(`Bundled entry point: ${ansiColors.yellow(entryPoint.name)}...`); + (0, fancy_log_1.default)(`Bundled entry point: ${ansi_colors_1.default.yellow(entryPoint.name)}...`); // support for 'dest' via esbuild#in/out const dest = entryPoint.dest?.replace(/\.[^/.]+$/, '') ?? entryPoint.name; // banner contents @@ -54,14 +90,14 @@ function bundleESMTask(opts) { }; // TS Boilerplate if (!opts.skipTSBoilerplateRemoval?.(entryPoint.name)) { - const tslibPath = path.join(require.resolve('tslib'), '../tslib.es6.js'); - banner.js += await fs.promises.readFile(tslibPath, 'utf-8'); + const tslibPath = path_1.default.join(require.resolve('tslib'), '../tslib.es6.js'); + banner.js += await fs_1.default.promises.readFile(tslibPath, 'utf-8'); } const contentsMapper = { name: 'contents-mapper', setup(build) { build.onLoad({ filter: /\.js$/ }, async ({ path }) => { - const contents = await fs.promises.readFile(path, 'utf-8'); + const contents = await fs_1.default.promises.readFile(path, 'utf-8'); // TS Boilerplate let newContents; if (!opts.skipTSBoilerplateRemoval?.(entryPoint.name)) { @@ -71,7 +107,7 @@ function bundleESMTask(opts) { newContents = contents; } // File Content Mapper - const mapper = opts.fileContentMapper?.(path); + const mapper = opts.fileContentMapper?.(path.replace(/\\/g, '/')); if (mapper) { newContents = await mapper(newContents); } @@ -85,11 +121,11 @@ function bundleESMTask(opts) { // We inline selected modules that are we depend on on startup without // a conditional `await import(...)` by hooking into the resolution. build.onResolve({ filter: /^minimist$/ }, () => { - return { path: path.join(REPO_ROOT_PATH, 'node_modules', 'minimist', 'index.js'), external: false }; + return { path: path_1.default.join(REPO_ROOT_PATH, 'node_modules', 'minimist', 'index.js'), external: false }; }); }, }; - const task = esbuild.build({ + const task = esbuild_1.default.build({ bundle: true, external: entryPoint.exclude, packages: 'external', // "external all the things", see https://esbuild.github.io/api/#packages @@ -108,11 +144,11 @@ function bundleESMTask(opts) { banner: entryPoint.name === 'vs/workbench/workbench.web.main' ? undefined : banner, // TODO@esm remove line when we stop supporting web-amd-esm-bridge entryPoints: [ { - in: path.join(REPO_ROOT_PATH, opts.src, `${entryPoint.name}.js`), + in: path_1.default.join(REPO_ROOT_PATH, opts.src, `${entryPoint.name}.js`), out: dest, } ], - outdir: path.join(REPO_ROOT_PATH, opts.src), + outdir: path_1.default.join(REPO_ROOT_PATH, opts.src), write: false, // enables res.outputFiles metafile: true, // enables res.metafile // minify: NOT enabled because we have a separate minify task that takes care of the TSLib banner as well @@ -126,9 +162,9 @@ function bundleESMTask(opts) { contents: Buffer.from(file.contents), sourceMap: sourceMapFile ? JSON.parse(sourceMapFile.text) : undefined, // support gulp-sourcemaps path: file.path, - base: path.join(REPO_ROOT_PATH, opts.src) + base: path_1.default.join(REPO_ROOT_PATH, opts.src) }; - files.push(new VinylFile(fileProps)); + files.push(new vinyl_1.default(fileProps)); } }); tasks.push(task); @@ -138,13 +174,13 @@ function bundleESMTask(opts) { }; bundleAsync().then((output) => { // bundle output (JS, CSS, SVG...) - es.readArray(output.files).pipe(bundlesStream); + event_stream_1.default.readArray(output.files).pipe(bundlesStream); // forward all resources - gulp.src(opts.resources ?? [], { base: `${opts.src}`, allowEmpty: true }).pipe(resourcesStream); + gulp_1.default.src(opts.resources ?? [], { base: `${opts.src}`, allowEmpty: true }).pipe(resourcesStream); }); - const result = es.merge(bundlesStream, resourcesStream); + const result = event_stream_1.default.merge(bundlesStream, resourcesStream); return result - .pipe(sourcemaps.write('./', { + .pipe(gulp_sourcemaps_1.default.write('./', { sourceRoot: undefined, addComment: true, includeContent: true @@ -152,7 +188,7 @@ function bundleESMTask(opts) { } function bundleTask(opts) { return function () { - return bundleESMTask(opts.esm).pipe(gulp.dest(opts.out)); + return bundleESMTask(opts.esm).pipe(gulp_1.default.dest(opts.out)); }; } function minifyTask(src, sourceMapBaseUrl) { @@ -160,11 +196,11 @@ function minifyTask(src, sourceMapBaseUrl) { return cb => { const cssnano = require('cssnano'); const svgmin = require('gulp-svgmin'); - const jsFilter = filter('**/*.js', { restore: true }); - const cssFilter = filter('**/*.css', { restore: true }); - const svgFilter = filter('**/*.svg', { restore: true }); - pump(gulp.src([src + '/**', '!' + src + '/**/*.map']), jsFilter, sourcemaps.init({ loadMaps: true }), es.map((f, cb) => { - esbuild.build({ + const jsFilter = (0, gulp_filter_1.default)('**/*.js', { restore: true }); + const cssFilter = (0, gulp_filter_1.default)('**/*.css', { restore: true }); + const svgFilter = (0, gulp_filter_1.default)('**/*.svg', { restore: true }); + (0, pump_1.default)(gulp_1.default.src([src + '/**', '!' + src + '/**/*.map']), jsFilter, gulp_sourcemaps_1.default.init({ loadMaps: true }), event_stream_1.default.map((f, cb) => { + esbuild_1.default.build({ entryPoints: [f.path], minify: true, sourcemap: 'external', @@ -187,12 +223,12 @@ function minifyTask(src, sourceMapBaseUrl) { cb(undefined, f); } }, cb); - }), jsFilter.restore, cssFilter, (0, postcss_1.gulpPostcss)([cssnano({ preset: 'default' })]), cssFilter.restore, svgFilter, svgmin(), svgFilter.restore, sourcemaps.write('./', { + }), jsFilter.restore, cssFilter, (0, postcss_1.gulpPostcss)([cssnano({ preset: 'default' })]), cssFilter.restore, svgFilter, svgmin(), svgFilter.restore, gulp_sourcemaps_1.default.write('./', { sourceMappingURL, sourceRoot: undefined, includeContent: true, addComment: true - }), gulp.dest(src + '-min'), (err) => cb(err)); + }), gulp_1.default.dest(src + '-min'), (err) => cb(err)); }; } //# sourceMappingURL=optimize.js.map \ No newline at end of file diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index 40efe87d6749..e12c85c3fa04 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -3,19 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as es from 'event-stream'; -import * as gulp from 'gulp'; -import * as filter from 'gulp-filter'; -import * as path from 'path'; -import * as fs from 'fs'; -import * as pump from 'pump'; -import * as VinylFile from 'vinyl'; +import es from 'event-stream'; +import gulp from 'gulp'; +import filter from 'gulp-filter'; +import path from 'path'; +import fs from 'fs'; +import pump from 'pump'; +import VinylFile from 'vinyl'; import * as bundle from './bundle'; import { gulpPostcss } from './postcss'; -import * as esbuild from 'esbuild'; -import * as sourcemaps from 'gulp-sourcemaps'; -import * as fancyLog from 'fancy-log'; -import * as ansiColors from 'ansi-colors'; +import esbuild from 'esbuild'; +import sourcemaps from 'gulp-sourcemaps'; +import fancyLog from 'fancy-log'; +import ansiColors from 'ansi-colors'; const REPO_ROOT_PATH = path.join(__dirname, '../..'); @@ -106,7 +106,7 @@ function bundleESMTask(opts: IBundleESMTaskOpts): NodeJS.ReadWriteStream { } // File Content Mapper - const mapper = opts.fileContentMapper?.(path); + const mapper = opts.fileContentMapper?.(path.replace(/\\/g, '/')); if (mapper) { newContents = await mapper(newContents); } diff --git a/build/lib/policies.js b/build/lib/policies.js index 466295b8ad54..bb7e45c1873f 100644 --- a/build/lib/policies.js +++ b/build/lib/policies.js @@ -3,13 +3,16 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); const child_process_1 = require("child_process"); const fs_1 = require("fs"); -const path = require("path"); -const byline = require("byline"); +const path_1 = __importDefault(require("path")); +const byline_1 = __importDefault(require("byline")); const ripgrep_1 = require("@vscode/ripgrep"); -const Parser = require("tree-sitter"); +const tree_sitter_1 = __importDefault(require("tree-sitter")); const { typescript } = require('tree-sitter-typescript'); const product = require('../../product.json'); const packageJson = require('../../package.json'); @@ -24,7 +27,11 @@ function isNlsStringArray(value) { } var PolicyType; (function (PolicyType) { - PolicyType[PolicyType["StringEnum"] = 0] = "StringEnum"; + PolicyType["Boolean"] = "boolean"; + PolicyType["Number"] = "number"; + PolicyType["Object"] = "object"; + PolicyType["String"] = "string"; + PolicyType["StringEnum"] = "stringEnum"; })(PolicyType || (PolicyType = {})); function renderADMLString(prefix, moduleName, nlsString, translations) { let value; @@ -37,17 +44,30 @@ function renderADMLString(prefix, moduleName, nlsString, translations) { if (!value) { value = nlsString.value; } - return `${value}`; + return `${value}`; +} +function renderProfileString(_prefix, moduleName, nlsString, translations) { + let value; + if (translations) { + const moduleTranslations = translations[moduleName]; + if (moduleTranslations) { + value = moduleTranslations[nlsString.nlsKey]; + } + } + if (!value) { + value = nlsString.value; + } + return value; } class BasePolicy { - policyType; + type; name; category; minimumVersion; description; moduleName; - constructor(policyType, name, category, minimumVersion, description, moduleName) { - this.policyType = policyType; + constructor(type, name, category, minimumVersion, description, moduleName) { + this.type = type; this.name = name; this.category = category; this.minimumVersion = minimumVersion; @@ -59,7 +79,7 @@ class BasePolicy { } renderADMX(regKey) { return [ - ``, + ``, ` `, ` `, ` `, @@ -77,17 +97,25 @@ class BasePolicy { renderADMLPresentation() { return `${this.renderADMLPresentationContents()}`; } + renderProfile() { + return [`${this.name}`, this.renderProfileValue()]; + } + renderProfileManifest(translations) { + return ` +${this.renderProfileManifestValue(translations)} +`; + } } class BooleanPolicy extends BasePolicy { static from(name, category, minimumVersion, description, moduleName, settingNode) { - const type = getStringProperty(settingNode, 'type'); + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'boolean') { return undefined; } return new BooleanPolicy(name, category, minimumVersion, description, moduleName); } constructor(name, category, minimumVersion, description, moduleName) { - super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); + super(PolicyType.Boolean, name, category, minimumVersion, description, moduleName); } renderADMXElements() { return [ @@ -99,19 +127,39 @@ class BooleanPolicy extends BasePolicy { renderADMLPresentationContents() { return `${this.name}`; } + renderProfileValue() { + return ``; + } + renderProfileManifestValue(translations) { + return `pfm_default + +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +boolean`; + } } -class IntPolicy extends BasePolicy { +class ParseError extends Error { + constructor(message, moduleName, node) { + super(`${message}. ${moduleName}.ts:${node.startPosition.row + 1}`); + } +} +class NumberPolicy extends BasePolicy { defaultValue; static from(name, category, minimumVersion, description, moduleName, settingNode) { - const type = getStringProperty(settingNode, 'type'); + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'number') { return undefined; } - const defaultValue = getIntProperty(settingNode, 'default'); + const defaultValue = getNumberProperty(moduleName, settingNode, 'default'); if (typeof defaultValue === 'undefined') { - throw new Error(`Missing required 'default' property.`); + throw new ParseError(`Missing required 'default' property.`, moduleName, settingNode); } - return new IntPolicy(name, category, minimumVersion, description, moduleName, defaultValue); + return new NumberPolicy(name, category, minimumVersion, description, moduleName, defaultValue); } constructor(name, category, minimumVersion, description, moduleName, defaultValue) { super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); @@ -126,17 +174,32 @@ class IntPolicy extends BasePolicy { renderADMLPresentationContents() { return `${this.name}`; } + renderProfileValue() { + return `${this.defaultValue}`; + } + renderProfileManifestValue(translations) { + return `pfm_default +${this.defaultValue} +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +integer`; + } } class StringPolicy extends BasePolicy { static from(name, category, minimumVersion, description, moduleName, settingNode) { - const type = getStringProperty(settingNode, 'type'); + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'string') { return undefined; } return new StringPolicy(name, category, minimumVersion, description, moduleName); } constructor(name, category, minimumVersion, description, moduleName) { - super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); + super(PolicyType.String, name, category, minimumVersion, description, moduleName); } renderADMXElements() { return [``]; @@ -144,28 +207,77 @@ class StringPolicy extends BasePolicy { renderADMLPresentationContents() { return ``; } + renderProfileValue() { + return ``; + } + renderProfileManifestValue(translations) { + return `pfm_default + +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +string`; + } +} +class ObjectPolicy extends BasePolicy { + static from(name, category, minimumVersion, description, moduleName, settingNode) { + const type = getStringProperty(moduleName, settingNode, 'type'); + if (type !== 'object' && type !== 'array') { + return undefined; + } + return new ObjectPolicy(name, category, minimumVersion, description, moduleName); + } + constructor(name, category, minimumVersion, description, moduleName) { + super(PolicyType.Object, name, category, minimumVersion, description, moduleName); + } + renderADMXElements() { + return [``]; + } + renderADMLPresentationContents() { + return ``; + } + renderProfileValue() { + return ``; + } + renderProfileManifestValue(translations) { + return `pfm_default + +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +string +`; + } } class StringEnumPolicy extends BasePolicy { enum_; enumDescriptions; static from(name, category, minimumVersion, description, moduleName, settingNode) { - const type = getStringProperty(settingNode, 'type'); + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'string') { return undefined; } - const enum_ = getStringArrayProperty(settingNode, 'enum'); + const enum_ = getStringArrayProperty(moduleName, settingNode, 'enum'); if (!enum_) { return undefined; } if (!isStringArray(enum_)) { - throw new Error(`Property 'enum' should not be localized.`); + throw new ParseError(`Property 'enum' should not be localized.`, moduleName, settingNode); } - const enumDescriptions = getStringArrayProperty(settingNode, 'enumDescriptions'); + const enumDescriptions = getStringArrayProperty(moduleName, settingNode, 'enumDescriptions'); if (!enumDescriptions) { - throw new Error(`Missing required 'enumDescriptions' property.`); + throw new ParseError(`Missing required 'enumDescriptions' property.`, moduleName, settingNode); } else if (!isNlsStringArray(enumDescriptions)) { - throw new Error(`Property 'enumDescriptions' should be localized.`); + throw new ParseError(`Property 'enumDescriptions' should be localized.`, moduleName, settingNode); } return new StringEnumPolicy(name, category, minimumVersion, description, moduleName, enum_, enumDescriptions); } @@ -190,8 +302,27 @@ class StringEnumPolicy extends BasePolicy { renderADMLPresentationContents() { return ``; } + renderProfileValue() { + return `${this.enum_[0]}`; + } + renderProfileManifestValue(translations) { + return `pfm_default +${this.enum_[0]} +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +string +pfm_range_list + + ${this.enum_.map(e => `${e}`).join('\n ')} +`; + } } -const IntQ = { +const NumberQ = { Q: `(number) @value`, value(matches) { const match = matches[0]; @@ -239,46 +370,52 @@ const StringArrayQ = { }); } }; -function getProperty(qtype, node, key) { - const query = new Parser.Query(typescript, `( +function getProperty(qtype, moduleName, node, key) { + const query = new tree_sitter_1.default.Query(typescript, `( (pair key: [(property_identifier)(string)] @key value: ${qtype.Q} ) - (#eq? @key ${key}) + (#any-of? @key "${key}" "'${key}'") )`); - return qtype.value(query.matches(node)); + try { + return qtype.value(query.matches(node)); + } + catch (e) { + throw new ParseError(e.message, moduleName, node); + } } -function getIntProperty(node, key) { - return getProperty(IntQ, node, key); +function getNumberProperty(moduleName, node, key) { + return getProperty(NumberQ, moduleName, node, key); } -function getStringProperty(node, key) { - return getProperty(StringQ, node, key); +function getStringProperty(moduleName, node, key) { + return getProperty(StringQ, moduleName, node, key); } -function getStringArrayProperty(node, key) { - return getProperty(StringArrayQ, node, key); +function getStringArrayProperty(moduleName, node, key) { + return getProperty(StringArrayQ, moduleName, node, key); } // TODO: add more policy types const PolicyTypes = [ BooleanPolicy, - IntPolicy, + NumberPolicy, StringEnumPolicy, StringPolicy, + ObjectPolicy ]; function getPolicy(moduleName, configurationNode, settingNode, policyNode, categories) { - const name = getStringProperty(policyNode, 'name'); + const name = getStringProperty(moduleName, policyNode, 'name'); if (!name) { - throw new Error(`Missing required 'name' property.`); + throw new ParseError(`Missing required 'name' property`, moduleName, policyNode); } else if (isNlsString(name)) { - throw new Error(`Property 'name' should be a literal string.`); + throw new ParseError(`Property 'name' should be a literal string`, moduleName, policyNode); } - const categoryName = getStringProperty(configurationNode, 'title'); + const categoryName = getStringProperty(moduleName, configurationNode, 'title'); if (!categoryName) { - throw new Error(`Missing required 'title' property.`); + throw new ParseError(`Missing required 'title' property`, moduleName, configurationNode); } else if (!isNlsString(categoryName)) { - throw new Error(`Property 'title' should be localized.`); + throw new ParseError(`Property 'title' should be localized`, moduleName, configurationNode); } const categoryKey = `${categoryName.nlsKey}:${categoryName.value}`; let category = categories.get(categoryKey); @@ -286,19 +423,19 @@ function getPolicy(moduleName, configurationNode, settingNode, policyNode, categ category = { moduleName, name: categoryName }; categories.set(categoryKey, category); } - const minimumVersion = getStringProperty(policyNode, 'minimumVersion'); + const minimumVersion = getStringProperty(moduleName, policyNode, 'minimumVersion'); if (!minimumVersion) { - throw new Error(`Missing required 'minimumVersion' property.`); + throw new ParseError(`Missing required 'minimumVersion' property.`, moduleName, policyNode); } else if (isNlsString(minimumVersion)) { - throw new Error(`Property 'minimumVersion' should be a literal string.`); + throw new ParseError(`Property 'minimumVersion' should be a literal string.`, moduleName, policyNode); } - const description = getStringProperty(settingNode, 'description'); + const description = getStringProperty(moduleName, policyNode, 'description') ?? getStringProperty(moduleName, settingNode, 'description'); if (!description) { - throw new Error(`Missing required 'description' property.`); + throw new ParseError(`Missing required 'description' property.`, moduleName, settingNode); } if (!isNlsString(description)) { - throw new Error(`Property 'description' should be localized.`); + throw new ParseError(`Property 'description' should be localized.`, moduleName, settingNode); } let result; for (const policyType of PolicyTypes) { @@ -307,21 +444,21 @@ function getPolicy(moduleName, configurationNode, settingNode, policyNode, categ } } if (!result) { - throw new Error(`Failed to parse policy '${name}'.`); + throw new ParseError(`Failed to parse policy '${name}'.`, moduleName, settingNode); } return result; } function getPolicies(moduleName, node) { - const query = new Parser.Query(typescript, ` + const query = new tree_sitter_1.default.Query(typescript, ` ( (call_expression function: (member_expression property: (property_identifier) @registerConfigurationFn) (#eq? @registerConfigurationFn registerConfiguration) arguments: (arguments (object (pair - key: [(property_identifier)(string)] @propertiesKey (#eq? @propertiesKey properties) + key: [(property_identifier)(string)] @propertiesKey (#any-of? @propertiesKey "properties" "'properties'") value: (object (pair - key: [(property_identifier)(string)] + key: [(property_identifier)(string)(computed_property_name)] value: (object (pair - key: [(property_identifier)(string)] @policyKey (#eq? @policyKey policy) + key: [(property_identifier)(string)] @policyKey (#any-of? @policyKey "policy" "'policy'") value: (object) @policy )) @setting )) @@ -341,7 +478,7 @@ async function getFiles(root) { return new Promise((c, e) => { const result = []; const rg = (0, child_process_1.spawn)(ripgrep_1.rgPath, ['-l', 'registerConfiguration\\(', '-g', 'src/**/*.ts', '-g', '!src/**/test/**', root]); - const stream = byline(rg.stdout.setEncoding('utf8')); + const stream = (0, byline_1.default)(rg.stdout.setEncoding('utf8')); stream.on('data', path => result.push(path)); stream.on('error', err => e(err)); stream.on('end', () => c(result)); @@ -389,6 +526,186 @@ function renderADML(appName, versions, categories, policies, translations) { `; } +function renderProfileManifest(appName, bundleIdentifier, _versions, _categories, policies, translations) { + const requiredPayloadFields = ` + + pfm_default + Configure ${appName} + pfm_name + PayloadDescription + pfm_title + Payload Description + pfm_type + string + + + pfm_default + ${appName} + pfm_name + PayloadDisplayName + pfm_require + always + pfm_title + Payload Display Name + pfm_type + string + + + pfm_default + ${bundleIdentifier} + pfm_name + PayloadIdentifier + pfm_require + always + pfm_title + Payload Identifier + pfm_type + string + + + pfm_default + ${bundleIdentifier} + pfm_name + PayloadType + pfm_require + always + pfm_title + Payload Type + pfm_type + string + + + pfm_default + + pfm_name + PayloadUUID + pfm_require + always + pfm_title + Payload UUID + pfm_type + string + + + pfm_default + 1 + pfm_name + PayloadVersion + pfm_range_list + + 1 + + pfm_require + always + pfm_title + Payload Version + pfm_type + integer + + + pfm_default + Microsoft + pfm_name + PayloadOrganization + pfm_title + Payload Organization + pfm_type + string + `; + const profileManifestSubkeys = policies.map(policy => { + return policy.renderProfileManifest(translations); + }).join(''); + return ` + + + + pfm_app_url + https://code.visualstudio.com/ + pfm_description + ${appName} Managed Settings + pfm_documentation_url + https://code.visualstudio.com/docs/setup/enterprise + pfm_domain + ${bundleIdentifier} + pfm_format_version + 1 + pfm_interaction + combined + pfm_last_modified + ${new Date().toISOString().replace(/\.\d+Z$/, 'Z')} + pfm_platforms + + macOS + + pfm_subkeys + + ${requiredPayloadFields} + ${profileManifestSubkeys} + + pfm_title + ${appName} + pfm_unique + + pfm_version + 1 + +`; +} +function renderMacOSPolicy(policies, translations) { + const appName = product.nameLong; + const bundleIdentifier = product.darwinBundleIdentifier; + const payloadUUID = product.darwinProfilePayloadUUID; + const UUID = product.darwinProfileUUID; + const versions = [...new Set(policies.map(p => p.minimumVersion)).values()].sort(); + const categories = [...new Set(policies.map(p => p.category))]; + const policyEntries = policies.map(policy => policy.renderProfile()) + .flat() + .map(entry => `\t\t\t\t${entry}`) + .join('\n'); + return { + profile: ` + + + + PayloadContent + + + PayloadDisplayName + ${appName} + PayloadIdentifier + ${bundleIdentifier}.${UUID} + PayloadType + ${bundleIdentifier} + PayloadUUID + ${UUID} + PayloadVersion + 1 +${policyEntries} + + + PayloadDescription + This profile manages ${appName}. For more information see https://code.visualstudio.com/docs/setup/enterprise + PayloadDisplayName + ${appName} + PayloadIdentifier + ${bundleIdentifier} + PayloadOrganization + Microsoft + PayloadType + Configuration + PayloadUUID + ${payloadUUID} + PayloadVersion + 1 + TargetDeviceType + 5 + +`, + manifests: [{ languageId: 'en-us', contents: renderProfileManifest(appName, bundleIdentifier, versions, categories, policies) }, + ...translations.map(({ languageId, languageTranslations }) => ({ languageId, contents: renderProfileManifest(appName, bundleIdentifier, versions, categories, policies, languageTranslations) })) + ] + }; +} function renderGP(policies, translations) { const appName = product.nameLong; const regKey = product.win32RegValueName; @@ -475,13 +792,13 @@ async function getNLS(extensionGalleryServiceUrl, resourceUrlTemplate, languageI return await getSpecificNLS(resourceUrlTemplate, languageId, latestCompatibleVersion); } async function parsePolicies() { - const parser = new Parser(); + const parser = new tree_sitter_1.default(); parser.setLanguage(typescript); const files = await getFiles(process.cwd()); - const base = path.join(process.cwd(), 'src'); + const base = path_1.default.join(process.cwd(), 'src'); const policies = []; for (const file of files) { - const moduleName = path.relative(base, file).replace(/\.ts$/i, '').replace(/\\/g, '/'); + const moduleName = path_1.default.relative(base, file).replace(/\.ts$/i, '').replace(/\\/g, '/'); const contents = await fs_1.promises.readFile(file, { encoding: 'utf8' }); const tree = parser.parse(contents); policies.push(...getPolicies(moduleName, tree.rootNode)); @@ -504,22 +821,56 @@ async function getTranslations() { return await Promise.all(languageIds.map(languageId => getNLS(extensionGalleryServiceUrl, resourceUrlTemplate, languageId, version) .then(languageTranslations => ({ languageId, languageTranslations })))); } -async function main() { - const [policies, translations] = await Promise.all([parsePolicies(), getTranslations()]); - const { admx, adml } = await renderGP(policies, translations); +async function windowsMain(policies, translations) { const root = '.build/policies/win32'; + const { admx, adml } = await renderGP(policies, translations); await fs_1.promises.rm(root, { recursive: true, force: true }); await fs_1.promises.mkdir(root, { recursive: true }); - await fs_1.promises.writeFile(path.join(root, `${product.win32RegValueName}.admx`), admx.replace(/\r?\n/g, '\n')); + await fs_1.promises.writeFile(path_1.default.join(root, `${product.win32RegValueName}.admx`), admx.replace(/\r?\n/g, '\n')); for (const { languageId, contents } of adml) { - const languagePath = path.join(root, languageId === 'en-us' ? 'en-us' : Languages[languageId]); + const languagePath = path_1.default.join(root, languageId === 'en-us' ? 'en-us' : Languages[languageId]); await fs_1.promises.mkdir(languagePath, { recursive: true }); - await fs_1.promises.writeFile(path.join(languagePath, `${product.win32RegValueName}.adml`), contents.replace(/\r?\n/g, '\n')); + await fs_1.promises.writeFile(path_1.default.join(languagePath, `${product.win32RegValueName}.adml`), contents.replace(/\r?\n/g, '\n')); + } +} +async function darwinMain(policies, translations) { + const bundleIdentifier = product.darwinBundleIdentifier; + if (!bundleIdentifier || !product.darwinProfilePayloadUUID || !product.darwinProfileUUID) { + throw new Error(`Missing required product information.`); + } + const root = '.build/policies/darwin'; + const { profile, manifests } = await renderMacOSPolicy(policies, translations); + await fs_1.promises.rm(root, { recursive: true, force: true }); + await fs_1.promises.mkdir(root, { recursive: true }); + await fs_1.promises.writeFile(path_1.default.join(root, `${bundleIdentifier}.mobileconfig`), profile.replace(/\r?\n/g, '\n')); + for (const { languageId, contents } of manifests) { + const languagePath = path_1.default.join(root, languageId === 'en-us' ? 'en-us' : Languages[languageId]); + await fs_1.promises.mkdir(languagePath, { recursive: true }); + await fs_1.promises.writeFile(path_1.default.join(languagePath, `${bundleIdentifier}.plist`), contents.replace(/\r?\n/g, '\n')); + } +} +async function main() { + const [policies, translations] = await Promise.all([parsePolicies(), getTranslations()]); + const platform = process.argv[2]; + if (platform === 'darwin') { + await darwinMain(policies, translations); + } + else if (platform === 'win32') { + await windowsMain(policies, translations); + } + else { + console.error(`Usage: node build/lib/policies `); + process.exit(1); } } if (require.main === module) { main().catch(err => { - console.error(err); + if (err instanceof ParseError) { + console.error(`Parse Error:`, err.message); + } + else { + console.error(err); + } process.exit(1); }); } diff --git a/build/lib/policies.ts b/build/lib/policies.ts index 68f6989f27a7..cf16ca2d5113 100644 --- a/build/lib/policies.ts +++ b/build/lib/policies.ts @@ -5,10 +5,10 @@ import { spawn } from 'child_process'; import { promises as fs } from 'fs'; -import * as path from 'path'; -import * as byline from 'byline'; +import path from 'path'; +import byline from 'byline'; import { rgPath } from '@vscode/ripgrep'; -import * as Parser from 'tree-sitter'; +import Parser from 'tree-sitter'; const { typescript } = require('tree-sitter-typescript'); const product = require('../../product.json'); const packageJson = require('../../package.json'); @@ -33,15 +33,24 @@ interface Category { } enum PolicyType { - StringEnum + Boolean = 'boolean', + Number = 'number', + Object = 'object', + String = 'string', + StringEnum = 'stringEnum', } interface Policy { + readonly name: string; + readonly type: PolicyType; readonly category: Category; readonly minimumVersion: string; renderADMX(regKey: string): string[]; renderADMLStrings(translations?: LanguageTranslations): string[]; renderADMLPresentation(): string; + renderProfile(): string[]; + // https://github.com/ProfileManifests/ProfileManifests/wiki/Manifest-Format + renderProfileManifest(translations?: LanguageTranslations): string; } function renderADMLString(prefix: string, moduleName: string, nlsString: NlsString, translations?: LanguageTranslations): string { @@ -59,13 +68,31 @@ function renderADMLString(prefix: string, moduleName: string, nlsString: NlsStri value = nlsString.value; } - return `${value}`; + return `${value}`; +} + +function renderProfileString(_prefix: string, moduleName: string, nlsString: NlsString, translations?: LanguageTranslations): string { + let value: string | undefined; + + if (translations) { + const moduleTranslations = translations[moduleName]; + + if (moduleTranslations) { + value = moduleTranslations[nlsString.nlsKey]; + } + } + + if (!value) { + value = nlsString.value; + } + + return value; } abstract class BasePolicy implements Policy { constructor( - protected policyType: PolicyType, - protected name: string, + readonly type: PolicyType, + readonly name: string, readonly category: Category, readonly minimumVersion: string, protected description: NlsString, @@ -78,7 +105,7 @@ abstract class BasePolicy implements Policy { renderADMX(regKey: string) { return [ - ``, + ``, ` `, ` `, ` `, @@ -102,6 +129,19 @@ abstract class BasePolicy implements Policy { } protected abstract renderADMLPresentationContents(): string; + + renderProfile() { + return [`${this.name}`, this.renderProfileValue()]; + } + + renderProfileManifest(translations?: LanguageTranslations): string { + return ` +${this.renderProfileManifestValue(translations)} +`; + } + + abstract renderProfileValue(): string; + abstract renderProfileManifestValue(translations?: LanguageTranslations): string; } class BooleanPolicy extends BasePolicy { @@ -114,7 +154,7 @@ class BooleanPolicy extends BasePolicy { moduleName: string, settingNode: Parser.SyntaxNode ): BooleanPolicy | undefined { - const type = getStringProperty(settingNode, 'type'); + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'boolean') { return undefined; @@ -130,7 +170,7 @@ class BooleanPolicy extends BasePolicy { description: NlsString, moduleName: string, ) { - super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); + super(PolicyType.Boolean, name, category, minimumVersion, description, moduleName); } protected renderADMXElements(): string[] { @@ -144,9 +184,32 @@ class BooleanPolicy extends BasePolicy { renderADMLPresentationContents() { return `${this.name}`; } + + renderProfileValue(): string { + return ``; + } + + renderProfileManifestValue(translations?: LanguageTranslations): string { + return `pfm_default + +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +boolean`; + } +} + +class ParseError extends Error { + constructor(message: string, moduleName: string, node: Parser.SyntaxNode) { + super(`${message}. ${moduleName}.ts:${node.startPosition.row + 1}`); + } } -class IntPolicy extends BasePolicy { +class NumberPolicy extends BasePolicy { static from( name: string, @@ -155,20 +218,20 @@ class IntPolicy extends BasePolicy { description: NlsString, moduleName: string, settingNode: Parser.SyntaxNode - ): IntPolicy | undefined { - const type = getStringProperty(settingNode, 'type'); + ): NumberPolicy | undefined { + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'number') { return undefined; } - const defaultValue = getIntProperty(settingNode, 'default'); + const defaultValue = getNumberProperty(moduleName, settingNode, 'default'); if (typeof defaultValue === 'undefined') { - throw new Error(`Missing required 'default' property.`); + throw new ParseError(`Missing required 'default' property.`, moduleName, settingNode); } - return new IntPolicy(name, category, minimumVersion, description, moduleName, defaultValue); + return new NumberPolicy(name, category, minimumVersion, description, moduleName, defaultValue); } private constructor( @@ -192,6 +255,23 @@ class IntPolicy extends BasePolicy { renderADMLPresentationContents() { return `${this.name}`; } + + renderProfileValue() { + return `${this.defaultValue}`; + } + + renderProfileManifestValue(translations?: LanguageTranslations) { + return `pfm_default +${this.defaultValue} +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +integer`; + } } class StringPolicy extends BasePolicy { @@ -204,7 +284,7 @@ class StringPolicy extends BasePolicy { moduleName: string, settingNode: Parser.SyntaxNode ): StringPolicy | undefined { - const type = getStringProperty(settingNode, 'type'); + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'string') { return undefined; @@ -220,7 +300,7 @@ class StringPolicy extends BasePolicy { description: NlsString, moduleName: string, ) { - super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); + super(PolicyType.String, name, category, minimumVersion, description, moduleName); } protected renderADMXElements(): string[] { @@ -230,6 +310,79 @@ class StringPolicy extends BasePolicy { renderADMLPresentationContents() { return ``; } + + renderProfileValue(): string { + return ``; + } + + renderProfileManifestValue(translations?: LanguageTranslations): string { + return `pfm_default + +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +string`; + } +} + +class ObjectPolicy extends BasePolicy { + + static from( + name: string, + category: Category, + minimumVersion: string, + description: NlsString, + moduleName: string, + settingNode: Parser.SyntaxNode + ): ObjectPolicy | undefined { + const type = getStringProperty(moduleName, settingNode, 'type'); + + if (type !== 'object' && type !== 'array') { + return undefined; + } + + return new ObjectPolicy(name, category, minimumVersion, description, moduleName); + } + + private constructor( + name: string, + category: Category, + minimumVersion: string, + description: NlsString, + moduleName: string, + ) { + super(PolicyType.Object, name, category, minimumVersion, description, moduleName); + } + + protected renderADMXElements(): string[] { + return [``]; + } + + renderADMLPresentationContents() { + return ``; + } + + renderProfileValue(): string { + return ``; + } + + renderProfileManifestValue(translations?: LanguageTranslations): string { + return `pfm_default + +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +string +`; + } } class StringEnumPolicy extends BasePolicy { @@ -242,28 +395,28 @@ class StringEnumPolicy extends BasePolicy { moduleName: string, settingNode: Parser.SyntaxNode ): StringEnumPolicy | undefined { - const type = getStringProperty(settingNode, 'type'); + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'string') { return undefined; } - const enum_ = getStringArrayProperty(settingNode, 'enum'); + const enum_ = getStringArrayProperty(moduleName, settingNode, 'enum'); if (!enum_) { return undefined; } if (!isStringArray(enum_)) { - throw new Error(`Property 'enum' should not be localized.`); + throw new ParseError(`Property 'enum' should not be localized.`, moduleName, settingNode); } - const enumDescriptions = getStringArrayProperty(settingNode, 'enumDescriptions'); + const enumDescriptions = getStringArrayProperty(moduleName, settingNode, 'enumDescriptions'); if (!enumDescriptions) { - throw new Error(`Missing required 'enumDescriptions' property.`); + throw new ParseError(`Missing required 'enumDescriptions' property.`, moduleName, settingNode); } else if (!isNlsStringArray(enumDescriptions)) { - throw new Error(`Property 'enumDescriptions' should be localized.`); + throw new ParseError(`Property 'enumDescriptions' should be localized.`, moduleName, settingNode); } return new StringEnumPolicy(name, category, minimumVersion, description, moduleName, enum_, enumDescriptions); @@ -299,6 +452,27 @@ class StringEnumPolicy extends BasePolicy { renderADMLPresentationContents() { return ``; } + + renderProfileValue() { + return `${this.enum_[0]}`; + } + + renderProfileManifestValue(translations?: LanguageTranslations): string { + return `pfm_default +${this.enum_[0]} +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +string +pfm_range_list + + ${this.enum_.map(e => `${e}`).join('\n ')} +`; + } } interface QType { @@ -306,7 +480,7 @@ interface QType { value(matches: Parser.QueryMatch[]): T | undefined; } -const IntQ: QType = { +const NumberQ: QType = { Q: `(number) @value`, value(matches: Parser.QueryMatch[]): number | undefined { @@ -369,7 +543,7 @@ const StringArrayQ: QType<(string | NlsString)[]> = { } }; -function getProperty(qtype: QType, node: Parser.SyntaxNode, key: string): T | undefined { +function getProperty(qtype: QType, moduleName: string, node: Parser.SyntaxNode, key: string): T | undefined { const query = new Parser.Query( typescript, `( @@ -377,31 +551,36 @@ function getProperty(qtype: QType, node: Parser.SyntaxNode, key: string): key: [(property_identifier)(string)] @key value: ${qtype.Q} ) - (#eq? @key ${key}) + (#any-of? @key "${key}" "'${key}'") )` ); - return qtype.value(query.matches(node)); + try { + return qtype.value(query.matches(node)); + } catch (e) { + throw new ParseError(e.message, moduleName, node); + } } -function getIntProperty(node: Parser.SyntaxNode, key: string): number | undefined { - return getProperty(IntQ, node, key); +function getNumberProperty(moduleName: string, node: Parser.SyntaxNode, key: string): number | undefined { + return getProperty(NumberQ, moduleName, node, key); } -function getStringProperty(node: Parser.SyntaxNode, key: string): string | NlsString | undefined { - return getProperty(StringQ, node, key); +function getStringProperty(moduleName: string, node: Parser.SyntaxNode, key: string): string | NlsString | undefined { + return getProperty(StringQ, moduleName, node, key); } -function getStringArrayProperty(node: Parser.SyntaxNode, key: string): (string | NlsString)[] | undefined { - return getProperty(StringArrayQ, node, key); +function getStringArrayProperty(moduleName: string, node: Parser.SyntaxNode, key: string): (string | NlsString)[] | undefined { + return getProperty(StringArrayQ, moduleName, node, key); } // TODO: add more policy types const PolicyTypes = [ BooleanPolicy, - IntPolicy, + NumberPolicy, StringEnumPolicy, StringPolicy, + ObjectPolicy ]; function getPolicy( @@ -411,20 +590,20 @@ function getPolicy( policyNode: Parser.SyntaxNode, categories: Map ): Policy { - const name = getStringProperty(policyNode, 'name'); + const name = getStringProperty(moduleName, policyNode, 'name'); if (!name) { - throw new Error(`Missing required 'name' property.`); + throw new ParseError(`Missing required 'name' property`, moduleName, policyNode); } else if (isNlsString(name)) { - throw new Error(`Property 'name' should be a literal string.`); + throw new ParseError(`Property 'name' should be a literal string`, moduleName, policyNode); } - const categoryName = getStringProperty(configurationNode, 'title'); + const categoryName = getStringProperty(moduleName, configurationNode, 'title'); if (!categoryName) { - throw new Error(`Missing required 'title' property.`); + throw new ParseError(`Missing required 'title' property`, moduleName, configurationNode); } else if (!isNlsString(categoryName)) { - throw new Error(`Property 'title' should be localized.`); + throw new ParseError(`Property 'title' should be localized`, moduleName, configurationNode); } const categoryKey = `${categoryName.nlsKey}:${categoryName.value}`; @@ -435,20 +614,20 @@ function getPolicy( categories.set(categoryKey, category); } - const minimumVersion = getStringProperty(policyNode, 'minimumVersion'); + const minimumVersion = getStringProperty(moduleName, policyNode, 'minimumVersion'); if (!minimumVersion) { - throw new Error(`Missing required 'minimumVersion' property.`); + throw new ParseError(`Missing required 'minimumVersion' property.`, moduleName, policyNode); } else if (isNlsString(minimumVersion)) { - throw new Error(`Property 'minimumVersion' should be a literal string.`); + throw new ParseError(`Property 'minimumVersion' should be a literal string.`, moduleName, policyNode); } - const description = getStringProperty(settingNode, 'description'); + const description = getStringProperty(moduleName, policyNode, 'description') ?? getStringProperty(moduleName, settingNode, 'description'); if (!description) { - throw new Error(`Missing required 'description' property.`); + throw new ParseError(`Missing required 'description' property.`, moduleName, settingNode); } if (!isNlsString(description)) { - throw new Error(`Property 'description' should be localized.`); + throw new ParseError(`Property 'description' should be localized.`, moduleName, settingNode); } let result: Policy | undefined; @@ -460,7 +639,7 @@ function getPolicy( } if (!result) { - throw new Error(`Failed to parse policy '${name}'.`); + throw new ParseError(`Failed to parse policy '${name}'.`, moduleName, settingNode); } return result; @@ -472,11 +651,11 @@ function getPolicies(moduleName: string, node: Parser.SyntaxNode): Policy[] { (call_expression function: (member_expression property: (property_identifier) @registerConfigurationFn) (#eq? @registerConfigurationFn registerConfiguration) arguments: (arguments (object (pair - key: [(property_identifier)(string)] @propertiesKey (#eq? @propertiesKey properties) + key: [(property_identifier)(string)] @propertiesKey (#any-of? @propertiesKey "properties" "'properties'") value: (object (pair - key: [(property_identifier)(string)] + key: [(property_identifier)(string)(computed_property_name)] value: (object (pair - key: [(property_identifier)(string)] @policyKey (#eq? @policyKey policy) + key: [(property_identifier)(string)] @policyKey (#any-of? @policyKey "policy" "'policy'") value: (object) @policy )) @setting )) @@ -551,6 +730,197 @@ function renderADML(appName: string, versions: string[], categories: Category[], `; } +function renderProfileManifest(appName: string, bundleIdentifier: string, _versions: string[], _categories: Category[], policies: Policy[], translations?: LanguageTranslations) { + + const requiredPayloadFields = ` + + pfm_default + Configure ${appName} + pfm_name + PayloadDescription + pfm_title + Payload Description + pfm_type + string + + + pfm_default + ${appName} + pfm_name + PayloadDisplayName + pfm_require + always + pfm_title + Payload Display Name + pfm_type + string + + + pfm_default + ${bundleIdentifier} + pfm_name + PayloadIdentifier + pfm_require + always + pfm_title + Payload Identifier + pfm_type + string + + + pfm_default + ${bundleIdentifier} + pfm_name + PayloadType + pfm_require + always + pfm_title + Payload Type + pfm_type + string + + + pfm_default + + pfm_name + PayloadUUID + pfm_require + always + pfm_title + Payload UUID + pfm_type + string + + + pfm_default + 1 + pfm_name + PayloadVersion + pfm_range_list + + 1 + + pfm_require + always + pfm_title + Payload Version + pfm_type + integer + + + pfm_default + Microsoft + pfm_name + PayloadOrganization + pfm_title + Payload Organization + pfm_type + string + `; + + const profileManifestSubkeys = policies.map(policy => { + return policy.renderProfileManifest(translations); + }).join(''); + + return ` + + + + pfm_app_url + https://code.visualstudio.com/ + pfm_description + ${appName} Managed Settings + pfm_documentation_url + https://code.visualstudio.com/docs/setup/enterprise + pfm_domain + ${bundleIdentifier} + pfm_format_version + 1 + pfm_interaction + combined + pfm_last_modified + ${new Date().toISOString().replace(/\.\d+Z$/, 'Z')} + pfm_platforms + + macOS + + pfm_subkeys + + ${requiredPayloadFields} + ${profileManifestSubkeys} + + pfm_title + ${appName} + pfm_unique + + pfm_version + 1 + +`; +} + +function renderMacOSPolicy(policies: Policy[], translations: Translations) { + const appName = product.nameLong; + const bundleIdentifier = product.darwinBundleIdentifier; + const payloadUUID = product.darwinProfilePayloadUUID; + const UUID = product.darwinProfileUUID; + + const versions = [...new Set(policies.map(p => p.minimumVersion)).values()].sort(); + const categories = [...new Set(policies.map(p => p.category))]; + + const policyEntries = + policies.map(policy => policy.renderProfile()) + .flat() + .map(entry => `\t\t\t\t${entry}`) + .join('\n'); + + + return { + profile: ` + + + + PayloadContent + + + PayloadDisplayName + ${appName} + PayloadIdentifier + ${bundleIdentifier}.${UUID} + PayloadType + ${bundleIdentifier} + PayloadUUID + ${UUID} + PayloadVersion + 1 +${policyEntries} + + + PayloadDescription + This profile manages ${appName}. For more information see https://code.visualstudio.com/docs/setup/enterprise + PayloadDisplayName + ${appName} + PayloadIdentifier + ${bundleIdentifier} + PayloadOrganization + Microsoft + PayloadType + Configuration + PayloadUUID + ${payloadUUID} + PayloadVersion + 1 + TargetDeviceType + 5 + +`, + manifests: [{ languageId: 'en-us', contents: renderProfileManifest(appName, bundleIdentifier, versions, categories, policies) }, + ...translations.map(({ languageId, languageTranslations }) => + ({ languageId, contents: renderProfileManifest(appName, bundleIdentifier, versions, categories, policies, languageTranslations) })) + ] + }; +} + function renderGP(policies: Policy[], translations: Translations) { const appName = product.nameLong; const regKey = product.win32RegValueName; @@ -696,11 +1066,10 @@ async function getTranslations(): Promise { )); } -async function main() { - const [policies, translations] = await Promise.all([parsePolicies(), getTranslations()]); +async function windowsMain(policies: Policy[], translations: Translations) { + const root = '.build/policies/win32'; const { admx, adml } = await renderGP(policies, translations); - const root = '.build/policies/win32'; await fs.rm(root, { recursive: true, force: true }); await fs.mkdir(root, { recursive: true }); @@ -713,9 +1082,46 @@ async function main() { } } +async function darwinMain(policies: Policy[], translations: Translations) { + const bundleIdentifier = product.darwinBundleIdentifier; + if (!bundleIdentifier || !product.darwinProfilePayloadUUID || !product.darwinProfileUUID) { + throw new Error(`Missing required product information.`); + } + const root = '.build/policies/darwin'; + const { profile, manifests } = await renderMacOSPolicy(policies, translations); + + await fs.rm(root, { recursive: true, force: true }); + await fs.mkdir(root, { recursive: true }); + await fs.writeFile(path.join(root, `${bundleIdentifier}.mobileconfig`), profile.replace(/\r?\n/g, '\n')); + + for (const { languageId, contents } of manifests) { + const languagePath = path.join(root, languageId === 'en-us' ? 'en-us' : Languages[languageId as keyof typeof Languages]); + await fs.mkdir(languagePath, { recursive: true }); + await fs.writeFile(path.join(languagePath, `${bundleIdentifier}.plist`), contents.replace(/\r?\n/g, '\n')); + } +} + +async function main() { + const [policies, translations] = await Promise.all([parsePolicies(), getTranslations()]); + const platform = process.argv[2]; + + if (platform === 'darwin') { + await darwinMain(policies, translations); + } else if (platform === 'win32') { + await windowsMain(policies, translations); + } else { + console.error(`Usage: node build/lib/policies `); + process.exit(1); + } +} + if (require.main === module) { main().catch(err => { - console.error(err); + if (err instanceof ParseError) { + console.error(`Parse Error:`, err.message); + } else { + console.error(err); + } process.exit(1); }); } diff --git a/build/lib/postcss.js b/build/lib/postcss.js index 356015ab1596..210a184e5f54 100644 --- a/build/lib/postcss.js +++ b/build/lib/postcss.js @@ -1,15 +1,18 @@ "use strict"; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.gulpPostcss = gulpPostcss; /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -const postcss = require("postcss"); -const es = require("event-stream"); +const postcss_1 = __importDefault(require("postcss")); +const event_stream_1 = __importDefault(require("event-stream")); function gulpPostcss(plugins, handleError) { - const instance = postcss(plugins); - return es.map((file, callback) => { + const instance = (0, postcss_1.default)(plugins); + return event_stream_1.default.map((file, callback) => { if (file.isNull()) { return callback(null, file); } diff --git a/build/lib/postcss.ts b/build/lib/postcss.ts index cf3121e221e7..9ec2188d13ac 100644 --- a/build/lib/postcss.ts +++ b/build/lib/postcss.ts @@ -2,9 +2,9 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as postcss from 'postcss'; -import * as File from 'vinyl'; -import * as es from 'event-stream'; +import postcss from 'postcss'; +import File from 'vinyl'; +import es from 'event-stream'; export function gulpPostcss(plugins: postcss.AcceptedPlugin[], handleError?: (err: Error) => void) { const instance = postcss(plugins); diff --git a/build/lib/preLaunch.js b/build/lib/preLaunch.js index 4791514fdfe7..75207fe50c03 100644 --- a/build/lib/preLaunch.js +++ b/build/lib/preLaunch.js @@ -3,13 +3,16 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); // @ts-check -const path = require("path"); +const path_1 = __importDefault(require("path")); const child_process_1 = require("child_process"); const fs_1 = require("fs"); const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'; -const rootDir = path.resolve(__dirname, '..', '..'); +const rootDir = path_1.default.resolve(__dirname, '..', '..'); function runProcess(command, args = []) { return new Promise((resolve, reject) => { const child = (0, child_process_1.spawn)(command, args, { cwd: rootDir, stdio: 'inherit', env: process.env, shell: process.platform === 'win32' }); @@ -19,7 +22,7 @@ function runProcess(command, args = []) { } async function exists(subdir) { try { - await fs_1.promises.stat(path.join(rootDir, subdir)); + await fs_1.promises.stat(path_1.default.join(rootDir, subdir)); return true; } catch { diff --git a/build/lib/preLaunch.ts b/build/lib/preLaunch.ts index e0ea274458a4..0c178afcb598 100644 --- a/build/lib/preLaunch.ts +++ b/build/lib/preLaunch.ts @@ -5,7 +5,7 @@ // @ts-check -import * as path from 'path'; +import path from 'path'; import { spawn } from 'child_process'; import { promises as fs } from 'fs'; diff --git a/build/lib/propertyInitOrderChecker.js b/build/lib/propertyInitOrderChecker.js new file mode 100644 index 000000000000..dbca887bc227 --- /dev/null +++ b/build/lib/propertyInitOrderChecker.js @@ -0,0 +1,367 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EntryKind = void 0; +const ts = __importStar(require("typescript")); +const path = __importStar(require("path")); +const fs = __importStar(require("fs")); +const TS_CONFIG_PATH = path.join(__dirname, '../../', 'src', 'tsconfig.json'); +// +// ############################################################################################# +// +// A custom typescript checker that ensure constructor properties are NOT used to initialize +// defined properties. This is needed for the times when `useDefineForClassFields` is gone. +// +// see https://github.com/microsoft/vscode/issues/243049, https://github.com/microsoft/vscode/issues/186726, +// https://github.com/microsoft/vscode/pull/241544 +// +// ############################################################################################# +// +const ignored = new Set([ + 'vs/base/common/arrays.ts', + 'vs/platform/extensionManagement/common/extensionsScannerService.ts', + 'vs/platform/configuration/common/configurations.ts', + 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.ts', + 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts', + 'vs/editor/common/model/textModelTokens.ts', + 'vs/editor/common/model/tokenizationTextModelPart.ts', + 'vs/editor/common/core/textEdit.ts', + 'vs/workbench/contrib/debug/common/debugStorage.ts', + 'vs/workbench/contrib/debug/common/debugModel.ts', + 'vs/workbench/api/common/extHostCommands.ts', + 'vs/editor/browser/view/viewLayer.ts', + 'vs/editor/browser/controller/editContext/textArea/textAreaEditContextInput.ts', + 'vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts', + 'vs/editor/browser/widget/diffEditor/utils.ts', + 'vs/editor/browser/observableCodeEditor.ts', + 'vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts', + 'vs/editor/browser/widget/diffEditor/diffEditorOptions.ts', + 'vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts', + 'vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature.ts', + 'vs/editor/browser/widget/diffEditor/components/diffEditorSash.ts', + 'vs/editor/browser/widget/diffEditor/utils/editorGutter.ts', + 'vs/editor/browser/widget/diffEditor/features/gutterFeature.ts', + 'vs/editor/browser/widget/diffEditor/features/revertButtonsFeature.ts', + 'vs/editor/browser/widget/diffEditor/diffEditorWidget.ts', + 'vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts', + 'vs/editor/contrib/inlineCompletions/browser/model/suggestWidgetAdapter.ts', + 'vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts', + 'vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts', + 'vs/editor/contrib/inlayHints/browser/inlayHintsController.ts', + 'vs/editor/contrib/inlineCompletions/browser/model/changeRecorder.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsSideBySideView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/originalEditorInlineDiffView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts', + 'vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts', + 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsAccessibleView.ts', + 'vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts', + 'vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts', + 'vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts', + 'vs/workbench/contrib/files/browser/views/openEditorsView.ts', + 'vs/workbench/contrib/chat/browser/chatContentParts/chatAttachmentsContentPart.ts', + 'vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts', + 'vs/workbench/contrib/chat/browser/chatInputPart.ts', + 'vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts', + 'vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts', + 'vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts', + 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts', + 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts', + 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts', + 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts', + 'vs/platform/terminal/common/capabilities/commandDetectionCapability.ts', + 'vs/workbench/contrib/testing/common/testExclusions.ts', + 'vs/workbench/contrib/testing/common/testResultStorage.ts', + 'vs/workbench/services/userDataProfile/browser/snippetsResource.ts', + 'vs/platform/quickinput/browser/quickInputController.ts', + 'vs/platform/userDataSync/common/abstractSynchronizer.ts', + 'vs/workbench/services/authentication/browser/authenticationExtensionsService.ts', + 'vs/workbench/services/textMate/browser/backgroundTokenization/textMateWorkerTokenizerController.ts', + 'vs/workbench/services/textMate/browser/textMateTokenizationFeatureImpl.ts', + 'vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts', + 'vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts', + 'vs/editor/browser/widget/multiDiffEditor/diffEditorItemTemplate.ts', + 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts', + 'vs/workbench/contrib/notebook/browser/diff/notebookMultiDiffEditor.ts', + 'vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts', + 'vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts', + 'vs/workbench/contrib/search/common/cacheState.ts', + 'vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts', + 'vs/workbench/contrib/search/browser/anythingQuickAccess.ts', + 'vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts', + 'vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts', + 'vs/workbench/contrib/testing/common/testExplorerFilterState.ts', + 'vs/workbench/contrib/testing/browser/testResultsView/testResultsTree.ts', + 'vs/workbench/contrib/testing/browser/testingOutputPeek.ts', + 'vs/workbench/contrib/testing/browser/explorerProjections/index.ts', + 'vs/workbench/contrib/testing/browser/testingExplorerFilter.ts', + 'vs/workbench/contrib/testing/browser/testingExplorerView.ts', + 'vs/workbench/contrib/testing/common/testServiceImpl.ts', + 'vs/platform/quickinput/browser/commandsQuickAccess.ts', + 'vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts', + 'vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts', + 'vs/workbench/contrib/debug/browser/debugMemory.ts', + 'vs/workbench/contrib/markers/browser/markersViewActions.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/viewZones.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts', + 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts', + 'vs/workbench/contrib/output/browser/outputServices.ts', + 'vs/workbench/contrib/terminalContrib/typeAhead/browser/terminalTypeAheadAddon.ts', + 'vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts', + 'vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts', + 'vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution.ts', + 'vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts', + 'vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts', + 'vs/workbench/contrib/welcomeDialog/browser/welcomeWidget.ts', + 'vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts', + 'vs/platform/terminal/node/ptyService.ts', + 'vs/workbench/api/common/extHostLanguageFeatures.ts', + 'vs/workbench/api/common/extHostSearch.ts', + 'vs/workbench/contrib/testing/test/common/testStubs.ts' +]); +const cancellationToken = { + isCancellationRequested: () => false, + throwIfCancellationRequested: () => { }, +}; +const seenFiles = new Set(); +let errorCount = 0; +function createProgram(tsconfigPath) { + const tsConfig = ts.readConfigFile(tsconfigPath, ts.sys.readFile); + const configHostParser = { fileExists: fs.existsSync, readDirectory: ts.sys.readDirectory, readFile: file => fs.readFileSync(file, 'utf8'), useCaseSensitiveFileNames: process.platform === 'linux' }; + const tsConfigParsed = ts.parseJsonConfigFileContent(tsConfig.config, configHostParser, path.resolve(path.dirname(tsconfigPath)), { noEmit: true }); + const compilerHost = ts.createCompilerHost(tsConfigParsed.options, true); + return ts.createProgram(tsConfigParsed.fileNames, tsConfigParsed.options, compilerHost); +} +const program = createProgram(TS_CONFIG_PATH); +program.getTypeChecker(); +for (const file of program.getSourceFiles()) { + if (!file || file.isDeclarationFile) { + continue; + } + const relativePath = path.relative(path.dirname(TS_CONFIG_PATH), file.fileName).replace(/\\/g, '/'); + if (ignored.has(relativePath)) { + continue; + } + visit(file); +} +if (seenFiles.size) { + console.log(); + console.log(`Found ${errorCount} error${errorCount === 1 ? '' : 's'} in ${seenFiles.size} file${seenFiles.size === 1 ? '' : 's'}.`); + process.exit(errorCount); +} +function visit(node) { + if (ts.isParameter(node) && ts.isParameterPropertyDeclaration(node, node.parent)) { + checkParameterPropertyDeclaration(node); + } + ts.forEachChild(node, visit); +} +function checkParameterPropertyDeclaration(param) { + const uses = [...collectReferences(param.name, [])]; + if (!uses.length) { + return; + } + const sourceFile = param.getSourceFile(); + if (!seenFiles.has(sourceFile)) { + if (seenFiles.size) { + console.log(``); + } + console.log(`${formatFileName(param)}:`); + seenFiles.add(sourceFile); + } + else { + console.log(``); + } + console.log(` Parameter property '${param.name.getText()}' is used before its declaration.`); + for (const { stack, container } of uses) { + const use = stack[stack.length - 1]; + console.log(` at ${formatLocation(use)}: ${formatMember(container)} -> ${formatStack(stack)}`); + errorCount++; + } +} +function* collectReferences(node, stack, requiresInvocationDepth = 0, seen = new Set()) { + for (const use of findAllReferencesInClass(node)) { + const container = findContainer(use); + if (!container || seen.has(container) || ts.isConstructorDeclaration(container)) { + continue; + } + seen.add(container); + const nextStack = [...stack, use]; + let nextRequiresInvocationDepth = requiresInvocationDepth; + if (isInvocation(use) && nextRequiresInvocationDepth > 0) { + nextRequiresInvocationDepth--; + } + if (ts.isPropertyDeclaration(container) && nextRequiresInvocationDepth === 0) { + yield { stack: nextStack, container }; + } + else if (requiresInvocation(container)) { + nextRequiresInvocationDepth++; + } + yield* collectReferences(container.name ?? container, nextStack, nextRequiresInvocationDepth, seen); + } +} +function requiresInvocation(definition) { + return ts.isMethodDeclaration(definition) || ts.isFunctionDeclaration(definition) || ts.isFunctionExpression(definition) || ts.isArrowFunction(definition); +} +function isInvocation(use) { + let location = use; + if (ts.isPropertyAccessExpression(location.parent) && location.parent.name === location) { + location = location.parent; + } + else if (ts.isElementAccessExpression(location.parent) && location.parent.argumentExpression === location) { + location = location.parent; + } + return ts.isCallExpression(location.parent) && location.parent.expression === location + || ts.isTaggedTemplateExpression(location.parent) && location.parent.tag === location; +} +function formatFileName(node) { + const sourceFile = node.getSourceFile(); + return path.resolve(sourceFile.fileName); +} +function formatLocation(node) { + const sourceFile = node.getSourceFile(); + const { line, character } = ts.getLineAndCharacterOfPosition(sourceFile, node.pos); + return `${formatFileName(sourceFile)}(${line + 1},${character + 1})`; +} +function formatStack(stack) { + return stack.slice().reverse().map((use) => formatUse(use)).join(' -> '); +} +function formatMember(container) { + const name = container.name?.getText(); + if (name) { + const className = findClass(container)?.name?.getText(); + if (className) { + return `${className}.${name}`; + } + return name; + } + return ''; +} +function formatUse(use) { + let text = use.getText(); + if (use.parent && ts.isPropertyAccessExpression(use.parent) && use.parent.name === use) { + if (use.parent.expression.kind === ts.SyntaxKind.ThisKeyword) { + text = `this.${text}`; + } + use = use.parent; + } + else if (use.parent && ts.isElementAccessExpression(use.parent) && use.parent.argumentExpression === use) { + if (use.parent.expression.kind === ts.SyntaxKind.ThisKeyword) { + text = `this['${text}']`; + } + use = use.parent; + } + if (ts.isCallExpression(use.parent)) { + text = `${text}(...)`; + } + return text; +} +function findContainer(node) { + return ts.findAncestor(node, ancestor => { + switch (ancestor.kind) { + case ts.SyntaxKind.PropertyDeclaration: + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.GetAccessor: + case ts.SyntaxKind.SetAccessor: + case ts.SyntaxKind.Constructor: + case ts.SyntaxKind.ClassStaticBlockDeclaration: + case ts.SyntaxKind.ArrowFunction: + case ts.SyntaxKind.FunctionExpression: + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.Parameter: + return true; + } + return false; + }); +} +function findClass(node) { + return ts.findAncestor(node, ts.isClassLike); +} +function* findAllReferencesInClass(node) { + const classDecl = findClass(node); + if (!classDecl) { + return []; + } + for (const ref of findAllReferences(node)) { + for (const entry of ref.references) { + if (entry.kind !== 1 /* EntryKind.Node */ || entry.node === node) { + continue; + } + if (findClass(entry.node) === classDecl) { + yield entry.node; + } + } + } +} +// NOTE: The following uses TypeScript internals and are subject to change from version to version. +function findAllReferences(node) { + const sourceFile = node.getSourceFile(); + const position = node.getStart(); + const name = ts.getTouchingPropertyName(sourceFile, position); + const options = { use: ts.FindAllReferences.FindReferencesUse.References }; + return ts.FindAllReferences.Core.getReferencedSymbolsForNode(position, name, program, [sourceFile], cancellationToken, options) ?? []; +} +var DefinitionKind; +(function (DefinitionKind) { + DefinitionKind[DefinitionKind["Symbol"] = 0] = "Symbol"; + DefinitionKind[DefinitionKind["Label"] = 1] = "Label"; + DefinitionKind[DefinitionKind["Keyword"] = 2] = "Keyword"; + DefinitionKind[DefinitionKind["This"] = 3] = "This"; + DefinitionKind[DefinitionKind["String"] = 4] = "String"; + DefinitionKind[DefinitionKind["TripleSlashReference"] = 5] = "TripleSlashReference"; +})(DefinitionKind || (DefinitionKind = {})); +/** @internal */ +var EntryKind; +(function (EntryKind) { + EntryKind[EntryKind["Span"] = 0] = "Span"; + EntryKind[EntryKind["Node"] = 1] = "Node"; + EntryKind[EntryKind["StringLiteral"] = 2] = "StringLiteral"; + EntryKind[EntryKind["SearchedLocalFoundProperty"] = 3] = "SearchedLocalFoundProperty"; + EntryKind[EntryKind["SearchedPropertyFoundLocal"] = 4] = "SearchedPropertyFoundLocal"; +})(EntryKind || (exports.EntryKind = EntryKind = {})); +//# sourceMappingURL=propertyInitOrderChecker.js.map \ No newline at end of file diff --git a/build/lib/propertyInitOrderChecker.ts b/build/lib/propertyInitOrderChecker.ts new file mode 100644 index 000000000000..dc18213566f5 --- /dev/null +++ b/build/lib/propertyInitOrderChecker.ts @@ -0,0 +1,417 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +import * as ts from 'typescript'; +import * as path from 'path'; +import * as fs from 'fs'; + +const TS_CONFIG_PATH = path.join(__dirname, '../../', 'src', 'tsconfig.json'); + +// +// ############################################################################################# +// +// A custom typescript checker that ensure constructor properties are NOT used to initialize +// defined properties. This is needed for the times when `useDefineForClassFields` is gone. +// +// see https://github.com/microsoft/vscode/issues/243049, https://github.com/microsoft/vscode/issues/186726, +// https://github.com/microsoft/vscode/pull/241544 +// +// ############################################################################################# +// + +const ignored = new Set([ + 'vs/base/common/arrays.ts', + 'vs/platform/extensionManagement/common/extensionsScannerService.ts', + 'vs/platform/configuration/common/configurations.ts', + 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.ts', + 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts', + 'vs/editor/common/model/textModelTokens.ts', + 'vs/editor/common/model/tokenizationTextModelPart.ts', + 'vs/editor/common/core/textEdit.ts', + 'vs/workbench/contrib/debug/common/debugStorage.ts', + 'vs/workbench/contrib/debug/common/debugModel.ts', + 'vs/workbench/api/common/extHostCommands.ts', + 'vs/editor/browser/view/viewLayer.ts', + 'vs/editor/browser/controller/editContext/textArea/textAreaEditContextInput.ts', + 'vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts', + 'vs/editor/browser/widget/diffEditor/utils.ts', + 'vs/editor/browser/observableCodeEditor.ts', + 'vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts', + 'vs/editor/browser/widget/diffEditor/diffEditorOptions.ts', + 'vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts', + 'vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature.ts', + 'vs/editor/browser/widget/diffEditor/components/diffEditorSash.ts', + 'vs/editor/browser/widget/diffEditor/utils/editorGutter.ts', + 'vs/editor/browser/widget/diffEditor/features/gutterFeature.ts', + 'vs/editor/browser/widget/diffEditor/features/revertButtonsFeature.ts', + 'vs/editor/browser/widget/diffEditor/diffEditorWidget.ts', + 'vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts', + 'vs/editor/contrib/inlineCompletions/browser/model/suggestWidgetAdapter.ts', + 'vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts', + 'vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts', + 'vs/editor/contrib/inlayHints/browser/inlayHintsController.ts', + 'vs/editor/contrib/inlineCompletions/browser/model/changeRecorder.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsSideBySideView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/originalEditorInlineDiffView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts', + 'vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts', + 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsAccessibleView.ts', + 'vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts', + 'vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts', + 'vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts', + 'vs/workbench/contrib/files/browser/views/openEditorsView.ts', + 'vs/workbench/contrib/chat/browser/chatContentParts/chatAttachmentsContentPart.ts', + 'vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts', + 'vs/workbench/contrib/chat/browser/chatInputPart.ts', + 'vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts', + 'vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts', + 'vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts', + 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts', + 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts', + 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts', + 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts', + 'vs/platform/terminal/common/capabilities/commandDetectionCapability.ts', + 'vs/workbench/contrib/testing/common/testExclusions.ts', + 'vs/workbench/contrib/testing/common/testResultStorage.ts', + 'vs/workbench/services/userDataProfile/browser/snippetsResource.ts', + 'vs/platform/quickinput/browser/quickInputController.ts', + 'vs/platform/userDataSync/common/abstractSynchronizer.ts', + 'vs/workbench/services/authentication/browser/authenticationExtensionsService.ts', + 'vs/workbench/services/textMate/browser/backgroundTokenization/textMateWorkerTokenizerController.ts', + 'vs/workbench/services/textMate/browser/textMateTokenizationFeatureImpl.ts', + 'vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts', + 'vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts', + 'vs/editor/browser/widget/multiDiffEditor/diffEditorItemTemplate.ts', + 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts', + 'vs/workbench/contrib/notebook/browser/diff/notebookMultiDiffEditor.ts', + 'vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts', + 'vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts', + 'vs/workbench/contrib/search/common/cacheState.ts', + 'vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts', + 'vs/workbench/contrib/search/browser/anythingQuickAccess.ts', + 'vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts', + 'vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts', + 'vs/workbench/contrib/testing/common/testExplorerFilterState.ts', + 'vs/workbench/contrib/testing/browser/testResultsView/testResultsTree.ts', + 'vs/workbench/contrib/testing/browser/testingOutputPeek.ts', + 'vs/workbench/contrib/testing/browser/explorerProjections/index.ts', + 'vs/workbench/contrib/testing/browser/testingExplorerFilter.ts', + 'vs/workbench/contrib/testing/browser/testingExplorerView.ts', + 'vs/workbench/contrib/testing/common/testServiceImpl.ts', + 'vs/platform/quickinput/browser/commandsQuickAccess.ts', + 'vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts', + 'vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts', + 'vs/workbench/contrib/debug/browser/debugMemory.ts', + 'vs/workbench/contrib/markers/browser/markersViewActions.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/viewZones.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts', + 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts', + 'vs/workbench/contrib/output/browser/outputServices.ts', + 'vs/workbench/contrib/terminalContrib/typeAhead/browser/terminalTypeAheadAddon.ts', + 'vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts', + 'vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts', + 'vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution.ts', + 'vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts', + 'vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts', + 'vs/workbench/contrib/welcomeDialog/browser/welcomeWidget.ts', + 'vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts', + 'vs/platform/terminal/node/ptyService.ts', + 'vs/workbench/api/common/extHostLanguageFeatures.ts', + 'vs/workbench/api/common/extHostSearch.ts', + 'vs/workbench/contrib/testing/test/common/testStubs.ts' +]); + + +const cancellationToken: ts.CancellationToken = { + isCancellationRequested: () => false, + throwIfCancellationRequested: () => { }, +}; + +const seenFiles = new Set(); +let errorCount = 0; + + + +function createProgram(tsconfigPath: string): ts.Program { + const tsConfig = ts.readConfigFile(tsconfigPath, ts.sys.readFile); + + const configHostParser: ts.ParseConfigHost = { fileExists: fs.existsSync, readDirectory: ts.sys.readDirectory, readFile: file => fs.readFileSync(file, 'utf8'), useCaseSensitiveFileNames: process.platform === 'linux' }; + const tsConfigParsed = ts.parseJsonConfigFileContent(tsConfig.config, configHostParser, path.resolve(path.dirname(tsconfigPath)), { noEmit: true }); + + const compilerHost = ts.createCompilerHost(tsConfigParsed.options, true); + + return ts.createProgram(tsConfigParsed.fileNames, tsConfigParsed.options, compilerHost); +} + +const program = createProgram(TS_CONFIG_PATH); + +program.getTypeChecker(); + +for (const file of program.getSourceFiles()) { + if (!file || file.isDeclarationFile) { + continue; + } + + const relativePath = path.relative(path.dirname(TS_CONFIG_PATH), file.fileName).replace(/\\/g, '/'); + if (ignored.has(relativePath)) { + continue; + } + + visit(file); +} + +if (seenFiles.size) { + console.log(); + console.log(`Found ${errorCount} error${errorCount === 1 ? '' : 's'} in ${seenFiles.size} file${seenFiles.size === 1 ? '' : 's'}.`); + process.exit(errorCount); +} + +function visit(node: ts.Node) { + if (ts.isParameter(node) && ts.isParameterPropertyDeclaration(node, node.parent)) { + checkParameterPropertyDeclaration(node); + } + + ts.forEachChild(node, visit); +} + +function checkParameterPropertyDeclaration(param: ts.ParameterPropertyDeclaration) { + const uses = [...collectReferences(param.name, [])]; + if (!uses.length) { + return; + } + + const sourceFile = param.getSourceFile(); + if (!seenFiles.has(sourceFile)) { + if (seenFiles.size) { + console.log(``); + } + console.log(`${formatFileName(param)}:`); + seenFiles.add(sourceFile); + } else { + console.log(``); + } + console.log(` Parameter property '${param.name.getText()}' is used before its declaration.`); + for (const { stack, container } of uses) { + const use = stack[stack.length - 1]; + console.log(` at ${formatLocation(use)}: ${formatMember(container)} -> ${formatStack(stack)}`); + errorCount++; + } +} + +interface InvalidUse { + stack: ts.Node[]; + container: ReferenceContainer; +} + +function* collectReferences(node: ts.Node, stack: ts.Node[], requiresInvocationDepth: number = 0, seen = new Set()): Generator { + for (const use of findAllReferencesInClass(node)) { + const container = findContainer(use); + if (!container || seen.has(container) || ts.isConstructorDeclaration(container)) { + continue; + } + seen.add(container); + + const nextStack = [...stack, use]; + + let nextRequiresInvocationDepth = requiresInvocationDepth; + if (isInvocation(use) && nextRequiresInvocationDepth > 0) { + nextRequiresInvocationDepth--; + } + + if (ts.isPropertyDeclaration(container) && nextRequiresInvocationDepth === 0) { + yield { stack: nextStack, container }; + } + else if (requiresInvocation(container)) { + nextRequiresInvocationDepth++; + } + + yield* collectReferences(container.name ?? container, nextStack, nextRequiresInvocationDepth, seen); + } +} + +function requiresInvocation(definition: ReferenceContainer): boolean { + return ts.isMethodDeclaration(definition) || ts.isFunctionDeclaration(definition) || ts.isFunctionExpression(definition) || ts.isArrowFunction(definition); +} + +function isInvocation(use: ts.Node): boolean { + let location = use; + if (ts.isPropertyAccessExpression(location.parent) && location.parent.name === location) { + location = location.parent; + } + else if (ts.isElementAccessExpression(location.parent) && location.parent.argumentExpression === location) { + location = location.parent; + } + return ts.isCallExpression(location.parent) && location.parent.expression === location + || ts.isTaggedTemplateExpression(location.parent) && location.parent.tag === location; +} + +function formatFileName(node: ts.Node): string { + const sourceFile = node.getSourceFile(); + return path.resolve(sourceFile.fileName); +} + +function formatLocation(node: ts.Node): string { + const sourceFile = node.getSourceFile(); + const { line, character } = ts.getLineAndCharacterOfPosition(sourceFile, node.pos); + return `${formatFileName(sourceFile)}(${line + 1},${character + 1})`; +} + +function formatStack(stack: ts.Node[]): string { + return stack.slice().reverse().map((use) => formatUse(use)).join(' -> '); +} + +function formatMember(container: ReferenceContainer): string { + const name = container.name?.getText(); + if (name) { + const className = findClass(container)?.name?.getText(); + if (className) { + return `${className}.${name}`; + } + return name; + } + return ''; +} + +function formatUse(use: ts.Node): string { + let text = use.getText(); + if (use.parent && ts.isPropertyAccessExpression(use.parent) && use.parent.name === use) { + if (use.parent.expression.kind === ts.SyntaxKind.ThisKeyword) { + text = `this.${text}`; + } + use = use.parent; + } + else if (use.parent && ts.isElementAccessExpression(use.parent) && use.parent.argumentExpression === use) { + if (use.parent.expression.kind === ts.SyntaxKind.ThisKeyword) { + text = `this['${text}']`; + } + use = use.parent; + } + if (ts.isCallExpression(use.parent)) { + text = `${text}(...)`; + } + return text; +} + +type ReferenceContainer = + | ts.PropertyDeclaration + | ts.MethodDeclaration + | ts.GetAccessorDeclaration + | ts.SetAccessorDeclaration + | ts.ConstructorDeclaration + | ts.ClassStaticBlockDeclaration + | ts.ArrowFunction + | ts.FunctionExpression + | ts.FunctionDeclaration + | ts.ParameterDeclaration; + +function findContainer(node: ts.Node): ReferenceContainer | undefined { + return ts.findAncestor(node, ancestor => { + switch (ancestor.kind) { + case ts.SyntaxKind.PropertyDeclaration: + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.GetAccessor: + case ts.SyntaxKind.SetAccessor: + case ts.SyntaxKind.Constructor: + case ts.SyntaxKind.ClassStaticBlockDeclaration: + case ts.SyntaxKind.ArrowFunction: + case ts.SyntaxKind.FunctionExpression: + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.Parameter: + return true; + } + return false; + }) as ReferenceContainer | undefined; +} + +function findClass(node: ts.Node): ts.ClassLikeDeclaration | undefined { + return ts.findAncestor(node, ts.isClassLike); +} + +function* findAllReferencesInClass(node: ts.Node): Generator { + const classDecl = findClass(node); + if (!classDecl) { + return []; + } + for (const ref of findAllReferences(node)) { + for (const entry of ref.references) { + if (entry.kind !== EntryKind.Node || entry.node === node) { + continue; + } + if (findClass(entry.node) === classDecl) { + yield entry.node; + } + } + } +} + +// NOTE: The following uses TypeScript internals and are subject to change from version to version. + +function findAllReferences(node: ts.Node): readonly SymbolAndEntries[] { + const sourceFile = node.getSourceFile(); + const position = node.getStart(); + const name: ts.Node = (ts as any).getTouchingPropertyName(sourceFile, position); + const options = { use: (ts as any).FindAllReferences.FindReferencesUse.References }; + return (ts as any).FindAllReferences.Core.getReferencedSymbolsForNode(position, name, program, [sourceFile], cancellationToken, options) ?? []; +} + +interface SymbolAndEntries { + readonly definition: Definition | undefined; + readonly references: readonly Entry[]; +} + +const enum DefinitionKind { + Symbol, + Label, + Keyword, + This, + String, + TripleSlashReference, +} + +type Definition = + | { readonly type: DefinitionKind.Symbol; readonly symbol: ts.Symbol } + | { readonly type: DefinitionKind.Label; readonly node: ts.Identifier } + | { readonly type: DefinitionKind.Keyword; readonly node: ts.Node } + | { readonly type: DefinitionKind.This; readonly node: ts.Node } + | { readonly type: DefinitionKind.String; readonly node: ts.StringLiteralLike } + | { readonly type: DefinitionKind.TripleSlashReference; readonly reference: ts.FileReference; readonly file: ts.SourceFile }; + +/** @internal */ +export const enum EntryKind { + Span, + Node, + StringLiteral, + SearchedLocalFoundProperty, + SearchedPropertyFoundLocal, +} +type NodeEntryKind = EntryKind.Node | EntryKind.StringLiteral | EntryKind.SearchedLocalFoundProperty | EntryKind.SearchedPropertyFoundLocal; +type Entry = NodeEntry | SpanEntry; +interface ContextWithStartAndEndNode { + start: ts.Node; + end: ts.Node; +} +type ContextNode = ts.Node | ContextWithStartAndEndNode; +interface NodeEntry { + readonly kind: NodeEntryKind; + readonly node: ts.Node; + readonly context?: ContextNode; +} +interface SpanEntry { + readonly kind: EntryKind.Span; + readonly fileName: string; + readonly textSpan: ts.TextSpan; +} diff --git a/build/lib/reporter.js b/build/lib/reporter.js index 9d4a1b4fd796..16bb44ec539d 100644 --- a/build/lib/reporter.js +++ b/build/lib/reporter.js @@ -3,13 +3,16 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.createReporter = createReporter; -const es = require("event-stream"); -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); -const fs = require("fs"); -const path = require("path"); +const event_stream_1 = __importDefault(require("event-stream")); +const fancy_log_1 = __importDefault(require("fancy-log")); +const ansi_colors_1 = __importDefault(require("ansi-colors")); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); class ErrorLog { id; constructor(id) { @@ -23,7 +26,7 @@ class ErrorLog { return; } this.startTime = new Date().getTime(); - fancyLog(`Starting ${ansiColors.green('compilation')}${this.id ? ansiColors.blue(` ${this.id}`) : ''}...`); + (0, fancy_log_1.default)(`Starting ${ansi_colors_1.default.green('compilation')}${this.id ? ansi_colors_1.default.blue(` ${this.id}`) : ''}...`); } onEnd() { if (--this.count > 0) { @@ -37,10 +40,10 @@ class ErrorLog { errors.map(err => { if (!seen.has(err)) { seen.add(err); - fancyLog(`${ansiColors.red('Error')}: ${err}`); + (0, fancy_log_1.default)(`${ansi_colors_1.default.red('Error')}: ${err}`); } }); - fancyLog(`Finished ${ansiColors.green('compilation')}${this.id ? ansiColors.blue(` ${this.id}`) : ''} with ${errors.length} errors after ${ansiColors.magenta((new Date().getTime() - this.startTime) + ' ms')}`); + (0, fancy_log_1.default)(`Finished ${ansi_colors_1.default.green('compilation')}${this.id ? ansi_colors_1.default.blue(` ${this.id}`) : ''} with ${errors.length} errors after ${ansi_colors_1.default.magenta((new Date().getTime() - this.startTime) + ' ms')}`); const regex = /^([^(]+)\((\d+),(\d+)\): (.*)$/s; const messages = errors .map(err => regex.exec(err)) @@ -49,7 +52,7 @@ class ErrorLog { .map(([, path, line, column, message]) => ({ path, line: parseInt(line), column: parseInt(column), message })); try { const logFileName = 'log' + (this.id ? `_${this.id}` : ''); - fs.writeFileSync(path.join(buildLogFolder, logFileName), JSON.stringify(messages)); + fs_1.default.writeFileSync(path_1.default.join(buildLogFolder, logFileName), JSON.stringify(messages)); } catch (err) { //noop @@ -65,9 +68,9 @@ function getErrorLog(id = '') { } return errorLog; } -const buildLogFolder = path.join(path.dirname(path.dirname(__dirname)), '.build'); +const buildLogFolder = path_1.default.join(path_1.default.dirname(path_1.default.dirname(__dirname)), '.build'); try { - fs.mkdirSync(buildLogFolder); + fs_1.default.mkdirSync(buildLogFolder); } catch (err) { // ignore @@ -81,7 +84,7 @@ function createReporter(id) { result.end = (emitError) => { errors.length = 0; errorLog.onStart(); - return es.through(undefined, function () { + return event_stream_1.default.through(undefined, function () { errorLog.onEnd(); if (emitError && errors.length > 0) { if (!errors.__logged__) { diff --git a/build/lib/reporter.ts b/build/lib/reporter.ts index 382e0c785464..c21fd841c0d1 100644 --- a/build/lib/reporter.ts +++ b/build/lib/reporter.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as es from 'event-stream'; -import * as fancyLog from 'fancy-log'; -import * as ansiColors from 'ansi-colors'; -import * as fs from 'fs'; -import * as path from 'path'; +import es from 'event-stream'; +import fancyLog from 'fancy-log'; +import ansiColors from 'ansi-colors'; +import fs from 'fs'; +import path from 'path'; class ErrorLog { constructor(public id: string) { diff --git a/build/lib/snapshotLoader.js b/build/lib/snapshotLoader.js index 0e58ceedffaa..7d9b3f154f17 100644 --- a/build/lib/snapshotLoader.js +++ b/build/lib/snapshotLoader.js @@ -3,6 +3,8 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.snaps = void 0; var snaps; (function (snaps) { const fs = require('fs'); @@ -52,5 +54,5 @@ var snaps; fs.writeFileSync(wrappedInputFilepath, wrappedInputFile); cp.execFileSync(mksnapshot, [wrappedInputFilepath, `--startup_blob`, startupBlobFilepath]); } -})(snaps || (snaps = {})); +})(snaps || (exports.snaps = snaps = {})); //# sourceMappingURL=snapshotLoader.js.map \ No newline at end of file diff --git a/build/lib/snapshotLoader.ts b/build/lib/snapshotLoader.ts index c3d66dba7e12..3cb2191144d3 100644 --- a/build/lib/snapshotLoader.ts +++ b/build/lib/snapshotLoader.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -namespace snaps { +export namespace snaps { const fs = require('fs'); const path = require('path'); diff --git a/build/lib/standalone.js b/build/lib/standalone.js index 16ae1e2b2d8f..0e7a9ecc7824 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -3,14 +3,50 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.extractEditor = extractEditor; exports.createESMSourcesAndResources2 = createESMSourcesAndResources2; -const fs = require("fs"); -const path = require("path"); -const tss = require("./treeshaking"); -const REPO_ROOT = path.join(__dirname, '../../'); -const SRC_DIR = path.join(REPO_ROOT, 'src'); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const tss = __importStar(require("./treeshaking")); +const REPO_ROOT = path_1.default.join(__dirname, '../../'); +const SRC_DIR = path_1.default.join(REPO_ROOT, 'src'); const dirCache = {}; function writeFile(filePath, contents) { function ensureDirs(dirPath) { @@ -18,21 +54,21 @@ function writeFile(filePath, contents) { return; } dirCache[dirPath] = true; - ensureDirs(path.dirname(dirPath)); - if (fs.existsSync(dirPath)) { + ensureDirs(path_1.default.dirname(dirPath)); + if (fs_1.default.existsSync(dirPath)) { return; } - fs.mkdirSync(dirPath); + fs_1.default.mkdirSync(dirPath); } - ensureDirs(path.dirname(filePath)); - fs.writeFileSync(filePath, contents); + ensureDirs(path_1.default.dirname(filePath)); + fs_1.default.writeFileSync(filePath, contents); } function extractEditor(options) { const ts = require('typescript'); - const tsConfig = JSON.parse(fs.readFileSync(path.join(options.sourcesRoot, 'tsconfig.monaco.json')).toString()); + const tsConfig = JSON.parse(fs_1.default.readFileSync(path_1.default.join(options.sourcesRoot, 'tsconfig.monaco.json')).toString()); let compilerOptions; if (tsConfig.extends) { - compilerOptions = Object.assign({}, require(path.join(options.sourcesRoot, tsConfig.extends)).compilerOptions, tsConfig.compilerOptions); + compilerOptions = Object.assign({}, require(path_1.default.join(options.sourcesRoot, tsConfig.extends)).compilerOptions, tsConfig.compilerOptions); delete tsConfig.extends; } else { @@ -62,7 +98,7 @@ function extractEditor(options) { const result = tss.shake(options); for (const fileName in result) { if (result.hasOwnProperty(fileName)) { - writeFile(path.join(options.destRoot, fileName), result[fileName]); + writeFile(path_1.default.join(options.destRoot, fileName), result[fileName]); } } const copied = {}; @@ -71,12 +107,12 @@ function extractEditor(options) { return; } copied[fileName] = true; - const srcPath = path.join(options.sourcesRoot, fileName); - const dstPath = path.join(options.destRoot, fileName); - writeFile(dstPath, fs.readFileSync(srcPath)); + const srcPath = path_1.default.join(options.sourcesRoot, fileName); + const dstPath = path_1.default.join(options.destRoot, fileName); + writeFile(dstPath, fs_1.default.readFileSync(srcPath)); }; const writeOutputFile = (fileName, contents) => { - writeFile(path.join(options.destRoot, fileName), contents); + writeFile(path_1.default.join(options.destRoot, fileName), contents); }; for (const fileName in result) { if (result.hasOwnProperty(fileName)) { @@ -86,14 +122,14 @@ function extractEditor(options) { const importedFileName = info.importedFiles[i].fileName; let importedFilePath = importedFileName; if (/(^\.\/)|(^\.\.\/)/.test(importedFilePath)) { - importedFilePath = path.join(path.dirname(fileName), importedFilePath); + importedFilePath = path_1.default.join(path_1.default.dirname(fileName), importedFilePath); } if (/\.css$/.test(importedFilePath)) { transportCSS(importedFilePath, copyFile, writeOutputFile); } else { - const pathToCopy = path.join(options.sourcesRoot, importedFilePath); - if (fs.existsSync(pathToCopy) && !fs.statSync(pathToCopy).isDirectory()) { + const pathToCopy = path_1.default.join(options.sourcesRoot, importedFilePath); + if (fs_1.default.existsSync(pathToCopy) && !fs_1.default.statSync(pathToCopy).isDirectory()) { copyFile(importedFilePath); } } @@ -107,18 +143,18 @@ function extractEditor(options) { ].forEach(copyFile); } function createESMSourcesAndResources2(options) { - const SRC_FOLDER = path.join(REPO_ROOT, options.srcFolder); - const OUT_FOLDER = path.join(REPO_ROOT, options.outFolder); - const OUT_RESOURCES_FOLDER = path.join(REPO_ROOT, options.outResourcesFolder); + const SRC_FOLDER = path_1.default.join(REPO_ROOT, options.srcFolder); + const OUT_FOLDER = path_1.default.join(REPO_ROOT, options.outFolder); + const OUT_RESOURCES_FOLDER = path_1.default.join(REPO_ROOT, options.outResourcesFolder); const getDestAbsoluteFilePath = (file) => { const dest = options.renames[file.replace(/\\/g, '/')] || file; if (dest === 'tsconfig.json') { - return path.join(OUT_FOLDER, `tsconfig.json`); + return path_1.default.join(OUT_FOLDER, `tsconfig.json`); } if (/\.ts$/.test(dest)) { - return path.join(OUT_FOLDER, dest); + return path_1.default.join(OUT_FOLDER, dest); } - return path.join(OUT_RESOURCES_FOLDER, dest); + return path_1.default.join(OUT_RESOURCES_FOLDER, dest); }; const allFiles = walkDirRecursive(SRC_FOLDER); for (const file of allFiles) { @@ -126,15 +162,15 @@ function createESMSourcesAndResources2(options) { continue; } if (file === 'tsconfig.json') { - const tsConfig = JSON.parse(fs.readFileSync(path.join(SRC_FOLDER, file)).toString()); + const tsConfig = JSON.parse(fs_1.default.readFileSync(path_1.default.join(SRC_FOLDER, file)).toString()); tsConfig.compilerOptions.module = 'es2022'; - tsConfig.compilerOptions.outDir = path.join(path.relative(OUT_FOLDER, OUT_RESOURCES_FOLDER), 'vs').replace(/\\/g, '/'); + tsConfig.compilerOptions.outDir = path_1.default.join(path_1.default.relative(OUT_FOLDER, OUT_RESOURCES_FOLDER), 'vs').replace(/\\/g, '/'); write(getDestAbsoluteFilePath(file), JSON.stringify(tsConfig, null, '\t')); continue; } if (/\.ts$/.test(file) || /\.d\.ts$/.test(file) || /\.css$/.test(file) || /\.js$/.test(file) || /\.ttf$/.test(file)) { // Transport the files directly - write(getDestAbsoluteFilePath(file), fs.readFileSync(path.join(SRC_FOLDER, file))); + write(getDestAbsoluteFilePath(file), fs_1.default.readFileSync(path_1.default.join(SRC_FOLDER, file))); continue; } console.log(`UNKNOWN FILE: ${file}`); @@ -148,10 +184,10 @@ function createESMSourcesAndResources2(options) { return result; } function _walkDirRecursive(dir, result, trimPos) { - const files = fs.readdirSync(dir); + const files = fs_1.default.readdirSync(dir); for (let i = 0; i < files.length; i++) { - const file = path.join(dir, files[i]); - if (fs.statSync(file).isDirectory()) { + const file = path_1.default.join(dir, files[i]); + if (fs_1.default.statSync(file).isDirectory()) { _walkDirRecursive(file, result, trimPos); } else { @@ -206,8 +242,8 @@ function transportCSS(module, enqueue, write) { if (!/\.css/.test(module)) { return false; } - const filename = path.join(SRC_DIR, module); - const fileContents = fs.readFileSync(filename).toString(); + const filename = path_1.default.join(SRC_DIR, module); + const fileContents = fs_1.default.readFileSync(filename).toString(); const inlineResources = 'base64'; // see https://github.com/microsoft/monaco-editor/issues/148 const newContents = _rewriteOrInlineUrls(fileContents, inlineResources === 'base64'); write(module, newContents); @@ -217,12 +253,12 @@ function transportCSS(module, enqueue, write) { const fontMatch = url.match(/^(.*).ttf\?(.*)$/); if (fontMatch) { const relativeFontPath = `${fontMatch[1]}.ttf`; // trim the query parameter - const fontPath = path.join(path.dirname(module), relativeFontPath); + const fontPath = path_1.default.join(path_1.default.dirname(module), relativeFontPath); enqueue(fontPath); return relativeFontPath; } - const imagePath = path.join(path.dirname(module), url); - const fileContents = fs.readFileSync(path.join(SRC_DIR, imagePath)); + const imagePath = path_1.default.join(path_1.default.dirname(module), url); + const fileContents = fs_1.default.readFileSync(path_1.default.join(SRC_DIR, imagePath)); const MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; let DATA = ';base64,' + fileContents.toString('base64'); if (!forceBase64 && /\.svg$/.test(url)) { diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index 8736583fb092..b2ae02f10073 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; +import fs from 'fs'; +import path from 'path'; import * as tss from './treeshaking'; const REPO_ROOT = path.join(__dirname, '../../'); diff --git a/build/lib/stats.js b/build/lib/stats.js index e089cb0c1b44..3f6d953ae407 100644 --- a/build/lib/stats.js +++ b/build/lib/stats.js @@ -3,11 +3,14 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.createStatsStream = createStatsStream; -const es = require("event-stream"); -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); +const event_stream_1 = __importDefault(require("event-stream")); +const fancy_log_1 = __importDefault(require("fancy-log")); +const ansi_colors_1 = __importDefault(require("ansi-colors")); class Entry { name; totalCount; @@ -28,13 +31,13 @@ class Entry { } else { if (this.totalCount === 1) { - return `Stats for '${ansiColors.grey(this.name)}': ${Math.round(this.totalSize / 1204)}KB`; + return `Stats for '${ansi_colors_1.default.grey(this.name)}': ${Math.round(this.totalSize / 1204)}KB`; } else { const count = this.totalCount < 100 - ? ansiColors.green(this.totalCount.toString()) - : ansiColors.red(this.totalCount.toString()); - return `Stats for '${ansiColors.grey(this.name)}': ${count} files, ${Math.round(this.totalSize / 1204)}KB`; + ? ansi_colors_1.default.green(this.totalCount.toString()) + : ansi_colors_1.default.red(this.totalCount.toString()); + return `Stats for '${ansi_colors_1.default.grey(this.name)}': ${count} files, ${Math.round(this.totalSize / 1204)}KB`; } } } @@ -43,7 +46,7 @@ const _entries = new Map(); function createStatsStream(group, log) { const entry = new Entry(group, 0, 0); _entries.set(entry.name, entry); - return es.through(function (data) { + return event_stream_1.default.through(function (data) { const file = data; if (typeof file.path === 'string') { entry.totalCount += 1; @@ -61,13 +64,13 @@ function createStatsStream(group, log) { }, function () { if (log) { if (entry.totalCount === 1) { - fancyLog(`Stats for '${ansiColors.grey(entry.name)}': ${Math.round(entry.totalSize / 1204)}KB`); + (0, fancy_log_1.default)(`Stats for '${ansi_colors_1.default.grey(entry.name)}': ${Math.round(entry.totalSize / 1204)}KB`); } else { const count = entry.totalCount < 100 - ? ansiColors.green(entry.totalCount.toString()) - : ansiColors.red(entry.totalCount.toString()); - fancyLog(`Stats for '${ansiColors.grey(entry.name)}': ${count} files, ${Math.round(entry.totalSize / 1204)}KB`); + ? ansi_colors_1.default.green(entry.totalCount.toString()) + : ansi_colors_1.default.red(entry.totalCount.toString()); + (0, fancy_log_1.default)(`Stats for '${ansi_colors_1.default.grey(entry.name)}': ${count} files, ${Math.round(entry.totalSize / 1204)}KB`); } } this.emit('end'); diff --git a/build/lib/stats.ts b/build/lib/stats.ts index fe4b22453b50..8db55d3e7774 100644 --- a/build/lib/stats.ts +++ b/build/lib/stats.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as es from 'event-stream'; -import * as fancyLog from 'fancy-log'; -import * as ansiColors from 'ansi-colors'; -import * as File from 'vinyl'; +import es from 'event-stream'; +import fancyLog from 'fancy-log'; +import ansiColors from 'ansi-colors'; +import File from 'vinyl'; class Entry { constructor(readonly name: string, public totalCount: number, public totalSize: number) { } diff --git a/build/lib/stylelint/validateVariableNames.js b/build/lib/stylelint/validateVariableNames.js index 6a50d1d6894a..b0e064e7b561 100644 --- a/build/lib/stylelint/validateVariableNames.js +++ b/build/lib/stylelint/validateVariableNames.js @@ -3,15 +3,18 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.getVariableNameValidator = getVariableNameValidator; const fs_1 = require("fs"); -const path = require("path"); +const path_1 = __importDefault(require("path")); const RE_VAR_PROP = /var\(\s*(--([\w\-\.]+))/g; let knownVariables; function getKnownVariableNames() { if (!knownVariables) { - const knownVariablesFileContent = (0, fs_1.readFileSync)(path.join(__dirname, './vscode-known-variables.json'), 'utf8').toString(); + const knownVariablesFileContent = (0, fs_1.readFileSync)(path_1.default.join(__dirname, './vscode-known-variables.json'), 'utf8').toString(); const knownVariablesInfo = JSON.parse(knownVariablesFileContent); knownVariables = new Set([...knownVariablesInfo.colors, ...knownVariablesInfo.others]); } diff --git a/build/lib/stylelint/validateVariableNames.ts b/build/lib/stylelint/validateVariableNames.ts index 6d9fa8a7cef5..b28aed13f4b9 100644 --- a/build/lib/stylelint/validateVariableNames.ts +++ b/build/lib/stylelint/validateVariableNames.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { readFileSync } from 'fs'; -import path = require('path'); +import path from 'path'; const RE_VAR_PROP = /var\(\s*(--([\w\-\.]+))/g; diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 3d1330415fdf..6b8419291cb6 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -39,6 +39,9 @@ "--vscode-button-secondaryForeground", "--vscode-button-secondaryHoverBackground", "--vscode-button-separator", + "--vscode-chart-axis", + "--vscode-chart-guide", + "--vscode-chart-line", "--vscode-charts-blue", "--vscode-charts-foreground", "--vscode-charts-green", @@ -49,11 +52,11 @@ "--vscode-charts-yellow", "--vscode-chat-avatarBackground", "--vscode-chat-avatarForeground", + "--vscode-chat-editedFileForeground", "--vscode-chat-requestBackground", "--vscode-chat-requestBorder", "--vscode-chat-slashCommandBackground", "--vscode-chat-slashCommandForeground", - "--vscode-chat-editedFileForeground", "--vscode-checkbox-background", "--vscode-checkbox-border", "--vscode-checkbox-foreground", @@ -133,6 +136,7 @@ "--vscode-dropdown-foreground", "--vscode-dropdown-listBackground", "--vscode-editor-background", + "--vscode-editor-compositionBorder", "--vscode-editor-findMatchBackground", "--vscode-editor-findMatchBorder", "--vscode-editor-findMatchForeground", @@ -155,7 +159,6 @@ "--vscode-editor-placeholder-foreground", "--vscode-editor-rangeHighlightBackground", "--vscode-editor-rangeHighlightBorder", - "--vscode-editor-compositionBorder", "--vscode-editor-selectionBackground", "--vscode-editor-selectionForeground", "--vscode-editor-selectionHighlightBackground", @@ -229,6 +232,8 @@ "--vscode-editorGutter-commentGlyphForeground", "--vscode-editorGutter-commentRangeForeground", "--vscode-editorGutter-commentUnresolvedGlyphForeground", + "--vscode-editorGutter-itemGlyphForeground", + "--vscode-editorGutter-itemBackground", "--vscode-editorGutter-deletedBackground", "--vscode-editorGutter-foldingControlForeground", "--vscode-editorGutter-modifiedBackground", @@ -276,6 +281,7 @@ "--vscode-editorMarkerNavigationInfo-headerBackground", "--vscode-editorMarkerNavigationWarning-background", "--vscode-editorMarkerNavigationWarning-headerBackground", + "--vscode-editorMinimap-inlineChatInserted", "--vscode-editorMultiCursor-primary-background", "--vscode-editorMultiCursor-primary-foreground", "--vscode-editorMultiCursor-secondary-background", @@ -346,6 +352,13 @@ "--vscode-extensionIcon-verifiedForeground", "--vscode-focusBorder", "--vscode-foreground", + "--vscode-gauge-background", + "--vscode-gauge-border", + "--vscode-gauge-errorBackground", + "--vscode-gauge-errorForeground", + "--vscode-gauge-foreground", + "--vscode-gauge-warningBackground", + "--vscode-gauge-warningForeground", "--vscode-icon-foreground", "--vscode-inlineChat-background", "--vscode-inlineChat-border", @@ -357,6 +370,26 @@ "--vscode-inlineChatInput-border", "--vscode-inlineChatInput-focusBorder", "--vscode-inlineChatInput-placeholderForeground", + "--vscode-inlineEdit-gutterIndicator-background", + "--vscode-inlineEdit-gutterIndicator-primaryBackground", + "--vscode-inlineEdit-gutterIndicator-primaryForeground", + "--vscode-inlineEdit-gutterIndicator-secondaryBackground", + "--vscode-inlineEdit-gutterIndicator-secondaryForeground", + "--vscode-inlineEdit-gutterIndicator-successfulBackground", + "--vscode-inlineEdit-gutterIndicator-successfulForeground", + "--vscode-inlineEdit-indicator-background", + "--vscode-inlineEdit-indicator-border", + "--vscode-inlineEdit-indicator-foreground", + "--vscode-inlineEdit-modifiedBackground", + "--vscode-inlineEdit-modifiedBorder", + "--vscode-inlineEdit-modifiedChangedLineBackground", + "--vscode-inlineEdit-modifiedChangedTextBackground", + "--vscode-inlineEdit-originalBackground", + "--vscode-inlineEdit-originalBorder", + "--vscode-inlineEdit-originalChangedLineBackground", + "--vscode-inlineEdit-originalChangedTextBackground", + "--vscode-inlineEdit-tabWillAcceptBorder", + "--vscode-inlineEdit-wordReplacementView-background", "--vscode-input-background", "--vscode-input-border", "--vscode-input-foreground", @@ -441,6 +474,7 @@ "--vscode-mergeEditor-conflict-unhandledUnfocused-border", "--vscode-mergeEditor-conflictingLines-background", "--vscode-minimap-background", + "--vscode-minimap-chatEditHighlight", "--vscode-minimap-errorHighlight", "--vscode-minimap-findMatchHighlight", "--vscode-minimap-foregroundOpacity", @@ -508,7 +542,10 @@ "--vscode-panelStickyScroll-shadow", "--vscode-panelTitle-activeBorder", "--vscode-panelTitle-activeForeground", + "--vscode-panelTitle-border", "--vscode-panelTitle-inactiveForeground", + "--vscode-panelTitleBadge-background", + "--vscode-panelTitleBadge-foreground", "--vscode-peekView-border", "--vscode-peekViewEditor-background", "--vscode-peekViewEditor-matchHighlightBackground", @@ -604,6 +641,7 @@ "--vscode-sideBarStickyScroll-border", "--vscode-sideBarStickyScroll-shadow", "--vscode-sideBarTitle-background", + "--vscode-sideBarTitle-border", "--vscode-sideBarTitle-foreground", "--vscode-sideBySideEditor-horizontalBorder", "--vscode-sideBySideEditor-verticalBorder", @@ -746,6 +784,8 @@ "--vscode-terminalStickyScroll-background", "--vscode-terminalStickyScroll-border", "--vscode-terminalStickyScrollHover-background", + "--vscode-terminalSymbolIcon-aliasForeground", + "--vscode-terminalSymbolIcon-flagForeground", "--vscode-testing-coverCountBadgeBackground", "--vscode-testing-coverCountBadgeForeground", "--vscode-testing-coveredBackground", @@ -763,7 +803,9 @@ "--vscode-testing-iconSkipped-retired", "--vscode-testing-iconUnset", "--vscode-testing-iconUnset-retired", - "--vscode-testing-message-error-decorationForeground", + "--vscode-testing-message-error-badgeBackground", + "--vscode-testing-message-error-badgeBorder", + "--vscode-testing-message-error-badgeForeground", "--vscode-testing-message-error-lineBackground", "--vscode-testing-message-info-decorationForeground", "--vscode-testing-message-info-lineBackground", @@ -812,8 +854,12 @@ "others": [ "--background-dark", "--background-light", + "--chat-editing-last-edit-shift", + "--chat-current-response-min-height", "--dropdown-padding-bottom", "--dropdown-padding-top", + "--inline-chat-frame-progress", + "--inline-chat-hint-progress", "--insert-border-color", "--last-tab-margin-right", "--monaco-monospace-font", @@ -824,6 +870,9 @@ "--notebook-diff-view-viewport-slider", "--notebook-find-horizontal-padding", "--notebook-find-width", + "--notebook-editor-font-family", + "--notebook-editor-font-size", + "--notebook-editor-font-weight", "--outline-element-color", "--separator-border", "--status-border-top-color", @@ -887,6 +936,9 @@ "--zoom-factor", "--test-bar-width", "--widget-color", - "--text-link-decoration" + "--text-link-decoration", + "--vscode-action-item-auto-timeout", + "--monaco-editor-warning-decoration", + "--animation-opacity" ] -} +} \ No newline at end of file diff --git a/build/lib/task.js b/build/lib/task.js index 597b2a0d3976..6887714681af 100644 --- a/build/lib/task.js +++ b/build/lib/task.js @@ -3,12 +3,15 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.series = series; exports.parallel = parallel; exports.define = define; -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); +const fancy_log_1 = __importDefault(require("fancy-log")); +const ansi_colors_1 = __importDefault(require("ansi-colors")); function _isPromise(p) { if (typeof p.then === 'function') { return true; @@ -21,14 +24,14 @@ function _renderTime(time) { async function _execute(task) { const name = task.taskName || task.displayName || ``; if (!task._tasks) { - fancyLog('Starting', ansiColors.cyan(name), '...'); + (0, fancy_log_1.default)('Starting', ansi_colors_1.default.cyan(name), '...'); } const startTime = process.hrtime(); await _doExecute(task); const elapsedArr = process.hrtime(startTime); const elapsedNanoseconds = (elapsedArr[0] * 1e9 + elapsedArr[1]); if (!task._tasks) { - fancyLog(`Finished`, ansiColors.cyan(name), 'after', ansiColors.magenta(_renderTime(elapsedNanoseconds / 1e6))); + (0, fancy_log_1.default)(`Finished`, ansi_colors_1.default.cyan(name), 'after', ansi_colors_1.default.magenta(_renderTime(elapsedNanoseconds / 1e6))); } } async function _doExecute(task) { diff --git a/build/lib/task.ts b/build/lib/task.ts index 7d2a4dee0169..6af239831785 100644 --- a/build/lib/task.ts +++ b/build/lib/task.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fancyLog from 'fancy-log'; -import * as ansiColors from 'ansi-colors'; +import fancyLog from 'fancy-log'; +import ansiColors from 'ansi-colors'; export interface BaseTask { displayName?: string; diff --git a/build/lib/test/i18n.test.js b/build/lib/test/i18n.test.js index b8f4a2bedef5..41aa8a7f668d 100644 --- a/build/lib/test/i18n.test.js +++ b/build/lib/test/i18n.test.js @@ -3,9 +3,45 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const assert = require("assert"); -const i18n = require("../i18n"); +const assert_1 = __importDefault(require("assert")); +const i18n = __importStar(require("../i18n")); suite('XLF Parser Tests', () => { const sampleXlf = 'Key #1Key #2 &'; const sampleTranslatedXlf = 'Key #1Кнопка #1Key #2 &Кнопка #2 &'; @@ -17,25 +53,25 @@ suite('XLF Parser Tests', () => { const xlf = new i18n.XLF('vscode-workbench'); xlf.addFile(name, keys, messages); const xlfString = xlf.toString(); - assert.strictEqual(xlfString.replace(/\s{2,}/g, ''), sampleXlf); + assert_1.default.strictEqual(xlfString.replace(/\s{2,}/g, ''), sampleXlf); }); test('XLF to keys & messages conversion', () => { i18n.XLF.parse(sampleTranslatedXlf).then(function (resolvedFiles) { - assert.deepStrictEqual(resolvedFiles[0].messages, translatedMessages); - assert.strictEqual(resolvedFiles[0].name, name); + assert_1.default.deepStrictEqual(resolvedFiles[0].messages, translatedMessages); + assert_1.default.strictEqual(resolvedFiles[0].name, name); }); }); test('JSON file source path to Transifex resource match', () => { const editorProject = 'vscode-editor', workbenchProject = 'vscode-workbench'; const platform = { name: 'vs/platform', project: editorProject }, editorContrib = { name: 'vs/editor/contrib', project: editorProject }, editor = { name: 'vs/editor', project: editorProject }, base = { name: 'vs/base', project: editorProject }, code = { name: 'vs/code', project: workbenchProject }, workbenchParts = { name: 'vs/workbench/contrib/html', project: workbenchProject }, workbenchServices = { name: 'vs/workbench/services/textfile', project: workbenchProject }, workbench = { name: 'vs/workbench', project: workbenchProject }; - assert.deepStrictEqual(i18n.getResource('vs/platform/actions/browser/menusExtensionPoint'), platform); - assert.deepStrictEqual(i18n.getResource('vs/editor/contrib/clipboard/browser/clipboard'), editorContrib); - assert.deepStrictEqual(i18n.getResource('vs/editor/common/modes/modesRegistry'), editor); - assert.deepStrictEqual(i18n.getResource('vs/base/common/errorMessage'), base); - assert.deepStrictEqual(i18n.getResource('vs/code/electron-main/window'), code); - assert.deepStrictEqual(i18n.getResource('vs/workbench/contrib/html/browser/webview'), workbenchParts); - assert.deepStrictEqual(i18n.getResource('vs/workbench/services/textfile/node/testFileService'), workbenchServices); - assert.deepStrictEqual(i18n.getResource('vs/workbench/browser/parts/panel/panelActions'), workbench); + assert_1.default.deepStrictEqual(i18n.getResource('vs/platform/actions/browser/menusExtensionPoint'), platform); + assert_1.default.deepStrictEqual(i18n.getResource('vs/editor/contrib/clipboard/browser/clipboard'), editorContrib); + assert_1.default.deepStrictEqual(i18n.getResource('vs/editor/common/modes/modesRegistry'), editor); + assert_1.default.deepStrictEqual(i18n.getResource('vs/base/common/errorMessage'), base); + assert_1.default.deepStrictEqual(i18n.getResource('vs/code/electron-main/window'), code); + assert_1.default.deepStrictEqual(i18n.getResource('vs/workbench/contrib/html/browser/webview'), workbenchParts); + assert_1.default.deepStrictEqual(i18n.getResource('vs/workbench/services/textfile/node/testFileService'), workbenchServices); + assert_1.default.deepStrictEqual(i18n.getResource('vs/workbench/browser/parts/panel/panelActions'), workbench); }); }); //# sourceMappingURL=i18n.test.js.map \ No newline at end of file diff --git a/build/lib/test/i18n.test.ts b/build/lib/test/i18n.test.ts index b8a68323dd71..4e4545548b86 100644 --- a/build/lib/test/i18n.test.ts +++ b/build/lib/test/i18n.test.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); -import i18n = require('../i18n'); +import assert from 'assert'; +import * as i18n from '../i18n'; suite('XLF Parser Tests', () => { const sampleXlf = 'Key #1Key #2 &'; diff --git a/build/lib/treeshaking.js b/build/lib/treeshaking.js index af06f4e3ec5d..d51eee91f1e3 100644 --- a/build/lib/treeshaking.js +++ b/build/lib/treeshaking.js @@ -3,13 +3,16 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.ShakeLevel = void 0; exports.toStringShakeLevel = toStringShakeLevel; exports.shake = shake; -const fs = require("fs"); -const path = require("path"); -const TYPESCRIPT_LIB_FOLDER = path.dirname(require.resolve('typescript/lib/lib.d.ts')); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const TYPESCRIPT_LIB_FOLDER = path_1.default.dirname(require.resolve('typescript/lib/lib.d.ts')); var ShakeLevel; (function (ShakeLevel) { ShakeLevel[ShakeLevel["Files"] = 0] = "Files"; @@ -30,7 +33,7 @@ function printDiagnostics(options, diagnostics) { for (const diag of diagnostics) { let result = ''; if (diag.file) { - result += `${path.join(options.sourcesRoot, diag.file.fileName)}`; + result += `${path_1.default.join(options.sourcesRoot, diag.file.fileName)}`; } if (diag.file && diag.start) { const location = diag.file.getLineAndCharacterOfPosition(diag.start); @@ -72,8 +75,8 @@ function createTypeScriptLanguageService(ts, options) { }); // Add additional typings options.typings.forEach((typing) => { - const filePath = path.join(options.sourcesRoot, typing); - FILES[typing] = fs.readFileSync(filePath).toString(); + const filePath = path_1.default.join(options.sourcesRoot, typing); + FILES[typing] = fs_1.default.readFileSync(filePath).toString(); }); // Resolve libs const RESOLVED_LIBS = processLibFiles(ts, options); @@ -104,19 +107,19 @@ function discoverAndReadFiles(ts, options) { if (options.redirects[moduleId]) { redirectedModuleId = options.redirects[moduleId]; } - const dts_filename = path.join(options.sourcesRoot, redirectedModuleId + '.d.ts'); - if (fs.existsSync(dts_filename)) { - const dts_filecontents = fs.readFileSync(dts_filename).toString(); + const dts_filename = path_1.default.join(options.sourcesRoot, redirectedModuleId + '.d.ts'); + if (fs_1.default.existsSync(dts_filename)) { + const dts_filecontents = fs_1.default.readFileSync(dts_filename).toString(); FILES[`${moduleId}.d.ts`] = dts_filecontents; continue; } - const js_filename = path.join(options.sourcesRoot, redirectedModuleId + '.js'); - if (fs.existsSync(js_filename)) { + const js_filename = path_1.default.join(options.sourcesRoot, redirectedModuleId + '.js'); + if (fs_1.default.existsSync(js_filename)) { // This is an import for a .js file, so ignore it... continue; } - const ts_filename = path.join(options.sourcesRoot, redirectedModuleId + '.ts'); - const ts_filecontents = fs.readFileSync(ts_filename).toString(); + const ts_filename = path_1.default.join(options.sourcesRoot, redirectedModuleId + '.ts'); + const ts_filecontents = fs_1.default.readFileSync(ts_filename).toString(); const info = ts.preProcessFile(ts_filecontents); for (let i = info.importedFiles.length - 1; i >= 0; i--) { const importedFileName = info.importedFiles[i].fileName; @@ -126,7 +129,7 @@ function discoverAndReadFiles(ts, options) { } let importedModuleId = importedFileName; if (/(^\.\/)|(^\.\.\/)/.test(importedModuleId)) { - importedModuleId = path.join(path.dirname(moduleId), importedModuleId); + importedModuleId = path_1.default.join(path_1.default.dirname(moduleId), importedModuleId); if (importedModuleId.endsWith('.js')) { // ESM: code imports require to be relative and have a '.js' file extension importedModuleId = importedModuleId.substr(0, importedModuleId.length - 3); } @@ -148,8 +151,8 @@ function processLibFiles(ts, options) { const key = `defaultLib:${filename}`; if (!result[key]) { // add this file - const filepath = path.join(TYPESCRIPT_LIB_FOLDER, filename); - const sourceText = fs.readFileSync(filepath).toString(); + const filepath = path_1.default.join(TYPESCRIPT_LIB_FOLDER, filename); + const sourceText = fs_1.default.readFileSync(filepath).toString(); result[key] = sourceText; // precess dependencies and "recurse" const info = ts.preProcessFile(sourceText); @@ -459,7 +462,7 @@ function markNodes(ts, languageService, options) { if (importText.endsWith('.js')) { // ESM: code imports require to be relative and to have a '.js' file extension importText = importText.substr(0, importText.length - 3); } - fullPath = path.join(path.dirname(nodeSourceFile.fileName), importText) + '.ts'; + fullPath = path_1.default.join(path_1.default.dirname(nodeSourceFile.fileName), importText) + '.ts'; } else { fullPath = importText + '.ts'; diff --git a/build/lib/treeshaking.ts b/build/lib/treeshaking.ts index cd17c5f02784..ac71bb205da7 100644 --- a/build/lib/treeshaking.ts +++ b/build/lib/treeshaking.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; +import fs from 'fs'; +import path from 'path'; import type * as ts from 'typescript'; const TYPESCRIPT_LIB_FOLDER = path.dirname(require.resolve('typescript/lib/lib.d.ts')); diff --git a/build/lib/tsb/builder.js b/build/lib/tsb/builder.js index cee2758e8967..84308191361a 100644 --- a/build/lib/tsb/builder.js +++ b/build/lib/tsb/builder.js @@ -3,16 +3,52 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.CancellationToken = void 0; exports.createTypeScriptBuilder = createTypeScriptBuilder; -const fs = require("fs"); -const path = require("path"); -const crypto = require("crypto"); -const utils = require("./utils"); -const colors = require("ansi-colors"); -const ts = require("typescript"); -const Vinyl = require("vinyl"); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const crypto_1 = __importDefault(require("crypto")); +const utils = __importStar(require("./utils")); +const ansi_colors_1 = __importDefault(require("ansi-colors")); +const typescript_1 = __importDefault(require("typescript")); +const vinyl_1 = __importDefault(require("vinyl")); const source_map_1 = require("source-map"); var CancellationToken; (function (CancellationToken) { @@ -26,7 +62,9 @@ function normalize(path) { function createTypeScriptBuilder(config, projectFile, cmd) { const _log = config.logFn; const host = new LanguageServiceHost(cmd, projectFile, _log); - const service = ts.createLanguageService(host, ts.createDocumentRegistry()); + const outHost = new LanguageServiceHost({ ...cmd, options: { ...cmd.options, sourceRoot: cmd.options.outDir } }, cmd.options.outDir ?? '', _log); + let lastCycleCheckVersion; + const service = typescript_1.default.createLanguageService(host, typescript_1.default.createDocumentRegistry()); const lastBuildVersion = Object.create(null); const lastDtsHash = Object.create(null); const userWantsDeclarations = cmd.options.declaration; @@ -42,6 +80,7 @@ function createTypeScriptBuilder(config, projectFile, cmd) { } if (!file.contents) { host.removeScriptSnapshot(file.path); + delete lastBuildVersion[normalize(file.path)]; } else { host.addScriptSnapshot(file.path, new VinylScriptSnapshot(file)); @@ -90,7 +129,7 @@ function createTypeScriptBuilder(config, projectFile, cmd) { if (/\.d\.ts$/.test(fileName)) { // if it's already a d.ts file just emit it signature const snapshot = host.getScriptSnapshot(fileName); - const signature = crypto.createHash('sha256') + const signature = crypto_1.default.createHash('sha256') .update(snapshot.getText(0, snapshot.getLength())) .digest('base64'); return resolve({ @@ -107,7 +146,7 @@ function createTypeScriptBuilder(config, projectFile, cmd) { continue; } if (/\.d\.ts$/.test(file.name)) { - signature = crypto.createHash('sha256') + signature = crypto_1.default.createHash('sha256') .update(file.text) .digest('base64'); if (!userWantsDeclarations) { @@ -115,7 +154,7 @@ function createTypeScriptBuilder(config, projectFile, cmd) { continue; } } - const vinyl = new Vinyl({ + const vinyl = new vinyl_1.default({ path: file.name, contents: Buffer.from(file.text), base: !config._emitWithoutBasePath && baseFor(host.getScriptSnapshot(fileName)) || undefined @@ -123,9 +162,9 @@ function createTypeScriptBuilder(config, projectFile, cmd) { if (!emitSourceMapsInStream && /\.js$/.test(file.name)) { const sourcemapFile = output.outputFiles.filter(f => /\.js\.map$/.test(f.name))[0]; if (sourcemapFile) { - const extname = path.extname(vinyl.relative); - const basename = path.basename(vinyl.relative, extname); - const dirname = path.dirname(vinyl.relative); + const extname = path_1.default.extname(vinyl.relative); + const basename = path_1.default.basename(vinyl.relative, extname); + const dirname = path_1.default.dirname(vinyl.relative); const tsname = (dirname === '.' ? '' : dirname + '/') + basename + '.ts'; let sourceMap = JSON.parse(sourcemapFile.text); sourceMap.sources[0] = tsname.replace(/\\/g, '/'); @@ -251,6 +290,11 @@ function createTypeScriptBuilder(config, projectFile, cmd) { lastDtsHash[fileName] = value.signature; filesWithChangedSignature.push(fileName); } + // line up for cycle check + const jsValue = value.files.find(candidate => candidate.basename.endsWith('.js')); + if (jsValue) { + outHost.addScriptSnapshot(jsValue.path, new ScriptSnapshot(String(jsValue.contents), new Date())); + } }).catch(e => { // can't just skip this or make a result up.. host.error(`ERROR emitting ${fileName}`); @@ -341,21 +385,42 @@ function createTypeScriptBuilder(config, projectFile, cmd) { }); } workOnNext(); + }).then(() => { + // check for cyclic dependencies + const thisCycleCheckVersion = outHost.getProjectVersion(); + if (thisCycleCheckVersion === lastCycleCheckVersion) { + return; + } + const oneCycle = outHost.hasCyclicDependency(); + lastCycleCheckVersion = thisCycleCheckVersion; + delete oldErrors[projectFile]; + if (oneCycle) { + const cycleError = { + category: typescript_1.default.DiagnosticCategory.Error, + code: 1, + file: undefined, + start: undefined, + length: undefined, + messageText: `CYCLIC dependency between ${oneCycle}` + }; + onError(cycleError); + newErrors[projectFile] = [cycleError]; + } }).then(() => { // store the build versions to not rebuilt the next time newLastBuildVersion.forEach((value, key) => { lastBuildVersion[key] = value; }); // print old errors and keep them - utils.collections.forEach(oldErrors, entry => { - entry.value.forEach(diag => onError(diag)); - newErrors[entry.key] = entry.value; - }); + for (const [key, value] of Object.entries(oldErrors)) { + value.forEach(diag => onError(diag)); + newErrors[key] = value; + } oldErrors = newErrors; // print stats const headNow = process.memoryUsage().heapUsed; const MB = 1024 * 1024; - _log('[tsb]', `time: ${colors.yellow((Date.now() - t1) + 'ms')} + \nmem: ${colors.cyan(Math.ceil(headNow / MB) + 'MB')} ${colors.bgcyan('delta: ' + Math.ceil((headNow - headUsed) / MB))}`); + _log('[tsb]', `time: ${ansi_colors_1.default.yellow((Date.now() - t1) + 'ms')} + \nmem: ${ansi_colors_1.default.cyan(Math.ceil(headNow / MB) + 'MB')} ${ansi_colors_1.default.bgcyan('delta: ' + Math.ceil((headNow - headUsed) / MB))}`); headUsed = headNow; }); } @@ -415,7 +480,7 @@ class LanguageServiceHost { this._snapshots = Object.create(null); this._filesInProject = new Set(_cmdLine.fileNames); this._filesAdded = new Set(); - this._dependencies = new utils.graph.Graph(s => s); + this._dependencies = new utils.graph.Graph(); this._dependenciesRecomputeList = []; this._fileNameToDeclaredModule = Object.create(null); this._projectVersion = 1; @@ -452,11 +517,11 @@ class LanguageServiceHost { let result = this._snapshots[filename]; if (!result && resolve) { try { - result = new VinylScriptSnapshot(new Vinyl({ + result = new VinylScriptSnapshot(new vinyl_1.default({ path: filename, - contents: fs.readFileSync(filename), + contents: fs_1.default.readFileSync(filename), base: this.getCompilationSettings().outDir, - stat: fs.statSync(filename) + stat: fs_1.default.statSync(filename) })); this.addScriptSnapshot(filename, result); } @@ -478,10 +543,6 @@ class LanguageServiceHost { } if (!old || old.getVersion() !== snapshot.getVersion()) { this._dependenciesRecomputeList.push(filename); - const node = this._dependencies.lookup(filename); - if (node) { - node.outgoing = Object.create(null); - } // (cheap) check for declare module LanguageServiceHost._declareModule.lastIndex = 0; let match; @@ -505,16 +566,16 @@ class LanguageServiceHost { return delete this._snapshots[filename]; } getCurrentDirectory() { - return path.dirname(this._projectPath); + return path_1.default.dirname(this._projectPath); } getDefaultLibFileName(options) { - return ts.getDefaultLibFilePath(options); + return typescript_1.default.getDefaultLibFilePath(options); } - directoryExists = ts.sys.directoryExists; - getDirectories = ts.sys.getDirectories; - fileExists = ts.sys.fileExists; - readFile = ts.sys.readFile; - readDirectory = ts.sys.readDirectory; + directoryExists = typescript_1.default.sys.directoryExists; + getDirectories = typescript_1.default.sys.getDirectories; + fileExists = typescript_1.default.sys.fileExists; + readFile = typescript_1.default.sys.readFile; + readDirectory = typescript_1.default.sys.readDirectory; // ---- dependency management collectDependents(filename, target) { while (this._dependenciesRecomputeList.length) { @@ -523,9 +584,19 @@ class LanguageServiceHost { filename = normalize(filename); const node = this._dependencies.lookup(filename); if (node) { - utils.collections.forEach(node.incoming, entry => target.push(entry.key)); + node.incoming.forEach(entry => target.push(entry.data)); } } + hasCyclicDependency() { + // Ensure dependencies are up to date + while (this._dependenciesRecomputeList.length) { + this._processFile(this._dependenciesRecomputeList.pop()); + } + const cycle = this._dependencies.findCycle(); + return cycle + ? cycle.join(' -> ') + : undefined; + } _processFile(filename) { if (filename.match(/.*\.d\.ts$/)) { return; @@ -536,21 +607,27 @@ class LanguageServiceHost { this._log('processFile', `Missing snapshot for: ${filename}`); return; } - const info = ts.preProcessFile(snapshot.getText(0, snapshot.getLength()), true); + const info = typescript_1.default.preProcessFile(snapshot.getText(0, snapshot.getLength()), true); + // (0) clear out old dependencies + this._dependencies.resetNode(filename); // (1) ///-references info.referencedFiles.forEach(ref => { - const resolvedPath = path.resolve(path.dirname(filename), ref.fileName); + const resolvedPath = path_1.default.resolve(path_1.default.dirname(filename), ref.fileName); const normalizedPath = normalize(resolvedPath); this._dependencies.inertEdge(filename, normalizedPath); }); // (2) import-require statements info.importedFiles.forEach(ref => { + if (!ref.fileName.startsWith('.')) { + // node module? + return; + } const stopDirname = normalize(this.getCurrentDirectory()); let dirname = filename; let found = false; while (!found && dirname.indexOf(stopDirname) === 0) { - dirname = path.dirname(dirname); - let resolvedPath = path.resolve(dirname, ref.fileName); + dirname = path_1.default.dirname(dirname); + let resolvedPath = path_1.default.resolve(dirname, ref.fileName); if (resolvedPath.endsWith('.js')) { resolvedPath = resolvedPath.slice(0, -3); } @@ -563,6 +640,10 @@ class LanguageServiceHost { this._dependencies.inertEdge(filename, normalizedPath + '.d.ts'); found = true; } + else if (this.getScriptSnapshot(normalizedPath + '.js')) { + this._dependencies.inertEdge(filename, normalizedPath + '.js'); + found = true; + } } if (!found) { for (const key in this._fileNameToDeclaredModule) { diff --git a/build/lib/tsb/builder.ts b/build/lib/tsb/builder.ts index 70c71591a6eb..7a1b0e0cbb44 100644 --- a/build/lib/tsb/builder.ts +++ b/build/lib/tsb/builder.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; -import * as path from 'path'; -import * as crypto from 'crypto'; +import fs from 'fs'; +import path from 'path'; +import crypto from 'crypto'; import * as utils from './utils'; -import * as colors from 'ansi-colors'; -import * as ts from 'typescript'; -import * as Vinyl from 'vinyl'; +import colors from 'ansi-colors'; +import ts from 'typescript'; +import Vinyl from 'vinyl'; import { RawSourceMap, SourceMapConsumer, SourceMapGenerator } from 'source-map'; export interface IConfiguration { @@ -42,6 +42,10 @@ export function createTypeScriptBuilder(config: IConfiguration, projectFile: str const _log = config.logFn; const host = new LanguageServiceHost(cmd, projectFile, _log); + + const outHost = new LanguageServiceHost({ ...cmd, options: { ...cmd.options, sourceRoot: cmd.options.outDir } }, cmd.options.outDir ?? '', _log); + let lastCycleCheckVersion: string; + const service = ts.createLanguageService(host, ts.createDocumentRegistry()); const lastBuildVersion: { [path: string]: string } = Object.create(null); const lastDtsHash: { [path: string]: string } = Object.create(null); @@ -61,6 +65,7 @@ export function createTypeScriptBuilder(config: IConfiguration, projectFile: str if (!file.contents) { host.removeScriptSnapshot(file.path); + delete lastBuildVersion[normalize(file.path)]; } else { host.addScriptSnapshot(file.path, new VinylScriptSnapshot(file)); } @@ -305,6 +310,13 @@ export function createTypeScriptBuilder(config: IConfiguration, projectFile: str lastDtsHash[fileName] = value.signature; filesWithChangedSignature.push(fileName); } + + // line up for cycle check + const jsValue = value.files.find(candidate => candidate.basename.endsWith('.js')); + if (jsValue) { + outHost.addScriptSnapshot(jsValue.path, new ScriptSnapshot(String(jsValue.contents), new Date())); + } + }).catch(e => { // can't just skip this or make a result up.. host.error(`ERROR emitting ${fileName}`); @@ -389,6 +401,7 @@ export function createTypeScriptBuilder(config: IConfiguration, projectFile: str } } + // (last) done else { resolve(); @@ -410,16 +423,40 @@ export function createTypeScriptBuilder(config: IConfiguration, projectFile: str workOnNext(); }).then(() => { + // check for cyclic dependencies + const thisCycleCheckVersion = outHost.getProjectVersion(); + if (thisCycleCheckVersion === lastCycleCheckVersion) { + return; + } + const oneCycle = outHost.hasCyclicDependency(); + lastCycleCheckVersion = thisCycleCheckVersion; + delete oldErrors[projectFile]; + + if (oneCycle) { + const cycleError: ts.Diagnostic = { + category: ts.DiagnosticCategory.Error, + code: 1, + file: undefined, + start: undefined, + length: undefined, + messageText: `CYCLIC dependency between ${oneCycle}` + }; + onError(cycleError); + newErrors[projectFile] = [cycleError]; + } + + }).then(() => { + // store the build versions to not rebuilt the next time newLastBuildVersion.forEach((value, key) => { lastBuildVersion[key] = value; }); // print old errors and keep them - utils.collections.forEach(oldErrors, entry => { - entry.value.forEach(diag => onError(diag)); - newErrors[entry.key] = entry.value; - }); + for (const [key, value] of Object.entries(oldErrors)) { + value.forEach(diag => onError(diag)); + newErrors[key] = value; + } oldErrors = newErrors; // print stats @@ -503,7 +540,7 @@ class LanguageServiceHost implements ts.LanguageServiceHost { this._snapshots = Object.create(null); this._filesInProject = new Set(_cmdLine.fileNames); this._filesAdded = new Set(); - this._dependencies = new utils.graph.Graph(s => s); + this._dependencies = new utils.graph.Graph(); this._dependenciesRecomputeList = []; this._fileNameToDeclaredModule = Object.create(null); @@ -576,10 +613,6 @@ class LanguageServiceHost implements ts.LanguageServiceHost { } if (!old || old.getVersion() !== snapshot.getVersion()) { this._dependenciesRecomputeList.push(filename); - const node = this._dependencies.lookup(filename); - if (node) { - node.outgoing = Object.create(null); - } // (cheap) check for declare module LanguageServiceHost._declareModule.lastIndex = 0; @@ -628,10 +661,21 @@ class LanguageServiceHost implements ts.LanguageServiceHost { filename = normalize(filename); const node = this._dependencies.lookup(filename); if (node) { - utils.collections.forEach(node.incoming, entry => target.push(entry.key)); + node.incoming.forEach(entry => target.push(entry.data)); } } + hasCyclicDependency(): string | undefined { + // Ensure dependencies are up to date + while (this._dependenciesRecomputeList.length) { + this._processFile(this._dependenciesRecomputeList.pop()!); + } + const cycle = this._dependencies.findCycle(); + return cycle + ? cycle.join(' -> ') + : undefined; + } + _processFile(filename: string): void { if (filename.match(/.*\.d\.ts$/)) { return; @@ -644,6 +688,9 @@ class LanguageServiceHost implements ts.LanguageServiceHost { } const info = ts.preProcessFile(snapshot.getText(0, snapshot.getLength()), true); + // (0) clear out old dependencies + this._dependencies.resetNode(filename); + // (1) ///-references info.referencedFiles.forEach(ref => { const resolvedPath = path.resolve(path.dirname(filename), ref.fileName); @@ -654,10 +701,18 @@ class LanguageServiceHost implements ts.LanguageServiceHost { // (2) import-require statements info.importedFiles.forEach(ref => { + + if (!ref.fileName.startsWith('.')) { + // node module? + return; + } + + const stopDirname = normalize(this.getCurrentDirectory()); let dirname = filename; let found = false; + while (!found && dirname.indexOf(stopDirname) === 0) { dirname = path.dirname(dirname); let resolvedPath = path.resolve(dirname, ref.fileName); @@ -673,6 +728,10 @@ class LanguageServiceHost implements ts.LanguageServiceHost { } else if (this.getScriptSnapshot(normalizedPath + '.d.ts')) { this._dependencies.inertEdge(filename, normalizedPath + '.d.ts'); found = true; + + } else if (this.getScriptSnapshot(normalizedPath + '.js')) { + this._dependencies.inertEdge(filename, normalizedPath + '.js'); + found = true; } } diff --git a/build/lib/tsb/index.js b/build/lib/tsb/index.js index 204c06e80ac9..843b76c823fc 100644 --- a/build/lib/tsb/index.js +++ b/build/lib/tsb/index.js @@ -3,17 +3,53 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.create = create; -const Vinyl = require("vinyl"); -const through = require("through"); -const builder = require("./builder"); -const ts = require("typescript"); +const vinyl_1 = __importDefault(require("vinyl")); +const through_1 = __importDefault(require("through")); +const builder = __importStar(require("./builder")); +const typescript_1 = __importDefault(require("typescript")); const stream_1 = require("stream"); const path_1 = require("path"); const utils_1 = require("./utils"); const fs_1 = require("fs"); -const log = require("fancy-log"); +const fancy_log_1 = __importDefault(require("fancy-log")); const transpiler_1 = require("./transpiler"); const colors = require("ansi-colors"); class EmptyDuplex extends stream_1.Duplex { @@ -32,31 +68,31 @@ function create(projectPath, existingOptions, config, onError = _defaultOnError) onError(diag.message); } else if (!diag.file || !diag.start) { - onError(ts.flattenDiagnosticMessageText(diag.messageText, '\n')); + onError(typescript_1.default.flattenDiagnosticMessageText(diag.messageText, '\n')); } else { const lineAndCh = diag.file.getLineAndCharacterOfPosition(diag.start); - onError(utils_1.strings.format('{0}({1},{2}): {3}', diag.file.fileName, lineAndCh.line + 1, lineAndCh.character + 1, ts.flattenDiagnosticMessageText(diag.messageText, '\n'))); + onError(utils_1.strings.format('{0}({1},{2}): {3}', diag.file.fileName, lineAndCh.line + 1, lineAndCh.character + 1, typescript_1.default.flattenDiagnosticMessageText(diag.messageText, '\n'))); } } - const parsed = ts.readConfigFile(projectPath, ts.sys.readFile); + const parsed = typescript_1.default.readConfigFile(projectPath, typescript_1.default.sys.readFile); if (parsed.error) { printDiagnostic(parsed.error); return createNullCompiler(); } - const cmdLine = ts.parseJsonConfigFileContent(parsed.config, ts.sys, (0, path_1.dirname)(projectPath), existingOptions); + const cmdLine = typescript_1.default.parseJsonConfigFileContent(parsed.config, typescript_1.default.sys, (0, path_1.dirname)(projectPath), existingOptions); if (cmdLine.errors.length > 0) { cmdLine.errors.forEach(printDiagnostic); return createNullCompiler(); } function logFn(topic, message) { if (config.verbose) { - log(colors.cyan(topic), message); + (0, fancy_log_1.default)(colors.cyan(topic), message); } } // FULL COMPILE stream doing transpile, syntax and semantic diagnostics function createCompileStream(builder, token) { - return through(function (file) { + return (0, through_1.default)(function (file) { // give the file to the compiler if (file.isStream()) { this.emit('error', 'no support for streams'); @@ -70,7 +106,7 @@ function create(projectPath, existingOptions, config, onError = _defaultOnError) } // TRANSPILE ONLY stream doing just TS to JS conversion function createTranspileStream(transpiler) { - return through(function (file) { + return (0, through_1.default)(function (file) { // give the file to the compiler if (file.isStream()) { this.emit('error', 'no support for streams'); @@ -116,7 +152,7 @@ function create(projectPath, existingOptions, config, onError = _defaultOnError) let path; for (; more && _pos < _fileNames.length; _pos++) { path = _fileNames[_pos]; - more = this.push(new Vinyl({ + more = this.push(new vinyl_1.default({ path, contents: (0, fs_1.readFileSync)(path), stat: (0, fs_1.statSync)(path), diff --git a/build/lib/tsb/index.ts b/build/lib/tsb/index.ts index 53c752d2655a..e577d386cd9d 100644 --- a/build/lib/tsb/index.ts +++ b/build/lib/tsb/index.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as Vinyl from 'vinyl'; -import * as through from 'through'; +import Vinyl from 'vinyl'; +import through from 'through'; import * as builder from './builder'; -import * as ts from 'typescript'; +import ts from 'typescript'; import { Readable, Writable, Duplex } from 'stream'; import { dirname } from 'path'; import { strings } from './utils'; import { readFileSync, statSync } from 'fs'; -import * as log from 'fancy-log'; +import log from 'fancy-log'; import { ESBuildTranspiler, ITranspiler, TscTranspiler } from './transpiler'; import colors = require('ansi-colors'); diff --git a/build/lib/tsb/transpiler.js b/build/lib/tsb/transpiler.js index a4439b8d7aeb..adccb1044166 100644 --- a/build/lib/tsb/transpiler.js +++ b/build/lib/tsb/transpiler.js @@ -3,28 +3,31 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.ESBuildTranspiler = exports.TscTranspiler = void 0; -const esbuild = require("esbuild"); -const ts = require("typescript"); -const threads = require("node:worker_threads"); -const Vinyl = require("vinyl"); +const esbuild_1 = __importDefault(require("esbuild")); +const typescript_1 = __importDefault(require("typescript")); +const node_worker_threads_1 = __importDefault(require("node:worker_threads")); +const vinyl_1 = __importDefault(require("vinyl")); const node_os_1 = require("node:os"); function transpile(tsSrc, options) { const isAmd = /\n(import|export)/m.test(tsSrc); - if (!isAmd && options.compilerOptions?.module === ts.ModuleKind.AMD) { + if (!isAmd && options.compilerOptions?.module === typescript_1.default.ModuleKind.AMD) { // enforce NONE module-system for not-amd cases - options = { ...options, ...{ compilerOptions: { ...options.compilerOptions, module: ts.ModuleKind.None } } }; + options = { ...options, ...{ compilerOptions: { ...options.compilerOptions, module: typescript_1.default.ModuleKind.None } } }; } - const out = ts.transpileModule(tsSrc, options); + const out = typescript_1.default.transpileModule(tsSrc, options); return { jsSrc: out.outputText, diag: out.diagnostics ?? [] }; } -if (!threads.isMainThread) { +if (!node_worker_threads_1.default.isMainThread) { // WORKER - threads.parentPort?.addListener('message', (req) => { + node_worker_threads_1.default.parentPort?.addListener('message', (req) => { const res = { jsSrcs: [], diagnostics: [] @@ -34,7 +37,7 @@ if (!threads.isMainThread) { res.jsSrcs.push(out.jsSrc); res.diagnostics.push(out.diag); } - threads.parentPort.postMessage(res); + node_worker_threads_1.default.parentPort.postMessage(res); }); } class OutputFileNameOracle { @@ -43,7 +46,7 @@ class OutputFileNameOracle { this.getOutputFileName = (file) => { try { // windows: path-sep normalizing - file = ts.normalizePath(file); + file = typescript_1.default.normalizePath(file); if (!cmdLine.options.configFilePath) { // this is needed for the INTERNAL getOutputFileNames-call below... cmdLine.options.configFilePath = configFilePath; @@ -53,7 +56,7 @@ class OutputFileNameOracle { file = file.slice(0, -5) + '.ts'; cmdLine.fileNames.push(file); } - const outfile = ts.getOutputFileNames(cmdLine, file, true)[0]; + const outfile = typescript_1.default.getOutputFileNames(cmdLine, file, true)[0]; if (isDts) { cmdLine.fileNames.pop(); } @@ -70,7 +73,7 @@ class OutputFileNameOracle { class TranspileWorker { static pool = 1; id = TranspileWorker.pool++; - _worker = new threads.Worker(__filename); + _worker = new node_worker_threads_1.default.Worker(__filename); _pending; _durations = []; constructor(outFileFn) { @@ -107,7 +110,7 @@ class TranspileWorker { } const outBase = options.compilerOptions?.outDir ?? file.base; const outPath = outFileFn(file.path); - outFiles.push(new Vinyl({ + outFiles.push(new vinyl_1.default({ path: outPath, base: outBase, contents: Buffer.from(jsSrc), @@ -249,7 +252,7 @@ class ESBuildTranspiler { compilerOptions: { ...this._cmdLine.options, ...{ - module: isExtension ? ts.ModuleKind.CommonJS : undefined + module: isExtension ? typescript_1.default.ModuleKind.CommonJS : undefined } } }), @@ -270,7 +273,7 @@ class ESBuildTranspiler { throw Error('file.contents must be a Buffer'); } const t1 = Date.now(); - this._jobs.push(esbuild.transform(file.contents, { + this._jobs.push(esbuild_1.default.transform(file.contents, { ...this._transformOpts, sourcefile: file.path, }).then(result => { @@ -281,7 +284,7 @@ class ESBuildTranspiler { } const outBase = this._cmdLine.options.outDir ?? file.base; const outPath = this._outputFileNames.getOutputFileName(file.path); - this.onOutfile(new Vinyl({ + this.onOutfile(new vinyl_1.default({ path: outPath, base: outBase, contents: Buffer.from(result.code), diff --git a/build/lib/tsb/transpiler.ts b/build/lib/tsb/transpiler.ts index ae841dcf88b7..16a3b3475388 100644 --- a/build/lib/tsb/transpiler.ts +++ b/build/lib/tsb/transpiler.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as esbuild from 'esbuild'; -import * as ts from 'typescript'; -import * as threads from 'node:worker_threads'; -import * as Vinyl from 'vinyl'; +import esbuild from 'esbuild'; +import ts from 'typescript'; +import threads from 'node:worker_threads'; +import Vinyl from 'vinyl'; import { cpus } from 'node:os'; interface TranspileReq { diff --git a/build/lib/tsb/utils.js b/build/lib/tsb/utils.js index 6ea66221b1b4..29cccf0f8334 100644 --- a/build/lib/tsb/utils.js +++ b/build/lib/tsb/utils.js @@ -4,54 +4,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); -exports.graph = exports.strings = exports.collections = void 0; -var collections; -(function (collections) { - const hasOwnProperty = Object.prototype.hasOwnProperty; - function lookup(collection, key) { - if (hasOwnProperty.call(collection, key)) { - return collection[key]; - } - return null; - } - collections.lookup = lookup; - function insert(collection, key, value) { - collection[key] = value; - } - collections.insert = insert; - function lookupOrInsert(collection, key, value) { - if (hasOwnProperty.call(collection, key)) { - return collection[key]; - } - else { - collection[key] = value; - return value; - } - } - collections.lookupOrInsert = lookupOrInsert; - function forEach(collection, callback) { - for (const key in collection) { - if (hasOwnProperty.call(collection, key)) { - callback({ - key: key, - value: collection[key] - }); - } - } - } - collections.forEach = forEach; - function contains(collection, key) { - return hasOwnProperty.call(collection, key); - } - collections.contains = contains; -})(collections || (exports.collections = collections = {})); +exports.graph = exports.strings = void 0; var strings; (function (strings) { - /** - * The empty string. The one and only. - */ - strings.empty = ''; - strings.eolUnix = '\r\n'; function format(value, ...rest) { return value.replace(/({\d+})/g, function (match) { const index = Number(match.substring(1, match.length - 1)); @@ -62,63 +17,83 @@ var strings; })(strings || (exports.strings = strings = {})); var graph; (function (graph) { - function newNode(data) { - return { - data: data, - incoming: {}, - outgoing: {} - }; + class Node { + data; + incoming = new Map(); + outgoing = new Map(); + constructor(data) { + this.data = data; + } } - graph.newNode = newNode; + graph.Node = Node; class Graph { - _hashFn; - _nodes = {}; - constructor(_hashFn) { - this._hashFn = _hashFn; - // empty - } - traverse(start, inwards, callback) { - const startNode = this.lookup(start); - if (!startNode) { - return; - } - this._traverse(startNode, inwards, {}, callback); - } - _traverse(node, inwards, seen, callback) { - const key = this._hashFn(node.data); - if (collections.contains(seen, key)) { - return; - } - seen[key] = true; - callback(node.data); - const nodes = inwards ? node.outgoing : node.incoming; - collections.forEach(nodes, (entry) => this._traverse(entry.value, inwards, seen, callback)); - } + _nodes = new Map(); inertEdge(from, to) { const fromNode = this.lookupOrInsertNode(from); const toNode = this.lookupOrInsertNode(to); - fromNode.outgoing[this._hashFn(to)] = toNode; - toNode.incoming[this._hashFn(from)] = fromNode; + fromNode.outgoing.set(toNode.data, toNode); + toNode.incoming.set(fromNode.data, fromNode); } - removeNode(data) { - const key = this._hashFn(data); - delete this._nodes[key]; - collections.forEach(this._nodes, (entry) => { - delete entry.value.outgoing[key]; - delete entry.value.incoming[key]; - }); + resetNode(data) { + const node = this._nodes.get(data); + if (!node) { + return; + } + for (const outDep of node.outgoing.values()) { + outDep.incoming.delete(node.data); + } + node.outgoing.clear(); } lookupOrInsertNode(data) { - const key = this._hashFn(data); - let node = collections.lookup(this._nodes, key); + let node = this._nodes.get(data); if (!node) { - node = newNode(data); - this._nodes[key] = node; + node = new Node(data); + this._nodes.set(data, node); } return node; } lookup(data) { - return collections.lookup(this._nodes, this._hashFn(data)); + return this._nodes.get(data) ?? null; + } + findCycle() { + let result; + let foundStartNodes = false; + const checked = new Set(); + for (const [_start, value] of this._nodes) { + if (Object.values(value.incoming).length > 0) { + continue; + } + foundStartNodes = true; + const dfs = (node, visited) => { + if (checked.has(node)) { + return; + } + if (visited.has(node)) { + result = [...visited, node].map(n => n.data); + const idx = result.indexOf(node.data); + result = result.slice(idx); + return; + } + visited.add(node); + for (const child of Object.values(node.outgoing)) { + dfs(child, visited); + if (result) { + break; + } + } + visited.delete(node); + checked.add(node); + }; + dfs(value, new Set()); + if (result) { + break; + } + } + if (!foundStartNodes) { + // everything is a cycle + return Array.from(this._nodes.keys()); + } + return result; } } graph.Graph = Graph; diff --git a/build/lib/tsb/utils.ts b/build/lib/tsb/utils.ts index 3b003e3a6e1d..59d1cab36d3d 100644 --- a/build/lib/tsb/utils.ts +++ b/build/lib/tsb/utils.ts @@ -3,54 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export module collections { - - const hasOwnProperty = Object.prototype.hasOwnProperty; - - export function lookup(collection: { [keys: string]: T }, key: string): T | null { - if (hasOwnProperty.call(collection, key)) { - return collection[key]; - } - return null; - } - - export function insert(collection: { [keys: string]: T }, key: string, value: T): void { - collection[key] = value; - } - - export function lookupOrInsert(collection: { [keys: string]: T }, key: string, value: T): T { - if (hasOwnProperty.call(collection, key)) { - return collection[key]; - } else { - collection[key] = value; - return value; - } - } - - export function forEach(collection: { [keys: string]: T }, callback: (entry: { key: string; value: T }) => void): void { - for (const key in collection) { - if (hasOwnProperty.call(collection, key)) { - callback({ - key: key, - value: collection[key] - }); - } - } - } - - export function contains(collection: { [keys: string]: any }, key: string): boolean { - return hasOwnProperty.call(collection, key); - } -} - -export module strings { - - /** - * The empty string. The one and only. - */ - export const empty = ''; - - export const eolUnix = '\r\n'; +export namespace strings { export function format(value: string, ...rest: any[]): string { return value.replace(/({\d+})/g, function (match) { @@ -60,80 +13,104 @@ export module strings { } } -export module graph { +export namespace graph { - export interface Node { - data: T; - incoming: { [key: string]: Node }; - outgoing: { [key: string]: Node }; - } + export class Node { - export function newNode(data: T): Node { - return { - data: data, - incoming: {}, - outgoing: {} - }; - } + readonly incoming = new Map>(); + readonly outgoing = new Map>(); - export class Graph { - - private _nodes: { [key: string]: Node } = {}; + constructor(readonly data: T) { - constructor(private _hashFn: (element: T) => string) { - // empty } + } - traverse(start: T, inwards: boolean, callback: (data: T) => void): void { - const startNode = this.lookup(start); - if (!startNode) { - return; - } - this._traverse(startNode, inwards, {}, callback); - } + export class Graph { - private _traverse(node: Node, inwards: boolean, seen: { [key: string]: boolean }, callback: (data: T) => void): void { - const key = this._hashFn(node.data); - if (collections.contains(seen, key)) { - return; - } - seen[key] = true; - callback(node.data); - const nodes = inwards ? node.outgoing : node.incoming; - collections.forEach(nodes, (entry) => this._traverse(entry.value, inwards, seen, callback)); - } + private _nodes = new Map>(); inertEdge(from: T, to: T): void { const fromNode = this.lookupOrInsertNode(from); const toNode = this.lookupOrInsertNode(to); - fromNode.outgoing[this._hashFn(to)] = toNode; - toNode.incoming[this._hashFn(from)] = fromNode; + fromNode.outgoing.set(toNode.data, toNode); + toNode.incoming.set(fromNode.data, fromNode); } - removeNode(data: T): void { - const key = this._hashFn(data); - delete this._nodes[key]; - collections.forEach(this._nodes, (entry) => { - delete entry.value.outgoing[key]; - delete entry.value.incoming[key]; - }); + resetNode(data: T): void { + const node = this._nodes.get(data); + if (!node) { + return; + } + for (const outDep of node.outgoing.values()) { + outDep.incoming.delete(node.data); + } + node.outgoing.clear(); } lookupOrInsertNode(data: T): Node { - const key = this._hashFn(data); - let node = collections.lookup(this._nodes, key); + let node = this._nodes.get(data); if (!node) { - node = newNode(data); - this._nodes[key] = node; + node = new Node(data); + this._nodes.set(data, node); } return node; } lookup(data: T): Node | null { - return collections.lookup(this._nodes, this._hashFn(data)); + return this._nodes.get(data) ?? null; + } + + findCycle(): T[] | undefined { + + let result: T[] | undefined; + let foundStartNodes = false; + const checked = new Set>(); + + for (const [_start, value] of this._nodes) { + + if (Object.values(value.incoming).length > 0) { + continue; + } + + foundStartNodes = true; + + const dfs = (node: Node, visited: Set>) => { + + if (checked.has(node)) { + return; + } + + if (visited.has(node)) { + result = [...visited, node].map(n => n.data); + const idx = result.indexOf(node.data); + result = result.slice(idx); + return; + } + visited.add(node); + for (const child of Object.values(node.outgoing)) { + dfs(child, visited); + if (result) { + break; + } + } + visited.delete(node); + checked.add(node); + }; + dfs(value, new Set()); + if (result) { + break; + } + } + + if (!foundStartNodes) { + // everything is a cycle + return Array.from(this._nodes.keys()); + } + + return result; } } diff --git a/build/lib/typings/event-stream.d.ts b/build/lib/typings/event-stream.d.ts index 260051be52e5..2b021ef258ee 100644 --- a/build/lib/typings/event-stream.d.ts +++ b/build/lib/typings/event-stream.d.ts @@ -1,7 +1,7 @@ declare module "event-stream" { import { Stream } from 'stream'; import { ThroughStream as _ThroughStream } from 'through'; - import * as File from 'vinyl'; + import File from 'vinyl'; export interface ThroughStream extends _ThroughStream { queue(data: File | null): any; diff --git a/build/lib/util.js b/build/lib/util.js index 82e4189dd1a7..8b6f03962817 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -3,6 +3,9 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.incremental = incremental; exports.debounce = debounce; @@ -23,20 +26,20 @@ exports.rebase = rebase; exports.filter = filter; exports.streamToPromise = streamToPromise; exports.getElectronVersion = getElectronVersion; -const es = require("event-stream"); -const _debounce = require("debounce"); -const _filter = require("gulp-filter"); -const rename = require("gulp-rename"); -const path = require("path"); -const fs = require("fs"); -const _rimraf = require("rimraf"); +const event_stream_1 = __importDefault(require("event-stream")); +const debounce_1 = __importDefault(require("debounce")); +const gulp_filter_1 = __importDefault(require("gulp-filter")); +const gulp_rename_1 = __importDefault(require("gulp-rename")); +const path_1 = __importDefault(require("path")); +const fs_1 = __importDefault(require("fs")); +const rimraf_1 = __importDefault(require("rimraf")); const url_1 = require("url"); -const ternaryStream = require("ternary-stream"); -const root = path.dirname(path.dirname(__dirname)); +const ternary_stream_1 = __importDefault(require("ternary-stream")); +const root = path_1.default.dirname(path_1.default.dirname(__dirname)); const NoCancellationToken = { isCancellationRequested: () => false }; function incremental(streamProvider, initial, supportsCancellation) { - const input = es.through(); - const output = es.through(); + const input = event_stream_1.default.through(); + const output = event_stream_1.default.through(); let state = 'idle'; let buffer = Object.create(null); const token = !supportsCancellation ? undefined : { isCancellationRequested: () => Object.keys(buffer).length > 0 }; @@ -45,7 +48,7 @@ function incremental(streamProvider, initial, supportsCancellation) { const stream = !supportsCancellation ? streamProvider() : streamProvider(isCancellable ? token : NoCancellationToken); input .pipe(stream) - .pipe(es.through(undefined, () => { + .pipe(event_stream_1.default.through(undefined, () => { state = 'idle'; eventuallyRun(); })) @@ -54,14 +57,14 @@ function incremental(streamProvider, initial, supportsCancellation) { if (initial) { run(initial, false); } - const eventuallyRun = _debounce(() => { + const eventuallyRun = (0, debounce_1.default)(() => { const paths = Object.keys(buffer); if (paths.length === 0) { return; } const data = paths.map(path => buffer[path]); buffer = Object.create(null); - run(es.readArray(data), true); + run(event_stream_1.default.readArray(data), true); }, 500); input.on('data', (f) => { buffer[f.path] = f; @@ -69,16 +72,16 @@ function incremental(streamProvider, initial, supportsCancellation) { eventuallyRun(); } }); - return es.duplex(input, output); + return event_stream_1.default.duplex(input, output); } function debounce(task, duration = 500) { - const input = es.through(); - const output = es.through(); + const input = event_stream_1.default.through(); + const output = event_stream_1.default.through(); let state = 'idle'; const run = () => { state = 'running'; task() - .pipe(es.through(undefined, () => { + .pipe(event_stream_1.default.through(undefined, () => { const shouldRunAgain = state === 'stale'; state = 'idle'; if (shouldRunAgain) { @@ -88,7 +91,7 @@ function debounce(task, duration = 500) { .pipe(output); }; run(); - const eventuallyRun = _debounce(() => run(), duration); + const eventuallyRun = (0, debounce_1.default)(() => run(), duration); input.on('data', () => { if (state === 'idle') { eventuallyRun(); @@ -97,13 +100,13 @@ function debounce(task, duration = 500) { state = 'stale'; } }); - return es.duplex(input, output); + return event_stream_1.default.duplex(input, output); } function fixWin32DirectoryPermissions() { if (!/win32/.test(process.platform)) { - return es.through(); + return event_stream_1.default.through(); } - return es.mapSync(f => { + return event_stream_1.default.mapSync(f => { if (f.stat && f.stat.isDirectory && f.stat.isDirectory()) { f.stat.mode = 16877; } @@ -111,7 +114,7 @@ function fixWin32DirectoryPermissions() { }); } function setExecutableBit(pattern) { - const setBit = es.mapSync(f => { + const setBit = event_stream_1.default.mapSync(f => { if (!f.stat) { f.stat = { isFile() { return true; } }; } @@ -121,13 +124,13 @@ function setExecutableBit(pattern) { if (!pattern) { return setBit; } - const input = es.through(); - const filter = _filter(pattern, { restore: true }); + const input = event_stream_1.default.through(); + const filter = (0, gulp_filter_1.default)(pattern, { restore: true }); const output = input .pipe(filter) .pipe(setBit) .pipe(filter.restore); - return es.duplex(input, output); + return event_stream_1.default.duplex(input, output); } function toFileUri(filePath) { const match = filePath.match(/^([a-z])\:(.*)$/i); @@ -137,27 +140,27 @@ function toFileUri(filePath) { return 'file://' + filePath.replace(/\\/g, '/'); } function skipDirectories() { - return es.mapSync(f => { + return event_stream_1.default.mapSync(f => { if (!f.isDirectory()) { return f; } }); } function cleanNodeModules(rulePath) { - const rules = fs.readFileSync(rulePath, 'utf8') + const rules = fs_1.default.readFileSync(rulePath, 'utf8') .split(/\r?\n/g) .map(line => line.trim()) .filter(line => line && !/^#/.test(line)); const excludes = rules.filter(line => !/^!/.test(line)).map(line => `!**/node_modules/${line}`); const includes = rules.filter(line => /^!/.test(line)).map(line => `**/node_modules/${line.substr(1)}`); - const input = es.through(); - const output = es.merge(input.pipe(_filter(['**', ...excludes])), input.pipe(_filter(includes))); - return es.duplex(input, output); + const input = event_stream_1.default.through(); + const output = event_stream_1.default.merge(input.pipe((0, gulp_filter_1.default)(['**', ...excludes])), input.pipe((0, gulp_filter_1.default)(includes))); + return event_stream_1.default.duplex(input, output); } function loadSourcemaps() { - const input = es.through(); + const input = event_stream_1.default.through(); const output = input - .pipe(es.map((f, cb) => { + .pipe(event_stream_1.default.map((f, cb) => { if (f.sourceMap) { cb(undefined, f); return; @@ -185,7 +188,7 @@ function loadSourcemaps() { return; } f.contents = Buffer.from(contents.replace(/\/\/# sourceMappingURL=(.*)$/g, ''), 'utf8'); - fs.readFile(path.join(path.dirname(f.path), lastMatch[1]), 'utf8', (err, contents) => { + fs_1.default.readFile(path_1.default.join(path_1.default.dirname(f.path), lastMatch[1]), 'utf8', (err, contents) => { if (err) { return cb(err); } @@ -193,54 +196,54 @@ function loadSourcemaps() { cb(undefined, f); }); })); - return es.duplex(input, output); + return event_stream_1.default.duplex(input, output); } function stripSourceMappingURL() { - const input = es.through(); + const input = event_stream_1.default.through(); const output = input - .pipe(es.mapSync(f => { + .pipe(event_stream_1.default.mapSync(f => { const contents = f.contents.toString('utf8'); f.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, ''), 'utf8'); return f; })); - return es.duplex(input, output); + return event_stream_1.default.duplex(input, output); } /** Splits items in the stream based on the predicate, sending them to onTrue if true, or onFalse otherwise */ -function $if(test, onTrue, onFalse = es.through()) { +function $if(test, onTrue, onFalse = event_stream_1.default.through()) { if (typeof test === 'boolean') { return test ? onTrue : onFalse; } - return ternaryStream(test, onTrue, onFalse); + return (0, ternary_stream_1.default)(test, onTrue, onFalse); } /** Operator that appends the js files' original path a sourceURL, so debug locations map */ function appendOwnPathSourceURL() { - const input = es.through(); + const input = event_stream_1.default.through(); const output = input - .pipe(es.mapSync(f => { + .pipe(event_stream_1.default.mapSync(f => { if (!(f.contents instanceof Buffer)) { throw new Error(`contents of ${f.path} are not a buffer`); } f.contents = Buffer.concat([f.contents, Buffer.from(`\n//# sourceURL=${(0, url_1.pathToFileURL)(f.path)}`)]); return f; })); - return es.duplex(input, output); + return event_stream_1.default.duplex(input, output); } function rewriteSourceMappingURL(sourceMappingURLBase) { - const input = es.through(); + const input = event_stream_1.default.through(); const output = input - .pipe(es.mapSync(f => { + .pipe(event_stream_1.default.mapSync(f => { const contents = f.contents.toString('utf8'); - const str = `//# sourceMappingURL=${sourceMappingURLBase}/${path.dirname(f.relative).replace(/\\/g, '/')}/$1`; + const str = `//# sourceMappingURL=${sourceMappingURLBase}/${path_1.default.dirname(f.relative).replace(/\\/g, '/')}/$1`; f.contents = Buffer.from(contents.replace(/\n\/\/# sourceMappingURL=(.*)$/gm, str)); return f; })); - return es.duplex(input, output); + return event_stream_1.default.duplex(input, output); } function rimraf(dir) { const result = () => new Promise((c, e) => { let retries = 0; const retry = () => { - _rimraf(dir, { maxBusyTries: 1 }, (err) => { + (0, rimraf_1.default)(dir, { maxBusyTries: 1 }, (err) => { if (!err) { return c(); } @@ -252,14 +255,14 @@ function rimraf(dir) { }; retry(); }); - result.taskName = `clean-${path.basename(dir).toLowerCase()}`; + result.taskName = `clean-${path_1.default.basename(dir).toLowerCase()}`; return result; } function _rreaddir(dirPath, prepend, result) { - const entries = fs.readdirSync(dirPath, { withFileTypes: true }); + const entries = fs_1.default.readdirSync(dirPath, { withFileTypes: true }); for (const entry of entries) { if (entry.isDirectory()) { - _rreaddir(path.join(dirPath, entry.name), `${prepend}/${entry.name}`, result); + _rreaddir(path_1.default.join(dirPath, entry.name), `${prepend}/${entry.name}`, result); } else { result.push(`${prepend}/${entry.name}`); @@ -272,20 +275,20 @@ function rreddir(dirPath) { return result; } function ensureDir(dirPath) { - if (fs.existsSync(dirPath)) { + if (fs_1.default.existsSync(dirPath)) { return; } - ensureDir(path.dirname(dirPath)); - fs.mkdirSync(dirPath); + ensureDir(path_1.default.dirname(dirPath)); + fs_1.default.mkdirSync(dirPath); } function rebase(count) { - return rename(f => { + return (0, gulp_rename_1.default)(f => { const parts = f.dirname ? f.dirname.split(/[\/\\]/) : []; - f.dirname = parts.slice(count).join(path.sep); + f.dirname = parts.slice(count).join(path_1.default.sep); }); } function filter(fn) { - const result = es.through(function (data) { + const result = event_stream_1.default.through(function (data) { if (fn(data)) { this.emit('data', data); } @@ -293,7 +296,7 @@ function filter(fn) { result.restore.push(data); } }); - result.restore = es.through(); + result.restore = event_stream_1.default.through(); return result; } function streamToPromise(stream) { @@ -303,7 +306,7 @@ function streamToPromise(stream) { }); } function getElectronVersion() { - const npmrc = fs.readFileSync(path.join(root, '.npmrc'), 'utf8'); + const npmrc = fs_1.default.readFileSync(path_1.default.join(root, '.npmrc'), 'utf8'); const electronVersion = /^target="(.*)"$/m.exec(npmrc)[1]; const msBuildId = /^ms_build_id="(.*)"$/m.exec(npmrc)[1]; return { electronVersion, msBuildId }; diff --git a/build/lib/util.ts b/build/lib/util.ts index 08921834676d..ad81730b3de3 100644 --- a/build/lib/util.ts +++ b/build/lib/util.ts @@ -3,18 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as es from 'event-stream'; -import _debounce = require('debounce'); -import * as _filter from 'gulp-filter'; -import * as rename from 'gulp-rename'; -import * as path from 'path'; -import * as fs from 'fs'; -import * as _rimraf from 'rimraf'; -import * as VinylFile from 'vinyl'; +import es from 'event-stream'; +import _debounce from 'debounce'; +import _filter from 'gulp-filter'; +import rename from 'gulp-rename'; +import path from 'path'; +import fs from 'fs'; +import _rimraf from 'rimraf'; +import VinylFile from 'vinyl'; import { ThroughStream } from 'through'; -import * as sm from 'source-map'; +import sm from 'source-map'; import { pathToFileURL } from 'url'; -import * as ternaryStream from 'ternary-stream'; +import ternaryStream from 'ternary-stream'; const root = path.dirname(path.dirname(__dirname)); diff --git a/build/lib/watch/index.js b/build/lib/watch/index.js index 86d2611febf9..69eca78fd704 100644 --- a/build/lib/watch/index.js +++ b/build/lib/watch/index.js @@ -3,6 +3,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); const watch = process.platform === 'win32' ? require('./watch-win32') : require('vscode-gulp-watch'); module.exports = function () { return watch.apply(null, arguments); diff --git a/build/lib/watch/watch-win32.js b/build/lib/watch/watch-win32.js index 934d8e8110f2..7b77981d620e 100644 --- a/build/lib/watch/watch-win32.js +++ b/build/lib/watch/watch-win32.js @@ -3,14 +3,17 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); -const path = require("path"); -const cp = require("child_process"); -const fs = require("fs"); -const File = require("vinyl"); -const es = require("event-stream"); -const filter = require("gulp-filter"); -const watcherPath = path.join(__dirname, 'watcher.exe'); +const path_1 = __importDefault(require("path")); +const child_process_1 = __importDefault(require("child_process")); +const fs_1 = __importDefault(require("fs")); +const vinyl_1 = __importDefault(require("vinyl")); +const event_stream_1 = __importDefault(require("event-stream")); +const gulp_filter_1 = __importDefault(require("gulp-filter")); +const watcherPath = path_1.default.join(__dirname, 'watcher.exe'); function toChangeType(type) { switch (type) { case '0': return 'change'; @@ -19,8 +22,8 @@ function toChangeType(type) { } } function watch(root) { - const result = es.through(); - let child = cp.spawn(watcherPath, [root]); + const result = event_stream_1.default.through(); + let child = child_process_1.default.spawn(watcherPath, [root]); child.stdout.on('data', function (data) { const lines = data.toString('utf8').split('\n'); for (let i = 0; i < lines.length; i++) { @@ -34,8 +37,8 @@ function watch(root) { if (/^\.git/.test(changePath) || /(^|\\)out($|\\)/.test(changePath)) { continue; } - const changePathFull = path.join(root, changePath); - const file = new File({ + const changePathFull = path_1.default.join(root, changePath); + const file = new vinyl_1.default({ path: changePathFull, base: root }); @@ -60,20 +63,20 @@ function watch(root) { const cache = Object.create(null); module.exports = function (pattern, options) { options = options || {}; - const cwd = path.normalize(options.cwd || process.cwd()); + const cwd = path_1.default.normalize(options.cwd || process.cwd()); let watcher = cache[cwd]; if (!watcher) { watcher = cache[cwd] = watch(cwd); } - const rebase = !options.base ? es.through() : es.mapSync(function (f) { + const rebase = !options.base ? event_stream_1.default.through() : event_stream_1.default.mapSync(function (f) { f.base = options.base; return f; }); return watcher - .pipe(filter(['**', '!.git{,/**}'], { dot: options.dot })) // ignore all things git - .pipe(filter(pattern, { dot: options.dot })) - .pipe(es.map(function (file, cb) { - fs.stat(file.path, function (err, stat) { + .pipe((0, gulp_filter_1.default)(['**', '!.git{,/**}'], { dot: options.dot })) // ignore all things git + .pipe((0, gulp_filter_1.default)(pattern, { dot: options.dot })) + .pipe(event_stream_1.default.map(function (file, cb) { + fs_1.default.stat(file.path, function (err, stat) { if (err && err.code === 'ENOENT') { return cb(undefined, file); } @@ -83,7 +86,7 @@ module.exports = function (pattern, options) { if (!stat.isFile()) { return cb(); } - fs.readFile(file.path, function (err, contents) { + fs_1.default.readFile(file.path, function (err, contents) { if (err && err.code === 'ENOENT') { return cb(undefined, file); } diff --git a/build/lib/watch/watch-win32.ts b/build/lib/watch/watch-win32.ts index afde6a79f221..bbfde6afba98 100644 --- a/build/lib/watch/watch-win32.ts +++ b/build/lib/watch/watch-win32.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as path from 'path'; -import * as cp from 'child_process'; -import * as fs from 'fs'; -import * as File from 'vinyl'; -import * as es from 'event-stream'; -import * as filter from 'gulp-filter'; +import path from 'path'; +import cp from 'child_process'; +import fs from 'fs'; +import File from 'vinyl'; +import es from 'event-stream'; +import filter from 'gulp-filter'; import { Stream } from 'stream'; const watcherPath = path.join(__dirname, 'watcher.exe'); diff --git a/build/linux/debian/calculate-deps.js b/build/linux/debian/calculate-deps.js index bbcb6bfc3de4..34276ce7705c 100644 --- a/build/linux/debian/calculate-deps.js +++ b/build/linux/debian/calculate-deps.js @@ -3,13 +3,16 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.generatePackageDeps = generatePackageDeps; const child_process_1 = require("child_process"); const fs_1 = require("fs"); const os_1 = require("os"); -const path = require("path"); -const manifests = require("../../../cgmanifest.json"); +const path_1 = __importDefault(require("path")); +const cgmanifest_json_1 = __importDefault(require("../../../cgmanifest.json")); const dep_lists_1 = require("./dep-lists"); function generatePackageDeps(files, arch, chromiumSysroot, vscodeSysroot) { const dependencies = files.map(file => calculatePackageDeps(file, arch, chromiumSysroot, vscodeSysroot)); @@ -29,7 +32,7 @@ function calculatePackageDeps(binaryPath, arch, chromiumSysroot, vscodeSysroot) console.error('Tried to stat ' + binaryPath + ' but failed.'); } // Get the Chromium dpkg-shlibdeps file. - const chromiumManifest = manifests.registrations.filter(registration => { + const chromiumManifest = cgmanifest_json_1.default.registrations.filter(registration => { return registration.component.type === 'git' && registration.component.git.name === 'chromium'; }); const dpkgShlibdepsUrl = `https://raw.githubusercontent.com/chromium/chromium/${chromiumManifest[0].version}/third_party/dpkg-shlibdeps/dpkg-shlibdeps.pl`; @@ -52,7 +55,7 @@ function calculatePackageDeps(binaryPath, arch, chromiumSysroot, vscodeSysroot) } cmd.push(`-l${chromiumSysroot}/usr/lib`); cmd.push(`-L${vscodeSysroot}/debian/libxkbfile1/DEBIAN/shlibs`); - cmd.push('-O', '-e', path.resolve(binaryPath)); + cmd.push('-O', '-e', path_1.default.resolve(binaryPath)); const dpkgShlibdepsResult = (0, child_process_1.spawnSync)('perl', cmd, { cwd: chromiumSysroot }); if (dpkgShlibdepsResult.status !== 0) { throw new Error(`dpkg-shlibdeps failed with exit code ${dpkgShlibdepsResult.status}. stderr:\n${dpkgShlibdepsResult.stderr} `); diff --git a/build/linux/debian/calculate-deps.ts b/build/linux/debian/calculate-deps.ts index 92f8065f2629..addc38696a88 100644 --- a/build/linux/debian/calculate-deps.ts +++ b/build/linux/debian/calculate-deps.ts @@ -6,8 +6,8 @@ import { spawnSync } from 'child_process'; import { constants, statSync } from 'fs'; import { tmpdir } from 'os'; -import path = require('path'); -import * as manifests from '../../../cgmanifest.json'; +import path from 'path'; +import manifests from '../../../cgmanifest.json'; import { additionalDeps } from './dep-lists'; import { DebianArchString } from './types'; diff --git a/build/linux/debian/dep-lists.js b/build/linux/debian/dep-lists.js index 3bb58fb1215d..8ac57b94d7fc 100644 --- a/build/linux/debian/dep-lists.js +++ b/build/linux/debian/dep-lists.js @@ -25,7 +25,7 @@ exports.referenceGeneratedDepsByArch = { 'ca-certificates', 'libasound2 (>= 1.0.17)', 'libatk-bridge2.0-0 (>= 2.5.3)', - 'libatk1.0-0 (>= 2.2.0)', + 'libatk1.0-0 (>= 2.11.90)', 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.14)', 'libc6 (>= 2.16)', @@ -36,7 +36,6 @@ exports.referenceGeneratedDepsByArch = { 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', 'libdbus-1-3 (>= 1.9.14)', - 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.1~beta3)', 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.37.3)', @@ -46,6 +45,7 @@ exports.referenceGeneratedDepsByArch = { 'libnss3 (>= 2:3.30)', 'libnss3 (>= 3.26)', 'libpango-1.0-0 (>= 1.14.0)', + 'libudev1 (>= 183)', 'libx11-6', 'libx11-6 (>= 2:1.4.99.1)', 'libxcb1 (>= 1.9.2)', @@ -62,7 +62,7 @@ exports.referenceGeneratedDepsByArch = { 'ca-certificates', 'libasound2 (>= 1.0.17)', 'libatk-bridge2.0-0 (>= 2.5.3)', - 'libatk1.0-0 (>= 2.2.0)', + 'libatk1.0-0 (>= 2.11.90)', 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.16)', 'libc6 (>= 2.17)', @@ -73,7 +73,6 @@ exports.referenceGeneratedDepsByArch = { 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', 'libdbus-1-3 (>= 1.9.14)', - 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.1~beta3)', 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.37.3)', @@ -87,6 +86,7 @@ exports.referenceGeneratedDepsByArch = { 'libstdc++6 (>= 5)', 'libstdc++6 (>= 5.2)', 'libstdc++6 (>= 6)', + 'libudev1 (>= 183)', 'libx11-6', 'libx11-6 (>= 2:1.4.99.1)', 'libxcb1 (>= 1.9.2)', @@ -103,7 +103,7 @@ exports.referenceGeneratedDepsByArch = { 'ca-certificates', 'libasound2 (>= 1.0.17)', 'libatk-bridge2.0-0 (>= 2.5.3)', - 'libatk1.0-0 (>= 2.2.0)', + 'libatk1.0-0 (>= 2.11.90)', 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.17)', 'libc6 (>= 2.25)', @@ -111,7 +111,6 @@ exports.referenceGeneratedDepsByArch = { 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', 'libdbus-1-3 (>= 1.9.14)', - 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.1~beta3)', 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.37.3)', @@ -125,6 +124,7 @@ exports.referenceGeneratedDepsByArch = { 'libstdc++6 (>= 5)', 'libstdc++6 (>= 5.2)', 'libstdc++6 (>= 6)', + 'libudev1 (>= 183)', 'libx11-6', 'libx11-6 (>= 2:1.4.99.1)', 'libxcb1 (>= 1.9.2)', diff --git a/build/linux/debian/dep-lists.ts b/build/linux/debian/dep-lists.ts index e3d78d1139ad..df119e8b485e 100644 --- a/build/linux/debian/dep-lists.ts +++ b/build/linux/debian/dep-lists.ts @@ -25,7 +25,7 @@ export const referenceGeneratedDepsByArch = { 'ca-certificates', 'libasound2 (>= 1.0.17)', 'libatk-bridge2.0-0 (>= 2.5.3)', - 'libatk1.0-0 (>= 2.2.0)', + 'libatk1.0-0 (>= 2.11.90)', 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.14)', 'libc6 (>= 2.16)', @@ -36,7 +36,6 @@ export const referenceGeneratedDepsByArch = { 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', 'libdbus-1-3 (>= 1.9.14)', - 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.1~beta3)', 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.37.3)', @@ -46,6 +45,7 @@ export const referenceGeneratedDepsByArch = { 'libnss3 (>= 2:3.30)', 'libnss3 (>= 3.26)', 'libpango-1.0-0 (>= 1.14.0)', + 'libudev1 (>= 183)', 'libx11-6', 'libx11-6 (>= 2:1.4.99.1)', 'libxcb1 (>= 1.9.2)', @@ -62,7 +62,7 @@ export const referenceGeneratedDepsByArch = { 'ca-certificates', 'libasound2 (>= 1.0.17)', 'libatk-bridge2.0-0 (>= 2.5.3)', - 'libatk1.0-0 (>= 2.2.0)', + 'libatk1.0-0 (>= 2.11.90)', 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.16)', 'libc6 (>= 2.17)', @@ -73,7 +73,6 @@ export const referenceGeneratedDepsByArch = { 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', 'libdbus-1-3 (>= 1.9.14)', - 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.1~beta3)', 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.37.3)', @@ -87,6 +86,7 @@ export const referenceGeneratedDepsByArch = { 'libstdc++6 (>= 5)', 'libstdc++6 (>= 5.2)', 'libstdc++6 (>= 6)', + 'libudev1 (>= 183)', 'libx11-6', 'libx11-6 (>= 2:1.4.99.1)', 'libxcb1 (>= 1.9.2)', @@ -103,7 +103,7 @@ export const referenceGeneratedDepsByArch = { 'ca-certificates', 'libasound2 (>= 1.0.17)', 'libatk-bridge2.0-0 (>= 2.5.3)', - 'libatk1.0-0 (>= 2.2.0)', + 'libatk1.0-0 (>= 2.11.90)', 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.17)', 'libc6 (>= 2.25)', @@ -111,7 +111,6 @@ export const referenceGeneratedDepsByArch = { 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', 'libdbus-1-3 (>= 1.9.14)', - 'libdrm2 (>= 2.4.75)', 'libexpat1 (>= 2.1~beta3)', 'libgbm1 (>= 17.1.0~rc2)', 'libglib2.0-0 (>= 2.37.3)', @@ -125,6 +124,7 @@ export const referenceGeneratedDepsByArch = { 'libstdc++6 (>= 5)', 'libstdc++6 (>= 5.2)', 'libstdc++6 (>= 6)', + 'libudev1 (>= 183)', 'libx11-6', 'libx11-6 (>= 2:1.4.99.1)', 'libxcb1 (>= 1.9.2)', diff --git a/build/linux/debian/install-sysroot.js b/build/linux/debian/install-sysroot.js index 354c67a2909e..16d8d01468f0 100644 --- a/build/linux/debian/install-sysroot.js +++ b/build/linux/debian/install-sysroot.js @@ -3,20 +3,23 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.getVSCodeSysroot = getVSCodeSysroot; exports.getChromiumSysroot = getChromiumSysroot; const child_process_1 = require("child_process"); const os_1 = require("os"); -const fs = require("fs"); -const https = require("https"); -const path = require("path"); +const fs_1 = __importDefault(require("fs")); +const https_1 = __importDefault(require("https")); +const path_1 = __importDefault(require("path")); const crypto_1 = require("crypto"); -const ansiColors = require("ansi-colors"); +const ansi_colors_1 = __importDefault(require("ansi-colors")); // Based on https://source.chromium.org/chromium/chromium/src/+/main:build/linux/sysroot_scripts/install-sysroot.py. const URL_PREFIX = 'https://msftelectronbuild.z5.web.core.windows.net'; const URL_PATH = 'sysroots/toolchain'; -const REPO_ROOT = path.dirname(path.dirname(path.dirname(__dirname))); +const REPO_ROOT = path_1.default.dirname(path_1.default.dirname(path_1.default.dirname(__dirname))); const ghApiHeaders = { Accept: 'application/vnd.github.v3+json', 'User-Agent': 'VSCode Build', @@ -29,7 +32,7 @@ const ghDownloadHeaders = { Accept: 'application/octet-stream', }; function getElectronVersion() { - const npmrc = fs.readFileSync(path.join(REPO_ROOT, '.npmrc'), 'utf8'); + const npmrc = fs_1.default.readFileSync(path_1.default.join(REPO_ROOT, '.npmrc'), 'utf8'); const electronVersion = /^target="(.*)"$/m.exec(npmrc)[1]; const msBuildId = /^ms_build_id="(.*)"$/m.exec(npmrc)[1]; return { electronVersion, msBuildId }; @@ -37,11 +40,11 @@ function getElectronVersion() { function getSha(filename) { const hash = (0, crypto_1.createHash)('sha256'); // Read file 1 MB at a time - const fd = fs.openSync(filename, 'r'); + const fd = fs_1.default.openSync(filename, 'r'); const buffer = Buffer.alloc(1024 * 1024); let position = 0; let bytesRead = 0; - while ((bytesRead = fs.readSync(fd, buffer, 0, buffer.length, position)) === buffer.length) { + while ((bytesRead = fs_1.default.readSync(fd, buffer, 0, buffer.length, position)) === buffer.length) { hash.update(buffer); position += bytesRead; } @@ -49,7 +52,7 @@ function getSha(filename) { return hash.digest('hex'); } function getVSCodeSysrootChecksum(expectedName) { - const checksums = fs.readFileSync(path.join(REPO_ROOT, 'build', 'checksums', 'vscode-sysroot.txt'), 'utf8'); + const checksums = fs_1.default.readFileSync(path_1.default.join(REPO_ROOT, 'build', 'checksums', 'vscode-sysroot.txt'), 'utf8'); for (const line of checksums.split('\n')) { const [checksum, name] = line.split(/\s+/); if (name === expectedName) { @@ -86,22 +89,22 @@ async function fetchUrl(options, retries = 10, retryDelay = 1000) { }); if (assetResponse.ok && (assetResponse.status >= 200 && assetResponse.status < 300)) { const assetContents = Buffer.from(await assetResponse.arrayBuffer()); - console.log(`Fetched response body buffer: ${ansiColors.magenta(`${assetContents.byteLength} bytes`)}`); + console.log(`Fetched response body buffer: ${ansi_colors_1.default.magenta(`${assetContents.byteLength} bytes`)}`); if (options.checksumSha256) { const actualSHA256Checksum = (0, crypto_1.createHash)('sha256').update(assetContents).digest('hex'); if (actualSHA256Checksum !== options.checksumSha256) { - throw new Error(`Checksum mismatch for ${ansiColors.cyan(asset.url)} (expected ${options.checksumSha256}, actual ${actualSHA256Checksum}))`); + throw new Error(`Checksum mismatch for ${ansi_colors_1.default.cyan(asset.url)} (expected ${options.checksumSha256}, actual ${actualSHA256Checksum}))`); } } - console.log(`Verified SHA256 checksums match for ${ansiColors.cyan(asset.url)}`); + console.log(`Verified SHA256 checksums match for ${ansi_colors_1.default.cyan(asset.url)}`); const tarCommand = `tar -xz -C ${options.dest}`; (0, child_process_1.execSync)(tarCommand, { input: assetContents }); console.log(`Fetch complete!`); return; } - throw new Error(`Request ${ansiColors.magenta(asset.url)} failed with status code: ${assetResponse.status}`); + throw new Error(`Request ${ansi_colors_1.default.magenta(asset.url)} failed with status code: ${assetResponse.status}`); } - throw new Error(`Request ${ansiColors.magenta('https://api.github.com')} failed with status code: ${response.status}`); + throw new Error(`Request ${ansi_colors_1.default.magenta('https://api.github.com')} failed with status code: ${response.status}`); } finally { clearTimeout(timeout); @@ -139,21 +142,21 @@ async function getVSCodeSysroot(arch) { if (!checksumSha256) { throw new Error(`Could not find checksum for ${expectedName}`); } - const sysroot = process.env['VSCODE_SYSROOT_DIR'] ?? path.join((0, os_1.tmpdir)(), `vscode-${arch}-sysroot`); - const stamp = path.join(sysroot, '.stamp'); + const sysroot = process.env['VSCODE_SYSROOT_DIR'] ?? path_1.default.join((0, os_1.tmpdir)(), `vscode-${arch}-sysroot`); + const stamp = path_1.default.join(sysroot, '.stamp'); const result = `${sysroot}/${triple}/${triple}/sysroot`; - if (fs.existsSync(stamp) && fs.readFileSync(stamp).toString() === expectedName) { + if (fs_1.default.existsSync(stamp) && fs_1.default.readFileSync(stamp).toString() === expectedName) { return result; } console.log(`Installing ${arch} root image: ${sysroot}`); - fs.rmSync(sysroot, { recursive: true, force: true }); - fs.mkdirSync(sysroot); + fs_1.default.rmSync(sysroot, { recursive: true, force: true }); + fs_1.default.mkdirSync(sysroot); await fetchUrl({ checksumSha256, assetName: expectedName, dest: sysroot }); - fs.writeFileSync(stamp, expectedName); + fs_1.default.writeFileSync(stamp, expectedName); return result; } async function getChromiumSysroot(arch) { @@ -168,24 +171,24 @@ async function getChromiumSysroot(arch) { const sysrootDict = sysrootInfo[sysrootArch]; const tarballFilename = sysrootDict['Tarball']; const tarballSha = sysrootDict['Sha256Sum']; - const sysroot = path.join((0, os_1.tmpdir)(), sysrootDict['SysrootDir']); + const sysroot = path_1.default.join((0, os_1.tmpdir)(), sysrootDict['SysrootDir']); const url = [URL_PREFIX, URL_PATH, tarballSha].join('/'); - const stamp = path.join(sysroot, '.stamp'); - if (fs.existsSync(stamp) && fs.readFileSync(stamp).toString() === url) { + const stamp = path_1.default.join(sysroot, '.stamp'); + if (fs_1.default.existsSync(stamp) && fs_1.default.readFileSync(stamp).toString() === url) { return sysroot; } console.log(`Installing Debian ${arch} root image: ${sysroot}`); - fs.rmSync(sysroot, { recursive: true, force: true }); - fs.mkdirSync(sysroot); - const tarball = path.join(sysroot, tarballFilename); + fs_1.default.rmSync(sysroot, { recursive: true, force: true }); + fs_1.default.mkdirSync(sysroot); + const tarball = path_1.default.join(sysroot, tarballFilename); console.log(`Downloading ${url}`); let downloadSuccess = false; for (let i = 0; i < 3 && !downloadSuccess; i++) { - fs.writeFileSync(tarball, ''); + fs_1.default.writeFileSync(tarball, ''); await new Promise((c) => { - https.get(url, (res) => { + https_1.default.get(url, (res) => { res.on('data', (chunk) => { - fs.appendFileSync(tarball, chunk); + fs_1.default.appendFileSync(tarball, chunk); }); res.on('end', () => { downloadSuccess = true; @@ -198,7 +201,7 @@ async function getChromiumSysroot(arch) { }); } if (!downloadSuccess) { - fs.rmSync(tarball); + fs_1.default.rmSync(tarball); throw new Error('Failed to download ' + url); } const sha = getSha(tarball); @@ -209,8 +212,8 @@ async function getChromiumSysroot(arch) { if (proc.status) { throw new Error('Tarball extraction failed with code ' + proc.status); } - fs.rmSync(tarball); - fs.writeFileSync(stamp, url); + fs_1.default.rmSync(tarball); + fs_1.default.writeFileSync(stamp, url); return sysroot; } //# sourceMappingURL=install-sysroot.js.map \ No newline at end of file diff --git a/build/linux/debian/install-sysroot.ts b/build/linux/debian/install-sysroot.ts index 8ea43a523cf3..aa10e39f95f0 100644 --- a/build/linux/debian/install-sysroot.ts +++ b/build/linux/debian/install-sysroot.ts @@ -5,12 +5,12 @@ import { spawnSync, execSync } from 'child_process'; import { tmpdir } from 'os'; -import * as fs from 'fs'; -import * as https from 'https'; -import * as path from 'path'; +import fs from 'fs'; +import https from 'https'; +import path from 'path'; import { createHash } from 'crypto'; import { DebianArchString } from './types'; -import * as ansiColors from 'ansi-colors'; +import ansiColors from 'ansi-colors'; // Based on https://source.chromium.org/chromium/chromium/src/+/main:build/linux/sysroot_scripts/install-sysroot.py. const URL_PREFIX = 'https://msftelectronbuild.z5.web.core.windows.net'; diff --git a/build/linux/dependencies-generator.js b/build/linux/dependencies-generator.js index 374c9b629913..448ab38c4a43 100644 --- a/build/linux/dependencies-generator.js +++ b/build/linux/dependencies-generator.js @@ -3,10 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ 'use strict'; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.getDependencies = getDependencies; const child_process_1 = require("child_process"); -const path = require("path"); +const path_1 = __importDefault(require("path")); const install_sysroot_1 = require("./debian/install-sysroot"); const calculate_deps_1 = require("./debian/calculate-deps"); const calculate_deps_2 = require("./rpm/calculate-deps"); @@ -23,7 +26,7 @@ const product = require("../../product.json"); // The reference dependencies, which one has to update when the new dependencies // are valid, are in dep-lists.ts const FAIL_BUILD_FOR_NEW_DEPENDENCIES = true; -// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/128.0.6613.162:chrome/installer/linux/BUILD.gn;l=64-80 +// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/132.0.6834.210:chrome/installer/linux/BUILD.gn;l=64-80 // and the Linux Archive build // Shared library dependencies that we already bundle. const bundledDeps = [ @@ -44,23 +47,23 @@ async function getDependencies(packageType, buildDir, applicationName, arch) { } // Get the files for which we want to find dependencies. const canAsar = false; // TODO@esm ASAR disabled in ESM - const nativeModulesPath = path.join(buildDir, 'resources', 'app', canAsar ? 'node_modules.asar.unpacked' : 'node_modules'); + const nativeModulesPath = path_1.default.join(buildDir, 'resources', 'app', canAsar ? 'node_modules.asar.unpacked' : 'node_modules'); const findResult = (0, child_process_1.spawnSync)('find', [nativeModulesPath, '-name', '*.node']); if (findResult.status) { console.error('Error finding files:'); console.error(findResult.stderr.toString()); return []; } - const appPath = path.join(buildDir, applicationName); + const appPath = path_1.default.join(buildDir, applicationName); // Add the native modules const files = findResult.stdout.toString().trimEnd().split('\n'); // Add the tunnel binary. - files.push(path.join(buildDir, 'bin', product.tunnelApplicationName)); + files.push(path_1.default.join(buildDir, 'bin', product.tunnelApplicationName)); // Add the main executable. files.push(appPath); // Add chrome sandbox and crashpad handler. - files.push(path.join(buildDir, 'chrome-sandbox')); - files.push(path.join(buildDir, 'chrome_crashpad_handler')); + files.push(path_1.default.join(buildDir, 'chrome-sandbox')); + files.push(path_1.default.join(buildDir, 'chrome_crashpad_handler')); // Generate the dependencies. let dependencies; if (packageType === 'deb') { diff --git a/build/linux/dependencies-generator.ts b/build/linux/dependencies-generator.ts index 3c7f6f5bc223..6c1f7b7570b5 100644 --- a/build/linux/dependencies-generator.ts +++ b/build/linux/dependencies-generator.ts @@ -6,7 +6,7 @@ 'use strict'; import { spawnSync } from 'child_process'; -import path = require('path'); +import path from 'path'; import { getChromiumSysroot, getVSCodeSysroot } from './debian/install-sysroot'; import { generatePackageDeps as generatePackageDepsDebian } from './debian/calculate-deps'; import { generatePackageDeps as generatePackageDepsRpm } from './rpm/calculate-deps'; @@ -25,7 +25,7 @@ import product = require('../../product.json'); // are valid, are in dep-lists.ts const FAIL_BUILD_FOR_NEW_DEPENDENCIES: boolean = true; -// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/128.0.6613.162:chrome/installer/linux/BUILD.gn;l=64-80 +// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/132.0.6834.210:chrome/installer/linux/BUILD.gn;l=64-80 // and the Linux Archive build // Shared library dependencies that we already bundle. const bundledDeps = [ diff --git a/build/linux/libcxx-fetcher.js b/build/linux/libcxx-fetcher.js index cfdc9498502e..d6c998e5aea9 100644 --- a/build/linux/libcxx-fetcher.js +++ b/build/linux/libcxx-fetcher.js @@ -3,23 +3,26 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.downloadLibcxxHeaders = downloadLibcxxHeaders; exports.downloadLibcxxObjects = downloadLibcxxObjects; // Can be removed once https://github.com/electron/electron-rebuild/pull/703 is available. -const fs = require("fs"); -const path = require("path"); -const debug = require("debug"); -const extract = require("extract-zip"); +const fs_1 = __importDefault(require("fs")); +const path_1 = __importDefault(require("path")); +const debug_1 = __importDefault(require("debug")); +const extract_zip_1 = __importDefault(require("extract-zip")); const get_1 = require("@electron/get"); -const root = path.dirname(path.dirname(__dirname)); -const d = debug('libcxx-fetcher'); +const root = path_1.default.dirname(path_1.default.dirname(__dirname)); +const d = (0, debug_1.default)('libcxx-fetcher'); async function downloadLibcxxHeaders(outDir, electronVersion, lib_name) { - if (await fs.existsSync(path.resolve(outDir, 'include'))) { + if (await fs_1.default.existsSync(path_1.default.resolve(outDir, 'include'))) { return; } - if (!await fs.existsSync(outDir)) { - await fs.mkdirSync(outDir, { recursive: true }); + if (!await fs_1.default.existsSync(outDir)) { + await fs_1.default.mkdirSync(outDir, { recursive: true }); } d(`downloading ${lib_name}_headers`); const headers = await (0, get_1.downloadArtifact)({ @@ -28,14 +31,14 @@ async function downloadLibcxxHeaders(outDir, electronVersion, lib_name) { artifactName: `${lib_name}_headers.zip`, }); d(`unpacking ${lib_name}_headers from ${headers}`); - await extract(headers, { dir: outDir }); + await (0, extract_zip_1.default)(headers, { dir: outDir }); } async function downloadLibcxxObjects(outDir, electronVersion, targetArch = 'x64') { - if (await fs.existsSync(path.resolve(outDir, 'libc++.a'))) { + if (await fs_1.default.existsSync(path_1.default.resolve(outDir, 'libc++.a'))) { return; } - if (!await fs.existsSync(outDir)) { - await fs.mkdirSync(outDir, { recursive: true }); + if (!await fs_1.default.existsSync(outDir)) { + await fs_1.default.mkdirSync(outDir, { recursive: true }); } d(`downloading libcxx-objects-linux-${targetArch}`); const objects = await (0, get_1.downloadArtifact)({ @@ -45,14 +48,14 @@ async function downloadLibcxxObjects(outDir, electronVersion, targetArch = 'x64' arch: targetArch, }); d(`unpacking libcxx-objects from ${objects}`); - await extract(objects, { dir: outDir }); + await (0, extract_zip_1.default)(objects, { dir: outDir }); } async function main() { const libcxxObjectsDirPath = process.env['VSCODE_LIBCXX_OBJECTS_DIR']; const libcxxHeadersDownloadDir = process.env['VSCODE_LIBCXX_HEADERS_DIR']; const libcxxabiHeadersDownloadDir = process.env['VSCODE_LIBCXXABI_HEADERS_DIR']; const arch = process.env['VSCODE_ARCH']; - const packageJSON = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')); + const packageJSON = JSON.parse(fs_1.default.readFileSync(path_1.default.join(root, 'package.json'), 'utf8')); const electronVersion = packageJSON.devDependencies.electron; if (!libcxxObjectsDirPath || !libcxxHeadersDownloadDir || !libcxxabiHeadersDownloadDir) { throw new Error('Required build env not set'); diff --git a/build/linux/libcxx-fetcher.ts b/build/linux/libcxx-fetcher.ts index 6abb67faa762..6bdbd8a4f302 100644 --- a/build/linux/libcxx-fetcher.ts +++ b/build/linux/libcxx-fetcher.ts @@ -5,10 +5,10 @@ // Can be removed once https://github.com/electron/electron-rebuild/pull/703 is available. -import * as fs from 'fs'; -import * as path from 'path'; -import * as debug from 'debug'; -import * as extract from 'extract-zip'; +import fs from 'fs'; +import path from 'path'; +import debug from 'debug'; +import extract from 'extract-zip'; import { downloadArtifact } from '@electron/get'; const root = path.dirname(path.dirname(__dirname)); diff --git a/build/linux/rpm/dep-lists.js b/build/linux/rpm/dep-lists.js index 6857f6b7c1a1..f45b6f34b843 100644 --- a/build/linux/rpm/dep-lists.js +++ b/build/linux/rpm/dep-lists.js @@ -46,13 +46,13 @@ exports.referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.18)(64bit)', 'libc.so.6(GLIBC_2.2.5)(64bit)', 'libc.so.6(GLIBC_2.25)(64bit)', + 'libc.so.6(GLIBC_2.27)(64bit)', 'libc.so.6(GLIBC_2.28)(64bit)', 'libc.so.6(GLIBC_2.3)(64bit)', 'libc.so.6(GLIBC_2.3.2)(64bit)', 'libc.so.6(GLIBC_2.3.3)(64bit)', 'libc.so.6(GLIBC_2.3.4)(64bit)', 'libc.so.6(GLIBC_2.4)(64bit)', - 'libc.so.6(GLIBC_2.5)(64bit)', 'libc.so.6(GLIBC_2.6)(64bit)', 'libc.so.6(GLIBC_2.7)(64bit)', 'libc.so.6(GLIBC_2.8)(64bit)', @@ -63,7 +63,6 @@ exports.referenceGeneratedDepsByArch = { 'libdbus-1.so.3(LIBDBUS_1_3)(64bit)', 'libdl.so.2()(64bit)', 'libdl.so.2(GLIBC_2.2.5)(64bit)', - 'libdrm.so.2()(64bit)', 'libexpat.so.1()(64bit)', 'libgbm.so.1()(64bit)', 'libgcc_s.so.1()(64bit)', @@ -87,6 +86,7 @@ exports.referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.30)(64bit)', 'libnss3.so(NSS_3.4)(64bit)', 'libnss3.so(NSS_3.5)(64bit)', + 'libnss3.so(NSS_3.6)(64bit)', 'libnss3.so(NSS_3.9.2)(64bit)', 'libnssutil3.so()(64bit)', 'libnssutil3.so(NSSUTIL_3.12.3)(64bit)', @@ -103,6 +103,8 @@ exports.referenceGeneratedDepsByArch = { 'libsmime3.so(NSS_3.10)(64bit)', 'libsmime3.so(NSS_3.2)(64bit)', 'libssl3.so(NSS_3.28)(64bit)', + 'libudev.so.1()(64bit)', + 'libudev.so.1(LIBUDEV_183)(64bit)', 'libutil.so.1()(64bit)', 'libutil.so.1(GLIBC_2.2.5)(64bit)', 'libxcb.so.1()(64bit)', @@ -139,9 +141,9 @@ exports.referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.17)', 'libc.so.6(GLIBC_2.18)', 'libc.so.6(GLIBC_2.25)', + 'libc.so.6(GLIBC_2.27)', 'libc.so.6(GLIBC_2.28)', 'libc.so.6(GLIBC_2.4)', - 'libc.so.6(GLIBC_2.5)', 'libc.so.6(GLIBC_2.6)', 'libc.so.6(GLIBC_2.7)', 'libc.so.6(GLIBC_2.8)', @@ -152,7 +154,6 @@ exports.referenceGeneratedDepsByArch = { 'libdbus-1.so.3(LIBDBUS_1_3)', 'libdl.so.2', 'libdl.so.2(GLIBC_2.4)', - 'libdrm.so.2', 'libexpat.so.1', 'libgbm.so.1', 'libgcc_s.so.1', @@ -178,6 +179,7 @@ exports.referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.30)', 'libnss3.so(NSS_3.4)', 'libnss3.so(NSS_3.5)', + 'libnss3.so(NSS_3.6)', 'libnss3.so(NSS_3.9.2)', 'libnssutil3.so', 'libnssutil3.so(NSSUTIL_3.12.3)', @@ -208,6 +210,8 @@ exports.referenceGeneratedDepsByArch = { 'libstdc++.so.6(GLIBCXX_3.4.22)', 'libstdc++.so.6(GLIBCXX_3.4.5)', 'libstdc++.so.6(GLIBCXX_3.4.9)', + 'libudev.so.1', + 'libudev.so.1(LIBUDEV_183)', 'libutil.so.1', 'libutil.so.1(GLIBC_2.4)', 'libxcb.so.1', @@ -238,6 +242,7 @@ exports.referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.17)(64bit)', 'libc.so.6(GLIBC_2.18)(64bit)', 'libc.so.6(GLIBC_2.25)(64bit)', + 'libc.so.6(GLIBC_2.27)(64bit)', 'libc.so.6(GLIBC_2.28)(64bit)', 'libcairo.so.2()(64bit)', 'libcurl.so.4()(64bit)', @@ -245,7 +250,6 @@ exports.referenceGeneratedDepsByArch = { 'libdbus-1.so.3(LIBDBUS_1_3)(64bit)', 'libdl.so.2()(64bit)', 'libdl.so.2(GLIBC_2.17)(64bit)', - 'libdrm.so.2()(64bit)', 'libexpat.so.1()(64bit)', 'libgbm.so.1()(64bit)', 'libgcc_s.so.1()(64bit)', @@ -270,6 +274,7 @@ exports.referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.30)(64bit)', 'libnss3.so(NSS_3.4)(64bit)', 'libnss3.so(NSS_3.5)(64bit)', + 'libnss3.so(NSS_3.6)(64bit)', 'libnss3.so(NSS_3.9.2)(64bit)', 'libnssutil3.so()(64bit)', 'libnssutil3.so(NSSUTIL_3.12.3)(64bit)', @@ -296,6 +301,8 @@ exports.referenceGeneratedDepsByArch = { 'libstdc++.so.6(GLIBCXX_3.4.22)(64bit)', 'libstdc++.so.6(GLIBCXX_3.4.5)(64bit)', 'libstdc++.so.6(GLIBCXX_3.4.9)(64bit)', + 'libudev.so.1()(64bit)', + 'libudev.so.1(LIBUDEV_183)(64bit)', 'libutil.so.1()(64bit)', 'libutil.so.1(GLIBC_2.17)(64bit)', 'libxcb.so.1()(64bit)', diff --git a/build/linux/rpm/dep-lists.ts b/build/linux/rpm/dep-lists.ts index 8fd025e51c23..d277ca7e6647 100644 --- a/build/linux/rpm/dep-lists.ts +++ b/build/linux/rpm/dep-lists.ts @@ -45,13 +45,13 @@ export const referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.18)(64bit)', 'libc.so.6(GLIBC_2.2.5)(64bit)', 'libc.so.6(GLIBC_2.25)(64bit)', + 'libc.so.6(GLIBC_2.27)(64bit)', 'libc.so.6(GLIBC_2.28)(64bit)', 'libc.so.6(GLIBC_2.3)(64bit)', 'libc.so.6(GLIBC_2.3.2)(64bit)', 'libc.so.6(GLIBC_2.3.3)(64bit)', 'libc.so.6(GLIBC_2.3.4)(64bit)', 'libc.so.6(GLIBC_2.4)(64bit)', - 'libc.so.6(GLIBC_2.5)(64bit)', 'libc.so.6(GLIBC_2.6)(64bit)', 'libc.so.6(GLIBC_2.7)(64bit)', 'libc.so.6(GLIBC_2.8)(64bit)', @@ -62,7 +62,6 @@ export const referenceGeneratedDepsByArch = { 'libdbus-1.so.3(LIBDBUS_1_3)(64bit)', 'libdl.so.2()(64bit)', 'libdl.so.2(GLIBC_2.2.5)(64bit)', - 'libdrm.so.2()(64bit)', 'libexpat.so.1()(64bit)', 'libgbm.so.1()(64bit)', 'libgcc_s.so.1()(64bit)', @@ -86,6 +85,7 @@ export const referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.30)(64bit)', 'libnss3.so(NSS_3.4)(64bit)', 'libnss3.so(NSS_3.5)(64bit)', + 'libnss3.so(NSS_3.6)(64bit)', 'libnss3.so(NSS_3.9.2)(64bit)', 'libnssutil3.so()(64bit)', 'libnssutil3.so(NSSUTIL_3.12.3)(64bit)', @@ -102,6 +102,8 @@ export const referenceGeneratedDepsByArch = { 'libsmime3.so(NSS_3.10)(64bit)', 'libsmime3.so(NSS_3.2)(64bit)', 'libssl3.so(NSS_3.28)(64bit)', + 'libudev.so.1()(64bit)', + 'libudev.so.1(LIBUDEV_183)(64bit)', 'libutil.so.1()(64bit)', 'libutil.so.1(GLIBC_2.2.5)(64bit)', 'libxcb.so.1()(64bit)', @@ -138,9 +140,9 @@ export const referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.17)', 'libc.so.6(GLIBC_2.18)', 'libc.so.6(GLIBC_2.25)', + 'libc.so.6(GLIBC_2.27)', 'libc.so.6(GLIBC_2.28)', 'libc.so.6(GLIBC_2.4)', - 'libc.so.6(GLIBC_2.5)', 'libc.so.6(GLIBC_2.6)', 'libc.so.6(GLIBC_2.7)', 'libc.so.6(GLIBC_2.8)', @@ -151,7 +153,6 @@ export const referenceGeneratedDepsByArch = { 'libdbus-1.so.3(LIBDBUS_1_3)', 'libdl.so.2', 'libdl.so.2(GLIBC_2.4)', - 'libdrm.so.2', 'libexpat.so.1', 'libgbm.so.1', 'libgcc_s.so.1', @@ -177,6 +178,7 @@ export const referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.30)', 'libnss3.so(NSS_3.4)', 'libnss3.so(NSS_3.5)', + 'libnss3.so(NSS_3.6)', 'libnss3.so(NSS_3.9.2)', 'libnssutil3.so', 'libnssutil3.so(NSSUTIL_3.12.3)', @@ -207,6 +209,8 @@ export const referenceGeneratedDepsByArch = { 'libstdc++.so.6(GLIBCXX_3.4.22)', 'libstdc++.so.6(GLIBCXX_3.4.5)', 'libstdc++.so.6(GLIBCXX_3.4.9)', + 'libudev.so.1', + 'libudev.so.1(LIBUDEV_183)', 'libutil.so.1', 'libutil.so.1(GLIBC_2.4)', 'libxcb.so.1', @@ -237,6 +241,7 @@ export const referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.17)(64bit)', 'libc.so.6(GLIBC_2.18)(64bit)', 'libc.so.6(GLIBC_2.25)(64bit)', + 'libc.so.6(GLIBC_2.27)(64bit)', 'libc.so.6(GLIBC_2.28)(64bit)', 'libcairo.so.2()(64bit)', 'libcurl.so.4()(64bit)', @@ -244,7 +249,6 @@ export const referenceGeneratedDepsByArch = { 'libdbus-1.so.3(LIBDBUS_1_3)(64bit)', 'libdl.so.2()(64bit)', 'libdl.so.2(GLIBC_2.17)(64bit)', - 'libdrm.so.2()(64bit)', 'libexpat.so.1()(64bit)', 'libgbm.so.1()(64bit)', 'libgcc_s.so.1()(64bit)', @@ -269,6 +273,7 @@ export const referenceGeneratedDepsByArch = { 'libnss3.so(NSS_3.30)(64bit)', 'libnss3.so(NSS_3.4)(64bit)', 'libnss3.so(NSS_3.5)(64bit)', + 'libnss3.so(NSS_3.6)(64bit)', 'libnss3.so(NSS_3.9.2)(64bit)', 'libnssutil3.so()(64bit)', 'libnssutil3.so(NSSUTIL_3.12.3)(64bit)', @@ -295,6 +300,8 @@ export const referenceGeneratedDepsByArch = { 'libstdc++.so.6(GLIBCXX_3.4.22)(64bit)', 'libstdc++.so.6(GLIBCXX_3.4.5)(64bit)', 'libstdc++.so.6(GLIBCXX_3.4.9)(64bit)', + 'libudev.so.1()(64bit)', + 'libudev.so.1(LIBUDEV_183)(64bit)', 'libutil.so.1()(64bit)', 'libutil.so.1(GLIBC_2.17)(64bit)', 'libxcb.so.1()(64bit)', diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index ef5c2e39ae52..6192ad5b2ec7 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -109,7 +109,7 @@ export interface ICommandMetadata { #include(vs/platform/markers/common/markers): IMarker, IMarkerData, IRelatedInformation #include(vs/editor/standalone/browser/colorizer): IColorizerOptions, IColorizerElementOptions #include(vs/base/common/scrollable): ScrollbarVisibility -#include(vs/base/common/themables): ThemeColor +#include(vs/base/common/themables): ThemeColor, ThemeIcon #include(vs/editor/common/core/editOperation): ISingleEditOperation #include(vs/editor/common/core/wordHelper): IWordAtPosition #includeAll(vs/editor/common/model): IScrollEvent @@ -136,7 +136,7 @@ declare namespace monaco.languages { #include(vs/editor/common/languageSelector): LanguageSelector, LanguageFilter #includeAll(vs/editor/standalone/browser/standaloneLanguages;languages.=>;editorCommon.=>editor.;model.=>editor.;IMarkerData=>editor.IMarkerData): #includeAll(vs/editor/common/languages/languageConfiguration): -#includeAll(vs/editor/common/languages;IMarkerData=>editor.IMarkerData;ISingleEditOperation=>editor.ISingleEditOperation;model.=>editor.): Token +#includeAll(vs/editor/common/languages;IMarkerData=>editor.IMarkerData;ISingleEditOperation=>editor.ISingleEditOperation;model.=>editor.;ThemeIcon=>editor.ThemeIcon): Token #include(vs/editor/common/languages/language): ILanguageExtensionPoint #includeAll(vs/editor/standalone/common/monarch/monarchTypes): diff --git a/build/monaco/monaco.usage.recipe b/build/monaco/monaco.usage.recipe index e3c8cdd09163..a3369eb25a7d 100644 --- a/build/monaco/monaco.usage.recipe +++ b/build/monaco/monaco.usage.recipe @@ -35,6 +35,6 @@ import * as editorAPI from './vs/editor/editor.api'; a = editorAPI.editor; a = editorAPI.languages; - const o: IObservable = null!; + const o: IObservable = null!; o.TChange; })(); diff --git a/build/npm/dirs.js b/build/npm/dirs.js index b9645e6e1371..9f653c724794 100644 --- a/build/npm/dirs.js +++ b/build/npm/dirs.js @@ -44,6 +44,7 @@ const dirs = [ 'extensions/typescript-language-features', 'extensions/vscode-api-tests', 'extensions/vscode-colorize-tests', + 'extensions/vscode-colorize-perf-tests', 'extensions/vscode-test-resolver', 'remote', 'remote/web', diff --git a/build/npm/gyp/package-lock.json b/build/npm/gyp/package-lock.json index a20d85c70dca..a6b04265a2a8 100644 --- a/build/npm/gyp/package-lock.json +++ b/build/npm/gyp/package-lock.json @@ -9,7 +9,30 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "node-gyp": "^10.1.0" + "node-gyp": "^12.1.0" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" } }, "node_modules/@isaacs/cliui": { @@ -30,88 +53,73 @@ "node": ">=12" } }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@npmcli/agent": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", - "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.0.tgz", + "integrity": "sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==", "dev": true, "license": "ISC", "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", - "lru-cache": "^10.0.1", + "lru-cache": "^11.2.1", "socks-proxy-agent": "^8.0.3" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", - "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-4.0.0.tgz", + "integrity": "sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==", "dev": true, "license": "ISC", "dependencies": { "semver": "^7.3.5" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/abbrev": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", - "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", "dev": true, "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, "engines": { "node": ">= 14" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "license": "MIT", "engines": { @@ -122,9 +130,9 @@ } }, "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", "engines": { @@ -134,65 +142,37 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/cacache": { - "version": "18.0.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.3.tgz", - "integrity": "sha512-qXCd4rh6I07cnDqh8V48/94Tc/WSfj+o3Gn6NZ0aZovS255bUx8O13uKxRFd2eWG0xgsco7+YItQNPaa5E85hg==", + "version": "20.0.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.2.tgz", + "integrity": "sha512-rVWvqtWcgSzB22wImrVto+7PmE+lUqv5dYzRHD0QJsfpSwTkW+GIqA4ykSt/CCjQlQle8USn8CO8vcWNrIqktg==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/fs": "^3.1.0", + "@npmcli/fs": "^4.0.0", "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", + "glob": "^11.0.3", + "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" + "p-map": "^7.0.2", + "ssri": "^13.0.0", + "unique-filename": "^4.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=6" + "node": ">=18" } }, "node_modules/color-convert": { @@ -216,9 +196,9 @@ "license": "MIT" }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -254,13 +234,13 @@ } }, "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -320,14 +300,32 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/foreground-child": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -351,24 +349,24 @@ } }, "node_modules/glob": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", - "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -382,9 +380,9 @@ "license": "ISC" }, "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", "dev": true, "license": "BSD-2-Clause" }, @@ -403,13 +401,13 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { @@ -440,26 +438,12 @@ "node": ">=0.8.19" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", "dev": true, "license": "MIT", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, "engines": { "node": ">= 12" } @@ -474,13 +458,6 @@ "node": ">=8" } }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true, - "license": "MIT" - }, "node_modules/isexe": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", @@ -492,86 +469,65 @@ } }, "node_modules/jackspeak": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", - "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=14" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "dev": true, - "license": "MIT" - }, "node_modules/lru-cache": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.0.tgz", - "integrity": "sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==", + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", "dev": true, "license": "ISC", "engines": { - "node": "14 || >=16.14" + "node": "20 || >=22" } }, "node_modules/make-fetch-happen": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", - "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.3.tgz", + "integrity": "sha512-iyyEpDty1mwW3dGlYXAJqC/azFn5PPvgKVwXayOGBSmKLxhKZ9fg4qIan2ePpp1vJIwfFiO34LAPZgq9SZW9Aw==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/agent": "^2.0.0", - "cacache": "^18.0.0", + "@npmcli/agent": "^4.0.0", + "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", - "is-lambda": "^1.0.1", "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", + "minipass-fetch": "^5.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "proc-log": "^4.2.0", + "negotiator": "^1.0.0", + "proc-log": "^6.0.0", "promise-retry": "^2.0.1", - "ssri": "^10.0.0" + "ssri": "^13.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -601,18 +557,18 @@ } }, "node_modules/minipass-fetch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", - "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.0.tgz", + "integrity": "sha512-fiCdUALipqgPWrOVTz9fw0XhcazULXOSU6ie40DDbX1F49p1dBrSRBuswndTx1x3vEb/g0FT7vC4c4C2u/mh3A==", "dev": true, "license": "MIT", "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" + "minizlib": "^3.0.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" }, "optionalDependencies": { "encoding": "^0.1.13" @@ -644,6 +600,13 @@ "node": ">=8" } }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/minipass-pipeline": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", @@ -670,6 +633,13 @@ "node": ">=8" } }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/minipass-sized": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", @@ -696,57 +666,37 @@ "node": ">=8" } }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } + "license": "ISC" }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "dev": true, "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" + "dependencies": { + "minipass": "^7.1.2" }, "engines": { - "node": ">=10" + "node": ">= 18" } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "dev": true, "license": "MIT", "engines": { @@ -754,66 +704,63 @@ } }, "node_modules/node-gyp": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.1.0.tgz", - "integrity": "sha512-B4J5M1cABxPc5PwfjhbV5hoy2DP9p8lFXASnEN6hugXOa61416tnTZ29x9sSwAd0o99XNIcpvDDy1swAExsVKA==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.1.0.tgz", + "integrity": "sha512-W+RYA8jBnhSr2vrTtlPYPc1K+CSjGpVDRZxcqJcERZ8ND3A1ThWPHRwctTx3qC3oW99jt726jhdz3Y6ky87J4g==", "dev": true, "license": "MIT", "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", - "glob": "^10.3.10", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^13.0.0", - "nopt": "^7.0.0", - "proc-log": "^3.0.0", + "make-fetch-happen": "^15.0.0", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^4.0.0" + "tar": "^7.5.2", + "tinyglobby": "^0.2.12", + "which": "^6.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/nopt": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", - "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", "dev": true, "license": "ISC", "dependencies": { - "abbrev": "^2.0.0" + "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", "dev": true, "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, "license": "BlueOak-1.0.0" }, @@ -828,30 +775,43 @@ } }, "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/proc-log": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", - "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", + "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", "dev": true, "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/promise-retry": { @@ -887,14 +847,11 @@ "optional": true }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -902,19 +859,6 @@ "node": ">=10" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -963,13 +907,13 @@ } }, "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", "dev": true, "license": "MIT", "dependencies": { - "ip-address": "^9.0.5", + "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" }, "engines": { @@ -978,13 +922,13 @@ } }, "node_modules/socks-proxy-agent": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", - "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.1.1", + "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" }, @@ -992,24 +936,17 @@ "node": ">= 14" } }, - "node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.0.tgz", + "integrity": "sha512-yizwGBpbCn4YomB2lzhZqrHLJoqFGXihNbib3ozhqF/cIp5ue+xSmOQrjNasEE62hFxsCcg/V/z23t4n8jMEng==", "dev": true, "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/string-width": { @@ -1077,9 +1014,9 @@ } }, "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "dev": true, "license": "MIT", "dependencies": { @@ -1117,89 +1054,69 @@ } }, "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.6.tgz", + "integrity": "sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" } }, - "node_modules/tar/node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "minipass": "^3.0.0" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" + "node": ">=12.0.0" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, "node_modules/unique-filename": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-4.0.0.tgz", + "integrity": "sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==", "dev": true, "license": "ISC", "dependencies": { - "unique-slug": "^4.0.0" + "unique-slug": "^5.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-5.0.0.tgz", + "integrity": "sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==", "dev": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", "dev": true, "license": "ISC", "dependencies": { @@ -1209,7 +1126,7 @@ "node-which": "bin/which.js" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/wrap-ansi": { @@ -1311,11 +1228,14 @@ } }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", "dev": true, - "license": "ISC" + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } } } } diff --git a/build/npm/gyp/package.json b/build/npm/gyp/package.json index a1564133a1ee..1b869649d321 100644 --- a/build/npm/gyp/package.json +++ b/build/npm/gyp/package.json @@ -4,7 +4,7 @@ "private": true, "license": "MIT", "devDependencies": { - "node-gyp": "^10.1.0" + "node-gyp": "^12.1.0" }, "scripts": {} } diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index 239f0688e097..458847afac58 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -62,6 +62,7 @@ function npmInstall(dir, opts) { log(dir, 'Installing dependencies...'); run(npm, command.split(' '), opts); } + removeParcelWatcherPrebuild(dir); } function setNpmrcConfig(dir, env) { @@ -76,17 +77,47 @@ function setNpmrcConfig(dir, env) { } } + // Force node-gyp to use process.config on macOS + // which defines clang variable as expected. Otherwise we + // run into compilation errors due to incorrect compiler + // configuration. + // NOTE: This means the process.config should contain + // the correct clang variable. So keep the version check + // in preinstall sync with this logic. + // Change was first introduced in https://github.com/nodejs/node/commit/6e0a2bb54c5bbeff0e9e33e1a0c683ed980a8a0f + if ((dir === 'remote' || dir === 'build') && process.platform === 'darwin') { + env['npm_config_force_process_config'] = 'true'; + } else { + delete env['npm_config_force_process_config']; + } + if (dir === 'build') { env['npm_config_target'] = process.versions.node; env['npm_config_arch'] = process.arch; } } +function removeParcelWatcherPrebuild(dir) { + const parcelModuleFolder = path.join(root, dir, 'node_modules', '@parcel'); + if (!fs.existsSync(parcelModuleFolder)) { + return; + } + + const parcelModules = fs.readdirSync(parcelModuleFolder); + for (const moduleName of parcelModules) { + if (moduleName.startsWith('watcher-')) { + const modulePath = path.join(parcelModuleFolder, moduleName); + fs.rmSync(modulePath, { recursive: true, force: true }); + log(dir, `Removed @parcel/watcher prebuilt module ${modulePath}`); + } + } +} + for (let dir of dirs) { if (dir === '') { - // already executed in root - continue; + removeParcelWatcherPrebuild(dir); + continue; // already executed in root } let opts; diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js index b98f22977bfd..41a17b016767 100644 --- a/build/npm/preinstall.js +++ b/build/npm/preinstall.js @@ -9,8 +9,8 @@ const minorNodeVersion = parseInt(nodeVersion[2]); const patchNodeVersion = parseInt(nodeVersion[3]); if (!process.env['VSCODE_SKIP_NODE_VERSION_CHECK']) { - if (majorNodeVersion < 20) { - console.error('\x1b[1;31m*** Please use latest Node.js v20 LTS for development.\x1b[0;0m'); + if (majorNodeVersion < 20 || (majorNodeVersion === 20 && minorNodeVersion < 18) || (majorNodeVersion === 20 && minorNodeVersion === 18 && patchNodeVersion < 1)) { + console.error('\x1b[1;31m*** Please use Node.js v20.18.1 or later for development.\x1b[0;0m'); throw new Error(); } } @@ -23,6 +23,7 @@ if (process.env['npm_execpath'].includes('yarn')) { const path = require('path'); const fs = require('fs'); const cp = require('child_process'); +const os = require('os'); if (process.platform === 'win32') { if (!hasSupportedVisualStudioVersion()) { @@ -32,6 +33,11 @@ if (process.platform === 'win32') { installHeaders(); } +if (process.arch !== os.arch()) { + console.error(`\x1b[1;31m*** ARCHITECTURE MISMATCH: The node.js process is ${process.arch}, but your OS architecture is ${os.arch()}. ***\x1b[0;0m`); + console.error(`\x1b[1;31m*** This can greatly increase the build time of vs code. ***\x1b[0;0m`); +} + function hasSupportedVisualStudioVersion() { const fs = require('fs'); const path = require('path'); diff --git a/build/package-lock.json b/build/package-lock.json index 3ea31d0908b7..b89cf7bba076 100644 --- a/build/package-lock.json +++ b/build/package-lock.json @@ -9,285 +9,498 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@azure/cosmos": "^3", - "@azure/identity": "^4.2.1", - "@electron/get": "^2.0.0", - "@types/ansi-colors": "^3.2.0", - "@types/byline": "^4.2.32", - "@types/debounce": "^1.0.0", - "@types/debug": "^4.1.5", - "@types/fancy-log": "^1.3.0", - "@types/fs-extra": "^9.0.12", - "@types/glob": "^7.1.1", - "@types/gulp": "^4.0.17", - "@types/gulp-filter": "^3.0.32", - "@types/gulp-gzip": "^0.0.31", - "@types/gulp-json-editor": "^2.2.31", - "@types/gulp-rename": "^0.0.33", + "@azure/core-auth": "^1.10.1", + "@azure/cosmos": "^4", + "@azure/identity": "^4.13.0", + "@azure/msal-node": "^5.0.2", + "@azure/storage-blob": "^12.30.0", + "@electron/get": "^4.0.2", + "@types/ansi-colors": "^3.2.6", + "@types/byline": "^4.2.36", + "@types/debounce": "^1.2.4", + "@types/debug": "^4.1.12", + "@types/fancy-log": "^2.0.2", + "@types/fs-extra": "^11.0.4", + "@types/glob": "^9.0.0", + "@types/gulp": "^4.0.18", + "@types/gulp-filter": "^3.0.41", + "@types/gulp-gzip": "^0.0.36", + "@types/gulp-json-editor": "^2.2.36", + "@types/gulp-rename": "^2.0.7", "@types/gulp-sort": "^2.0.4", - "@types/gulp-sourcemaps": "^0.0.32", - "@types/mime": "0.0.29", - "@types/minimatch": "^3.0.3", - "@types/minimist": "^1.2.1", - "@types/mocha": "^9.1.1", - "@types/node": "20.x", - "@types/pump": "^1.0.1", - "@types/rimraf": "^2.0.4", - "@types/through": "^0.0.29", - "@types/through2": "^2.0.36", - "@types/workerpool": "^6.4.0", - "@types/xml2js": "0.0.33", - "@vscode/iconv-lite-umd": "0.7.0", - "@vscode/vsce": "2.20.1", + "@types/gulp-sourcemaps": "^0.0.38", + "@types/jws": "^3.2.11", + "@types/mime": "4.0.0", + "@types/minimatch": "^6.0.0", + "@types/minimist": "^1.2.5", + "@types/mocha": "^10.0.10", + "@types/node": "25.x", + "@types/pump": "^1.1.3", + "@types/rimraf": "^4.0.5", + "@types/through": "^0.0.33", + "@types/through2": "^2.0.41", + "@types/workerpool": "^9.0.0", + "@types/xml2js": "0.4.14", + "@vscode/iconv-lite-umd": "0.7.1", + "@vscode/ripgrep": "^1.17.0", + "@vscode/vsce": "3.7.1", "byline": "^5.0.0", "debug": "^4.3.2", - "electron-osx-sign": "^0.4.16", - "esbuild": "0.23.0", + "electron-osx-sign": "^0.6.0", + "esbuild": "0.27.2", "extract-zip": "^2.0.1", - "gulp-merge-json": "^2.1.1", + "gulp-merge-json": "^2.2.1", "gulp-sort": "^2.0.0", - "jsonc-parser": "^2.3.0", - "mime": "^1.4.1", - "source-map": "0.6.1", + "jsonc-parser": "^3.3.1", + "jws": "^4.0.1", + "mime": "^4.1.0", + "source-map": "0.7.6", "ternary-stream": "^3.0.0", "through2": "^4.0.2", + "tree-sitter": "^0.25.0", "vscode-universal-bundler": "^0.1.3", - "workerpool": "^6.4.0", - "yauzl": "^2.10.0" + "workerpool": "^10.0.1", + "yauzl": "^3.2.0" }, "optionalDependencies": { - "tree-sitter": "^0.20.5", - "tree-sitter-typescript": "^0.20.5", + "tree-sitter-typescript": "^0.23.2", "vscode-gulp-watch": "^5.0.3" } }, - "node_modules/@azure/abort-controller": { + "node_modules/@azu/format-text": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.2.tgz", - "integrity": "sha512-XUyTo+bcyxHEf+jlN2MXA7YU9nxVehaubngHV1MIZZaqYmZqykkoeAz/JMMEeR7t3TcyDwbFa3Zw8BZywmIx4g==", + "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.2.tgz", + "integrity": "sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@azu/style-format": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azu/style-format/-/style-format-1.0.1.tgz", + "integrity": "sha512-AHcTojlNBdD/3/KxIKlg8sxIWHfOtQszLvOpagLTO+bjC3u7SAszu1lf//u7JJC50aUSH+BVWDD/KvaA6Gfn5g==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "@azu/format-text": "^1.0.1" + } + }, + "node_modules/@azure-rest/core-client": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.5.1.tgz", + "integrity": "sha512-EHaOXW0RYDKS5CFffnixdyRPak5ytiCtU7uXDcP/uiY+A6jFRwNGzzJBiznkCzvi5EYpY+YWinieqHb0oY916A==", "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.0.0" + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0", + "@azure/core-tracing": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8.0.0" + "node": ">=20.0.0" } }, - "node_modules/@azure/core-asynciterator-polyfill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@azure/core-asynciterator-polyfill/-/core-asynciterator-polyfill-1.0.0.tgz", - "integrity": "sha512-kmv8CGrPfN9SwMwrkiBK9VTQYxdFQEGe0BmQk+M8io56P9KNzpAxcWE/1fxJj7uouwN4kXF0BHW8DNlgx+wtCg==", - "dev": true + "node_modules/@azure/abort-controller": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", + "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } }, "node_modules/@azure/core-auth": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.5.0.tgz", - "integrity": "sha512-udzoBuYG1VBoHVohDTrvKjyzel34zt77Bhp7dQntVGGD0ehVq48owENbBG8fIgkHRNUBQH5k1r0hpoMu5L8+kw==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", + "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", "dev": true, + "license": "MIT", "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-util": "^1.1.0", - "tslib": "^2.2.0" + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=20.0.0" } }, "node_modules/@azure/core-client": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.5.0.tgz", - "integrity": "sha512-YNk8i9LT6YcFdFO+RRU0E4Ef+A8Y5lhXo6lz61rwbG8Uo7kSqh0YqK04OexiilM43xd6n3Y9yBhLnb1NFNI9dA==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz", + "integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==", "dev": true, + "license": "MIT", "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-asynciterator-polyfill": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-rest-pipeline": "^1.5.0", - "@azure/core-tracing": "1.0.0-preview.13", - "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=20.0.0" } }, - "node_modules/@azure/core-client/node_modules/@azure/core-tracing": { - "version": "1.0.0-preview.13", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", - "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "node_modules/@azure/core-http-compat": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.3.1.tgz", + "integrity": "sha512-az9BkXND3/d5VgdRRQVkiJb2gOmDU8Qcq4GvjtBmDICNiQ9udFmDk4ZpSB5Qq1OmtDJGlQAfBaS4palFsazQ5g==", "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/api": "^1.0.1", - "tslib": "^2.2.0" + "@azure/abort-controller": "^2.1.2", + "@azure/core-client": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=20.0.0" } }, - "node_modules/@azure/core-rest-pipeline": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.7.0.tgz", - "integrity": "sha512-e2awPzwMKHrmvYgZ0qIKNkqnCM1QoDs7A0rOiS3OSAlOQOz/kL7PPKHXwFMuBeaRvS8i7fgobJn79q2Cji5f+Q==", + "node_modules/@azure/core-lro": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz", + "integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==", "dev": true, + "license": "MIT", "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-tracing": "1.0.0-preview.13", + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.2.0", "@azure/logger": "^1.0.0", - "form-data": "^4.0.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "tslib": "^2.2.0", - "uuid": "^8.3.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@azure/core-rest-pipeline/node_modules/@azure/core-tracing": { - "version": "1.0.0-preview.13", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.0-preview.13.tgz", - "integrity": "sha512-KxDlhXyMlh2Jhj2ykX6vNEU0Vou4nHr025KoSEiz7cS3BNiHNaZcdECk/DmLkEB0as5T7b/TpRcehJ5yV6NeXQ==", + "node_modules/@azure/core-paging": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz", + "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", "dev": true, + "license": "MIT", "dependencies": { - "@opentelemetry/api": "^1.0.1", - "tslib": "^2.2.0" + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.2.tgz", + "integrity": "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=20.0.0" } }, "node_modules/@azure/core-tracing": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.0.1.tgz", - "integrity": "sha512-I5CGMoLtX+pI17ZdiFJZgxMJApsK6jjfm85hpgp3oazCdq5Wxgh4wMr7ge/TTWW1B5WBuvIOI1fMU/FrOAMKrw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", + "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.2.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=20.0.0" } }, "node_modules/@azure/core-util": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.9.0.tgz", - "integrity": "sha512-AfalUQ1ZppaKuxPPMsFEUdX6GZPB3d9paR9d/TTL7Ow2De8cJaC7ibi7kWVlFAVPCYo31OcnGymc0R89DX8Oaw==", + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", + "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", "dev": true, + "license": "MIT", "dependencies": { - "@azure/abort-controller": "^2.0.0", + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@azure/core-util/node_modules/@azure/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "node_modules/@azure/core-xml": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@azure/core-xml/-/core-xml-1.5.0.tgz", + "integrity": "sha512-D/sdlJBMJfx7gqoj66PKVmhDDaU6TKA49ptcolxdas29X7AfvLTmfAGLjAcIMBK7UZ2o4lygHIqVckOlQU3xWw==", "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "fast-xml-parser": "^5.0.7", + "tslib": "^2.8.1" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@azure/cosmos": { - "version": "3.17.3", - "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-3.17.3.tgz", - "integrity": "sha512-wBglkQ6Irjv5Vo2iw8fd6eYj60WYRSSg4/0DBkeOP6BwQ4RA91znsOHd6s3qG6UAbNgYuzC9Nnq07vlFFZkHEw==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.9.0.tgz", + "integrity": "sha512-3LzLibuVgbhWEv6B9Hz7bMn0KAKKlr3Rn9xPulZ6Xx69GuwgWmQ4qmkrfYBBBFCJDQXoVAvVGBYkt9VlW8hmOg==", "dev": true, + "license": "MIT", "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.3.0", - "@azure/core-rest-pipeline": "^1.2.0", - "@azure/core-tracing": "^1.0.0", - "debug": "^4.1.1", + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-rest-pipeline": "^1.19.1", + "@azure/core-tracing": "^1.2.0", + "@azure/core-util": "^1.11.0", + "@azure/keyvault-keys": "^4.9.0", + "@azure/logger": "^1.1.4", "fast-json-stable-stringify": "^2.1.0", - "jsbi": "^3.1.3", - "node-abort-controller": "^3.0.0", - "priorityqueuejs": "^1.0.0", - "semaphore": "^1.0.5", - "tslib": "^2.2.0", - "universal-user-agent": "^6.0.0", - "uuid": "^8.3.0" + "priorityqueuejs": "^2.0.0", + "semaphore": "^1.1.0", + "tslib": "^2.8.1" }, "engines": { - "node": ">=14.0.0" + "node": ">=20.0.0" } }, "node_modules/@azure/identity": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.2.1.tgz", - "integrity": "sha512-U8hsyC9YPcEIzoaObJlRDvp7KiF0MGS7xcWbyJSVvXRkC/HXo1f0oYeBYmEvVgRfacw7GHf6D6yAoh9JHz6A5Q==", + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.0.tgz", + "integrity": "sha512-uWC0fssc+hs1TGGVkkghiaFkkS7NkTxfnCH+Hdg+yTehTpMcehpok4PgUKKdyCH+9ldu6FhiHRv84Ntqj1vVcw==", "dev": true, + "license": "MIT", "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.5.0", - "@azure/core-client": "^1.4.0", - "@azure/core-rest-pipeline": "^1.1.0", + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.3.0", + "@azure/core-util": "^1.11.0", "@azure/logger": "^1.0.0", - "@azure/msal-browser": "^3.11.1", - "@azure/msal-node": "^2.9.2", - "events": "^3.0.0", - "jws": "^4.0.0", - "open": "^8.0.0", - "stoppable": "^1.1.0", + "@azure/msal-browser": "^4.2.0", + "@azure/msal-node": "^3.5.0", + "open": "^10.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/identity/node_modules/@azure/msal-common": { + "version": "15.14.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.14.1.tgz", + "integrity": "sha512-IkzF7Pywt6QKTS0kwdCv/XV8x8JXknZDvSjj/IccooxnP373T5jaadO3FnOrbWo3S0UqkfIDyZNTaQ/oAgRdXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/identity/node_modules/@azure/msal-node": { + "version": "3.8.6", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.8.6.tgz", + "integrity": "sha512-XTmhdItcBckcVVTy65Xp+42xG4LX5GK+9AqAsXPXk4IqUNv+LyQo5TMwNjuFYBfAB2GTG9iSQGk+QLc03vhf3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "15.14.1", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@azure/keyvault-common": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@azure/keyvault-common/-/keyvault-common-2.0.0.tgz", + "integrity": "sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.3.0", + "@azure/core-client": "^1.5.0", + "@azure/core-rest-pipeline": "^1.8.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.10.0", + "@azure/logger": "^1.1.4", "tslib": "^2.2.0" }, "engines": { "node": ">=18.0.0" } }, + "node_modules/@azure/keyvault-keys": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@azure/keyvault-keys/-/keyvault-keys-4.10.0.tgz", + "integrity": "sha512-eDT7iXoBTRZ2n3fLiftuGJFD+yjkiB1GNqzU2KbY1TLYeXeSPVTVgn2eJ5vmRTZ11978jy2Kg2wI7xa9Tyr8ag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure-rest/core-client": "^2.3.3", + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-http-compat": "^2.2.0", + "@azure/core-lro": "^2.7.2", + "@azure/core-paging": "^1.6.2", + "@azure/core-rest-pipeline": "^1.19.0", + "@azure/core-tracing": "^1.2.0", + "@azure/core-util": "^1.11.0", + "@azure/keyvault-common": "^2.0.0", + "@azure/logger": "^1.1.4", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@azure/logger": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.1.tgz", - "integrity": "sha512-QYQeaJ+A5x6aMNu8BG5qdsVBnYBop9UMwgUvGihSjf1PdZZXB+c/oMdM2ajKwzobLBh9e9QuMQkN9iL+IxLBLA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", + "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.0.0" + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8.0.0" + "node": ">=20.0.0" } }, "node_modules/@azure/msal-browser": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.17.0.tgz", - "integrity": "sha512-csccKXmW2z7EkZ0I3yAoW/offQt+JECdTIV/KrnRoZyM7wCSsQWODpwod8ZhYy7iOyamcHApR9uCh0oD1M+0/A==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.27.0.tgz", + "integrity": "sha512-bZ8Pta6YAbdd0o0PEaL1/geBsPrLEnyY/RDWqvF1PP9RUH8EMLvUMGoZFYS6jSlUan6KZ9IMTLCnwpWWpQRK/w==", "dev": true, + "license": "MIT", "dependencies": { - "@azure/msal-common": "14.12.0" + "@azure/msal-common": "15.13.3" }, "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-common": { - "version": "14.12.0", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.12.0.tgz", - "integrity": "sha512-IDDXmzfdwmDkv4SSmMEyAniJf6fDu3FJ7ncOjlxkDuT85uSnLEhZi3fGZpoR7T4XZpOMx9teM9GXBgrfJgyeBw==", + "version": "15.13.3", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.13.3.tgz", + "integrity": "sha512-shSDU7Ioecya+Aob5xliW9IGq1Ui8y4EVSdWGyI1Gbm4Vg61WpP95LuzcY214/wEjSn6w4PZYD4/iVldErHayQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-node": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.9.2.tgz", - "integrity": "sha512-8tvi6Cos3m+0KmRbPjgkySXi+UQU/QiuVRFnrxIwt5xZlEEFa69O04RTaNESGgImyBBlYbo2mfE8/U8Bbdk1WQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.0.2.tgz", + "integrity": "sha512-3tHeJghckgpTX98TowJoXOjKGuds0L+FKfeHJtoZFl2xvwE6RF65shZJzMQ5EQZWXzh3sE1i9gE+m3aRMachjA==", "dev": true, + "license": "MIT", "dependencies": { - "@azure/msal-common": "14.12.0", + "@azure/msal-common": "16.0.2", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" }, "engines": { - "node": ">=16" + "node": ">=20" + } + }, + "node_modules/@azure/msal-node/node_modules/@azure/msal-common": { + "version": "16.0.2", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.0.2.tgz", + "integrity": "sha512-ZJ/UR7lyqIntURrIJCyvScwJFanM9QhJYcJCheB21jZofGKpP9QxWgvADANo7UkresHKzV+6YwoeZYP7P7HvUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/storage-blob": { + "version": "12.30.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.30.0.tgz", + "integrity": "sha512-peDCR8blSqhsAKDbpSP/o55S4sheNwSrblvCaHUZ5xUI73XA7ieUGGwrONgD/Fng0EoDe1VOa3fAQ7+WGB3Ocg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.3", + "@azure/core-http-compat": "^2.2.0", + "@azure/core-lro": "^2.2.0", + "@azure/core-paging": "^1.6.2", + "@azure/core-rest-pipeline": "^1.19.1", + "@azure/core-tracing": "^1.2.0", + "@azure/core-util": "^1.11.0", + "@azure/core-xml": "^1.4.5", + "@azure/logger": "^1.1.4", + "@azure/storage-common": "^12.2.0", + "events": "^3.0.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/storage-common": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/@azure/storage-common/-/storage-common-12.2.0.tgz", + "integrity": "sha512-YZLxiJ3vBAAnFbG3TFuAMUlxZRexjQX5JDQxOkFGb6e2TpoxH3xyHI6idsMe/QrWtj41U/KoqBxlayzhS+LlwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-http-compat": "^2.2.0", + "@azure/core-rest-pipeline": "^1.19.1", + "@azure/core-tracing": "^1.2.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.1.4", + "events": "^3.3.0", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, "node_modules/@electron/asar": { @@ -317,34 +530,36 @@ } }, "node_modules/@electron/get": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", - "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-4.0.2.tgz", + "integrity": "sha512-n9fRt/nzzOOZdDtTP3kT6GVdo0ro9FgMKCoS520kQMIiKBhpGmPny6yK/lER3tOCKr+wLYW1O25D9oI6ZinwCA==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.1.1", - "env-paths": "^2.2.0", - "fs-extra": "^8.1.0", - "got": "^11.8.5", + "env-paths": "^3.0.0", + "got": "^14.4.5", + "graceful-fs": "^4.2.11", "progress": "^2.0.3", - "semver": "^6.2.0", + "semver": "^7.6.3", "sumchecker": "^3.0.1" }, "engines": { - "node": ">=12" + "node": ">=22.12.0" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", - "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" @@ -354,13 +569,14 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", - "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -370,13 +586,14 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", - "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -386,13 +603,14 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", - "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -402,13 +620,14 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", - "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -418,13 +637,14 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", - "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -434,13 +654,14 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", - "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -450,13 +671,14 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", - "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -466,13 +688,14 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", - "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -482,13 +705,14 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", - "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -498,13 +722,14 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", - "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -514,13 +739,14 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", - "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -530,13 +756,14 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", - "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -546,13 +773,14 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", - "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -562,13 +790,14 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", - "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -578,13 +807,14 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", - "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -594,13 +824,14 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", - "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -609,14 +840,32 @@ "node": ">=18" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", - "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -626,13 +875,14 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", - "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -642,13 +892,14 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", - "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -657,14 +908,32 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", - "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" @@ -674,13 +943,14 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", - "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -690,13 +960,14 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", - "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -706,13 +977,14 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", - "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -721,6 +993,79 @@ "node": ">=18" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "dev": true, + "license": "MIT" + }, "node_modules/@malept/cross-spawn-promise": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", @@ -743,86 +1088,445 @@ "node": ">= 12.13.0" } }, - "node_modules/@opentelemetry/api": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.0.3.tgz", - "integrity": "sha512-puWxACExDe9nxbBB3lOymQFrLYml2dVOrd7USiVRnSbgXE+KwBu+HxFvxrzfqsiSda9IWsXJG1ef7C1O2/GmKQ==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, "engines": { - "node": ">=8.0.0" + "node": ">= 8" } }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/config-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-creator/-/config-creator-10.2.2.tgz", + "integrity": "sha512-BynOBe7Hn3LJjb3CqCHZjeNB09s/vgf0baBaHVw67w7gHF0d25c3ZsZ5+vv8TgwSchRdUCRrbbcq5i2B1fJ2QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-loader/-/config-loader-10.2.2.tgz", + "integrity": "sha512-ndjjQNgLg4DIcMJp4iaRD6xb9ijWQZVbd9694Ol2IszBIbGPPkwZHzJYKICbTBmh6AH/pLr0CiCaWdGJU7RbpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "ajv": "^8.17.1", + "debug": "^4.4.1", + "rc-config-loader": "^4.1.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/core": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/core/-/core-10.2.2.tgz", + "integrity": "sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "structured-source": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/formatter/-/formatter-10.2.2.tgz", + "integrity": "sha512-10f/eKV+8YdGKNQmoDUD1QnYL7TzhI2kzyx95vsJKbEa8akzLAR5ZrWIZ3LbcMmBLzxlSQMMccRmi05yDQ5YDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "@textlint/linter-formatter": "^15.2.0", + "@textlint/module-interop": "^15.2.0", + "@textlint/types": "^15.2.0", + "chalk": "^5.4.1", + "debug": "^4.4.1", + "pluralize": "^8.0.0", + "strip-ansi": "^7.1.0", + "table": "^6.9.0", + "terminal-link": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@secretlint/node": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/node/-/node-10.2.2.tgz", + "integrity": "sha512-eZGJQgcg/3WRBwX1bRnss7RmHHK/YlP/l7zOQsrjexYt6l+JJa5YhUmHbuGXS94yW0++3YkEJp0kQGYhiw1DMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-loader": "^10.2.2", + "@secretlint/core": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "@secretlint/source-creator": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "p-map": "^7.0.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/profiler": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/profiler/-/profiler-10.2.2.tgz", + "integrity": "sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/resolver": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/resolver/-/resolver-10.2.2.tgz", + "integrity": "sha512-3md0cp12e+Ae5V+crPQYGd6aaO7ahw95s28OlULGyclyyUtf861UoRGS2prnUrKh7MZb23kdDOyGCYb9br5e4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/secretlint-formatter-sarif": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-formatter-sarif/-/secretlint-formatter-sarif-10.2.2.tgz", + "integrity": "sha512-ojiF9TGRKJJw308DnYBucHxkpNovDNu1XvPh7IfUp0A12gzTtxuWDqdpuVezL7/IP8Ua7mp5/VkDMN9OLp1doQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-sarif-builder": "^3.2.0" + } + }, + "node_modules/@secretlint/secretlint-rule-no-dotenv": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.2.2.tgz", + "integrity": "sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/secretlint-rule-preset-recommend": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.2.2.tgz", + "integrity": "sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/source-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/source-creator/-/source-creator-10.2.2.tgz", + "integrity": "sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2", + "istextorbinary": "^9.5.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/types": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/types/-/types-10.2.2.tgz", + "integrity": "sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@sindresorhus/is": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz", + "integrity": "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" }, "funding": { "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@textlint/ast-node-types": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.5.0.tgz", + "integrity": "sha512-K0LEuuTo4rza8yDrlYkRdXLao8Iz/QBMsQdIxRrOOrLYb4HAtZaypZ78c+J6rDA1UlGxadZVLmkkiv4KV5fMKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-15.5.0.tgz", + "integrity": "sha512-DPTm2+VXKID41qKQWagg/4JynM6hEEpvbq0PlGsEoC4Xm7IqXIxFym3mSf5+ued0cuiIV1hR9kgXjqGdP035tw==", "dev": true, + "license": "MIT", "dependencies": { - "defer-to-connect": "^2.0.0" + "@azu/format-text": "^1.0.2", + "@azu/style-format": "^1.0.1", + "@textlint/module-interop": "15.5.0", + "@textlint/resolver": "15.5.0", + "@textlint/types": "15.5.0", + "chalk": "^4.1.2", + "debug": "^4.4.3", + "js-yaml": "^4.1.1", + "lodash": "^4.17.21", + "pluralize": "^2.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "table": "^6.9.0", + "text-table": "^0.2.0" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "node_modules/@textlint/linter-formatter/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": ">= 6" + "node": ">=7.0.0" } }, - "node_modules/@types/ansi-colors": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@types/ansi-colors/-/ansi-colors-3.2.0.tgz", - "integrity": "sha512-0caWAhXht9N2lOdMzJLXybsSkYCx1QOdxx6pae48tswI9QV3DFX26AoOpy0JxwhCb+zISTqmd6H8t9Zby9BoZg==", - "dev": true + "node_modules/@textlint/linter-formatter/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" }, - "node_modules/@types/byline": { - "version": "4.2.32", - "resolved": "https://registry.npmjs.org/@types/byline/-/byline-4.2.32.tgz", - "integrity": "sha512-qtlm/J6XOO9p+Ep/ZB5+mCFEDhzWDDHWU4a1eReN7lkPZXW9rkloq2jcAhvKKmlO5tL2GSvKROb+PTsNVhBiyQ==", + "node_modules/@textlint/linter-formatter/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/pluralize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-2.0.0.tgz", + "integrity": "sha512-TqNZzQCD4S42De9IfnnBvILN7HAW7riLqsCyp8lgjXeysyPlX5HhqKAcJHHHb9XskE4/a+7VGC9zzx8Ls0jOAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { - "@types/node": "*" + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "node_modules/@textlint/linter-formatter/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/module-interop": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-15.5.0.tgz", + "integrity": "sha512-rqfouEhBEgZlR9umswWXXRBcmmSM28Trpr9b0duzgehKYVc7wSQCuQMagr6YBJa2NRMfRNinupusbJXMg0ij2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/resolver": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.5.0.tgz", + "integrity": "sha512-kK5nFbg5N3kVoZExQI/dnYjCInmTltvXDnuCRrBxHI01i6kO/o8R7Lc2aFkAZ6/NUZuRPalkyDdwZJke4/R2wg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/types": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.5.0.tgz", + "integrity": "sha512-EjAPbuA+3NyQ9WyFP7iUlddi35F3mGrf4tb4cZM0nWywbtEJ3+XAYqL+5RsF0qFeSguxGir09NdZOWrG9wVOUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@textlint/ast-node-types": "15.5.0" + } + }, + "node_modules/@types/ansi-colors": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@types/ansi-colors/-/ansi-colors-3.2.6.tgz", + "integrity": "sha512-IAfWMGE7pYvqt22qPZ5Amtd16XtAHipTFlJ1S+ztpUu3WudDbqGna40ktsOQcTrLOO+DODGRCJFFrqfV+0fNQA==", + "deprecated": "This is a stub types definition. ansi-colors provides its own type definitions, so you do not need this installed.", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "*" + } + }, + "node_modules/@types/byline": { + "version": "4.2.36", + "resolved": "https://registry.npmjs.org/@types/byline/-/byline-4.2.36.tgz", + "integrity": "sha512-dO55KDSaOSE+3T8TwP66mzn0u/PM/aSedVMr1tby7WBNjfLIuS6IbYXi1mlau49sVSVB+gXKJscWE0JO3tlXDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, "node_modules/@types/debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/debounce/-/debounce-1.0.0.tgz", - "integrity": "sha1-QXVgIAMx4buE1y2oU5EQLC/NYbc= sha512-B7FcD9ry40L831t7iuHQyDfYi+qVEV75qkEI2ROOyfjb2PfkAspL+NK6B2A0BceMuNhiYRmtKTNnNP7Ul4N2Pg==", - "dev": true + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/debounce/-/debounce-1.2.4.tgz", + "integrity": "sha512-jBqiORIzKDOToaF63Fm//haOCHuwQuLa2202RK4MozpA6lh93eCBc+/8+wZn5OzjJt3ySdc+74SXWXB55Ewtyw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/debug": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.9.tgz", - "integrity": "sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/ms": "*" } @@ -840,29 +1544,32 @@ "dev": true }, "node_modules/@types/fancy-log": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@types/fancy-log/-/fancy-log-1.3.0.tgz", - "integrity": "sha512-mQjDxyOM1Cpocd+vm1kZBP7smwKZ4TNokFeds9LV7OZibmPJFEzY3+xZMrKfUdNT71lv8GoCPD6upKwHxubClw==", - "dev": true + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/fancy-log/-/fancy-log-2.0.2.tgz", + "integrity": "sha512-SXVJvqWjsl90VwBfp7w4iQ0iO+vxAjQImglcpwbV9GkqNoUD5/p9Wsgetl40F1WL7pzWFN/eZPTF1g5FZXJsIw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/fs-extra": { - "version": "9.0.12", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.12.tgz", - "integrity": "sha512-I+bsBr67CurCGnSenZZ7v94gd3tc3+Aj2taxMT4yu4ABLuOgOjeFxX3dokG24ztSRg5tnT00sL8BszO7gSMoIw==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", "dev": true, + "license": "MIT", "dependencies": { + "@types/jsonfile": "*", "@types/node": "*" } }, "node_modules/@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-9.0.0.tgz", + "integrity": "sha512-00UxlRaIUvYm4R4W9WYkN8/J+kV8fmOQ7okeH6YFtGWFMt3odD45tpG5yA5wnL7HE6lLgjaTW5n14ju2hl2NNA==", + "deprecated": "This is a stub types definition. glob provides its own type definitions, so you do not need this installed.", "dev": true, + "license": "MIT", "dependencies": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" + "glob": "*" } }, "node_modules/@types/glob-stream": { @@ -876,10 +1583,11 @@ } }, "node_modules/@types/gulp": { - "version": "4.0.17", - "resolved": "https://registry.npmjs.org/@types/gulp/-/gulp-4.0.17.tgz", - "integrity": "sha512-+pKQynu2C/HS16kgmDlAicjtFYP8kaa86eE9P0Ae7GB5W29we/E2TIdbOWtEZD5XkpY+jr8fyqfwO6SWZecLpQ==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@types/gulp/-/gulp-4.0.18.tgz", + "integrity": "sha512-IqkYa4sXkwH2uwqO2aXYOoAisJpLX13BPaS6lmEAoG4BbgOay3qqGQFsT9LMSSQVMQlEKU7wTUW0sPV46V0olw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "@types/undertaker": ">=1.2.6", @@ -888,42 +1596,54 @@ } }, "node_modules/@types/gulp-filter": { - "version": "3.0.32", - "resolved": "https://registry.npmjs.org/@types/gulp-filter/-/gulp-filter-3.0.32.tgz", - "integrity": "sha512-JvY4qTxXehoK2yCUxYVxTMvckVbDM5TWHWeUoYJyX31gwFqw4YDD6JNzhuTxI3yHPUTY4BBRTqgm6puQEZVCNg==", + "version": "3.0.41", + "resolved": "https://registry.npmjs.org/@types/gulp-filter/-/gulp-filter-3.0.41.tgz", + "integrity": "sha512-v+7p521daAT0lXbMqciybyAktHF8plxW5+OHez7cM2JZitJN7DPw0QaU6FQkeLjBTZHEhQS/1HeQOwMiBlRFEg==", "dev": true, + "license": "MIT", "dependencies": { - "@types/minimatch": "*", + "@types/minimatch": "<=5", "@types/node": "*", "@types/vinyl": "*" } }, + "node_modules/@types/gulp-filter/node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/gulp-gzip": { - "version": "0.0.31", - "resolved": "https://registry.npmjs.org/@types/gulp-gzip/-/gulp-gzip-0.0.31.tgz", - "integrity": "sha512-KQjHz1FTqLse8/EiktfhN/vo33vamX4gVAQhMTp55STDBA7UToW5CJqYyP7iRorkHK9/aJ2nL2hLkNZkY4M8+w==", + "version": "0.0.36", + "resolved": "https://registry.npmjs.org/@types/gulp-gzip/-/gulp-gzip-0.0.36.tgz", + "integrity": "sha512-zGdc+LGAvcqVXu9ydx0OJLL39C23QYmlptqkVEnWz+03YfPMWBBjMGE0VOvr6AApm08r1G4r5QItRJaGjJQWuA==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/gulp-json-editor": { - "version": "2.2.31", - "resolved": "https://registry.npmjs.org/@types/gulp-json-editor/-/gulp-json-editor-2.2.31.tgz", - "integrity": "sha512-piis0ImYAy0dt18R4EtTbAY+RV8jwTq5VisnUV6OfP8kD4743aHGkAdAd08No4NY3rFa5mD6ytIu8L0YU7nL9w==", + "version": "2.2.36", + "resolved": "https://registry.npmjs.org/@types/gulp-json-editor/-/gulp-json-editor-2.2.36.tgz", + "integrity": "sha512-BX6KjseCoZ09thBmF1QPZnDGtsLndNuhAziUbKqX8x+UDvlFjv2JLX5IeNOqMU2Sc5RkgaClyAU6JA0r19FEKw==", "dev": true, + "license": "MIT", "dependencies": { "@types/js-beautify": "*", "@types/node": "*" } }, "node_modules/@types/gulp-rename": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/gulp-rename/-/gulp-rename-0.0.33.tgz", - "integrity": "sha512-FIZQvbZJj6V1gHPTzO+g/BCWpDur7fJrroae4gwV3LaoHBQ+MrR9sB+2HssK8fHv4WdY6hVNxkcft9bYatuPIA==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/gulp-rename/-/gulp-rename-2.0.7.tgz", + "integrity": "sha512-W1xIGdui1w4AB1ylt/b8jrpV1MTNfeKmMFfJXsN/NOtSHjRg2w4Wp9H96J+Ld9tZenTtU0u5LatpGFJvOBN5EA==", "dev": true, + "license": "MIT", "dependencies": { - "@types/node": "*" + "@types/node": "*", + "@types/vinyl": "*" } }, "node_modules/@types/gulp-sort": { @@ -937,12 +1657,14 @@ } }, "node_modules/@types/gulp-sourcemaps": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@types/gulp-sourcemaps/-/gulp-sourcemaps-0.0.32.tgz", - "integrity": "sha512-+7BAmptW2bxyJnJcCEuie7vLoop3FwWgCdBMzyv7MYXED/HeNMeQuX7uPCkp4vfU1TTu4CYFH0IckNPvo0VePA==", + "version": "0.0.38", + "resolved": "https://registry.npmjs.org/@types/gulp-sourcemaps/-/gulp-sourcemaps-0.0.38.tgz", + "integrity": "sha512-9wm+P44eM/VKZSYwGDwalOH6rnvNckwfPgz0gEUlTzjKH1OsSbexdvE8aKBt+8syEkq827Fu+4gV17/EyUtH9w==", "dev": true, + "license": "MIT", "dependencies": { - "@types/node": "*" + "@types/node": "*", + "@types/vinyl": "*" } }, "node_modules/@types/gulp-util": { @@ -961,7 +1683,8 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/js-beautify": { "version": "1.8.0", @@ -969,38 +1692,61 @@ "integrity": "sha512-/siF86XrwDKLuHe8l7h6NhrAWgLdgqbxmjZv9NvGWmgYRZoTipkjKiWb0SQHy/jcR+ee0GvbG6uGd+LEBMGNvA==", "dev": true }, - "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "node_modules/@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/jws": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/@types/jws/-/jws-3.2.11.tgz", + "integrity": "sha512-OOaTrLV6XdF1XvBgMeH1MjNuOaGCrRZWNSIds1AQaRgLdOWlAk2yMsfrJn+ekLgUow3xksWIM231lyFab7mHHw==", + "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/mime": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-0.0.29.tgz", - "integrity": "sha1-+8/TMFc7kS71nu7hRgK/rOYwdUs= sha512-EqWQSlonwbNgLMq2dMkokuzv/pwevb4q0JrPjfc7zzieG/cpqt+HsCE9dYoQd1snp2zlksl6k3rQ4LLfyQbQdA==", - "dev": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-4.0.0.tgz", + "integrity": "sha512-5eEkJZ/BLvTE3vXGKkWlyTSUVZuzj23Wj8PoyOq2lt5I3CYbiLBOPb3XmCW6QcuOibIUE6emHXHt9E/F/rCa6w==", + "deprecated": "This is a stub types definition. mime provides its own type definitions, so you do not need this installed.", + "dev": true, + "license": "MIT", + "dependencies": { + "mime": "*" + } }, "node_modules/@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-6.0.0.tgz", + "integrity": "sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==", + "deprecated": "This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed.", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "*" + } }, "node_modules/@types/minimist": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", - "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", - "dev": true + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true, + "license": "MIT" }, "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/ms": { "version": "0.7.32", @@ -1009,56 +1755,66 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, - "node_modules/@types/pump": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/pump/-/pump-1.0.1.tgz", - "integrity": "sha1-roFXzv7wTRpNJMHMkdQDwvXaXNA= sha512-WGcg4jdczx60mEh0pWLUhw/2215BFYfSob5fHp/fJFZ+UFOiOxIqCnqRGSLWRsJ6Hvh4u2SBokxHDQ0wF9ujqQ==", + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", "dev": true, - "dependencies": { - "@types/node": "*" - } + "license": "MIT" }, - "node_modules/@types/responselike": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "node_modules/@types/pump": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@types/pump/-/pump-1.1.3.tgz", + "integrity": "sha512-ZyooTTivmOwPfOwLVaszkF8Zq6mvavgjuHYitZhrIjfQAJDH+kIP3N+MzpG1zDAslsHvVz6Q8ECfivix3qLJaQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/rimraf": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-2.0.4.tgz", - "integrity": "sha512-8gBudvllD2A/c0CcEX/BivIDorHFt5UI5m46TsNj8DjWCCTTZT74kEe4g+QsY7P/B9WdO98d82zZgXO/RQzu2Q==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-4.0.5.tgz", + "integrity": "sha512-DTCZoIQotB2SUJnYgrEx43cQIUYOlNZz0AZPbKU4PSLYTUdML5Gox0++z4F9kQocxStrCmRNhi4x5x/UlwtKUA==", + "deprecated": "This is a stub types definition. rimraf provides its own type definitions, so you do not need this installed.", "dev": true, + "license": "MIT", "dependencies": { - "@types/glob": "*", - "@types/node": "*" + "rimraf": "*" } }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/through": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.29.tgz", - "integrity": "sha512-9a7C5VHh+1BKblaYiq+7Tfc+EOmjMdZaD1MYtkQjSoxgB69tBjW98ry6SKsi4zEIWztLOMRuL87A3bdT/Fc/4w==", + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz", + "integrity": "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/through2": { - "version": "2.0.36", - "resolved": "https://registry.npmjs.org/@types/through2/-/through2-2.0.36.tgz", - "integrity": "sha512-vuifQksQHJXhV9McpVsXKuhnf3lsoX70PnhcqIAbs9dqLH2NgrGz0DzZPDY3+Yh6eaRqcE1gnCQ6QhBn1/PT5A==", + "version": "2.0.41", + "resolved": "https://registry.npmjs.org/@types/through2/-/through2-2.0.41.tgz", + "integrity": "sha512-ryQ0tidWkb1O1JuYvWKyMLYEtOWDqF5mHerJzKz/gQpoAaJq2l/dsMPBF0B5BNVT34rbARYJ5/tsZwLfUi2kwQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } @@ -1103,19 +1859,25 @@ } }, "node_modules/@types/workerpool": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@types/workerpool/-/workerpool-6.4.0.tgz", - "integrity": "sha512-SIF2/169pDsLKeM8GQGHkOFifGalDbZgiBSaLUnnlVSRsAOenkAvQ6h4uhV2W+PZZczS+8LQxACwNkSykdT91A==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/workerpool/-/workerpool-9.0.0.tgz", + "integrity": "sha512-gEiZU7P4NzkhqcSwHlSx1CddtzzEjFD/uWegp2tV5DXQABOE4qlH4H8ptCm/S5DDvCViqFj7yMrN3yMBh1pkQg==", + "deprecated": "This is a stub types definition. workerpool provides its own type definitions, so you do not need this installed.", "dev": true, + "license": "MIT", "dependencies": { - "@types/node": "*" + "workerpool": "*" } }, "node_modules/@types/xml2js": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.0.33.tgz", - "integrity": "sha1-IMXdZGAkUoTWSlVpABW5XkCft94= sha512-6dx6V6EdddqLjhxGdQrNdSu+O+3F7tOlyj660SpkO4/5uDSZM+LXcGQKAFnIJbvTzkQ6d2g3rWxyEXVwYAUoJg==", - "dev": true + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz", + "integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } }, "node_modules/@types/yauzl": { "version": "2.9.2", @@ -1127,33 +1889,83 @@ "@types/node": "*" } }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.2.tgz", + "integrity": "sha512-IlqQ/Gv22xUC1r/WQm4StLkYQmaaTsXAhUVsNE0+xiyf0yRFiH5++q78U3bw6bLKDCTmh0uqKB9eG9+Bt75Dkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@vscode/iconv-lite-umd": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz", - "integrity": "sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg==", - "dev": true + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.1.tgz", + "integrity": "sha512-tK6k0DXFHW7q5+GGuGZO+phpAqpxO4WXl+BLc/8/uOk3RsM2ssAL3CQUQDb1TGfwltjsauhN6S4ghYZzs4sPFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vscode/ripgrep": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@vscode/ripgrep/-/ripgrep-1.17.0.tgz", + "integrity": "sha512-mBRKm+ASPkUcw4o9aAgfbusIu6H4Sdhw09bjeP1YOBFTJEZAnrnk6WZwzv8NEjgC82f7ILvhmb1WIElSugea6g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "https-proxy-agent": "^7.0.2", + "proxy-from-env": "^1.1.0", + "yauzl": "^2.9.2" + } + }, + "node_modules/@vscode/ripgrep/node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } }, "node_modules/@vscode/vsce": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-2.20.1.tgz", - "integrity": "sha512-ilbvoqvR/1/zseRPBAzYR6aKqSJ+jvda4/BqIwOqTxajpvLtEpK3kMLs77+dJdrlygS+VrP7Yhad8j0ukyD96g==", + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.7.1.tgz", + "integrity": "sha512-OTm2XdMt2YkpSn2Nx7z2EJtSuhRHsTPYsSK59hr3v8jRArK+2UEoju4Jumn1CmpgoBLGI6ReHLJ/czYltNUW3g==", "dev": true, + "license": "MIT", "dependencies": { - "azure-devops-node-api": "^11.0.1", - "chalk": "^2.4.2", + "@azure/identity": "^4.1.0", + "@secretlint/node": "^10.1.2", + "@secretlint/secretlint-formatter-sarif": "^10.1.2", + "@secretlint/secretlint-rule-no-dotenv": "^10.1.2", + "@secretlint/secretlint-rule-preset-recommend": "^10.1.2", + "@vscode/vsce-sign": "^2.0.0", + "azure-devops-node-api": "^12.5.0", + "chalk": "^4.1.2", "cheerio": "^1.0.0-rc.9", - "commander": "^6.1.0", - "glob": "^7.0.6", + "cockatiel": "^3.1.2", + "commander": "^12.1.0", + "form-data": "^4.0.0", + "glob": "^11.0.0", "hosted-git-info": "^4.0.2", "jsonc-parser": "^3.2.0", "leven": "^3.1.0", - "markdown-it": "^12.3.2", + "markdown-it": "^14.1.0", "mime": "^1.3.4", "minimatch": "^3.0.3", "parse-semver": "^1.1.1", "read": "^1.0.7", + "secretlint": "^10.1.2", "semver": "^7.5.2", - "tmp": "^0.2.1", + "tmp": "^0.2.3", "typed-rest-client": "^1.8.4", "url-join": "^4.0.1", "xml2js": "^0.5.0", @@ -1161,40 +1973,308 @@ "yazl": "^2.2.2" }, "bin": { - "vsce": "vsce" + "vsce": "vsce" + }, + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "keytar": "^7.7.0" + } + }, + "node_modules/@vscode/vsce-sign": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign/-/vsce-sign-2.0.9.tgz", + "integrity": "sha512-8IvaRvtFyzUnGGl3f5+1Cnor3LqaUWvhaUjAYO8Y39OUYlOf3cRd+dowuQYLpZcP3uwSG+mURwjEBOSq4SOJ0g==", + "dev": true, + "hasInstallScript": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optionalDependencies": { + "@vscode/vsce-sign-alpine-arm64": "2.0.6", + "@vscode/vsce-sign-alpine-x64": "2.0.6", + "@vscode/vsce-sign-darwin-arm64": "2.0.6", + "@vscode/vsce-sign-darwin-x64": "2.0.6", + "@vscode/vsce-sign-linux-arm": "2.0.6", + "@vscode/vsce-sign-linux-arm64": "2.0.6", + "@vscode/vsce-sign-linux-x64": "2.0.6", + "@vscode/vsce-sign-win32-arm64": "2.0.6", + "@vscode/vsce-sign-win32-x64": "2.0.6" + } + }, + "node_modules/@vscode/vsce-sign-alpine-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-arm64/-/vsce-sign-alpine-arm64-2.0.6.tgz", + "integrity": "sha512-wKkJBsvKF+f0GfsUuGT0tSW0kZL87QggEiqNqK6/8hvqsXvpx8OsTEc3mnE1kejkh5r+qUyQ7PtF8jZYN0mo8Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "alpine" + ] + }, + "node_modules/@vscode/vsce-sign-alpine-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-x64/-/vsce-sign-alpine-x64-2.0.6.tgz", + "integrity": "sha512-YoAGlmdK39vKi9jA18i4ufBbd95OqGJxRvF3n6ZbCyziwy3O+JgOpIUPxv5tjeO6gQfx29qBivQ8ZZTUF2Ba0w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "alpine" + ] + }, + "node_modules/@vscode/vsce-sign-darwin-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-arm64/-/vsce-sign-darwin-arm64-2.0.6.tgz", + "integrity": "sha512-5HMHaJRIQuozm/XQIiJiA0W9uhdblwwl2ZNDSSAeXGO9YhB9MH5C4KIHOmvyjUnKy4UCuiP43VKpIxW1VWP4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-darwin-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-x64/-/vsce-sign-darwin-x64-2.0.6.tgz", + "integrity": "sha512-25GsUbTAiNfHSuRItoQafXOIpxlYj+IXb4/qarrXu7kmbH94jlm5sdWSCKrrREs8+GsXF1b+l3OB7VJy5jsykw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-linux-arm": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm/-/vsce-sign-linux-arm-2.0.6.tgz", + "integrity": "sha512-UndEc2Xlq4HsuMPnwu7420uqceXjs4yb5W8E2/UkaHBB9OWCwMd3/bRe/1eLe3D8kPpxzcaeTyXiK3RdzS/1CA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-linux-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm64/-/vsce-sign-linux-arm64-2.0.6.tgz", + "integrity": "sha512-cfb1qK7lygtMa4NUl2582nP7aliLYuDEVpAbXJMkDq1qE+olIw/es+C8j1LJwvcRq1I2yWGtSn3EkDp9Dq5FdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-linux-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-x64/-/vsce-sign-linux-x64-2.0.6.tgz", + "integrity": "sha512-/olerl1A4sOqdP+hjvJ1sbQjKN07Y3DVnxO4gnbn/ahtQvFrdhUi0G1VsZXDNjfqmXw57DmPi5ASnj/8PGZhAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-win32-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.6.tgz", + "integrity": "sha512-ivM/MiGIY0PJNZBoGtlRBM/xDpwbdlCWomUWuLmIxbi1Cxe/1nooYrEQoaHD8ojVRgzdQEUzMsRbyF5cJJgYOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vscode/vsce-sign-win32-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.6.tgz", + "integrity": "sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vscode/vsce/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@vscode/vsce/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@vscode/vsce/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@vscode/vsce/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vscode/vsce/node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/vsce/node_modules/glob/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/vsce/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@vscode/vsce/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" }, "engines": { - "node": ">= 14" - }, - "optionalDependencies": { - "keytar": "^7.7.0" + "node": ">=4" } }, - "node_modules/@vscode/vsce/node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "node_modules/@vscode/vsce/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": ">= 6" + "node": ">=8" } }, - "node_modules/@vscode/vsce/node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "dev": true + "node_modules/@vscode/vsce/node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } }, - "node_modules/@vscode/vsce/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "node_modules/@vscode/vsce/node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3" } }, "node_modules/@xmldom/xmldom": { @@ -1207,15 +2287,33 @@ } }, "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "4" + "debug": "^4.3.4" }, "engines": { - "node": ">= 6.0.0" + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/ansi-colors": { @@ -1230,6 +2328,22 @@ "node": ">=0.10.0" } }, + "node_modules/ansi-escapes": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", + "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-gray": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", @@ -1242,6 +2356,19 @@ "node": ">=0.10.0" } }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -1280,13 +2407,14 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", - "devOptional": true, + "optional": true, "engines": { "node": ">=0.10.0" } @@ -1295,7 +2423,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", - "devOptional": true, + "optional": true, "engines": { "node": ">=0.10.0" } @@ -1304,11 +2432,21 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", - "devOptional": true, + "optional": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/async-done": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", @@ -1327,30 +2465,62 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k= sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" }, "node_modules/azure-devops-node-api": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.2.0.tgz", - "integrity": "sha512-XdiGPhrpaT5J8wdERRKs5g8E0Zy1pvOYTli7z9E8nmOn3YGp4FhtjhrOyFmX/8veWCwdI69mCHKJw6l+4J/bHA==", + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz", + "integrity": "sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==", "dev": true, + "license": "MIT", "dependencies": { "tunnel": "0.0.6", "typed-rest-client": "^1.8.4" } }, + "node_modules/b4a": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", + "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -1375,10 +2545,27 @@ "node": ">=8" } }, + "node_modules/binaryextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz", + "integrity": "sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, "optional": true, "dependencies": { "buffer": "^5.5.0", @@ -1405,11 +2592,19 @@ "dev": true, "optional": true }, + "node_modules/boundary": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz", + "integrity": "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1431,6 +2626,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, "funding": [ { "type": "github", @@ -1488,6 +2684,22 @@ "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw= sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", "dev": true }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/byline": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", @@ -1497,44 +2709,88 @@ "node": ">=0.10.0" } }, + "node_modules/byte-counter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/byte-counter/-/byte-counter-0.1.0.tgz", + "integrity": "sha512-jheRLVMeUKrDBjVw2O5+k4EvR4t9wtxHL+bo/LxfkxsVeuGMy3a5SEGgXdAFA4FSzTrU8rQXQIrsZ3oBq5a0pQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10.6.0" + "node": ">=14.16" } }, "node_modules/cacheable-request": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "version": "13.0.18", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-13.0.18.tgz", + "integrity": "sha512-rFWadDRKJs3s2eYdXlGggnBZKG7MTblkFBB0YllFds+UYnfogDp2wcR6JN97FhRkHTvq59n2vhNoHNZn29dh/Q==", "dev": true, + "license": "MIT", "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" + "@types/http-cache-semantics": "^4.0.4", + "get-stream": "^9.0.1", + "http-cache-semantics": "^4.2.0", + "keyv": "^5.5.5", + "mimic-response": "^4.0.0", + "normalize-url": "^8.1.1", + "responselike": "^4.0.2" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, + "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -1620,6 +2876,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, "optional": true }, "node_modules/clone": { @@ -1635,31 +2892,22 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg= sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", - "devOptional": true, + "optional": true, "engines": { "node": ">= 0.10" } }, - "node_modules/clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==", - "dev": true, - "dependencies": { - "mimic-response": "^1.0.0" - } - }, "node_modules/clone-stats": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", - "devOptional": true + "optional": true }, "node_modules/cloneable-readable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", - "devOptional": true, + "optional": true, "dependencies": { "inherits": "^2.0.1", "process-nextick-args": "^2.0.0", @@ -1670,7 +2918,7 @@ "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "devOptional": true, + "optional": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -1681,6 +2929,16 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/cockatiel": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz", + "integrity": "sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1710,6 +2968,7 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -1717,6 +2976,16 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/compare-version": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", @@ -1739,10 +3008,11 @@ "devOptional": true }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -1781,12 +3051,13 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1801,7 +3072,8 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "devOptional": true, + "dev": true, + "optional": true, "dependencies": { "mimic-response": "^3.1.0" }, @@ -1816,7 +3088,8 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "devOptional": true, + "dev": true, + "optional": true, "engines": { "node": ">=10" }, @@ -1828,44 +3101,53 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, "optional": true, "engines": { "node": ">=4.0.0" } }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "node_modules/default-browser": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.4.0.tgz", + "integrity": "sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==", "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, "engines": { - "node": ">=10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", "dev": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/define-properties": { @@ -1884,8 +3166,9 @@ "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk= sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -1894,6 +3177,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "dev": true, "optional": true, "engines": { "node": ">=8" @@ -1971,6 +3255,21 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/duplexify": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", @@ -1983,6 +3282,13 @@ "stream-shift": "^1.0.0" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -1992,12 +3298,30 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/editions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/editions/-/editions-6.22.0.tgz", + "integrity": "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "version-range": "^4.15.0" + }, + "engines": { + "ecmascript": ">= es5", + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/electron-osx-sign": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.16.tgz", - "integrity": "sha512-ziMWfc3NmQlwnWLW6EaZq8nH2BWVng/atX5GWsGwhexJYpdW6hsg//MkAfRTRx1kR3Veiqkeiog1ibkbA4x0rg==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.6.0.tgz", + "integrity": "sha512-+hiIEb2Xxk6eDKJ2FFlpofCnemCbjbT5jz+BKGpVBrRNT3kWTGs4DfNX6IzGwgi33hUcXF+kFs9JW+r6Wc1LRg==", "deprecated": "Please use @electron/osx-sign moving forward. Be aware the API is slightly different", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "bluebird": "^3.5.0", "compare-version": "^0.1.2", @@ -2029,11 +3353,18 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "devOptional": true, + "dev": true, "dependencies": { "once": "^1.4.0" } @@ -2051,22 +3382,37 @@ } }, "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" + "license": "MIT", + "engines": { + "node": ">=18" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -2076,6 +3422,36 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, "engines": { "node": ">= 0.4" } @@ -2088,11 +3464,12 @@ "optional": true }, "node_modules/esbuild": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", - "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -2100,30 +3477,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.0", - "@esbuild/android-arm": "0.23.0", - "@esbuild/android-arm64": "0.23.0", - "@esbuild/android-x64": "0.23.0", - "@esbuild/darwin-arm64": "0.23.0", - "@esbuild/darwin-x64": "0.23.0", - "@esbuild/freebsd-arm64": "0.23.0", - "@esbuild/freebsd-x64": "0.23.0", - "@esbuild/linux-arm": "0.23.0", - "@esbuild/linux-arm64": "0.23.0", - "@esbuild/linux-ia32": "0.23.0", - "@esbuild/linux-loong64": "0.23.0", - "@esbuild/linux-mips64el": "0.23.0", - "@esbuild/linux-ppc64": "0.23.0", - "@esbuild/linux-riscv64": "0.23.0", - "@esbuild/linux-s390x": "0.23.0", - "@esbuild/linux-x64": "0.23.0", - "@esbuild/netbsd-x64": "0.23.0", - "@esbuild/openbsd-arm64": "0.23.0", - "@esbuild/openbsd-x64": "0.23.0", - "@esbuild/sunos-x64": "0.23.0", - "@esbuild/win32-arm64": "0.23.0", - "@esbuild/win32-ia32": "0.23.0", - "@esbuild/win32-x64": "0.23.0" + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" } }, "node_modules/escape-string-regexp": { @@ -2136,18 +3515,30 @@ } }, "node_modules/events": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", - "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.x" } }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, "optional": true, "engines": { "node": ">=6" @@ -2157,7 +3548,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "devOptional": true, + "optional": true, "dependencies": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" @@ -2186,6 +3577,17 @@ "@types/yauzl": "^2.9.1" } }, + "node_modules/extract-zip/node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "node_modules/fancy-log": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", @@ -2201,17 +3603,95 @@ "node": ">= 0.10" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-parser": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.3.tgz", + "integrity": "sha512-2O3dkPAAC6JavuMm8+4+pgTk+5hoAs+CjZ+sWcQLkX9+/tHRuTkQh/Oaifr8qDmZ8iEHb771Ea6G8CdwkrgvYA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^2.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, + "license": "MIT", "dependencies": { "pend": "~1.2.0" } @@ -2255,6 +3735,23 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fork-stream": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz", @@ -2262,37 +3759,52 @@ "dev": true }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { "node": ">= 6" } }, + "node_modules/form-data-encoder": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.1.0.tgz", + "integrity": "sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, "optional": true }, "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz", + "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=14.14" } }, "node_modules/fs.realpath": { @@ -2319,21 +3831,28 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -2342,6 +3861,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -2361,6 +3894,7 @@ "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true, "optional": true }, "node_modules/glob": { @@ -2414,22 +3948,6 @@ "node": ">=10.0" } }, - "node_modules/global-agent/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "optional": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/globalthis": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", @@ -2446,60 +3964,155 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", "dev": true, + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.1.3" + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "version": "14.6.6", + "resolved": "https://registry.npmjs.org/got/-/got-14.6.6.tgz", + "integrity": "sha512-QLV1qeYSo5l13mQzWgP/y0LbMr5Plr5fJilgAIwgnwseproEbtNym8xpLsDzeZ6MWXgNE6kdWGBjdh3zT/Qerg==", "dev": true, + "license": "MIT", "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" + "@sindresorhus/is": "^7.0.1", + "byte-counter": "^0.1.0", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^13.0.12", + "decompress-response": "^10.0.0", + "form-data-encoder": "^4.0.2", + "http2-wrapper": "^2.2.1", + "keyv": "^5.5.3", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^4.0.1", + "responselike": "^4.0.2", + "type-fest": "^4.26.1" }, "engines": { - "node": ">=10.19.0" + "node": ">=20" }, "funding": { "url": "https://github.com/sindresorhus/got?sponsor=1" } }, + "node_modules/got/node_modules/decompress-response": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-10.0.0.tgz", + "integrity": "sha512-oj7KWToJuuxlPr7VV0vabvxEIiqNMo+q0NueIiL3XhtwC6FVOX7Hr1c0C4eD0bmf7Zr+S/dSf2xvkH3Ad6sU3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^4.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/got/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "devOptional": true + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "devOptional": true, + "license": "ISC" }, "node_modules/gulp-merge-json": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/gulp-merge-json/-/gulp-merge-json-2.1.1.tgz", - "integrity": "sha512-VhvAlcf+dcCb94j/2yDPWxJ3X7x4P/Xwcrv1dhjYuRgvADwFJmaQwl4zbuq+GDZvzMacbVncWtEdsETpUSkhYw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/gulp-merge-json/-/gulp-merge-json-2.2.1.tgz", + "integrity": "sha512-w1+ka0xPu/VSEAVxvYlwEASpyZuHGVpBBtYakd4xpw+dF5QiwNh81qkNGnhGvtUINh94IInleQNs0rzy8KdyRg==", "dev": true, + "license": "MIT", "dependencies": { - "json5": "^2.1.3", + "json5": "^2.2.3", + "lodash.clonedeep": "^4.5.0", "lodash.mergewith": "^4.6.1", - "plugin-error": "^1.0.1", + "plugin-error": "^2.0.1", "through": "^2.3.8", - "vinyl": "^2.1.0" + "vinyl": "^3.0.0" + } + }, + "node_modules/gulp-merge-json/node_modules/plugin-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-2.0.1.tgz", + "integrity": "sha512-zMakqvIDyY40xHOvzXka0kUvf40nYIuwRE8dWhti2WtjQZ31xAgBZBhxsK7vK3QbRXS1Xms/LO7B5cuAsfB2Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^1.0.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gulp-merge-json/node_modules/replace-ext": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", + "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/gulp-merge-json/node_modules/vinyl": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", + "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^2.1.2", + "remove-trailing-separator": "^1.1.0", + "replace-ext": "^2.0.0", + "teex": "^1.0.1" + }, + "engines": { + "node": ">=10.13.0" } }, "node_modules/gulp-sort": { @@ -2545,23 +4158,12 @@ "node": ">=4" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -2569,11 +4171,15 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, "engines": { "node": ">= 0.4" }, @@ -2586,6 +4192,7 @@ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -2625,55 +4232,59 @@ } }, "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, + "license": "MIT", "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", "dev": true, + "license": "MIT", "dependencies": { "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" + "resolve-alpn": "^1.2.0" }, "engines": { "node": ">=10.19.0" } }, "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", "dev": true, + "license": "MIT", "dependencies": { - "agent-base": "6", + "agent-base": "^7.0.2", "debug": "4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, "funding": [ { "type": "github", @@ -2690,6 +4301,29 @@ ], "optional": true }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -2711,6 +4345,7 @@ "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, "optional": true }, "node_modules/is-binary-path": { @@ -2726,15 +4361,16 @@ } }, "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", "dev": true, + "license": "MIT", "bin": { "is-docker": "cli.js" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2744,7 +4380,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "devOptional": true, + "optional": true, "dependencies": { "is-plain-object": "^2.0.4" }, @@ -2761,6 +4397,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2773,6 +4419,25 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -2786,7 +4451,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "devOptional": true, + "optional": true, "dependencies": { "isobject": "^3.0.1" }, @@ -2794,6 +4459,19 @@ "node": ">=0.10.0" } }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -2801,15 +4479,19 @@ "optional": true }, "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", "dev": true, + "license": "MIT", "dependencies": { - "is-docker": "^2.0.0" + "is-inside-container": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/isarray": { @@ -2840,22 +4522,71 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8= sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "devOptional": true, + "optional": true, "engines": { "node": ">=0.10.0" } }, - "node_modules/jsbi": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.1.4.tgz", - "integrity": "sha512-52QRRFSsi9impURE8ZUbzAMCLjPm4THO7H2fcuIvaaeFTbSysvkodbQQXIVsNgq/ypDbq6dJiuGKL0vZ/i9hUg==", - "dev": true + "node_modules/istextorbinary": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz", + "integrity": "sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "binaryextensions": "^6.11.0", + "editions": "^6.21.0", + "textextensions": "^6.11.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" }, "node_modules/json-stringify-safe": { "version": "5.0.1", @@ -2865,10 +4596,11 @@ "optional": true }, "node_modules/json5": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz", - "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -2877,16 +4609,21 @@ } }, "node_modules/jsonc-parser": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.0.tgz", - "integrity": "sha512-b0EBt8SWFNnixVdvoR2ZtEGa9ZqLhbJnOjezn+WP+8kspFm+PFYDN8Z4Bc7pRlDjvuVcADSUkroIuTWWn/YiIA==", - "dev": true + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" }, "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -2908,59 +4645,48 @@ } }, "node_modules/jsonwebtoken/node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", "dev": true, + "license": "MIT", "dependencies": { - "buffer-equal-constant-time": "1.0.1", + "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "node_modules/jsonwebtoken/node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", + "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", "dev": true, + "license": "MIT", "dependencies": { - "jwa": "^1.4.1", + "jwa": "^1.4.2", "safe-buffer": "^5.0.1" } }, - "node_modules/jsonwebtoken/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/jwa": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", - "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "dev": true, + "license": "MIT", "dependencies": { - "buffer-equal-constant-time": "1.0.1", + "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "node_modules/jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", "dev": true, + "license": "MIT", "dependencies": { - "jwa": "^2.0.0", + "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, @@ -2977,12 +4703,13 @@ } }, "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "version": "5.5.5", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.5.tgz", + "integrity": "sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ==", "dev": true, + "license": "MIT", "dependencies": { - "json-buffer": "3.0.1" + "@keyv/serialize": "^1.1.1" } }, "node_modules/leven": { @@ -2995,19 +4722,28 @@ } }, "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", "dev": true, + "license": "MIT", "dependencies": { - "uc.micro": "^1.0.1" + "uc.micro": "^2.0.0" } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true, + "license": "MIT" }, "node_modules/lodash.mergewith": { "version": "4.6.2", @@ -3015,20 +4751,31 @@ "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", "dev": true }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "devOptional": true, + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -3037,28 +4784,21 @@ } }, "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, "bin": { - "markdown-it": "bin/markdown-it.js" - } - }, - "node_modules/markdown-it/node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "markdown-it": "bin/markdown-it.mjs" } }, "node_modules/matcher": { @@ -3087,11 +4827,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" }, "node_modules/merge-stream": { "version": "2.0.0", @@ -3099,46 +4850,80 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz", + "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==", "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", "bin": { - "mime": "cli.js" + "mime": "bin/cli.js" }, "engines": { - "node": ">=4" + "node": ">=16" } }, "node_modules/mime-db": { - "version": "1.45.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", - "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.28", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", - "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, + "license": "MIT", "dependencies": { - "mime-db": "1.45.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" } }, "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/minimatch": { @@ -3157,19 +4942,31 @@ "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "devOptional": true + "dev": true + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, "optional": true }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" }, "node_modules/mute-stream": { "version": "0.0.8", @@ -3177,22 +4974,18 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, - "node_modules/nan": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", - "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", - "optional": true - }, "node_modules/napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true, "optional": true }, "node_modules/node-abi": { "version": "3.30.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.30.0.tgz", "integrity": "sha512-qWO5l3SCqbwQavymOmtTVuCWZE23++S+rxyoHjXqUmPyzRcaoI4lA2gO55/drddGnedAyjA7sk76SfQ5lfUMnw==", + "dev": true, "optional": true, "dependencies": { "semver": "^7.3.5" @@ -3201,33 +4994,74 @@ "node": ">=10" } }, - "node_modules/node-abi/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "optional": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "devOptional": true, + "license": "MIT", "bin": { - "semver": "bin/semver.js" + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-sarif-builder": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.4.0.tgz", + "integrity": "sha512-tGnJW6OKRii9u/b2WiUViTJS+h7Apxx17qsMUjsUeNDiMMX5ZFf8F8Fcz7PAQ6omvOxHZtvDTmOYKJQwmfpjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" }, "engines": { - "node": ">=10" + "node": ">=20" } }, - "node_modules/node-abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", - "dev": true + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, - "node_modules/node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "optional": true + "license": "ISC" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -3239,12 +5073,13 @@ } }, "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.1.tgz", + "integrity": "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3272,10 +5107,11 @@ } }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -3297,35 +5133,38 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E= sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "devOptional": true, + "dev": true, "dependencies": { "wrappy": "1" } }, "node_modules/open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", "dev": true, + "license": "MIT", "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz", + "integrity": "sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=14.16" } }, "node_modules/p-limit": { @@ -3343,6 +5182,57 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-json/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parse-node-version": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", @@ -3413,17 +5303,66 @@ "node": ">=8" } }, + "node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA= sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -3458,7 +5397,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", - "devOptional": true, + "optional": true, "dependencies": { "ansi-colors": "^1.0.1", "arr-diff": "^4.0.0", @@ -3469,10 +5408,21 @@ "node": ">= 0.10" } }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/prebuild-install": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "dev": true, "optional": true, "dependencies": { "detect-libc": "^2.0.0", @@ -3496,10 +5446,11 @@ } }, "node_modules/priorityqueuejs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/priorityqueuejs/-/priorityqueuejs-1.0.0.tgz", - "integrity": "sha1-LuTyPCVgkT4IwHzlzN1t498sWvg= sha512-lg++21mreCEOuGWTbO5DnJKAdxfjrdN0S9ysoW9SzdSJvbkWpkaDdpG/cdsPCsEnoLUwmd9m3WcZhngW7yKA2g==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/priorityqueuejs/-/priorityqueuejs-2.0.0.tgz", + "integrity": "sha512-19BMarhgpq3x4ccvVi8k2QpJZcymo/iFUcrhPd4V96kYGovOdTsWwy7fxChYi4QY+m2EnGBWSX9Buakz+tWNQQ==", + "dev": true, + "license": "MIT" }, "node_modules/process-nextick-args": { "version": "2.0.1", @@ -3516,23 +5467,41 @@ "node": ">=0.4.0" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true, + "license": "MIT" + }, "node_modules/pump": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "devOptional": true, + "dev": true, "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -3541,11 +5510,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/quick-lru": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -3557,6 +5548,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, "optional": true, "dependencies": { "deep-extend": "^0.6.0", @@ -3568,6 +5560,19 @@ "rc": "cli.js" } }, + "node_modules/rc-config-loader": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.3.tgz", + "integrity": "sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "js-yaml": "^4.1.0", + "json5": "^2.2.2", + "require-from-string": "^2.0.2" + } + }, "node_modules/read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", @@ -3580,6 +5585,52 @@ "node": ">=0.8" } }, + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -3616,29 +5667,109 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", - "devOptional": true, + "optional": true, "engines": { "node": ">= 0.10" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-alpn": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-4.0.2.tgz", + "integrity": "sha512-cGk8IbWEAnaCpdAt1BHzJ3Ahz5ewDJa0KseTsE3qIRMJ3C698W8psM7byCeWVpd/Ha7FUYzuRVzXoKoM6nRUbA==", "dev": true, + "license": "MIT", "dependencies": { - "lowercase-keys": "^2.0.0" + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.2.tgz", + "integrity": "sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.0", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", + "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "path-scurry": "^2.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/roarr": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", @@ -3657,6 +5788,43 @@ "node": ">=8.0" } }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -3669,6 +5837,28 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, + "node_modules/secretlint": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/secretlint/-/secretlint-10.2.2.tgz", + "integrity": "sha512-xVpkeHV/aoWe4vP4TansF622nBEImzCY73y/0042DuJ29iKIaqgoJ8fGxre3rVSHHbxar4FdJobmTnLp9AU0eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-creator": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/node": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "debug": "^4.4.1", + "globby": "^14.1.0", + "read-pkg": "^9.0.1" + }, + "bin": { + "secretlint": "bin/secretlint.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/semaphore": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/semaphore/-/semaphore-1.1.0.tgz", @@ -3679,12 +5869,16 @@ } }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/semver-compare": { @@ -3710,23 +5904,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3749,15 +5926,73 @@ } }, "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -3766,10 +6001,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, "funding": [ { "type": "github", @@ -3790,6 +6039,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, "funding": [ { "type": "github", @@ -3811,15 +6061,119 @@ "simple-concat": "^1.0.0" } }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">=0.10.0" + "node": ">= 12" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, + "node_modules/spdx-license-ids": { + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/sprintf-js": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", @@ -3827,16 +6181,6 @@ "dev": true, "optional": true }, - "node_modules/stoppable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", - "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", - "dev": true, - "engines": { - "node": ">=4", - "npm": ">=6" - } - }, "node_modules/stream-exhaust": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", @@ -3849,6 +6193,18 @@ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -3858,6 +6214,123 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", @@ -3899,11 +6372,35 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo= sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, "optional": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/strnum": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", + "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/structured-source": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz", + "integrity": "sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boundary": "^2.0.0" + } + }, "node_modules/sumchecker": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", @@ -3928,10 +6425,92 @@ "node": ">=4" } }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dev": true, + "license": "MIT", "optional": true, "dependencies": { "chownr": "^1.1.1", @@ -3944,6 +6523,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, "optional": true, "dependencies": { "bl": "^4.0.3", @@ -3956,6 +6536,33 @@ "node": ">=6" } }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/terminal-link": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-4.0.0.tgz", + "integrity": "sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "supports-hyperlinks": "^3.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ternary-stream": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/ternary-stream/-/ternary-stream-3.0.0.tgz", @@ -3978,6 +6585,39 @@ "readable-stream": "2 || 3" } }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/textextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz", + "integrity": "sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -4003,10 +6643,11 @@ } }, "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.14" } @@ -4024,38 +6665,101 @@ } }, "node_modules/tree-sitter": { - "version": "0.20.6", - "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.20.6.tgz", - "integrity": "sha512-GxJodajVpfgb3UREzzIbtA1hyRnTxVbWVXrbC6sk4xTMH5ERMBJk9HJNq4c8jOJeUaIOmLcwg+t6mez/PDvGqg==", + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.25.0.tgz", + "integrity": "sha512-PGZZzFW63eElZJDe/b/R/LbsjDDYJa5UEjLZJB59RQsMX+fo0j54fqBPn1MGKav/QNa0JR0zBiVaikYDWCj5KQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + } + }, + "node_modules/tree-sitter-javascript": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/tree-sitter-javascript/-/tree-sitter-javascript-0.23.1.tgz", + "integrity": "sha512-/bnhbrTD9frUYHQTiYnPcxyHORIw157ERBa6dqzaKxvR/x3PC4Yzd+D1pZIMS6zNg2v3a8BZ0oK7jHqsQo9fWA==", "hasInstallScript": true, + "license": "MIT", "optional": true, "dependencies": { - "nan": "^2.18.0", - "prebuild-install": "^7.1.1" + "node-addon-api": "^8.2.2", + "node-gyp-build": "^4.8.2" + }, + "peerDependencies": { + "tree-sitter": "^0.21.1" + }, + "peerDependenciesMeta": { + "tree-sitter": { + "optional": true + } + } + }, + "node_modules/tree-sitter-javascript/node_modules/node-addon-api": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.1.tgz", + "integrity": "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==", + "license": "MIT", + "optional": true, + "engines": { + "node": "^18 || ^20 || >= 21" } }, "node_modules/tree-sitter-typescript": { - "version": "0.20.5", - "resolved": "https://registry.npmjs.org/tree-sitter-typescript/-/tree-sitter-typescript-0.20.5.tgz", - "integrity": "sha512-RzK/Pc6k4GiXvInIBlo8ZggekP6rODfW2P6KHFCTSUHENsw6ynh+iacFhfkJRa4MT8EIN2WHygFJ7076/+eHKg==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/tree-sitter-typescript/-/tree-sitter-typescript-0.23.2.tgz", + "integrity": "sha512-e04JUUKxTT53/x3Uq1zIL45DoYKVfHH4CZqwgZhPg5qYROl5nQjV+85ruFzFGZxu+QeFVbRTPDRnqL9UbU4VeA==", "hasInstallScript": true, + "license": "MIT", "optional": true, "dependencies": { - "nan": "^2.18.0", - "tree-sitter": "^0.20.6" + "node-addon-api": "^8.2.2", + "node-gyp-build": "^4.8.2", + "tree-sitter-javascript": "^0.23.1" + }, + "peerDependencies": { + "tree-sitter": "^0.21.0" + }, + "peerDependenciesMeta": { + "tree-sitter": { + "optional": true + } + } + }, + "node_modules/tree-sitter-typescript/node_modules/node-addon-api": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.1.tgz", + "integrity": "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==", + "license": "MIT", + "optional": true, + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/tree-sitter/node_modules/node-addon-api": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.1.tgz", + "integrity": "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" } }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "dev": true + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" }, "node_modules/tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.6.11 <=0.7.0 || >=0.7.3" } @@ -4064,6 +6768,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, "optional": true, "dependencies": { "safe-buffer": "^5.0.1" @@ -4090,6 +6795,7 @@ "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", "integrity": "sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==", "dev": true, + "license": "MIT", "dependencies": { "qs": "^6.9.1", "tunnel": "0.0.6", @@ -4097,36 +6803,47 @@ } }, "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" }, "node_modules/underscore": { "version": "1.13.7", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, - "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", - "dev": true + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 4.0.0" + "node": ">= 10.0.0" } }, "node_modules/url-join": { @@ -4150,11 +6867,35 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/version-range": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/version-range/-/version-range-4.15.0.tgz", + "integrity": "sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==", + "dev": true, + "license": "Artistic-2.0", + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/vinyl": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", - "devOptional": true, + "optional": true, "dependencies": { "clone": "^2.1.1", "clone-buffer": "^1.0.0", @@ -4230,40 +6971,15 @@ } }, "node_modules/vscode-universal-bundler/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, - "node_modules/vscode-universal-bundler/node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/vscode-universal-bundler/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/vscode-universal-bundler/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -4279,15 +6995,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/vscode-universal-bundler/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4304,16 +7011,167 @@ } }, "node_modules/workerpool": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.0.tgz", - "integrity": "sha512-r64Ea3glXY2RVzMeNxB+4J+0YHAVzUdV4cM5nHi4BBC2LvnO1pWFAIYKYuGcPElbg1/7eEiaPtZ/jzCjIUuGBg==", - "dev": true + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-10.0.1.tgz", + "integrity": "sha512-NAnKwZJxWlj/U1cp6ZkEtPE+GQY1S6KtOS3AlCiPfPFLxV3m64giSp7g2LsNJxzYCocDT7TSl+7T0sgrDp3KoQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "devOptional": true + "dev": true + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/xml2js": { "version": "0.5.0", @@ -4359,25 +7217,20 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "devOptional": true + "dev": true }, "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", + "integrity": "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==", "dev": true, + "license": "MIT", "dependencies": { "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" - } - }, - "node_modules/yazl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", - "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", - "dev": true, - "dependencies": { - "buffer-crc32": "~0.2.3" + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/yocto-queue": { diff --git a/build/package.json b/build/package.json index 9644be421c71..398a3074dcb8 100644 --- a/build/package.json +++ b/build/package.json @@ -3,51 +3,58 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@azure/cosmos": "^3", - "@azure/identity": "^4.2.1", - "@electron/get": "^2.0.0", - "@types/ansi-colors": "^3.2.0", - "@types/byline": "^4.2.32", - "@types/debounce": "^1.0.0", - "@types/debug": "^4.1.5", - "@types/fancy-log": "^1.3.0", - "@types/fs-extra": "^9.0.12", - "@types/glob": "^7.1.1", - "@types/gulp": "^4.0.17", - "@types/gulp-filter": "^3.0.32", - "@types/gulp-gzip": "^0.0.31", - "@types/gulp-json-editor": "^2.2.31", - "@types/gulp-rename": "^0.0.33", + "@azure/core-auth": "^1.10.1", + "@azure/cosmos": "^4", + "@azure/identity": "^4.13.0", + "@azure/msal-node": "^5.0.2", + "@azure/storage-blob": "^12.30.0", + "@electron/get": "^4.0.2", + "@types/ansi-colors": "^3.2.6", + "@types/byline": "^4.2.36", + "@types/debounce": "^1.2.4", + "@types/debug": "^4.1.12", + "@types/fancy-log": "^2.0.2", + "@types/fs-extra": "^11.0.4", + "@types/glob": "^9.0.0", + "@types/gulp": "^4.0.18", + "@types/gulp-filter": "^3.0.41", + "@types/gulp-gzip": "^0.0.36", + "@types/gulp-json-editor": "^2.2.36", + "@types/gulp-rename": "^2.0.7", "@types/gulp-sort": "^2.0.4", - "@types/gulp-sourcemaps": "^0.0.32", - "@types/mime": "0.0.29", - "@types/minimatch": "^3.0.3", - "@types/minimist": "^1.2.1", - "@types/mocha": "^9.1.1", - "@types/node": "20.x", - "@types/pump": "^1.0.1", - "@types/rimraf": "^2.0.4", - "@types/through": "^0.0.29", - "@types/through2": "^2.0.36", - "@types/workerpool": "^6.4.0", - "@types/xml2js": "0.0.33", - "@vscode/iconv-lite-umd": "0.7.0", - "@vscode/vsce": "2.20.1", + "@types/gulp-sourcemaps": "^0.0.38", + "@types/jws": "^3.2.11", + "@types/mime": "4.0.0", + "@types/minimatch": "^6.0.0", + "@types/minimist": "^1.2.5", + "@types/mocha": "^10.0.10", + "@types/node": "25.x", + "@types/pump": "^1.1.3", + "@types/rimraf": "^4.0.5", + "@types/through": "^0.0.33", + "@types/through2": "^2.0.41", + "@types/workerpool": "^9.0.0", + "@types/xml2js": "0.4.14", + "@vscode/iconv-lite-umd": "0.7.1", + "@vscode/ripgrep": "^1.17.0", + "@vscode/vsce": "3.7.1", "byline": "^5.0.0", "debug": "^4.3.2", - "electron-osx-sign": "^0.4.16", - "esbuild": "0.23.0", + "electron-osx-sign": "^0.6.0", + "esbuild": "0.27.2", "extract-zip": "^2.0.1", - "gulp-merge-json": "^2.1.1", + "gulp-merge-json": "^2.2.1", "gulp-sort": "^2.0.0", - "jsonc-parser": "^2.3.0", - "mime": "^1.4.1", - "source-map": "0.6.1", + "jsonc-parser": "^3.3.1", + "jws": "^4.0.1", + "mime": "^4.1.0", + "source-map": "0.7.6", "ternary-stream": "^3.0.0", "through2": "^4.0.2", + "tree-sitter": "^0.25.0", "vscode-universal-bundler": "^0.1.3", - "workerpool": "^6.4.0", - "yauzl": "^2.10.0" + "workerpool": "^10.0.1", + "yauzl": "^3.2.0" }, "type": "commonjs", "scripts": { @@ -56,8 +63,7 @@ "npmCheckJs": "../node_modules/.bin/tsc --noEmit" }, "optionalDependencies": { - "tree-sitter": "^0.20.5", - "tree-sitter-typescript": "^0.20.5", + "tree-sitter-typescript": "^0.23.2", "vscode-gulp-watch": "^5.0.3" } } diff --git a/build/tsconfig.json b/build/tsconfig.json index ce7a493a7aac..f3ad981d62fe 100644 --- a/build/tsconfig.json +++ b/build/tsconfig.json @@ -4,7 +4,7 @@ "lib": [ "ES2020" ], - "module": "commonjs", + "module": "nodenext", "alwaysStrict": true, "removeComments": false, "preserveConstEnums": true, diff --git a/build/win32/Cargo.lock b/build/win32/Cargo.lock index 8d317f7cab0f..822d94f6cc72 100644 --- a/build/win32/Cargo.lock +++ b/build/win32/Cargo.lock @@ -1,18 +1,49 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] -name = "bitflags" -version = "1.3.2" +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +dependencies = [ + "find-msvc-tools", + "shlex", +] [[package]] name = "cfg-if" @@ -20,20 +51,39 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "crc" -version = "3.0.1" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" dependencies = [ "crc-catalog", ] [[package]] name = "crc-catalog" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crossbeam-channel" @@ -56,53 +106,60 @@ dependencies = [ ] [[package]] -name = "dirs-next" -version = "2.0.0" +name = "erased-serde" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" dependencies = [ - "cfg-if", - "dirs-sys-next", + "serde", ] [[package]] -name = "dirs-sys-next" -version = "0.1.2" +name = "find-msvc-tools" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] -name = "getrandom" -version = "0.2.7" +name = "hermit-abi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ - "cfg-if", - "libc", - "wasi", + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", ] [[package]] -name = "hermit-abi" -version = "0.3.9" +name = "iana-time-zone-haiku" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] [[package]] name = "inno_updater" -version = "0.11.1" +version = "0.13.2" dependencies = [ "byteorder", "crc", "slog", "slog-async", "slog-term", - "windows-sys 0.42.0", + "windows-sys 0.61.2", ] [[package]] @@ -122,12 +179,37 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +[[package]] +name = "js-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "num_threads" version = "0.1.6" @@ -145,59 +227,80 @@ checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.20" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] [[package]] -name = "redox_syscall" -version = "0.2.13" +name = "rustversion" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ - "bitflags", + "serde_core", ] [[package]] -name = "redox_users" -version = "0.4.3" +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ - "getrandom", - "redox_syscall", - "thiserror", + "serde_derive", ] [[package]] -name = "rustversion" -version = "1.0.7" +name = "serde_derive" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "slog" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" +checksum = "9b3b8565691b22d2bdfc066426ed48f837fc0c5f2c8cad8d9718f7f99d6995c1" +dependencies = [ + "anyhow", + "erased-serde", + "rustversion", + "serde_core", +] [[package]] name = "slog-async" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "766c59b252e62a34651412870ff55d8c4e6d04df19b43eecb2703e417b097ffe" +checksum = "72c8038f898a2c79507940990f05386455b3a317d8f18d4caea7cbc3d5096b84" dependencies = [ "crossbeam-channel", "slog", @@ -207,10 +310,11 @@ dependencies = [ [[package]] name = "slog-term" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6e022d0b998abfe5c3782c1f03551a596269450ccd677ea51c56f8b214610e8" +checksum = "5cb1fc680b38eed6fad4c02b3871c09d2c81db8c96aa4e9c0a34904c830f09b5" dependencies = [ + "chrono", "is-terminal", "slog", "term", @@ -220,9 +324,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.98" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -237,33 +341,11 @@ checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi", -] - -[[package]] -name = "thiserror" -version = "1.0.31" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "windows-sys 0.61.2", ] [[package]] @@ -295,51 +377,112 @@ checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +name = "wasm-bindgen" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] [[package]] -name = "winapi" -version = "0.3.9" +name = "wasm-bindgen-macro" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "quote", + "wasm-bindgen-macro-support", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "wasm-bindgen-macro-support" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "wasm-bindgen-shared" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] [[package]] -name = "windows-sys" -version = "0.42.0" +name = "windows-core" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", ] [[package]] @@ -351,52 +494,43 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-targets" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.52.5" @@ -409,48 +543,24 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.52.5" diff --git a/build/win32/Cargo.toml b/build/win32/Cargo.toml index 9dd726089348..41e838baaf94 100644 --- a/build/win32/Cargo.toml +++ b/build/win32/Cargo.toml @@ -1,18 +1,18 @@ [package] name = "inno_updater" -version = "0.11.1" +version = "0.13.2" authors = ["Microsoft "] build = "build.rs" [dependencies] -byteorder = "1.4.3" -crc = "3.0.1" -slog = "2.7.0" -slog-async = "2.7.0" -slog-term = "2.9.1" +byteorder = "1.5.0" +crc = "3.4.0" +slog = "2.8.2" +slog-async = "2.8.0" +slog-term = "2.9.2" [target.'cfg(windows)'.dependencies.windows-sys] -version = "0.42" +version = "0.61" features = [ "Win32_Foundation", "Win32_System_Shutdown", diff --git a/build/win32/code.iss b/build/win32/code.iss index fca3d1e9d9b8..cad6e399f66b 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -35,6 +35,11 @@ ArchitecturesAllowed={#ArchitecturesAllowed} ArchitecturesInstallIn64BitMode={#ArchitecturesInstallIn64BitMode} WizardStyle=modern +// We've seen an uptick on broken installations from updates which were unable +// to shutdown VS Code. We rely on the fact that the update signals +// that VS Code is ready to be shutdown, so we're good to use `force` here. +CloseApplications=force + #ifdef Sign SignTool=esrp #endif @@ -911,7 +916,7 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.plist\OpenWithProgid Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.plist\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.plist"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.plist"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Properties file}"; Flags: uninsdeletekey; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.plist"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles -Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.plist\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\plist.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.plist\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.plist\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.plist\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles @@ -1115,14 +1120,6 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg\D Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svg\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles -Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.svgz\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles -Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.svgz\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.svgz"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles -Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,SVGZ}"; Flags: uninsdeletekey; Tasks: associatewithfiles -Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles -Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\default.ico"; Tasks: associatewithfiles -Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz\shell\open"; ValueType: string; ValueName: "Icon"; ValueData: """{app}\{#ExeBasename}.exe"""; Tasks: associatewithfiles -Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.svgz\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles - Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.t\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.t\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.t"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.t"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Perl}"; Flags: uninsdeletekey; Tasks: associatewithfiles diff --git a/build/win32/explorer-appx-fetcher.js b/build/win32/explorer-appx-fetcher.js index 554b449d872c..78d2317147ec 100644 --- a/build/win32/explorer-appx-fetcher.js +++ b/build/win32/explorer-appx-fetcher.js @@ -3,23 +3,26 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ 'use strict'; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; Object.defineProperty(exports, "__esModule", { value: true }); exports.downloadExplorerAppx = downloadExplorerAppx; -const fs = require("fs"); -const debug = require("debug"); -const extract = require("extract-zip"); -const path = require("path"); +const fs_1 = __importDefault(require("fs")); +const debug_1 = __importDefault(require("debug")); +const extract_zip_1 = __importDefault(require("extract-zip")); +const path_1 = __importDefault(require("path")); const get_1 = require("@electron/get"); -const root = path.dirname(path.dirname(__dirname)); -const d = debug('explorer-appx-fetcher'); +const root = path_1.default.dirname(path_1.default.dirname(__dirname)); +const d = (0, debug_1.default)('explorer-appx-fetcher'); async function downloadExplorerAppx(outDir, quality = 'stable', targetArch = 'x64') { const fileNamePrefix = quality === 'insider' ? 'code_insiders' : 'code'; const fileName = `${fileNamePrefix}_explorer_${targetArch}.zip`; - if (await fs.existsSync(path.resolve(outDir, 'resources.pri'))) { + if (await fs_1.default.existsSync(path_1.default.resolve(outDir, 'resources.pri'))) { return; } - if (!await fs.existsSync(outDir)) { - await fs.mkdirSync(outDir, { recursive: true }); + if (!await fs_1.default.existsSync(outDir)) { + await fs_1.default.mkdirSync(outDir, { recursive: true }); } d(`downloading ${fileName}`); const artifact = await (0, get_1.downloadArtifact)({ @@ -34,14 +37,14 @@ async function downloadExplorerAppx(outDir, quality = 'stable', targetArch = 'x6 } }); d(`unpacking from ${fileName}`); - await extract(artifact, { dir: fs.realpathSync(outDir) }); + await (0, extract_zip_1.default)(artifact, { dir: fs_1.default.realpathSync(outDir) }); } async function main(outputDir) { const arch = process.env['VSCODE_ARCH']; if (!outputDir) { throw new Error('Required build env not set'); } - const product = JSON.parse(fs.readFileSync(path.join(root, 'product.json'), 'utf8')); + const product = JSON.parse(fs_1.default.readFileSync(path_1.default.join(root, 'product.json'), 'utf8')); await downloadExplorerAppx(outputDir, product.quality, arch); } if (require.main === module) { diff --git a/build/win32/explorer-appx-fetcher.ts b/build/win32/explorer-appx-fetcher.ts index 89fbb57c064c..95121cd65031 100644 --- a/build/win32/explorer-appx-fetcher.ts +++ b/build/win32/explorer-appx-fetcher.ts @@ -5,10 +5,10 @@ 'use strict'; -import * as fs from 'fs'; -import * as debug from 'debug'; -import * as extract from 'extract-zip'; -import * as path from 'path'; +import fs from 'fs'; +import debug from 'debug'; +import extract from 'extract-zip'; +import path from 'path'; import { downloadArtifact } from '@electron/get'; const root = path.dirname(path.dirname(__dirname)); diff --git a/build/win32/inno_updater.exe b/build/win32/inno_updater.exe index 5f969cc326ca..2ca110dea1fd 100644 Binary files a/build/win32/inno_updater.exe and b/build/win32/inno_updater.exe differ diff --git a/cglicenses.json b/cglicenses.json index 0b4e03e502bc..8b8710fb8f53 100644 --- a/cglicenses.json +++ b/cglicenses.json @@ -587,5 +587,31 @@ "ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR", "IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE." ] + }, + { + "name": "@azure/msal-node-runtime", + "fullLicenseText": [ + "MIT License", + "", + "Copyright (c) Microsoft Corporation. All rights reserved.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + "SOFTWARE" + ] } ] diff --git a/cgmanifest.json b/cgmanifest.json index a77b2b35eb5f..eb5b37d39a75 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "63df87866a2d74d20fb28e96d51ef32aea40c21e" + "commitHash": "5c0cb964bca15fcf41718d54f4b8d70d6b9079de" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "128.0.6613.162" + "version": "132.0.6834.210" }, { "component": { @@ -516,11 +516,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "efbec04208a3c8588d4e7f076c47d29dddf47976" + "commitHash": "4819c99baa28bf2c1baf411ba100c467fec3d486" } }, "isOnlyProductionDependency": true, - "version": "20.17.0" + "version": "20.18.3" }, { "component": { @@ -528,12 +528,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "8ac9d16162df2faf5d441158d77ff3adff79ee8d" + "commitHash": "f98501308a973e0aee2414315b426e5de2c03a60" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "32.1.2" + "version": "34.3.2" }, { "component": { diff --git a/cli/.cargo/config.toml b/cli/.cargo/config.toml new file mode 100644 index 000000000000..ad9374d4a908 --- /dev/null +++ b/cli/.cargo/config.toml @@ -0,0 +1,6 @@ +[target.'cfg(all(target_os = "windows", any(target_arch = "i686", target_arch = "x86_64", target_arch = "x86")))'] +rustflags = ["-Ctarget-feature=+crt-static", "-Clink-args=/guard:cf", "-Clink-args=/CETCOMPAT"] + +# CETCOMPAT is not supported on ARM binaries +[target.'cfg(all(target_os = "windows", not(any(target_arch = "i686", target_arch = "x86_64", target_arch = "x86"))))'] +rustflags = ["-Ctarget-feature=+crt-static", "-Clink-args=/guard:cf"] diff --git a/cli/CONTRIBUTING.md b/cli/CONTRIBUTING.md index d119f1ac98a8..4809fccd0809 100644 --- a/cli/CONTRIBUTING.md +++ b/cli/CONTRIBUTING.md @@ -8,7 +8,7 @@ For the moment, we require OpenSSL on Windows, where it is not usually installed by default. To install it: -1. Install (clone) vcpkg [using their instructions](https://github.com/Microsoft/vcpkg#quick-start-windows) +1. Follow steps 1 and 2 of [Set up vcpkg](https://learn.microsoft.com/en-us/vcpkg/get_started/get-started-msbuild?pivots=shell-powershell#1---set-up-vcpkg) to obtain the executable. 1. Add the location of the `vcpkg` directory to your system or user PATH. 1. Run`vcpkg install openssl:x64-windows-static-md` (after restarting your terminal for PATH changes to apply) 1. You should be able to then `cargo build` successfully diff --git a/cli/Cargo.lock b/cli/Cargo.lock index 27fe79896a2a..d7a84bcc2792 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -1,21 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] -name = "addr2line" -version = "0.21.0" +name = "adler2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" @@ -26,12 +17,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -58,9 +43,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -90,6 +75,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "async-broadcast" version = "0.5.1" @@ -100,6 +94,18 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener 5.3.0", + "event-listener-strategy 0.5.2", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-channel" version = "2.3.1" @@ -196,7 +202,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -225,13 +231,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -247,25 +253,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] -name = "backtrace" -version = "0.3.71" +name = "base64" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bit-vec" @@ -331,9 +328,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "cc" @@ -343,28 +340,33 @@ checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" dependencies = [ - "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.5", + "windows-link", ] [[package]] name = "clap" -version = "4.5.4" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", "clap_derive", @@ -372,9 +374,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstream", "anstyle", @@ -384,28 +386,28 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "code-cli" version = "0.1.0" dependencies = [ "async-trait", - "base64", + "base64 0.22.1", "bytes", "cfg-if", "chrono", @@ -413,13 +415,13 @@ dependencies = [ "clap_lex", "console", "const_format", - "core-foundation", + "core-foundation 0.10.1", "dialoguer", - "dirs 5.0.1", + "dirs 6.0.0", "flate2", "futures", "gethostname", - "hyper", + "hyper 0.14.28", "indicatif", "keyring", "lazy_static", @@ -428,9 +430,9 @@ dependencies = [ "open", "opentelemetry", "pin-project", - "rand 0.8.5", + "rand 0.9.2", "regex", - "reqwest", + "reqwest 0.12.9", "rmp-serde", "serde", "serde_bytes", @@ -440,15 +442,15 @@ dependencies = [ "sysinfo", "tar", "tempfile", - "thiserror", + "thiserror 2.0.18", "tokio", "tokio-util", "tunnels", "url", "uuid", "winapi", - "winreg 0.50.0", - "zbus", + "winreg 0.55.0", + "zbus 5.4.0", "zip", ] @@ -469,31 +471,31 @@ dependencies = [ [[package]] name = "console" -version = "0.15.8" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" dependencies = [ "encode_unicode", - "lazy_static", "libc", + "once_cell", "unicode-width", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "const_format" -version = "0.2.32" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.32" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ "proc-macro2", "quote", @@ -510,6 +512,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -536,9 +548,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] @@ -585,11 +597,22 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "dialoguer" -version = "0.10.4" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59c6f2989294b9a498d3ad5491a79c6deb604617378e1cdc4bfc1c1361fe2f87" +checksum = "25f104b501bf2364e78d0d3974cbc774f738f5865306ed128e1e0d7499c0ad96" dependencies = [ "console", "shell-words", @@ -619,11 +642,11 @@ dependencies = [ [[package]] name = "dirs" -version = "5.0.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "dirs-sys 0.4.1", + "dirs-sys 0.5.0", ] [[package]] @@ -633,27 +656,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.5", "winapi", ] [[package]] name = "dirs-sys" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users", - "windows-sys 0.48.0", + "redox_users 0.5.2", + "windows-sys 0.61.2", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] name = "encode_unicode" -version = "0.3.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "encoding_rs" @@ -664,6 +698,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + [[package]] name = "enumflags2" version = "0.7.9" @@ -682,7 +722,7 @@ checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -693,12 +733,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -789,9 +829,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.30" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" dependencies = [ "crc32fast", "libz-sys", @@ -830,9 +870,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -845,9 +885,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -855,15 +895,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -872,9 +912,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" @@ -903,32 +943,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -954,12 +994,12 @@ dependencies = [ [[package]] name = "gethostname" -version = "0.4.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ - "libc", - "windows-targets 0.48.5", + "rustix 1.1.3", + "windows-link", ] [[package]] @@ -985,10 +1025,16 @@ dependencies = [ ] [[package]] -name = "gimli" -version = "0.28.1" +name = "getrandom" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] [[package]] name = "h2" @@ -1001,8 +1047,8 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", - "indexmap 2.2.6", + "http 0.2.12", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -1017,9 +1063,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "heck" @@ -1065,6 +1111,16 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + [[package]] name = "http-body" version = "0.4.6" @@ -1072,7 +1128,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.4.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.4.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -1099,8 +1178,8 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", - "http-body", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -1112,6 +1191,25 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -1119,10 +1217,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.28", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.5.2", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "hyper 1.5.2", + "pin-project-lite", + "socket2 0.5.7", + "tokio", + "tower-service", + "tracing", ] [[package]] @@ -1148,14 +1281,143 @@ dependencies = [ "cc", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", ] [[package]] @@ -1170,25 +1432,25 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.16.1", ] [[package]] name = "indicatif" -version = "0.17.8" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +checksum = "9375e112e4b463ec1b1c6c011953545c65a30164fbab5b581df32b3abf0dcb88" dependencies = [ "console", - "instant", - "number_prefix", "portable-atomic", "unicode-width", + "unit-prefix", + "web-time", ] [[package]] @@ -1283,15 +1545,15 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libredox" @@ -1305,9 +1567,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.16" +version = "1.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" +checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" dependencies = [ "cc", "pkg-config", @@ -1336,6 +1598,18 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "lock_api" version = "0.4.12" @@ -1348,9 +1622,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "md5" @@ -1390,22 +1664,23 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ - "adler", + "adler2", + "simd-adler32", ] [[package]] name = "mio" -version = "0.8.11" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -1439,12 +1714,16 @@ dependencies = [ ] [[package]] -name = "ntapi" -version = "0.4.1" +name = "nix" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "winapi", + "bitflags 2.5.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset 0.9.1", ] [[package]] @@ -1527,31 +1806,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - -[[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.19.0" @@ -1560,9 +1814,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "open" -version = "4.2.0" +version = "5.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a083c0c7e5e4a8ec4176346cf61f67ac674e8bfb059d9226e1c54a96b377c12" +checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc" dependencies = [ "is-wsl", "libc", @@ -1571,9 +1825,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ "bitflags 2.5.0", "cfg-if", @@ -1592,7 +1846,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -1603,9 +1857,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ "cc", "libc", @@ -1615,9 +1869,9 @@ dependencies = [ [[package]] name = "opentelemetry" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4b8347cc26099d3aeee044065ecc3ae11469796b4d65d065a23a584ed92a6f" +checksum = "9591d937bc0e6d2feb6f71a559540ab300ea49955229c347a517a28d27784c54" dependencies = [ "opentelemetry_api", "opentelemetry_sdk", @@ -1625,24 +1879,25 @@ dependencies = [ [[package]] name = "opentelemetry_api" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed41783a5bf567688eb38372f2b7a8530f5a607a4b49d38dd7573236c23ca7e2" +checksum = "8a81f725323db1b1206ca3da8bb19874bbd3f57c3bcd59471bfb04525b265b9b" dependencies = [ "futures-channel", "futures-util", "indexmap 1.9.3", + "js-sys", "once_cell", "pin-project-lite", - "thiserror", + "thiserror 1.0.69", "urlencoding", ] [[package]] name = "opentelemetry_sdk" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b3a2a91fdbfdd4d212c0dcc2ab540de2c2bcbbd90be17de7a7daf8822d010c1" +checksum = "fa8e705a0612d48139799fcbaba0d4a90f06277153e43dd2bdc16c6f0edd8026" dependencies = [ "async-trait", "crossbeam-channel", @@ -1651,9 +1906,10 @@ dependencies = [ "futures-util", "once_cell", "opentelemetry_api", + "ordered-float", "percent-encoding", "rand 0.8.5", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", ] @@ -1664,6 +1920,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-float" +version = "3.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-stream" version = "0.2.0" @@ -1710,7 +1975,7 @@ dependencies = [ "libc", "redox_syscall 0.5.1", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -1733,22 +1998,22 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -1836,7 +2101,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit 0.23.10+spec-1.0.0", ] [[package]] @@ -1857,6 +2131,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "rand" version = "0.7.3" @@ -1881,6 +2161,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -1901,6 +2191,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -1919,6 +2219,15 @@ dependencies = [ "getrandom 0.2.15", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -1954,14 +2263,25 @@ checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom 0.2.15", "libredox", - "thiserror", + "thiserror 1.0.69", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror 2.0.18", ] [[package]] name = "regex" -version = "1.10.4" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -1971,9 +2291,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -1982,9 +2302,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" @@ -1992,16 +2312,16 @@ version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", "futures-util", "h2", - "http", - "http-body", - "hyper", - "hyper-tls", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.28", + "hyper-tls 0.5.0", "ipnet", "js-sys", "log", @@ -2010,14 +2330,53 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg 0.50.0", +] + +[[package]] +name = "reqwest" +version = "0.12.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.2", + "hyper-tls 0.6.0", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 2.2.0", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tokio-native-tls", "tokio-util", "tower-service", "url", @@ -2025,7 +2384,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "winreg 0.50.0", + "windows-registry", ] [[package]] @@ -2041,11 +2400,10 @@ dependencies = [ [[package]] name = "rmp-serde" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" +checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" dependencies = [ - "byteorder", "rmp", "serde", ] @@ -2074,7 +2432,7 @@ dependencies = [ "sha1", "sha2", "subtle", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-util", ] @@ -2109,18 +2467,12 @@ dependencies = [ "russh-cryptovec", "serde", "sha2", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "yasna", ] -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - [[package]] name = "rustix" version = "0.37.27" @@ -2148,13 +2500,44 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.2", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64", + "base64 0.21.7", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +dependencies = [ + "zeroize", ] [[package]] @@ -2191,7 +2574,7 @@ dependencies = [ "openssl", "rand 0.8.5", "serde", - "zbus", + "zbus 3.15.2", ] [[package]] @@ -2201,7 +2584,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ "bitflags 2.5.0", - "core-foundation", + "core-foundation 0.9.4", "core-foundation-sys", "libc", "security-framework-sys", @@ -2219,42 +2602,55 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.202" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.14" +version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" dependencies = [ "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", - "ryu", + "memchr", "serde", + "serde_core", + "zmij", ] [[package]] @@ -2265,7 +2661,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -2293,9 +2689,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -2323,6 +2719,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + [[package]] name = "slab" version = "0.4.9" @@ -2358,6 +2760,22 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -2389,9 +2807,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.65" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -2404,18 +2822,33 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "sysinfo" -version = "0.29.11" +version = "0.37.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd727fc423c2060f6c92d9534cef765c65a6ed3f428a03d7def74a8c4348e666" +checksum = "16607d5caffd1c07ce073528f9ed972d88db15dd44023fa57142963be3feb11f" dependencies = [ - "cfg-if", - "core-foundation-sys", "libc", - "ntapi", - "once_cell", - "winapi", ] [[package]] @@ -2425,7 +2858,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -2441,9 +2874,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.40" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" dependencies = [ "filetime", "libc", @@ -2452,34 +2885,55 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand 2.1.0", + "once_cell", "rustix 0.38.34", - "windows-sys 0.52.0", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", ] [[package]] name = "thiserror" -version = "1.0.61" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.18", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] @@ -2502,49 +2956,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] -name = "tinyvec" -version = "1.6.0" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" -version = "1.37.0" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ - "backtrace", "bytes", "libc", "mio", - "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.7", + "socket2 0.6.1", "tokio-macros", "tracing", - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -2584,9 +3031,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -2602,15 +3049,45 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.6", - "toml_datetime", - "winnow", + "indexmap 2.13.0", + "toml_datetime 0.6.6", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap 2.13.0", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "winnow 0.7.14", +] + +[[package]] +name = "toml_parser" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ + "winnow 0.7.14", ] [[package]] @@ -2638,7 +3115,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -2665,13 +3142,13 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 0.2.12", "httparse", "log", "native-tls", "rand 0.8.5", "sha1", - "thiserror", + "thiserror 1.0.69", "url", "utf-8", ] @@ -2684,16 +3161,16 @@ dependencies = [ "async-trait", "chrono", "futures", - "hyper", + "hyper 0.14.28", "log", "os_info", "rand 0.8.5", - "reqwest", + "reqwest 0.11.27", "russh", "russh-keys", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-tungstenite", "tokio-util", @@ -2721,32 +3198,17 @@ dependencies = [ "winapi", ] -[[package]] -name = "unicode-bidi" -version = "0.3.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" - [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" -[[package]] -name = "unicode-normalization" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" @@ -2754,11 +3216,17 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "unit-prefix" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" + [[package]] name = "url" -version = "2.5.0" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -2777,6 +3245,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.1" @@ -2785,11 +3265,11 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.8.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.3.4", "serde", ] @@ -2832,6 +3312,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.92" @@ -2853,7 +3342,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -2887,7 +3376,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -2921,6 +3410,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2949,7 +3448,43 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", ] [[package]] @@ -2967,7 +3502,34 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", ] [[package]] @@ -2987,18 +3549,35 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -3009,9 +3588,15 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -3021,9 +3606,15 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -3033,15 +3624,27 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -3051,9 +3654,15 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -3063,9 +3672,15 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -3075,9 +3690,15 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -3087,9 +3708,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" @@ -3100,6 +3727,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.8.0" @@ -3119,6 +3755,34 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "xattr" version = "1.3.1" @@ -3150,13 +3814,37 @@ dependencies = [ "num-bigint", ] +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure", +] + [[package]] name = "zbus" version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6" dependencies = [ - "async-broadcast", + "async-broadcast 0.5.1", "async-process", "async-recursion", "async-trait", @@ -3168,7 +3856,7 @@ dependencies = [ "futures-sink", "futures-util", "hex", - "nix", + "nix 0.26.4", "once_cell", "ordered-stream", "rand 0.8.5", @@ -3181,9 +3869,39 @@ dependencies = [ "uds_windows", "winapi", "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", + "zbus_macros 3.15.2", + "zbus_names 2.6.1", + "zvariant 3.15.2", +] + +[[package]] +name = "zbus" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbddd8b6cb25d5d8ec1b23277b45299a98bfb220f1761ca11e186d5c702507f8" +dependencies = [ + "async-broadcast 0.7.2", + "async-recursion", + "async-trait", + "enumflags2", + "event-listener 5.3.0", + "futures-core", + "futures-util", + "hex", + "nix 0.29.0", + "ordered-stream", + "serde", + "serde_repr", + "static_assertions", + "tokio", + "tracing", + "uds_windows", + "windows-sys 0.59.0", + "winnow 0.7.14", + "xdg-home", + "zbus_macros 5.4.0", + "zbus_names 4.3.1", + "zvariant 5.9.2", ] [[package]] @@ -3192,12 +3910,27 @@ version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "regex", "syn 1.0.109", - "zvariant_utils", + "zvariant_utils 1.0.1", +] + +[[package]] +name = "zbus_macros" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac404d48b4e9cf193c8b49589f3280ceca5ff63519e7e64f55b4cf9c47ce146" +dependencies = [ + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.87", + "zbus_names 4.3.1", + "zvariant 5.9.2", + "zvariant_utils 3.3.0", ] [[package]] @@ -3208,7 +3941,39 @@ checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d" dependencies = [ "serde", "static_assertions", - "zvariant", + "zvariant 3.15.2", +] + +[[package]] +name = "zbus_names" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" +dependencies = [ + "serde", + "winnow 0.7.14", + "zvariant 5.9.2", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", + "synstructure", ] [[package]] @@ -3217,19 +3982,51 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "zip" -version = "0.6.6" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +checksum = "84e9a772a54b54236b9b744aaaf8d7be01b4d6e99725523cb82cb32d1c81b1d7" dependencies = [ - "byteorder", + "arbitrary", "crc32fast", "crossbeam-utils", + "displaydoc", "flate2", + "indexmap 2.13.0", + "memchr", + "thiserror 2.0.18", "time", ] +[[package]] +name = "zmij" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" + [[package]] name = "zvariant" version = "3.15.2" @@ -3241,7 +4038,21 @@ dependencies = [ "libc", "serde", "static_assertions", - "zvariant_derive", + "zvariant_derive 3.15.2", +] + +[[package]] +name = "zvariant" +version = "5.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b64ef4f40c7951337ddc7023dd03528a57a3ce3408ee9da5e948bd29b232c4" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow 0.7.14", + "zvariant_derive 5.9.2", + "zvariant_utils 3.3.0", ] [[package]] @@ -3250,11 +4061,24 @@ version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.109", - "zvariant_utils", + "zvariant_utils 1.0.1", +] + +[[package]] +name = "zvariant_derive" +version = "5.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "484d5d975eb7afb52cc6b929c13d3719a20ad650fea4120e6310de3fc55e415c" +dependencies = [ + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.87", + "zvariant_utils 3.3.0", ] [[package]] @@ -3267,3 +4091,16 @@ dependencies = [ "quote", "syn 1.0.109", ] + +[[package]] +name = "zvariant_utils" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 2.0.87", + "winnow 0.7.14", +] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 2907ff3d7e70..16dd16ff3730 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -12,62 +12,62 @@ path = "src/lib.rs" name = "code" [dependencies] -futures = "0.3.28" -clap = { version = "4.3.0", features = ["derive", "env"] } -open = "4.1.0" -reqwest = { version = "0.11.22", default-features = false, features = ["json", "stream", "native-tls"] } -tokio = { version = "1.28.2", features = ["full"] } -tokio-util = { version = "0.7.8", features = ["compat", "codec"] } -flate2 = { version = "1.0.26", default-features = false, features = ["zlib"] } -zip = { version = "0.6.6", default-features = false, features = ["time", "deflate-zlib"] } -regex = "1.8.3" -lazy_static = "1.4.0" -sysinfo = { version = "0.29.0", default-features = false } +futures = "0.3.31" +clap = { version = "4.5.54", features = ["derive", "env"] } +open = "5.3.3" +reqwest = { version = "0.12.9", default-features = false, features = ["json", "stream", "native-tls"] } +tokio = { version = "1.49.0", features = ["full"] } +tokio-util = { version = "0.7.18", features = ["compat", "codec"] } +flate2 = { version = "1.1.8", default-features = false, features = ["zlib"] } +zip = { version = "2.3.0", default-features = false, features = ["time", "deflate-zlib"] } +regex = "1.12.2" +lazy_static = "1.5.0" +sysinfo = { version = "0.37.2", default-features = false } serde = { version = "1.0.163", features = ["derive"] } -serde_json = "1.0.96" -rmp-serde = "1.1.1" -uuid = { version = "1.4", features = ["serde", "v4"] } -dirs = "5.0.1" -rand = "0.8.5" -opentelemetry = { version = "0.19.0", features = ["rt-tokio"] } -serde_bytes = "0.11.9" -chrono = { version = "0.4.26", features = ["serde", "std", "clock"], default-features = false } -gethostname = "0.4.3" -libc = "0.2.144" +serde_json = "1.0.149" +rmp-serde = "1.3.1" +uuid = { version = "1.16", features = ["serde", "v4"] } +dirs = "6.0.0" +rand = "0.9.2" +opentelemetry = { version = "0.20.0", features = ["rt-tokio"] } +serde_bytes = "0.11.19" +chrono = { version = "0.4.43", features = ["serde", "std", "clock"], default-features = false } +gethostname = "1.1.0" +libc = "0.2.180" tunnels = { git = "https://github.com/microsoft/dev-tunnels", rev = "8cae9b2a24c65c6c1958f5a0e77d72b23b5c6c30", default-features = false, features = ["connections"] } keyring = { version = "2.0.3", default-features = false, features = ["linux-secret-service-rt-tokio-crypto-openssl", "platform-windows", "platform-macos", "linux-keyutils"] } -dialoguer = "0.10.4" +dialoguer = "0.12.0" hyper = { version = "0.14.26", features = ["server", "http1", "runtime"] } -indicatif = "0.17.4" -tempfile = "3.5.0" -clap_lex = "0.7.0" -url = "2.3.1" -async-trait = "0.1.68" -log = "0.4.18" -const_format = "0.2.31" -sha2 = "0.10.6" -base64 = "0.21.2" +indicatif = "0.18.3" +tempfile = "3.12.0" +clap_lex = "0.7.7" +url = "2.5.4" +async-trait = "0.1.89" +log = "0.4.29" +const_format = "0.2.35" +sha2 = "0.10.9" +base64 = "0.22.1" shell-escape = "0.1.5" -thiserror = "1.0.40" -cfg-if = "1.0.0" -pin-project = "1.1.0" -console = "0.15.7" -bytes = "1.4.0" -tar = "0.4.38" +thiserror = "2.0.18" +cfg-if = "1.0.4" +pin-project = "1.1.10" +console = "0.16.2" +bytes = "1.11.0" +tar = "0.4.44" [build-dependencies] serde = { version="1.0.163", features = ["derive"] } -serde_json = "1.0.96" +serde_json = "1.0.149" [target.'cfg(windows)'.dependencies] -winreg = "0.50.0" +winreg = "0.55.0" winapi = "0.3.9" [target.'cfg(target_os = "macos")'.dependencies] -core-foundation = "0.9.3" +core-foundation = "0.10.1" [target.'cfg(target_os = "linux")'.dependencies] -zbus = { version = "3.13.1", default-features = false, features = ["tokio"] } +zbus = { version = "5.4.0", default-features = false, features = ["tokio"] } [patch.crates-io] russh = { git = "https://github.com/microsoft/vscode-russh", branch = "main" } diff --git a/cli/ThirdPartyNotices.txt b/cli/ThirdPartyNotices.txt index 4bbc90b30ee2..cc255c04cfdc 100644 --- a/cli/ThirdPartyNotices.txt +++ b/cli/ThirdPartyNotices.txt @@ -684,16 +684,6 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [deps-image]: https://deps.rs/repo/github/RustCrypto/utils/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/utils -[msrv-1.39]: https://img.shields.io/badge/rustc-1.39.0+-blue.svg -[msrv-1.40]: https://img.shields.io/badge/rustc-1.40.0+-blue.svg -[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg -[msrv-1.45]: https://img.shields.io/badge/rustc-1.45.0+-blue.svg -[msrv-1.51]: https://img.shields.io/badge/rustc-1.51.0+-blue.svg -[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg -[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg -[msrv-1.59]: https://img.shields.io/badge/rustc-1.59.0+-blue.svg -[msrv-1.60]: https://img.shields.io/badge/rustc-1.60.0+-blue.svg - [//]: # (crates) [`blobby`]: ./blobby @@ -738,16 +728,6 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [deps-image]: https://deps.rs/repo/github/RustCrypto/utils/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/utils -[msrv-1.39]: https://img.shields.io/badge/rustc-1.39.0+-blue.svg -[msrv-1.40]: https://img.shields.io/badge/rustc-1.40.0+-blue.svg -[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg -[msrv-1.45]: https://img.shields.io/badge/rustc-1.45.0+-blue.svg -[msrv-1.51]: https://img.shields.io/badge/rustc-1.51.0+-blue.svg -[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg -[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg -[msrv-1.59]: https://img.shields.io/badge/rustc-1.59.0+-blue.svg -[msrv-1.60]: https://img.shields.io/badge/rustc-1.60.0+-blue.svg - [//]: # (crates) [`blobby`]: ./blobby @@ -962,7 +942,7 @@ chrono 0.4.38 - MIT OR Apache-2.0 https://github.com/chronotope/chrono Rust-chrono is dual-licensed under The MIT License [1] and -Apache 2.0 License [2]. Copyright (c) 2014--2017, Kang Seonghoon and +Apache 2.0 License [2]. Copyright (c) 2014--2025, Kang Seonghoon and contributors. Nota Bene: This is same as the Rust Project's own license. @@ -1525,16 +1505,6 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [deps-image]: https://deps.rs/repo/github/RustCrypto/utils/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/utils -[msrv-1.39]: https://img.shields.io/badge/rustc-1.39.0+-blue.svg -[msrv-1.40]: https://img.shields.io/badge/rustc-1.40.0+-blue.svg -[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg -[msrv-1.45]: https://img.shields.io/badge/rustc-1.45.0+-blue.svg -[msrv-1.51]: https://img.shields.io/badge/rustc-1.51.0+-blue.svg -[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg -[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg -[msrv-1.59]: https://img.shields.io/badge/rustc-1.59.0+-blue.svg -[msrv-1.60]: https://img.shields.io/badge/rustc-1.60.0+-blue.svg - [//]: # (crates) [`blobby`]: ./blobby @@ -1674,10 +1644,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg [deps-image]: https://deps.rs/repo/github/RustCrypto/traits/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/traits -[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg -[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg -[msrv-1.60]: https://img.shields.io/badge/rustc-1.60.0+-blue.svg -[msrv-1.65]: https://img.shields.io/badge/rustc-1.65.0+-blue.svg +[msrv-1.85]: https://img.shields.io/badge/rustc-1.85.0+-blue.svg [//]: # (crates) @@ -1741,7 +1708,7 @@ SOFTWARE. deranged 0.3.11 - MIT OR Apache-2.0 https://github.com/jhpratt/deranged -Copyright (c) 2022 Jacob Pratt et al. +Copyright (c) 2024 Jacob Pratt et al. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -1839,10 +1806,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg [deps-image]: https://deps.rs/repo/github/RustCrypto/traits/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/traits -[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg -[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg -[msrv-1.60]: https://img.shields.io/badge/rustc-1.60.0+-blue.svg -[msrv-1.65]: https://img.shields.io/badge/rustc-1.65.0+-blue.svg +[msrv-1.85]: https://img.shields.io/badge/rustc-1.85.0+-blue.svg [//]: # (crates) @@ -1928,6 +1892,36 @@ SOFTWARE. --------------------------------------------------------- +displaydoc 0.2.5 - MIT OR Apache-2.0 +https://github.com/yaahc/displaydoc + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + encode_unicode 0.3.6 - MIT/Apache-2.0 https://github.com/tormol/encode_unicode @@ -2242,7 +2236,7 @@ DEALINGS IN THE SOFTWARE. flate2 1.0.30 - MIT OR Apache-2.0 https://github.com/rust-lang/flate2-rs -Copyright (c) 2014 Alex Crichton +Copyright (c) 2014-2025 Alex Crichton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -2358,7 +2352,7 @@ SOFTWARE. form_urlencoded 1.2.1 - MIT OR Apache-2.0 https://github.com/servo/rust-url -Copyright (c) 2013-2022 The rust-url developers +Copyright (c) 2013-2025 The rust-url developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -3195,16 +3189,6 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [deps-image]: https://deps.rs/repo/github/RustCrypto/utils/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/utils -[msrv-1.39]: https://img.shields.io/badge/rustc-1.39.0+-blue.svg -[msrv-1.40]: https://img.shields.io/badge/rustc-1.40.0+-blue.svg -[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg -[msrv-1.45]: https://img.shields.io/badge/rustc-1.45.0+-blue.svg -[msrv-1.51]: https://img.shields.io/badge/rustc-1.51.0+-blue.svg -[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg -[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg -[msrv-1.59]: https://img.shields.io/badge/rustc-1.59.0+-blue.svg -[msrv-1.60]: https://img.shields.io/badge/rustc-1.60.0+-blue.svg - [//]: # (crates) [`blobby`]: ./blobby @@ -3248,9 +3232,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg [deps-image]: https://deps.rs/repo/github/RustCrypto/MACs/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/MACs -[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg -[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg -[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg +[msrv-1.85]: https://img.shields.io/badge/rustc-1.85.0+-blue.svg [//]: # (crates) @@ -3314,7 +3296,7 @@ https://github.com/hyperium/http-body The MIT License (MIT) -Copyright (c) 2019-2024 Sean McArthur & Hyper Contributors +Copyright (c) 2019-2025 Sean McArthur & Hyper Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -3346,7 +3328,7 @@ DEALINGS IN THE SOFTWARE. httparse 1.8.0 - MIT/Apache-2.0 https://github.com/seanmonstar/httparse -Copyright (c) 2015-2024 Sean McArthur +Copyright (c) 2015-2025 Sean McArthur Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -3400,7 +3382,7 @@ https://github.com/hyperium/hyper The MIT License (MIT) -Copyright (c) 2014-2021 Sean McArthur +Copyright (c) 2014-2025 Sean McArthur Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -3513,10 +3495,572 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -idna 0.5.0 - MIT OR Apache-2.0 +icu_collections 1.5.0 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + +icu_locid 1.5.0 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + +icu_locid_transform 1.5.0 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + +icu_locid_transform_data 1.5.0 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + +icu_normalizer 1.5.0 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + +icu_normalizer_data 1.5.0 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + +icu_properties 1.5.1 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + +icu_properties_data 1.5.0 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + +icu_provider 1.5.0 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + +icu_provider_macros 1.5.0 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + +idna 1.0.3 - MIT OR Apache-2.0 https://github.com/servo/rust-url/ -Copyright (c) 2013-2022 The rust-url developers +Copyright (c) 2013-2025 The rust-url developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +idna_adapter 1.2.0 - Apache-2.0 OR MIT +https://github.com/hsivonen/idna_adapter + +Copyright (c) The rust-url developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -3628,16 +4172,6 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [deps-image]: https://deps.rs/repo/github/RustCrypto/utils/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/utils -[msrv-1.39]: https://img.shields.io/badge/rustc-1.39.0+-blue.svg -[msrv-1.40]: https://img.shields.io/badge/rustc-1.40.0+-blue.svg -[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg -[msrv-1.45]: https://img.shields.io/badge/rustc-1.45.0+-blue.svg -[msrv-1.51]: https://img.shields.io/badge/rustc-1.51.0+-blue.svg -[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg -[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg -[msrv-1.59]: https://img.shields.io/badge/rustc-1.59.0+-blue.svg -[msrv-1.60]: https://img.shields.io/badge/rustc-1.60.0+-blue.svg - [//]: # (crates) [`blobby`]: ./blobby @@ -3917,8 +4451,6 @@ DEALINGS IN THE SOFTWARE. lazy_static 1.4.0 - MIT/Apache-2.0 https://github.com/rust-lang-nursery/lazy-static.rs -Copyright (c) 2010 The Rust Project Developers - Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the @@ -4101,6 +4633,59 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- +litemap 0.7.4 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + lock_api 0.4.12 - MIT OR Apache-2.0 https://github.com/Amanieu/parking_lot @@ -4670,7 +5255,7 @@ DEALINGS IN THE SOFTWARE. num_cpus 1.16.0 - MIT OR Apache-2.0 https://github.com/seanmonstar/num_cpus -Copyright (c) 2015 +Copyright (c) 2015-2025 Sean McArthur Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -4814,7 +5399,7 @@ OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -openssl 0.10.66 - Apache-2.0 +openssl 0.10.70 - Apache-2.0 https://github.com/sfackler/rust-openssl Copyright 2011-2017 Google Inc. @@ -4893,7 +5478,7 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -openssl-sys 0.9.103 - MIT +openssl-sys 0.9.105 - MIT https://github.com/sfackler/rust-openssl The MIT License (MIT) @@ -6146,7 +6731,7 @@ DEALINGS IN THE SOFTWARE. percent-encoding 2.3.1 - MIT OR Apache-2.0 https://github.com/servo/rust-url/ -Copyright (c) 2013-2022 The rust-url developers +Copyright (c) 2013-2025 The rust-url developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -6860,7 +7445,7 @@ DEALINGS IN THE SOFTWARE. reqwest 0.11.27 - MIT OR Apache-2.0 https://github.com/seanmonstar/reqwest -Copyright (c) 2016 Sean McArthur +Copyright (c) 2016-2025 Sean McArthur Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -7713,7 +8298,7 @@ DEALINGS IN THE SOFTWARE. secret-service 3.0.1 - MIT OR Apache-2.0 https://github.com/hwchen/secret-service-rs -Copyright (c) 2016 secret-service Developers +Copyright (c) 2025 secret-service Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -7999,9 +8584,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg [deps-image]: https://deps.rs/repo/github/RustCrypto/hashes/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/hashes -[msrv-1.71]: https://img.shields.io/badge/rustc-1.71.0+-blue.svg -[msrv-1.72]: https://img.shields.io/badge/rustc-1.72.0+-blue.svg -[msrv-1.74]: https://img.shields.io/badge/rustc-1.74.0+-blue.svg +[msrv-1.85]: https://img.shields.io/badge/rustc-1.85.0+-blue.svg [//]: # (crates) @@ -8094,9 +8677,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg [deps-image]: https://deps.rs/repo/github/RustCrypto/hashes/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/hashes -[msrv-1.71]: https://img.shields.io/badge/rustc-1.71.0+-blue.svg -[msrv-1.72]: https://img.shields.io/badge/rustc-1.72.0+-blue.svg -[msrv-1.74]: https://img.shields.io/badge/rustc-1.74.0+-blue.svg +[msrv-1.85]: https://img.shields.io/badge/rustc-1.85.0+-blue.svg [//]: # (crates) @@ -8363,6 +8944,38 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- +stable_deref_trait 1.2.0 - MIT/Apache-2.0 +https://github.com/storyyeller/stable_deref_trait + +Copyright (c) 2017 Robert Grosse + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + static_assertions 1.1.0 - MIT OR Apache-2.0 https://github.com/nvzqz/static-assertions-rs @@ -8671,6 +9284,22 @@ Apache License --------------------------------------------------------- +synstructure 0.13.1 - MIT +https://github.com/mystor/synstructure + +The MIT License (MIT) + +Copyright 2016 Nika Layzell + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + sysinfo 0.29.11 - MIT https://github.com/GuillaumeGomez/sysinfo @@ -8939,42 +9568,55 @@ SOFTWARE. --------------------------------------------------------- -tinyvec 1.6.0 - Zlib OR Apache-2.0 OR MIT -https://github.com/Lokathor/tinyvec +tinystr 0.7.6 - Unicode-3.0 +https://github.com/unicode-org/icu4x -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +UNICODE LICENSE V3 -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +COPYRIGHT AND PERMISSION NOTICE -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------- +Copyright © 2020-2024 Unicode, Inc. ---------------------------------------------------------- +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. -tinyvec_macros 0.1.1 - MIT OR Apache-2.0 OR Zlib -https://github.com/Soveu/tinyvec_macros +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. -MIT License +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. -Copyright (c) 2020 Soveu +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +SPDX-License-Identifier: Unicode-3.0 -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. --------------------------------------------------------- --------------------------------------------------------- @@ -9347,7 +9989,7 @@ https://github.com/seanmonstar/try-lock The MIT License (MIT) -Copyright (c) 2018-2023 Sean McArthur +Copyright (c) 2018-2025 Sean McArthur Copyright (c) 2016 Alex Crichton Permission is hereby granted, free of charge, to any person obtaining a copy @@ -9462,10 +10104,8 @@ MIT License --------------------------------------------------------- -unicode-bidi 0.3.15 - MIT OR Apache-2.0 -https://github.com/servo/unicode-bidi - -Copyright (c) 2015 The Rust Project Developers +unicode-ident 1.0.12 - (MIT OR Apache-2.0) AND Unicode-DFS-2016 +https://github.com/dtolnay/unicode-ident Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -9494,8 +10134,10 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -unicode-ident 1.0.12 - (MIT OR Apache-2.0) AND Unicode-DFS-2016 -https://github.com/dtolnay/unicode-ident +unicode-width 0.1.12 - MIT OR Apache-2.0 +https://github.com/unicode-rs/unicode-width + +Copyright (c) 2015 The Rust Project Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -9524,8 +10166,8 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -unicode-normalization 0.1.23 - MIT/Apache-2.0 -https://github.com/unicode-rs/unicode-normalization +unicode-xid 0.2.4 - MIT OR Apache-2.0 +https://github.com/unicode-rs/unicode-xid Copyright (c) 2015 The Rust Project Developers @@ -9556,10 +10198,10 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -unicode-width 0.1.12 - MIT OR Apache-2.0 -https://github.com/unicode-rs/unicode-width +url 2.5.4 - MIT OR Apache-2.0 +https://github.com/servo/rust-url -Copyright (c) 2015 The Rust Project Developers +Copyright (c) 2013-2025 The rust-url developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -9588,10 +10230,37 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -unicode-xid 0.2.4 - MIT OR Apache-2.0 -https://github.com/unicode-rs/unicode-xid +urlencoding 2.1.3 - MIT +https://github.com/kornelski/rust_urlencoding -Copyright (c) 2015 The Rust Project Developers +The MIT License (MIT) + +© 2016 Bertram Truong +© 2021 Kornel Lesiński + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +utf-8 0.7.6 - MIT OR Apache-2.0 +https://github.com/SimonSapin/rust-utf8 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -9620,10 +10289,10 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -url 2.5.0 - MIT OR Apache-2.0 -https://github.com/servo/rust-url +utf16_iter 1.0.5 - Apache-2.0 OR MIT +https://github.com/hsivonen/utf16_iter -Copyright (c) 2013-2022 The rust-url developers +Copyright Mozilla Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -9652,37 +10321,10 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -urlencoding 2.1.3 - MIT -https://github.com/kornelski/rust_urlencoding - -The MIT License (MIT) - -© 2016 Bertram Truong -© 2021 Kornel Lesiński - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. ---------------------------------------------------------- - ---------------------------------------------------------- +utf8_iter 1.0.4 - Apache-2.0 OR MIT +https://github.com/hsivonen/utf8_iter -utf-8 0.7.6 - MIT OR Apache-2.0 -https://github.com/SimonSapin/rust-utf8 +Copyright Mozilla Foundation Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -10628,6 +11270,91 @@ THE SOFTWARE. --------------------------------------------------------- +write16 1.0.0 - Apache-2.0 OR MIT +https://github.com/hsivonen/write16 + +Copyright Mozilla Foundation + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + +writeable 0.5.5 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + xattr 1.3.1 - MIT/Apache-2.0 https://github.com/Stebalien/xattr @@ -10706,6 +11433,112 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI --------------------------------------------------------- +yoke 0.7.5 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + +yoke-derive 0.7.5 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + zbus 3.15.2 - MIT https://github.com/dbus2/zbus/ @@ -10808,6 +11641,112 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- +zerofrom 0.1.5 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + +zerofrom-derive 0.1.5 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + zeroize 1.7.0 - Apache-2.0 OR MIT https://github.com/RustCrypto/utils/tree/master/zeroize @@ -10830,16 +11769,6 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [deps-image]: https://deps.rs/repo/github/RustCrypto/utils/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/utils -[msrv-1.39]: https://img.shields.io/badge/rustc-1.39.0+-blue.svg -[msrv-1.40]: https://img.shields.io/badge/rustc-1.40.0+-blue.svg -[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg -[msrv-1.45]: https://img.shields.io/badge/rustc-1.45.0+-blue.svg -[msrv-1.51]: https://img.shields.io/badge/rustc-1.51.0+-blue.svg -[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg -[msrv-1.57]: https://img.shields.io/badge/rustc-1.57.0+-blue.svg -[msrv-1.59]: https://img.shields.io/badge/rustc-1.59.0+-blue.svg -[msrv-1.60]: https://img.shields.io/badge/rustc-1.60.0+-blue.svg - [//]: # (crates) [`blobby`]: ./blobby @@ -10862,6 +11791,112 @@ Unless you explicitly state otherwise, any contribution intentionally submitted --------------------------------------------------------- +zerovec 0.10.4 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + +zerovec-derive 0.10.3 - Unicode-3.0 +https://github.com/unicode-org/icu4x + +UNICODE LICENSE V3 + +COPYRIGHT AND PERMISSION NOTICE + +Copyright © 2020-2024 Unicode, Inc. + +NOTICE TO USER: Carefully read the following legal agreement. BY +DOWNLOADING, INSTALLING, COPYING OR OTHERWISE USING DATA FILES, AND/OR +SOFTWARE, YOU UNEQUIVOCALLY ACCEPT, AND AGREE TO BE BOUND BY, ALL OF THE +TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU DO NOT AGREE, DO NOT +DOWNLOAD, INSTALL, COPY, DISTRIBUTE OR USE THE DATA FILES OR SOFTWARE. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of data files and any associated documentation (the "Data Files") or +software and any associated documentation (the "Software") to deal in the +Data Files or Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, and/or sell +copies of the Data Files or Software, and to permit persons to whom the +Data Files or Software are furnished to do so, provided that either (a) +this copyright and permission notice appear with all copies of the Data +Files or Software, or (b) this copyright and permission notice appear in +associated Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF +THIRD PARTY RIGHTS. + +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE +BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, +OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THE DATA +FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in these Data Files or Software without prior written +authorization of the copyright holder. + +SPDX-License-Identifier: Unicode-3.0 + +— + +Portions of ICU4X may have been adapted from ICU4C and/or ICU4J. +ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation and others. +--------------------------------------------------------- + +--------------------------------------------------------- + zip 0.6.6 - MIT https://github.com/zip-rs/zip2 diff --git a/cli/build.rs b/cli/build.rs index 16078e34044b..64a05e88a046 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -133,7 +133,7 @@ fn apply_build_environment_variables() { } else { PathBuf::from_str(&v).unwrap() }; - println!("cargo:warning=loading product.json from <{:?}>", path); + println!("cargo:warning=loading product.json from <{path:?}>"); apply_build_from_product_json(&path); } diff --git a/cli/src/auth.rs b/cli/src/auth.rs index 51942c96c755..d4d62a8bf104 100644 --- a/cli/src/auth.rs +++ b/cli/src/auth.rs @@ -93,10 +93,9 @@ impl AuthProvider { pub fn get_default_scopes(&self) -> String { match self { - AuthProvider::Microsoft => format!( - "{}/.default+offline_access+profile+openid", - PROD_FIRST_PARTY_APP_ID - ), + AuthProvider::Microsoft => { + format!("{PROD_FIRST_PARTY_APP_ID}/.default+offline_access+profile+openid") + } AuthProvider::Github => "read:user+read:org".to_string(), } } @@ -105,7 +104,7 @@ impl AuthProvider { #[derive(Serialize, Deserialize, Debug, Clone)] pub struct StoredCredential { #[serde(rename = "p")] - provider: AuthProvider, + pub(crate) provider: AuthProvider, #[serde(rename = "a")] access_token: String, #[serde(rename = "r")] @@ -122,7 +121,7 @@ async fn get_github_user( ) -> Result { client .get(GH_USER_ENDPOINT) - .header("Authorization", format!("token {}", access_token)) + .header("Authorization", format!("token {access_token}")) .header("User-Agent", get_default_user_agent()) .send() .await @@ -666,12 +665,12 @@ impl Auth { .into(); } - return StatusError { + StatusError { body: String::from_utf8_lossy(&body).to_string(), status_code, url: url.to_string(), } - .into(); + .into() } /// Implements the device code flow, returning the credentials upon success. async fn do_device_code_flow(&self) -> Result { @@ -690,7 +689,7 @@ impl Auth { } let provider = prompt_options( - format!("How would you like to log in to {}?", PRODUCT_NAME_LONG), + format!("How would you like to log in to {PRODUCT_NAME_LONG}?"), &[AuthProvider::Microsoft, AuthProvider::Github], )?; diff --git a/cli/src/bin/code/legacy_args.rs b/cli/src/bin/code/legacy_args.rs index 0bd92c92fd3d..b61611a80579 100644 --- a/cli/src/bin/code/legacy_args.rs +++ b/cli/src/bin/code/legacy_args.rs @@ -85,6 +85,8 @@ pub fn try_parse_legacy( subcommand: ExtensionSubcommand::Install(InstallExtensionArgs { id_or_path: exts, pre_release: args.contains_key("pre-release"), + donot_include_pack_and_dependencies: args + .contains_key("do-not-include-pack-dependencies"), force: args.contains_key("force"), }), desktop_code_options, diff --git a/cli/src/bin/code/main.rs b/cli/src/bin/code/main.rs index b104976b9ab3..b73d0aa885b0 100644 --- a/cli/src/bin/code/main.rs +++ b/cli/src/bin/code/main.rs @@ -103,7 +103,7 @@ async fn main() -> Result<(), std::convert::Infallible> { serve_web::serve_web(context!(), sw_args).await } - Some(args::Commands::Tunnel(tunnel_args)) => match tunnel_args.subcommand { + Some(args::Commands::Tunnel(mut tunnel_args)) => match tunnel_args.subcommand.take() { Some(args::TunnelSubcommand::Prune) => tunnels::prune(context!()).await, Some(args::TunnelSubcommand::Unregister) => tunnels::unregister(context!()).await, Some(args::TunnelSubcommand::Kill) => tunnels::kill(context!()).await, @@ -116,7 +116,7 @@ async fn main() -> Result<(), std::convert::Infallible> { tunnels::user(context!(), user_command).await } Some(args::TunnelSubcommand::Service(service_args)) => { - tunnels::service(context_no_logger(), service_args).await + tunnels::service(context_no_logger(), tunnel_args, service_args).await } Some(args::TunnelSubcommand::ForwardInternal(forward_args)) => { tunnels::forward(context_no_logger(), forward_args).await @@ -153,7 +153,7 @@ fn print_and_exit(err: E) -> ! where E: std::fmt::Display, { - log::emit(log::Level::Error, "", &format!("{}", err)); + log::emit(log::Level::Error, "", &format!("{err}")); std::process::exit(1); } diff --git a/cli/src/commands/args.rs b/cli/src/commands/args.rs index 7a8cf1f4f62b..52020627ca59 100644 --- a/cli/src/commands/args.rs +++ b/cli/src/commands/args.rs @@ -216,12 +216,6 @@ pub struct ServeWebArgs { /// Specifies the directory that server data is kept in. #[clap(long)] pub server_data_dir: Option, - /// Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code. - #[clap(long)] - pub user_data_dir: Option, - /// Set the root path for extensions. - #[clap(long)] - pub extensions_dir: Option, } #[derive(Args, Debug, Clone)] @@ -283,23 +277,26 @@ impl ExtensionSubcommand { target.push("--show-versions".to_string()); } if let Some(category) = &args.category { - target.push(format!("--category={}", category)); + target.push(format!("--category={category}")); } } ExtensionSubcommand::Install(args) => { for id in args.id_or_path.iter() { - target.push(format!("--install-extension={}", id)); + target.push(format!("--install-extension={id}")); } if args.pre_release { target.push("--pre-release".to_string()); } + if args.donot_include_pack_and_dependencies { + target.push("do-not-include-pack-dependencies".to_string()); + } if args.force { target.push("--force".to_string()); } } ExtensionSubcommand::Uninstall(args) => { for id in args.id.iter() { - target.push(format!("--uninstall-extension={}", id)); + target.push(format!("--uninstall-extension={id}")); } } ExtensionSubcommand::Update => { @@ -333,6 +330,10 @@ pub struct InstallExtensionArgs { #[clap(long)] pub pre_release: bool, + /// Don't include installing pack and dependencies of the extension + #[clap(long)] + pub donot_include_pack_and_dependencies: bool, + /// Update to the latest version of the extension if it's already installed. #[clap(long)] pub force: bool, @@ -439,11 +440,11 @@ impl EditorOptions { target.push("--wait".to_string()); } if let Some(locale) = &self.locale { - target.push(format!("--locale={}", locale)); + target.push(format!("--locale={locale}")); } if !self.enable_proposed_api.is_empty() { for id in self.enable_proposed_api.iter() { - target.push(format!("--enable-proposed-api={}", id)); + target.push(format!("--enable-proposed-api={id}")); } } self.code_options.add_code_args(target); @@ -480,10 +481,10 @@ pub struct OutputFormatOptions { impl DesktopCodeOptions { pub fn add_code_args(&self, target: &mut Vec) { if let Some(extensions_dir) = &self.extensions_dir { - target.push(format!("--extensions-dir={}", extensions_dir)); + target.push(format!("--extensions-dir={extensions_dir}")); } if let Some(user_data_dir) = &self.user_data_dir { - target.push(format!("--user-data-dir={}", user_data_dir)); + target.push(format!("--user-data-dir={user_data_dir}")); } } } @@ -522,13 +523,13 @@ impl GlobalOptions { target.push("--verbose".to_string()); } if let Some(log) = self.log { - target.push(format!("--log={}", log)); + target.push(format!("--log={log}")); } if self.disable_telemetry { target.push("--disable-telemetry".to_string()); } if let Some(telemetry_level) = &self.telemetry_level { - target.push(format!("--telemetry-level={}", telemetry_level)); + target.push(format!("--telemetry-level={telemetry_level}")); } } } @@ -578,16 +579,16 @@ impl EditorTroubleshooting { target.push("--disable-extensions".to_string()); } for id in self.disable_extension.iter() { - target.push(format!("--disable-extension={}", id)); + target.push(format!("--disable-extension={id}")); } if let Some(sync) = &self.sync { - target.push(format!("--sync={}", sync)); + target.push(format!("--sync={sync}")); } if let Some(port) = &self.inspect_extensions { - target.push(format!("--inspect-extensions={}", port)); + target.push(format!("--inspect-extensions={port}")); } if let Some(port) = &self.inspect_brk_extensions { - target.push(format!("--inspect-brk-extensions={}", port)); + target.push(format!("--inspect-brk-extensions={port}")); } if self.disable_gpu { target.push("--disable-gpu".to_string()); diff --git a/cli/src/commands/serve_web.rs b/cli/src/commands/serve_web.rs index 4acb9a18c739..2ddefe130833 100644 --- a/cli/src/commands/serve_web.rs +++ b/cli/src/commands/serve_web.rs @@ -125,7 +125,7 @@ pub async fn serve_web(ctx: CommandContext, mut args: ServeWebArgs) -> Result Result Option<(Release, Str let (quality_commit, remaining) = path.split_at(i); let (quality, commit) = quality_commit.split_at(quality_commit_sep); + let commit = &commit[1..]; if !is_commit_hash(commit) { return None; @@ -445,14 +443,14 @@ mod response { pub fn connection_err(err: hyper::Error) -> Response { Response::builder() .status(503) - .body(Body::from(format!("Error connecting to server: {:?}", err))) + .body(Body::from(format!("Error connecting to server: {err:?}"))) .unwrap() } pub fn code_err(err: CodeError) -> Response { Response::builder() .status(500) - .body(Body::from(format!("Error serving request: {}", err))) + .body(Body::from(format!("Error serving request: {err}"))) .unwrap() } @@ -550,9 +548,11 @@ impl ConnectionManager { Err(_) => Quality::Stable, }); + let now = Instant::now(); let latest_version = tokio::sync::Mutex::new(cache.get().first().map(|latest_commit| { ( - Instant::now() - Duration::from_secs(RELEASE_CHECK_INTERVAL), + now.checked_sub(Duration::from_secs(RELEASE_CHECK_INTERVAL)) + .unwrap_or(now), // handle 0-ish instants, #233155 Release { name: String::from("0.0.0"), // Version information not stored on cache commit: latest_commit.clone(), @@ -778,14 +778,6 @@ impl ConnectionManager { cmd.arg("--server-data-dir"); cmd.arg(a); } - if let Some(a) = &args.args.user_data_dir { - cmd.arg("--user-data-dir"); - cmd.arg(a); - } - if let Some(a) = &args.args.extensions_dir { - cmd.arg("--extensions-dir"); - cmd.arg(a); - } if args.args.without_connection_token { cmd.arg("--without-connection-token"); } diff --git a/cli/src/commands/tunnels.rs b/cli/src/commands/tunnels.rs index dc4206e83160..06d8dc842262 100644 --- a/cli/src/commands/tunnels.rs +++ b/cli/src/commands/tunnels.rs @@ -21,7 +21,7 @@ use tokio::{ use super::{ args::{ - AuthProvider, CliCore, CommandShellArgs, ExistingTunnelArgs, TunnelForwardArgs, + AuthProvider, CliCore, CommandShellArgs, ExistingTunnelArgs, TunnelArgs, TunnelForwardArgs, TunnelRenameArgs, TunnelServeArgs, TunnelServiceSubCommands, TunnelUserSubCommands, }, CommandContext, @@ -104,12 +104,16 @@ fn fulfill_existing_tunnel_args( } struct TunnelServiceContainer { - args: CliCore, + core_args: CliCore, + tunnel_args: TunnelArgs, } impl TunnelServiceContainer { - fn new(args: CliCore) -> Self { - Self { args } + fn new(core_args: CliCore, tunnel_args: TunnelArgs) -> Self { + Self { + core_args, + tunnel_args, + } } } @@ -120,7 +124,8 @@ impl ServiceContainer for TunnelServiceContainer { log: log::Logger, launcher_paths: LauncherPaths, ) -> Result<(), AnyError> { - let csa = (&self.args).into(); + let mut csa = (&self.core_args).into(); + self.tunnel_args.serve_args.server_args.apply_to(&mut csa); serve_with_csa( launcher_paths, log, @@ -213,7 +218,7 @@ pub async fn command_shell(ctx: CommandContext, args: CommandShellArgs) -> Resul match socket { Ok((read, write)) => servers.push(serve_stream(read, write, params.clone())), Err(e) => { - error!(params.log, &format!("Error accepting connection: {}", e)); + error!(params.log, &format!("Error accepting connection: {e}")); return Ok(1); } } @@ -242,8 +247,27 @@ async fn is_port_available(host: IpAddr, port: u16) -> bool { .is_ok() } +fn make_service_args<'a: 'c, 'b: 'c, 'c>( + root_path: &'a str, + tunnel_args: &'b TunnelArgs, +) -> Vec<&'c str> { + let mut args = ["--verbose", "--cli-data-dir", root_path, "tunnel"].to_vec(); + + if let Some(d) = tunnel_args.serve_args.server_args.extensions_dir.as_ref() { + args.extend_from_slice(&["--extensions-dir", d]); + } + if let Some(d) = tunnel_args.serve_args.server_args.server_data_dir.as_ref() { + args.extend_from_slice(&["--server-data-dir", d]); + } + + args.extend_from_slice(&["service", "internal-run"]); + + args +} + pub async fn service( ctx: CommandContext, + tunnel_args: TunnelArgs, service_args: TunnelServiceSubCommands, ) -> Result { let manager = create_service_manager(ctx.log.clone(), &ctx.paths); @@ -265,21 +289,11 @@ pub async fn service( legal::require_consent(&ctx.paths, args.accept_server_license_terms)?; let current_exe = canonical_exe().map_err(|e| wrap(e, "could not get current exe"))?; + let root_path = ctx.paths.root().as_os_str().to_string_lossy(); + let args = make_service_args(&root_path, &tunnel_args); - manager - .register( - current_exe, - &[ - "--verbose", - "--cli-data-dir", - ctx.paths.root().as_os_str().to_string_lossy().as_ref(), - "tunnel", - "service", - "internal-run", - ], - ) - .await?; - ctx.log.result(format!("Service successfully installed! You can use `{} tunnel service log` to monitor it, and `{} tunnel service uninstall` to remove it.", APPLICATION_NAME, APPLICATION_NAME)); + manager.register(current_exe, &args).await?; + ctx.log.result(format!("Service successfully installed! You can use `{APPLICATION_NAME} tunnel service log` to monitor it, and `{APPLICATION_NAME} tunnel service uninstall` to remove it.")); } TunnelServiceSubCommands::Uninstall => { manager.unregister().await?; @@ -289,7 +303,10 @@ pub async fn service( } TunnelServiceSubCommands::InternalRun => { manager - .run(ctx.paths.clone(), TunnelServiceContainer::new(ctx.args)) + .run( + ctx.paths.clone(), + TunnelServiceContainer::new(ctx.args, tunnel_args), + ) .await?; } } @@ -312,8 +329,8 @@ pub async fn user(ctx: CommandContext, user_args: TunnelUserSubCommands) -> Resu auth.clear_credentials()?; } TunnelUserSubCommands::Show => { - if let Ok(Some(_)) = auth.get_current_credential() { - ctx.log.result("logged in"); + if let Ok(Some(sc)) = auth.get_current_credential() { + ctx.log.result(format!("logged in with provider {}", sc.provider)); } else { ctx.log.result("not logged in"); return Ok(1); diff --git a/cli/src/commands/update.rs b/cli/src/commands/update.rs index 30c918f7e854..c53d9e30218e 100644 --- a/cli/src/commands/update.rs +++ b/cli/src/commands/update.rs @@ -36,7 +36,7 @@ pub async fn update(ctx: CommandContext, args: StandaloneUpdateArgs) -> Result Result Result { let vm = CodeVersionManager::new(ctx.log.clone(), &ctx.paths, platform); let version = vm.get_preferred_version(); - println!("Current quality: {}", version); + println!("Current quality: {version}"); match vm.try_get_entrypoint(&version).await { Some(p) => println!("Installation path: {}", p.display()), None => println!("No existing installation found"), diff --git a/cli/src/desktop/version_manager.rs b/cli/src/desktop/version_manager.rs index f2a093b5c776..f9f307365c88 100644 --- a/cli/src/desktop/version_manager.rs +++ b/cli/src/desktop/version_manager.rs @@ -41,13 +41,13 @@ impl RequestedVersion { pub fn get_command(&self) -> String { match self { RequestedVersion::Default => { - format!("code version use {}", QUALITY) + format!("code version use {QUALITY}") } RequestedVersion::Commit(commit) => { - format!("code version use {}/{}", QUALITY, commit) + format!("code version use {QUALITY}/{commit}") } RequestedVersion::Path(path) => { - format!("code version use {}", path) + format!("code version use {path}") } } } @@ -57,12 +57,12 @@ impl std::fmt::Display for RequestedVersion { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { RequestedVersion::Default => { - write!(f, "{}", QUALITY) + write!(f, "{QUALITY}") } RequestedVersion::Commit(commit) => { - write!(f, "{}/{}", QUALITY, commit) + write!(f, "{QUALITY}/{commit}") } - RequestedVersion::Path(path) => write!(f, "{}", path), + RequestedVersion::Path(path) => write!(f, "{path}"), } } } @@ -222,17 +222,14 @@ impl CodeVersionManager { /// Shows a nice UI prompt to users asking them if they want to install the /// requested version. pub fn prompt_to_install(version: &RequestedVersion) { - println!( - "No installation of {} {} was found.", - QUALITYLESS_PRODUCT_NAME, version - ); + println!("No installation of {QUALITYLESS_PRODUCT_NAME} {version} was found."); if let RequestedVersion::Default = version { if let Some(uri) = PRODUCT_DOWNLOAD_URL { // todo: on some platforms, we may be able to help automate installation. For example, // we can unzip the app ourselves on macOS and on windows we can download and spawn the GUI installer #[cfg(target_os = "linux")] - println!("Install it from your system's package manager or {}, restart your shell, and try again.", uri); + println!("Install it from your system's package manager or {uri}, restart your shell, and try again."); #[cfg(target_os = "macos")] println!("Download and unzip it from {} and try again.", uri); #[cfg(target_os = "windows")] @@ -447,7 +444,7 @@ mod tests { // developers can run this test and debug output manually; VS Code will not // be installed in CI, so the test only makes sure it doesn't error out let result = detect_installed_program(&log::Logger::test()); - println!("result: {:?}", result); + println!("result: {result:?}"); assert!(result.is_ok()); } diff --git a/cli/src/download_cache.rs b/cli/src/download_cache.rs index cd02b02d75a5..87ca1924a798 100644 --- a/cli/src/download_cache.rs +++ b/cli/src/download_cache.rs @@ -90,7 +90,7 @@ impl DownloadCache { return Ok(target_dir); } - let temp_dir = self.path.join(format!("{}{}", name, STAGING_SUFFIX)); + let temp_dir = self.path.join(format!("{name}{STAGING_SUFFIX}")); let _ = remove_dir_all(&temp_dir).await; // cleanup any existing create_dir_all(&temp_dir).map_err(|e| wrap(e, "error creating server directory"))?; diff --git a/cli/src/log.rs b/cli/src/log.rs index 1180f2c82c26..f58f49b21764 100644 --- a/cli/src/log.rs +++ b/cli/src/log.rs @@ -149,7 +149,7 @@ impl LogSink for StdioLogSink { } fn write_result(&self, message: &str) { - println!("{}", message); + println!("{message}"); } } @@ -214,7 +214,7 @@ impl Logger { } pub fn span(&self, name: &str) -> SpanBuilder { - self.tracer.span_builder(format!("serverlauncher/{}", name)) + self.tracer.span_builder(format!("serverlauncher/{name}")) } pub fn tracer(&self) -> &Tracer { @@ -237,8 +237,8 @@ impl Logger { pub fn prefixed(&self, prefix: &str) -> Logger { Logger { prefix: Some(match &self.prefix { - Some(p) => format!("{}{} ", p, prefix), - None => format!("{} ", prefix), + Some(p) => format!("{p}{prefix} "), + None => format!("{prefix} "), }), ..self.clone() } @@ -282,7 +282,7 @@ pub struct DownloadLogger<'a> { logger: &'a Logger, } -impl<'a> crate::util::io::ReportCopyProgress for DownloadLogger<'a> { +impl crate::util::io::ReportCopyProgress for DownloadLogger<'_> { fn report_progress(&mut self, bytes_so_far: u64, total_bytes: u64) { if total_bytes > 0 { self.logger.emit( @@ -312,22 +312,19 @@ fn format(level: Level, prefix: &str, message: &str, use_colors: bool) -> String if use_colors { if let Some(c) = level.color_code() { - return format!( - "\x1b[2m[{}]\x1b[0m {}{}\x1b[0m {}{}\n", - timestamp, c, name, prefix, message - ); + return format!("\x1b[2m[{timestamp}]\x1b[0m {c}{name}\x1b[0m {prefix}{message}\n"); } } - format!("[{}] {} {}{}\n", timestamp, name, prefix, message) + format!("[{timestamp}] {name} {prefix}{message}\n") } pub fn emit(level: Level, prefix: &str, message: &str) { let line = format(level, prefix, message, *COLORS_ENABLED); if level == Level::Trace && *COLORS_ENABLED { - print!("\x1b[2m{}\x1b[0m", line); + print!("\x1b[2m{line}\x1b[0m"); } else { - print!("{}", line); + print!("{line}"); } } diff --git a/cli/src/options.rs b/cli/src/options.rs index e37a718a49a4..7d152c0e1ecf 100644 --- a/cli/src/options.rs +++ b/cli/src/options.rs @@ -70,8 +70,7 @@ impl TryFrom<&str> for Quality { "insiders" | "insider" => Ok(Quality::Insiders), "exploration" => Ok(Quality::Exploration), _ => Err(format!( - "Unknown quality: {}. Must be one of stable, insiders, or exploration.", - s + "Unknown quality: {s}. Must be one of stable, insiders, or exploration." )), } } diff --git a/cli/src/rpc.rs b/cli/src/rpc.rs index d48d777a5dcc..911cb4ccac0d 100644 --- a/cli/src/rpc.rs +++ b/cli/src/rpc.rs @@ -105,7 +105,7 @@ impl RpcMethodBuilder { F: Fn(P, &C) -> Result + Send + Sync + 'static, { if self.methods.contains_key(method_name) { - panic!("Method already registered: {}", method_name); + panic!("Method already registered: {method_name}"); } let serial = self.serializer.clone(); @@ -121,7 +121,7 @@ impl RpcMethodBuilder { id, error: ResponseError { code: 0, - message: format!("{:?}", err), + message: format!("{err:?}"), }, }) }) @@ -135,7 +135,7 @@ impl RpcMethodBuilder { id, error: ResponseError { code: -1, - message: format!("{:?}", err), + message: format!("{err:?}"), }, }) }), @@ -165,7 +165,7 @@ impl RpcMethodBuilder { id, error: ResponseError { code: 0, - message: format!("{:?}", err), + message: format!("{err:?}"), }, }) })) @@ -186,7 +186,7 @@ impl RpcMethodBuilder { id, error: ResponseError { code: -1, - message: format!("{:?}", err), + message: format!("{err:?}"), }, }) }), @@ -226,7 +226,7 @@ impl RpcMethodBuilder { id, error: ResponseError { code: 0, - message: format!("{:?}", err), + message: format!("{err:?}"), }, }) })) @@ -259,7 +259,7 @@ impl RpcMethodBuilder { id, error: ResponseError { code: -1, - message: format!("{:?}", err), + message: format!("{err:?}"), }, }) }), @@ -431,7 +431,7 @@ impl RpcDispatcher { id, error: ResponseError { code: -1, - message: format!("Method not found: {}", method_name), + message: format!("Method not found: {method_name}"), }, }) })), diff --git a/cli/src/self_update.rs b/cli/src/self_update.rs index 936c6627ac0d..45d661e5af98 100644 --- a/cli/src/self_update.rs +++ b/cli/src/self_update.rs @@ -122,7 +122,7 @@ fn validate_cli_is_good(exe_path: &Path) -> Result<(), AnyError> { let o = new_std_command(exe_path) .args(["--version"]) .output() - .map_err(|e| CorruptDownload(format!("could not execute new binary, aborting: {}", e)))?; + .map_err(|e| CorruptDownload(format!("could not execute new binary, aborting: {e}")))?; if !o.status.success() { let msg = format!( diff --git a/cli/src/state.rs b/cli/src/state.rs index 534c15567639..5bc655ef2c17 100644 --- a/cli/src/state.rs +++ b/cli/src/state.rs @@ -155,17 +155,14 @@ impl LauncherPaths { if let Err(e) = std::fs::rename(&old_dir, &new_dir) { // no logger exists at this point in the lifecycle, so just log to stderr - eprintln!( - "Failed to migrate old CLI data directory, will create a new one ({})", - e - ); + eprintln!("Failed to migrate old CLI data directory, will create a new one ({e})"); } Self::new_for_path(new_dir) } pub fn new(root: Option) -> Result { - let root = root.unwrap_or_else(|| format!("~/{}/cli", DEFAULT_DATA_PARENT_DIR)); + let root = root.unwrap_or_else(|| format!("~/{DEFAULT_DATA_PARENT_DIR}/cli")); let mut replaced = root.to_owned(); for token in HOME_DIR_ALTS { if root.contains(token) { diff --git a/cli/src/tunnels/code_server.rs b/cli/src/tunnels/code_server.rs index 465f6e24230c..cf00bc428358 100644 --- a/cli/src/tunnels/code_server.rs +++ b/cli/src/tunnels/code_server.rs @@ -67,6 +67,7 @@ pub struct CodeServerArgs { pub show_versions: bool, pub category: Option, pub pre_release: bool, + pub donot_include_pack_and_dependencies: bool, pub force: bool, pub start_server: bool, // connection tokens @@ -91,21 +92,21 @@ impl CodeServerArgs { pub fn command_arguments(&self) -> Vec { let mut args = Vec::new(); if let Some(i) = &self.socket_path { - args.push(format!("--socket-path={}", i)); + args.push(format!("--socket-path={i}")); } else { if let Some(i) = &self.host { - args.push(format!("--host={}", i)); + args.push(format!("--host={i}")); } if let Some(i) = &self.port { - args.push(format!("--port={}", i)); + args.push(format!("--port={i}")); } } if let Some(i) = &self.connection_token { - args.push(format!("--connection-token={}", i)); + args.push(format!("--connection-token={i}")); } if let Some(i) = &self.connection_token_file { - args.push(format!("--connection-token-file={}", i)); + args.push(format!("--connection-token-file={i}")); } if self.without_connection_token { args.push(String::from("--without-connection-token")); @@ -114,14 +115,14 @@ impl CodeServerArgs { args.push(String::from("--accept-server-license-terms")); } if let Some(i) = self.telemetry_level { - args.push(format!("--telemetry-level={}", i)); + args.push(format!("--telemetry-level={i}")); } if let Some(i) = self.log { - args.push(format!("--log={}", i)); + args.push(format!("--log={i}")); } for extension in &self.install_extensions { - args.push(format!("--install-extension={}", extension)); + args.push(format!("--install-extension={extension}")); } if !&self.install_extensions.is_empty() { if self.pre_release { @@ -132,7 +133,7 @@ impl CodeServerArgs { } } for extension in &self.uninstall_extensions { - args.push(format!("--uninstall-extension={}", extension)); + args.push(format!("--uninstall-extension={extension}")); } if self.update_extensions { args.push(String::from("--update-extensions")); @@ -143,14 +144,14 @@ impl CodeServerArgs { args.push(String::from("--show-versions")); } if let Some(i) = &self.category { - args.push(format!("--category={}", i)); + args.push(format!("--category={i}")); } } if let Some(d) = &self.server_data_dir { - args.push(format!("--server-data-dir={}", d)); + args.push(format!("--server-data-dir={d}")); } if let Some(d) = &self.extensions_dir { - args.push(format!("--extensions-dir={}", d)); + args.push(format!("--extensions-dir={d}")); } if self.start_server { args.push(String::from("--start-server")); @@ -487,7 +488,7 @@ impl<'a> ServerBuilder<'a> { let mut cmd = self.get_base_command(); cmd.arg("--start-server") .arg("--enable-remote-auto-shutdown") - .arg(format!("--port={}", port)); + .arg(format!("--port={port}")); let child = self.spawn_server_process(cmd).await?; let log_file = self.get_logfile()?; @@ -503,7 +504,7 @@ impl<'a> ServerBuilder<'a> { } Ok(Err(s)) => { origin.kill().await; - return Err(CodeError::ServerUnexpectedExit(format!("{}", s)).into()); + return Err(CodeError::ServerUnexpectedExit(format!("{s}")).into()); } Ok(Ok(p)) => p, }; @@ -577,7 +578,7 @@ impl<'a> ServerBuilder<'a> { } Ok(Err(s)) => { origin.kill().await; - return Err(CodeError::ServerUnexpectedExit(format!("{}", s)).into()); + return Err(CodeError::ServerUnexpectedExit(format!("{s}")).into()); } Ok(Ok(socket)) => socket, }; @@ -616,7 +617,7 @@ impl<'a> ServerBuilder<'a> { .stderr(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped()) .spawn() - .map_err(|e| CodeError::ServerUnexpectedExit(format!("{}", e)))?; + .map_err(|e| CodeError::ServerUnexpectedExit(format!("{e}")))?; self.server_paths .write_pid(child.id().expect("expected server to have pid"))?; @@ -677,7 +678,7 @@ where f.write_all(b"\n")?; } if write_directly { - println!("{}", line); + println!("{line}"); } else { trace!(plog, line); } @@ -732,7 +733,7 @@ where } fn get_extensions_flag(extension_id: &str) -> String { - format!("--install-extension={}", extension_id) + format!("--install-extension={extension_id}") } /// A type that can be used to scan stdout from the VS Code server. Returns @@ -829,7 +830,7 @@ pub fn print_listening(log: &log::Logger, tunnel_name: &str) { } } - let message = &format!("\nOpen this link in your browser {}\n", addr); + let message = &format!("\nOpen this link in your browser {addr}\n"); log.result(message); } diff --git a/cli/src/tunnels/control_server.rs b/cli/src/tunnels/control_server.rs index dfb5e381179f..74fd247cfcde 100644 --- a/cli/src/tunnels/control_server.rs +++ b/cli/src/tunnels/control_server.rs @@ -566,7 +566,7 @@ async fn process_socket( { debug!(log, "closing socket reader: {}", e); socket_tx - .send(SocketSignal::CloseWith(CloseReason(format!("{}", e)))) + .send(SocketSignal::CloseWith(CloseReason(format!("{e}")))) .await .ok(); } @@ -1062,7 +1062,6 @@ fn handle_challenge_issue( let mut auth_state = auth_state.lock().unwrap(); if let AuthState::WaitingForChallenge(Some(s)) = &*auth_state { - println!("looking for token {}, got {:?}", s, params.token); match ¶ms.token { Some(t) if s != t => return Err(CodeError::AuthChallengeBadToken.into()), None => return Err(CodeError::AuthChallengeBadToken.into()), @@ -1192,7 +1191,7 @@ async fn handle_acquire_cli( let release = match params.commit_id { Some(commit) => Release { - name: format!("{} CLI", PRODUCT_NAME_LONG), + name: format!("{PRODUCT_NAME_LONG} CLI"), commit, platform: params.platform, quality: params.quality, diff --git a/cli/src/tunnels/dev_tunnels.rs b/cli/src/tunnels/dev_tunnels.rs index a964b4463844..0168ee60d896 100644 --- a/cli/src/tunnels/dev_tunnels.rs +++ b/cli/src/tunnels/dev_tunnels.rs @@ -65,7 +65,7 @@ mod tunnel_flags { flags |= IS_WSL_INSTALLED; } - format!("_flag{}", flags) + format!("_flag{flags}") } } @@ -282,8 +282,7 @@ fn get_host_token_from_tunnel(tunnel: &Tunnel) -> String { fn is_valid_name(name: &str) -> Result<(), InvalidTunnelName> { if name.len() > MAX_TUNNEL_NAME_LENGTH { return Err(InvalidTunnelName(format!( - "Names cannot be longer than {} characters. Please try a different name.", - MAX_TUNNEL_NAME_LENGTH + "Names cannot be longer than {MAX_TUNNEL_NAME_LENGTH} characters. Please try a different name." ))); } @@ -617,7 +616,7 @@ impl DevTunnels { Err(e) => { return Err(AnyError::from(TunnelCreationFailed( name.to_string(), - format!("{:?}", e), + format!("{e:?}"), ))) } Ok(t) => break t, @@ -789,7 +788,7 @@ impl DevTunnels { let mut placeholder_name = Self::get_placeholder_name(); if !is_name_free(&placeholder_name) { for i in 2.. { - let fixed_name = format!("{}{}", placeholder_name, i); + let fixed_name = format!("{placeholder_name}{i}"); if is_name_free(&fixed_name) { placeholder_name = fixed_name; break; diff --git a/cli/src/tunnels/nosleep_linux.rs b/cli/src/tunnels/nosleep_linux.rs index 818910fee8ec..0559c9abff83 100644 --- a/cli/src/tunnels/nosleep_linux.rs +++ b/cli/src/tunnels/nosleep_linux.rs @@ -64,8 +64,7 @@ impl SleepInhibitor { return Err(wrap( e2, format!( - "error requesting sleep inhibition, pminhibitor gave {}, screensaver gave", - e1 + "error requesting sleep inhibition, pminhibitor gave {e1}, screensaver gave" ), ) .into()); diff --git a/cli/src/tunnels/paths.rs b/cli/src/tunnels/paths.rs index a0cd43cd83c8..3d7d718a3cd0 100644 --- a/cli/src/tunnels/paths.rs +++ b/cli/src/tunnels/paths.rs @@ -64,7 +64,7 @@ impl ServerPaths { // VS Code Server pid pub fn write_pid(&self, pid: u32) -> Result<(), WrappedError> { - write(&self.pidfile, format!("{}", pid)).map_err(|e| { + write(&self.pidfile, format!("{pid}")).map_err(|e| { wrap( e, format!("error writing process id into {}", self.pidfile.display()), @@ -155,5 +155,5 @@ pub fn get_all_servers(lp: &LauncherPaths) -> Vec { } pub fn get_server_folder_name(quality: Quality, commit: &str) -> String { - format!("{}-{}", quality, commit) + format!("{quality}-{commit}") } diff --git a/cli/src/tunnels/service.rs b/cli/src/tunnels/service.rs index dba68f3b6141..66bdf7a8e63a 100644 --- a/cli/src/tunnels/service.rs +++ b/cli/src/tunnels/service.rs @@ -85,7 +85,7 @@ pub(crate) async fn tail_log_file(log_file: &Path) -> Result<(), AnyError> { let mut rx = tailf(file, 20); while let Some(line) = rx.recv().await { match line { - TailEvent::Line(l) => print!("{}", l), + TailEvent::Line(l) => print!("{l}"), TailEvent::Reset => println!("== Tunnel service restarted =="), TailEvent::Err(e) => return Err(wrap(e, "error reading log file").into()), } diff --git a/cli/src/tunnels/service_linux.rs b/cli/src/tunnels/service_linux.rs index 80599ba3c323..0a3e2df6ea2e 100644 --- a/cli/src/tunnels/service_linux.rs +++ b/cli/src/tunnels/service_linux.rs @@ -62,7 +62,7 @@ impl SystemdService { } fn service_name_string() -> String { - format!("{}-tunnel.service", APPLICATION_NAME) + format!("{APPLICATION_NAME}-tunnel.service") } } diff --git a/cli/src/tunnels/shutdown_signal.rs b/cli/src/tunnels/shutdown_signal.rs index 9914b33bb929..1193426abd15 100644 --- a/cli/src/tunnels/shutdown_signal.rs +++ b/cli/src/tunnels/shutdown_signal.rs @@ -28,7 +28,7 @@ impl fmt::Display for ShutdownSignal { match self { ShutdownSignal::CtrlC => write!(f, "Ctrl-C received"), ShutdownSignal::ParentProcessKilled(p) => { - write!(f, "Parent process {} no longer exists", p) + write!(f, "Parent process {p} no longer exists") } ShutdownSignal::ExeUninstalled => { write!(f, "Executable no longer exists") diff --git a/cli/src/tunnels/singleton_server.rs b/cli/src/tunnels/singleton_server.rs index 6189c06a493b..f183f935ce30 100644 --- a/cli/src/tunnels/singleton_server.rs +++ b/cli/src/tunnels/singleton_server.rs @@ -142,7 +142,7 @@ pub fn make_singleton_server( } } -pub async fn start_singleton_server<'a>( +pub async fn start_singleton_server( args: SingletonServerArgs<'_>, ) -> Result { let shutdown_rx = ShutdownRequest::create_rx([ @@ -239,7 +239,8 @@ impl BroadcastLogSink { fn replay_and_subscribe( &self, - ) -> ConcatReceivable, mpsc::UnboundedReceiver>, broadcast::Receiver>> { + ) -> ConcatReceivable, mpsc::UnboundedReceiver>, broadcast::Receiver>> + { let (log_replay_tx, log_replay_rx) = mpsc::unbounded_channel(); for log in self.recent.lock().unwrap().iter() { diff --git a/cli/src/util/command.rs b/cli/src/util/command.rs index fb9fb4f91d5a..19c17b749b2b 100644 --- a/cli/src/util/command.rs +++ b/cli/src/util/command.rs @@ -146,7 +146,7 @@ pub async fn kill_tree(process_id: u32) -> Result<(), CodeError> { .stdout(Stdio::piped()) .spawn() .map_err(|e| CodeError::CommandFailed { - command: format!("pgrep -P {}", parent_id), + command: format!("pgrep -P {parent_id}"), code: -1, output: e.to_string(), })?; diff --git a/cli/src/util/errors.rs b/cli/src/util/errors.rs index 67519d5437e7..b7ed029bb98d 100644 --- a/cli/src/util/errors.rs +++ b/cli/src/util/errors.rs @@ -41,7 +41,7 @@ impl From for WrappedError { "error requesting {}", e.url().map_or("", |u| u.as_str()) ), - original: format!("{}", e), + original: format!("{e}"), } } } @@ -53,7 +53,7 @@ where { WrappedError { message: message.into(), - original: format!("{:?}", original), + original: format!("{original:?}"), } } @@ -64,7 +64,7 @@ where { WrappedError { message: message.into(), - original: format!("{}", original), + original: format!("{original}"), } } @@ -93,10 +93,7 @@ impl StatusError { let body = res.text().await.map_err(|e| { wrap( e, - format!( - "failed to read response body on {} code from {}", - status_code, url - ), + format!("failed to read response body on {status_code} code from {url}"), ) })?; @@ -290,7 +287,7 @@ pub struct CannotForwardControlPort(); impl std::fmt::Display for CannotForwardControlPort { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Cannot forward or unforward port {}.", CONTROL_PORT) + write!(f, "Cannot forward or unforward port {CONTROL_PORT}.") } } @@ -308,7 +305,7 @@ pub struct ServiceAlreadyRegistered(); impl std::fmt::Display for ServiceAlreadyRegistered { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "Already registered the service. Run `{} tunnel service uninstall` to unregister it first", APPLICATION_NAME) + write!(f, "Already registered the service. Run `{APPLICATION_NAME} tunnel service uninstall` to unregister it first") } } @@ -434,7 +431,7 @@ impl Display for DbusConnectFailedError { str.push_str(&self.0); str.push('\n'); - write!(f, "{}", str) + write!(f, "{str}") } } diff --git a/cli/src/util/io.rs b/cli/src/util/io.rs index 93a7efbfdd93..4b8118a219f8 100644 --- a/cli/src/util/io.rs +++ b/cli/src/util/io.rs @@ -235,6 +235,7 @@ mod tests { .write(true) .read(true) .create(true) + .truncate(true) .open(&file_path) .unwrap(); @@ -271,6 +272,7 @@ mod tests { .write(true) .read(true) .create(true) + .truncate(true) .open(&file_path) .unwrap(); @@ -305,6 +307,7 @@ mod tests { .write(true) .read(true) .create(true) + .truncate(true) .open(&file_path) .unwrap(); let mut rng = rand::thread_rng(); @@ -313,7 +316,7 @@ mod tests { let base_line = "Elit ipsum cillum ex cillum. Adipisicing consequat cupidatat do proident ut in sunt Lorem ipsum tempor. Eiusmod ipsum Lorem labore exercitation sunt pariatur excepteur fugiat cillum velit cillum enim. Nisi Lorem cupidatat ad enim velit officia eiusmod esse tempor aliquip. Deserunt pariatur tempor in duis culpa esse sit nulla irure ullamco ipsum voluptate non laboris. Occaecat officia nulla officia mollit do aliquip reprehenderit ad incididunt."; for i in 0..100 { let line = format!("{}: {}", i, &base_line[..rng.gen_range(0..base_line.len())]); - writeln!(&mut read_file, "{}", line).unwrap(); + writeln!(&mut read_file, "{line}").unwrap(); written.push(line); } write!(&mut read_file, "partial line").unwrap(); diff --git a/cli/src/util/prereqs.rs b/cli/src/util/prereqs.rs index 0f49ab20887c..44c859772e38 100644 --- a/cli/src/util/prereqs.rs +++ b/cli/src/util/prereqs.rs @@ -15,24 +15,21 @@ use super::errors::CodeError; lazy_static! { static ref LDCONFIG_STDC_RE: Regex = Regex::new(r"libstdc\+\+.* => (.+)").unwrap(); - static ref LDD_VERSION_RE: BinRegex = BinRegex::new(r"^ldd.*(.+)\.(.+)\s").unwrap(); + static ref LDD_VERSION_RE: BinRegex = BinRegex::new(r"^ldd.*\s(\d+)\.(\d+)(?:\.(\d+))?\s").unwrap(); static ref GENERIC_VERSION_RE: Regex = Regex::new(r"^([0-9]+)\.([0-9]+)$").unwrap(); static ref LIBSTD_CXX_VERSION_RE: BinRegex = BinRegex::new(r"GLIBCXX_([0-9]+)\.([0-9]+)(?:\.([0-9]+))?").unwrap(); static ref MIN_LDD_VERSION: SimpleSemver = SimpleSemver::new(2, 28, 0); - static ref MIN_LEGACY_LDD_VERSION: SimpleSemver = SimpleSemver::new(2, 17, 0); } #[cfg(target_arch = "arm")] lazy_static! { static ref MIN_CXX_VERSION: SimpleSemver = SimpleSemver::new(3, 4, 26); - static ref MIN_LEGACY_CXX_VERSION: SimpleSemver = SimpleSemver::new(3, 4, 22); } #[cfg(not(target_arch = "arm"))] lazy_static! { static ref MIN_CXX_VERSION: SimpleSemver = SimpleSemver::new(3, 4, 25); - static ref MIN_LEGACY_CXX_VERSION: SimpleSemver = SimpleSemver::new(3, 4, 19); } const NIXOS_TEST_PATH: &str = "/etc/NIXOS"; @@ -74,12 +71,11 @@ impl PreReqChecker { } else { println!("!!! WARNING: Skipping server pre-requisite check !!!"); println!("!!! Server stability is not guaranteed. Proceed at your own risk. !!!"); - // Use the legacy server for #210029 (Ok(true), Ok(true)) }; match (&gnu_a, &gnu_b, is_nixos) { - (Ok(false), Ok(false), _) | (_, _, true) => { + (Ok(true), Ok(true), _) | (_, _, true) => { return Ok(if cfg!(target_arch = "x86_64") { Platform::LinuxX64 } else if cfg!(target_arch = "arm") { @@ -88,15 +84,6 @@ impl PreReqChecker { Platform::LinuxARM64 }); } - (Ok(_), Ok(_), _) => { - return Ok(if cfg!(target_arch = "x86_64") { - Platform::LinuxX64Legacy - } else if cfg!(target_arch = "arm") { - Platform::LinuxARM32Legacy - } else { - Platform::LinuxARM64Legacy - }); - } _ => {} }; @@ -121,7 +108,7 @@ impl PreReqChecker { let bullets = errors .iter() - .map(|e| format!(" - {}", e)) + .map(|e| format!(" - {e}")) .collect::>() .join("\n"); @@ -142,15 +129,14 @@ async fn check_musl_interpreter() -> Result<(), String> { if fs::metadata(MUSL_PATH).await.is_err() { return Err(format!( - "find {}, which is required to run the {} in musl environments", - MUSL_PATH, QUALITYLESS_SERVER_NAME + "find {MUSL_PATH}, which is required to run the {QUALITYLESS_SERVER_NAME} in musl environments" )); } Ok(()) } -/// Checks the glibc version, returns "true" if the legacy server is required. +/// Checks the glibc version, returns "true" if the default server is required. #[cfg(target_os = "linux")] async fn check_glibc_version() -> Result { #[cfg(target_env = "gnu")] @@ -170,8 +156,6 @@ async fn check_glibc_version() -> Result { if let Some(v) = version { return if v >= *MIN_LDD_VERSION { - Ok(false) - } else if v >= *MIN_LEGACY_LDD_VERSION { Ok(true) } else { Err(format!( @@ -193,10 +177,17 @@ async fn check_is_nixos() -> bool { /// Do not remove this check. /// Provides a way to skip the server glibc requirements check from -/// outside the install flow. A system process can create this -/// file before the server is downloaded and installed. +/// outside the install flow. +/// +/// 1) A system process can create this +/// file before the server is downloaded and installed. +/// +/// 2) An environment variable declared in host +/// that contains path to a glibc sysroot satisfying the +/// minimum requirements. #[cfg(not(windows))] pub async fn skip_requirements_check() -> bool { + std::env::var("VSCODE_SERVER_CUSTOM_GLIBC_LINKER").is_ok() || fs::metadata("/tmp/vscode-skip-server-requirements-check") .await .is_ok() @@ -207,7 +198,7 @@ pub async fn skip_requirements_check() -> bool { false } -/// Checks the glibc++ version, returns "true" if the legacy server is required. +/// Checks the glibc++ version, returns "true" if the default server is required. #[cfg(target_os = "linux")] async fn check_glibcxx_version() -> Result { let mut libstdc_path: Option = None; @@ -231,8 +222,7 @@ async fn check_glibcxx_version() -> Result { Some(path) => match fs::read(&path).await { Ok(contents) => check_for_sufficient_glibcxx_versions(contents), Err(e) => Err(format!( - "validate GLIBCXX version for GNU environments, but could not: {}", - e + "validate GLIBCXX version for GNU environments, but could not: {e}" )), }, None => Err("find libstdc++.so or ldconfig for GNU environments".to_owned()), @@ -252,10 +242,6 @@ fn check_for_sufficient_glibcxx_versions(contents: Vec) -> Result= &*MIN_CXX_VERSION { - return Ok(false); - } - - if max_version >= &*MIN_LEGACY_CXX_VERSION { return Ok(true); } } @@ -403,5 +389,18 @@ mod tests { extract_ldd_version(&actual), Some(SimpleSemver::new(2, 31, 0)), ); + + let actual2 = "ldd (GNU libc) 2.40.9000 + Copyright (C) 2024 Free Software Foundation, Inc. + This is free software; see the source for copying conditions. There is NO + warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + Written by Roland McGrath and Ulrich Drepper." + .to_owned() + .into_bytes(); + assert_eq!( + extract_ldd_version(&actual2), + Some(SimpleSemver::new(2, 40, 0)), + ); } + } diff --git a/cli/src/util/zipper.rs b/cli/src/util/zipper.rs index 5521fa42c542..f7b7ab9fe5ad 100644 --- a/cli/src/util/zipper.rs +++ b/cli/src/util/zipper.rs @@ -60,7 +60,7 @@ where } let mut file = archive .by_index(i) - .map_err(|e| wrap(e, format!("could not open zip entry {}", i)))?; + .map_err(|e| wrap(e, format!("could not open zip entry {i}")))?; let outpath: PathBuf = match file.enclosed_name() { Some(path) => { diff --git a/eslint.config.js b/eslint.config.js index 53a377bbde79..2b005721dee1 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -219,22 +219,17 @@ export default tseslint.config( { // Files should (only) be removed from the list they adopt the leak detector 'exclude': [ - 'src/vs/editor/contrib/codeAction/test/browser/codeActionModel.test.ts', 'src/vs/platform/configuration/test/common/configuration.test.ts', 'src/vs/platform/opener/test/common/opener.test.ts', 'src/vs/platform/registry/test/common/platform.test.ts', 'src/vs/platform/workspace/test/common/workspace.test.ts', 'src/vs/platform/workspaces/test/electron-main/workspaces.test.ts', - 'src/vs/workbench/api/test/browser/mainThreadConfiguration.test.ts', - 'src/vs/workbench/api/test/node/extHostTunnelService.test.ts', 'src/vs/workbench/contrib/bulkEdit/test/browser/bulkCellEdits.test.ts', 'src/vs/workbench/contrib/chat/test/common/chatWordCounter.test.ts', - 'src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts', 'src/vs/workbench/contrib/extensions/test/common/extensionQuery.test.ts', 'src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts', 'src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts', 'src/vs/workbench/contrib/tasks/test/common/problemMatcher.test.ts', - 'src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts', 'src/vs/workbench/services/commands/test/common/commandService.test.ts', 'src/vs/workbench/services/userActivity/test/browser/domActivityTracker.test.ts', 'src/vs/workbench/test/browser/quickAccess.test.ts' @@ -256,13 +251,20 @@ export default tseslint.config( 'local': pluginLocal, }, rules: { + 'no-restricted-syntax': [ + 'warn', + { + 'selector': `TSArrayType > TSUnionType`, + 'message': 'Use Array<...> for arrays of union types.' + }, + ], 'local/vscode-dts-create-func': 'warn', 'local/vscode-dts-literal-or-types': 'warn', 'local/vscode-dts-string-type-literals': 'warn', 'local/vscode-dts-interface-naming': 'warn', 'local/vscode-dts-cancellation': 'warn', + 'local/vscode-dts-use-export': 'warn', 'local/vscode-dts-use-thenable': 'warn', - 'local/vscode-dts-region-comments': 'warn', 'local/vscode-dts-vscode-in-comments': 'warn', 'local/vscode-dts-provider-naming': [ 'warn', @@ -367,6 +369,7 @@ export default tseslint.config( 'jsdoc/require-returns': 'warn' } }, + // common/browser layer { files: [ 'src/**/{common,browser}/**/*.ts' @@ -381,6 +384,38 @@ export default tseslint.config( 'local/code-amd-node-module': 'warn' } }, + // node/electron layer + { + files: [ + 'src/*.ts', + 'src/**/{node,electron-main,electron-utility}/**/*.ts' + ], + languageOptions: { + parser: tseslint.parser, + }, + plugins: { + 'local': pluginLocal, + }, + rules: { + 'no-restricted-globals': [ + 'warn', + 'name', + 'length', + 'event', + 'closed', + 'external', + 'status', + 'origin', + 'orientation', + 'context', + // Below are globals that are unsupported in ESM + '__dirname', + '__filename', + 'require' + ] + } + }, + // browser/electron-sandbox layer { files: [ 'src/**/{browser,electron-sandbox}/**/*.ts' @@ -698,6 +733,7 @@ export default tseslint.config( ] } }, + // electron-utility layer { files: [ 'src/**/electron-utility/**/*.ts' @@ -784,6 +820,7 @@ export default tseslint.config( 'string_decoder', 'tas-client-umd', 'tls', + 'undici-types', 'url', 'util', 'v8-inspect-profiler', @@ -792,6 +829,7 @@ export default tseslint.config( 'worker_threads', '@xterm/addon-clipboard', '@xterm/addon-image', + '@xterm/addon-ligatures', '@xterm/addon-search', '@xterm/addon-serialize', '@xterm/addon-unicode11', @@ -959,6 +997,7 @@ export default tseslint.config( { 'target': 'src/vs/workbench/api/~', 'restrictions': [ + '@c4312/eventsource-umd', 'vscode', 'vs/base/~', 'vs/base/parts/*/~', diff --git a/extensions/coffeescript/package.json b/extensions/coffeescript/package.json index 44f423c7dfc7..615138a280a9 100644 --- a/extensions/coffeescript/package.json +++ b/extensions/coffeescript/package.json @@ -49,7 +49,8 @@ ], "configurationDefaults": { "[coffeescript]": { - "diffEditor.ignoreTrimWhitespace": false + "diffEditor.ignoreTrimWhitespace": false, + "editor.defaultColorDecorators": "never" } } }, diff --git a/extensions/configuration-editing/package-lock.json b/extensions/configuration-editing/package-lock.json index 199dcf7f7554..6619002d1209 100644 --- a/extensions/configuration-editing/package-lock.json +++ b/extensions/configuration-editing/package-lock.json @@ -9,250 +9,208 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@octokit/rest": "19.0.4", - "jsonc-parser": "^3.2.0", + "@octokit/rest": "^22.0.1", + "jsonc-parser": "^3.3.1", "tunnel": "^0.0.6" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.0.0" } }, "node_modules/@octokit/auth-token": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.4.tgz", - "integrity": "sha512-TWFX7cZF2LXoCvdmJWY7XVPi74aSY0+FfBZNSXEXFkMpjcqsQwDSYVv5FhRFaI0V1ECnwbz4j59T/G+rXNWaIQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", + "license": "MIT", "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/@octokit/core": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.2.4.tgz", - "integrity": "sha512-rYKilwgzQ7/imScn3M9/pFfUf4I1AZEH3KhyJmtPdE2zfaXAn2mFfUy4FbKewzc2We5y/LlKLj36fWJLKC2SIQ==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", + "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", + "license": "MIT", "dependencies": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "^9.0.3", + "@octokit/request": "^10.0.6", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "before-after-hook": "^4.0.0", + "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/@octokit/endpoint": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.6.tgz", - "integrity": "sha512-5L4fseVRUsDFGR00tMWD/Trdeeihn999rTMGRMC1G/Ldi1uWlWJzI98H4Iak5DB/RVvQuyMYKqSK/R6mbSOQyg==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.2.tgz", + "integrity": "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ==", + "license": "MIT", "dependencies": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/@octokit/graphql": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.6.tgz", - "integrity": "sha512-Fxyxdy/JH0MnIB5h+UQ3yCoh1FG4kWXfFKkpWqjZHw/p+Kc8Y44Hu/kCgNBT6nU1shNumEchmW/sUO1JuQnPcw==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz", + "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", + "license": "MIT", "dependencies": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "universal-user-agent": "^6.0.0" + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/@octokit/openapi-types": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-18.0.0.tgz", - "integrity": "sha512-V8GImKs3TeQRxRtXFpG2wl19V7444NIOTDF24AWuIbmNaNYOQMWRbjcGDXV5B+0n887fgDcuMNOmlul+k+oJtw==" + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", + "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", + "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-4.3.1.tgz", - "integrity": "sha512-h8KKxESmSFTcXX409CAxlaOYscEDvN2KGQRsLCGT1NSqRW+D6EXLVQ8vuHhFznS9MuH9QYw1GfsUN30bg8hjVA==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", + "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", + "license": "MIT", "dependencies": { - "@octokit/types": "^7.5.0" + "@octokit/types": "^16.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" }, "peerDependencies": { - "@octokit/core": ">=4" - } - }, - "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { - "version": "13.13.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.13.1.tgz", - "integrity": "sha512-4EuKSk3N95UBWFau3Bz9b3pheQ8jQYbKmBL5+GSuY8YDPDwu03J4BjI+66yNi8aaX/3h1qDpb0mbBkLdr+cfGQ==" - }, - "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.5.1.tgz", - "integrity": "sha512-Zk4OUMLCSpXNI8KZZn47lVLJSsgMyCimsWWQI5hyjZg7hdYm0kjotaIkbG0Pp8SfU2CofMBzonboTqvzn3FrJA==", - "dependencies": { - "@octokit/openapi-types": "^13.11.0" + "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz", + "integrity": "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, "peerDependencies": { - "@octokit/core": ">=3" + "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.8.1.tgz", - "integrity": "sha512-QrlaTm8Lyc/TbU7BL/8bO49vp+RZ6W3McxxmmQTgYxf2sWkO8ZKuj4dLhPNJD6VCUW1hetCmeIM0m6FTVpDiEg==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", + "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", + "license": "MIT", "dependencies": { - "@octokit/types": "^8.1.1", - "deprecation": "^2.3.1" + "@octokit/types": "^16.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" }, "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-14.0.0.tgz", - "integrity": "sha512-HNWisMYlR8VCnNurDU6os2ikx0s0VyEjDYHNS/h4cgb8DeOxQ0n72HyinUtdDVxJhFy3FWLGl0DJhfEWk3P5Iw==" - }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-8.2.1.tgz", - "integrity": "sha512-8oWMUji8be66q2B9PmEIUyQm00VPDPun07umUWSaCwxmeaquFBro4Hcc3ruVoDo3zkQyZBlRvhIMEYS3pBhanw==", - "dependencies": { - "@octokit/openapi-types": "^14.0.0" + "@octokit/core": ">=6" } }, "node_modules/@octokit/request": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.8.tgz", - "integrity": "sha512-ow4+pkVQ+6XVVsekSYBzJC0VTVvh/FCTUUgTsboGq+DTeWdyIFV8WSCdo0RIxk6wSkBTHqIK1mYuY7nOBXOchw==", + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.7.tgz", + "integrity": "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==", + "license": "MIT", "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" + "@octokit/endpoint": "^11.0.2", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "fast-content-type-parse": "^3.0.0", + "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/@octokit/request-error": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.3.tgz", - "integrity": "sha512-crqw3V5Iy2uOU5Np+8M/YexTlT8zxCfI+qu+LxUB7SZpje4Qmx3mub5DfEKSO8Ylyk0aogi6TYdf6kxzh2BguQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz", + "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", + "license": "MIT", "dependencies": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" + "@octokit/types": "^16.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/@octokit/rest": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.4.tgz", - "integrity": "sha512-LwG668+6lE8zlSYOfwPj4FxWdv/qFXYBpv79TWIQEpBLKA9D/IMcWsF/U9RGpA3YqMVDiTxpgVpEW3zTFfPFTA==", + "version": "22.0.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.1.tgz", + "integrity": "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==", + "license": "MIT", "dependencies": { - "@octokit/core": "^4.0.0", - "@octokit/plugin-paginate-rest": "^4.0.0", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^6.0.0" + "@octokit/core": "^7.0.6", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/plugin-request-log": "^6.0.0", + "@octokit/plugin-rest-endpoint-methods": "^17.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/@octokit/types": { - "version": "9.3.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.3.2.tgz", - "integrity": "sha512-D4iHGTdAnEEVsB8fl95m1hiz7D5YiRdQ9b/OEb3BYRVwbLsGHcRVPz+u+BgRLNk0Q0/4iZCBqDN96j2XNxfXrA==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz", + "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", + "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "@octokit/openapi-types": "^27.0.0" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" - }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", + "license": "Apache-2.0" + }, + "node_modules/fast-content-type-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" } - } + ], + "license": "MIT" }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" }, "node_modules/tunnel": { "version": "0.0.6", @@ -263,34 +221,17 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "license": "ISC" } } } diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index f6bfd7518957..66e18bcab14b 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -25,8 +25,8 @@ "watch": "gulp watch-extension:configuration-editing" }, "dependencies": { - "jsonc-parser": "^3.2.0", - "@octokit/rest": "19.0.4", + "@octokit/rest": "^22.0.1", + "jsonc-parser": "^3.3.1", "tunnel": "^0.0.6" }, "capabilities": { @@ -49,6 +49,7 @@ "settings.json", "launch.json", "tasks.json", + "mcp.json", "keybindings.json", "extensions.json", "argv.json", @@ -56,7 +57,8 @@ "devcontainer.json", ".devcontainer.json" ] - }, { + }, + { "id": "json", "extensions": [ ".code-profile" @@ -116,6 +118,10 @@ "fileMatch": "/.vscode/tasks.json", "url": "vscode://schemas/tasks" }, + { + "fileMatch": "/.vscode/mcp.json", + "url": "vscode://schemas/mcp" + }, { "fileMatch": "%APP_SETTINGS_HOME%/tasks.json", "url": "vscode://schemas/tasks" @@ -163,7 +169,7 @@ ] }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/configuration-editing/src/extensionsProposals.ts b/extensions/configuration-editing/src/extensionsProposals.ts index 6438281dc0b0..ba1c33e7845c 100644 --- a/extensions/configuration-editing/src/extensionsProposals.ts +++ b/extensions/configuration-editing/src/extensionsProposals.ts @@ -30,29 +30,3 @@ export async function provideInstalledExtensionProposals(existing: string[], add } return []; } - -export async function provideWorkspaceTrustExtensionProposals(existing: string[], range: vscode.Range): Promise { - if (Array.isArray(existing)) { - const extensions = vscode.extensions.all.filter(e => e.packageJSON.main); - const extensionProposals = extensions.filter(e => existing.indexOf(e.id) === -1); - if (extensionProposals.length) { - return extensionProposals.map(e => { - const item = new vscode.CompletionItem(e.id); - const insertText = `"${e.id}": {\n\t"supported": false,\n\t"version": "${e.packageJSON.version}"\n}`; - item.kind = vscode.CompletionItemKind.Value; - item.insertText = insertText; - item.range = range; - item.filterText = insertText; - return item; - }); - } else { - const example = new vscode.CompletionItem(vscode.l10n.t("Example")); - example.insertText = '"vscode.csharp: {\n\t"supported": false,\n\t"version": "0.0.0"\n}`;"'; - example.kind = vscode.CompletionItemKind.Value; - example.range = range; - return [example]; - } - } - - return []; -} diff --git a/extensions/configuration-editing/src/settingsDocumentHelper.ts b/extensions/configuration-editing/src/settingsDocumentHelper.ts index 6135df5315a5..12b50f31759d 100644 --- a/extensions/configuration-editing/src/settingsDocumentHelper.ts +++ b/extensions/configuration-editing/src/settingsDocumentHelper.ts @@ -127,7 +127,7 @@ export class SettingsDocument { completions.push(this.newSimpleCompletionItem(getText('separator'), range, vscode.l10n.t("a conditional separator (' - ') that only shows when surrounded by variables with values"))); completions.push(this.newSimpleCompletionItem(getText('activeRepositoryName'), range, vscode.l10n.t("the name of the active repository (e.g. vscode)"))); completions.push(this.newSimpleCompletionItem(getText('activeRepositoryBranchName'), range, vscode.l10n.t("the name of the active branch in the active repository (e.g. main)"))); - + completions.push(this.newSimpleCompletionItem(getText('activeEditorState'), range, vscode.l10n.t("the state of the active editor (e.g. modified)."))); return completions; } diff --git a/extensions/cpp/language-configuration.json b/extensions/cpp/language-configuration.json index 0bf8df9dc010..cb1fb733b999 100644 --- a/extensions/cpp/language-configuration.json +++ b/extensions/cpp/language-configuration.json @@ -1,29 +1,94 @@ { "comments": { "lineComment": "//", - "blockComment": ["/*", "*/"] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - { "open": "[", "close": "]" }, - { "open": "{", "close": "}" }, - { "open": "(", "close": ")" }, - { "open": "'", "close": "'", "notIn": ["string", "comment"] }, - { "open": "\"", "close": "\"", "notIn": ["string"] }, - { "open": "/*", "close": "*/", "notIn": ["string", "comment"] }, - { "open": "/**", "close": " */", "notIn": ["string"] } + { + "open": "[", + "close": "]" + }, + { + "open": "{", + "close": "}" + }, + { + "open": "(", + "close": ")" + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + }, + { + "open": "/*", + "close": "*/", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "/**", + "close": " */", + "notIn": [ + "string" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"], - ["<", ">"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "'", + "'" + ], + [ + "<", + ">" + ] ], "wordPattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\~\\!\\@\\#\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\s]+)", "folding": { @@ -32,6 +97,14 @@ "end": "^\\s*#pragma\\s+endregion\\b" } }, + "indentationRules": { + "decreaseIndentPattern": { + "pattern": "^\\s*[\\}\\]\\)].*$" + }, + "increaseIndentPattern": { + "pattern": "^.*(\\{[^}]*|\\([^)]*|\\[[^\\]]*)$" + }, + }, "onEnterRules": [ { // Decrease indentation after single line if/else if/else, for, or while @@ -41,6 +114,19 @@ "action": { "indent": "outdent" } - } + }, + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, ] } diff --git a/extensions/csharp/cgmanifest.json b/extensions/csharp/cgmanifest.json index 58a7408dbbe7..58ae5ece50ae 100644 --- a/extensions/csharp/cgmanifest.json +++ b/extensions/csharp/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "dotnet/csharp-tmLanguage", "repositoryUrl": "https://github.com/dotnet/csharp-tmLanguage", - "commitHash": "d63e2661d4e0c83b6c7810eb1d0eedc5da843b04" + "commitHash": "1381bedfb087c18aca67af8278050d11bc9d9349" } }, "license": "MIT", diff --git a/extensions/csharp/language-configuration.json b/extensions/csharp/language-configuration.json index d8698b46c090..7db6640f4c59 100644 --- a/extensions/csharp/language-configuration.json +++ b/extensions/csharp/language-configuration.json @@ -1,32 +1,112 @@ { "comments": { "lineComment": "//", - "blockComment": ["/*", "*/"] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - { "open": "'", "close": "'", "notIn": ["string", "comment"] }, - { "open": "\"", "close": "\"", "notIn": ["string", "comment"] } + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + { + "open": "'", + "close": "'", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string", + "comment" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["<", ">"], - ["'", "'"], - ["\"", "\""] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "<", + ">" + ], + [ + "'", + "'" + ], + [ + "\"", + "\"" + ] ], "folding": { "markers": { "start": "^\\s*#region\\b", "end": "^\\s*#endregion\\b" } - } + }, + "onEnterRules": [ + // Add // when pressing enter from inside line comment + // We do not want to match /// (a documentation comment) + { + "beforeText": { + "pattern": "[^\/]\/\/[^\/].*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, + // Add /// when pressing enter from anywhere inside a documentation comment. + // Documentation comments are not valid after non-whitespace. + { + "beforeText": { + "pattern": "^\\s*\/\/\/" + }, + "action": { + "indent": "none", + "appendText": "/// " + } + }, + ] } diff --git a/extensions/csharp/package.json b/extensions/csharp/package.json index c8af5a25f1f0..d3d4f262d9f8 100644 --- a/extensions/csharp/package.json +++ b/extensions/csharp/package.json @@ -37,7 +37,10 @@ { "language": "csharp", "scopeName": "source.cs", - "path": "./syntaxes/csharp.tmLanguage.json" + "path": "./syntaxes/csharp.tmLanguage.json", + "tokenTypes": { + "meta.interpolation": "other" + } } ], "snippets": [ diff --git a/extensions/csharp/syntaxes/csharp.tmLanguage.json b/extensions/csharp/syntaxes/csharp.tmLanguage.json index 4a2497a064ab..1afcc3053b60 100644 --- a/extensions/csharp/syntaxes/csharp.tmLanguage.json +++ b/extensions/csharp/syntaxes/csharp.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/dotnet/csharp-tmLanguage/commit/d63e2661d4e0c83b6c7810eb1d0eedc5da843b04", + "version": "https://github.com/dotnet/csharp-tmLanguage/commit/1381bedfb087c18aca67af8278050d11bc9d9349", "name": "C#", "scopeName": "source.cs", "patterns": [ @@ -714,11 +714,11 @@ }, "enum-declaration": { "begin": "(?=\\benum\\b)", - "end": "(?<=\\})", + "end": "(?<=\\})|(?=;)", "patterns": [ { "begin": "(?=enum)", - "end": "(?=\\{)", + "end": "(?=\\{)|(?=;)", "patterns": [ { "include": "#comment" @@ -805,7 +805,7 @@ }, "interface-declaration": { "begin": "(?=\\binterface\\b)", - "end": "(?<=\\})", + "end": "(?<=\\})|(?=;)", "patterns": [ { "begin": "(?x)\n(interface)\\b\\s+\n(@?[_[:alpha:]][_[:alnum:]]*)", @@ -817,7 +817,7 @@ "name": "entity.name.type.interface.cs" } }, - "end": "(?=\\{)", + "end": "(?=\\{)|(?=;)", "patterns": [ { "include": "#comment" @@ -4206,7 +4206,7 @@ ] }, "invocation-expression": { - "begin": "(?x)\n(?:\n (?:(\\?)\\s*)? # preceding null-conditional operator?\n (\\.)\\s*| # preceding dot?\n (->)\\s* # preceding pointer arrow?\n)?\n(@?[_[:alpha:]][_[:alnum:]]*)\\s* # method name\n(\n <\n (?\n [^<>()]++|\n <\\g*+>|\n \\(\\g*+\\)\n )*+\n >\\s*\n)? # type arguments\n(?=\\() # open paren of argument list", + "begin": "(?x)\n(?:\n (?:(\\?)\\s*)? # preceding null-conditional operator?\n (\\.)\\s*| # preceding dot?\n (->)\\s* # preceding pointer arrow?\n)?\n(@?[_[:alpha:]][_[:alnum:]]*)\\s* # method name\n(\n <\n (?\n [^<>()]|\n \\((?:[^<>()]|<[^<>()]*>|\\([^<>()]*\\))*\\)|\n <\\g*>\n )*\n >\\s*\n)? # type arguments\n(?=\\() # open paren of argument list", "beginCaptures": { "1": { "name": "keyword.operator.null-conditional.cs" diff --git a/extensions/css-language-features/client/src/dropOrPaste/dropOrPasteResource.ts b/extensions/css-language-features/client/src/dropOrPaste/dropOrPasteResource.ts index 6a4c38d24171..aa057878a01e 100644 --- a/extensions/css-language-features/client/src/dropOrPaste/dropOrPasteResource.ts +++ b/extensions/css-language-features/client/src/dropOrPaste/dropOrPasteResource.ts @@ -9,7 +9,8 @@ import { getDocumentDir, Mimes, Schemes } from './shared'; import { UriList } from './uriList'; class DropOrPasteResourceProvider implements vscode.DocumentDropEditProvider, vscode.DocumentPasteEditProvider { - readonly kind = vscode.DocumentDropOrPasteEditKind.Empty.append('css', 'url'); + + readonly kind = vscode.DocumentDropOrPasteEditKind.Empty.append('css', 'link', 'url'); async provideDocumentDropEdits( document: vscode.TextDocument, diff --git a/extensions/css-language-features/client/tsconfig.json b/extensions/css-language-features/client/tsconfig.json index 5cf131d0677c..5284e0938583 100644 --- a/extensions/css-language-features/client/tsconfig.json +++ b/extensions/css-language-features/client/tsconfig.json @@ -9,7 +9,6 @@ }, "include": [ "src/**/*", - "../../../src/vscode-dts/vscode.d.ts", - "../../../src/vscode-dts/vscode.proposed.documentPaste.d.ts" + "../../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/css-language-features/package-lock.json b/extensions/css-language-features/package-lock.json index 289e29e2abbe..7ef1b266912f 100644 --- a/extensions/css-language-features/package-lock.json +++ b/extensions/css-language-features/package-lock.json @@ -9,70 +9,67 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "vscode-languageclient": "^10.0.0-next.13", - "vscode-uri": "^3.0.8" + "vscode-languageclient": "^10.0.0-next.19", + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.77.0" } }, - "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@types/node": { + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", + "dev": true, + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "undici-types": "~7.16.0" } }, "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -81,55 +78,56 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.6.tgz", - "integrity": "sha512-KCSvUNsFiVciG9iqjJKBZOd66CN3ZKohDlYRmoOi+pd8l15MFLZ8wRG4c+wuzePGba/8WcCG2TM+C/GVlvuaeA==", + "version": "9.0.0-next.11", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.11.tgz", + "integrity": "sha512-u6LElQNbSiE9OugEEmrUKwH6+8BpPz2S5MDHvQUqHL//I4Q8GPikKLOUf856UnbLkZdhxaPrExac1lA3XwpIPA==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageclient": { - "version": "10.0.0-next.13", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.13.tgz", - "integrity": "sha512-KLsOMJoYpkk36PIgcOjyZ4AekOfzp4kdWdRRbVKeVvSIrwrn/4RSZr0NlD6EvUBBJSsJW4WDrYY7Y3znkqa6+w==", + "version": "10.0.0-next.19", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.19.tgz", + "integrity": "sha512-sJtO8y0Dxs4ue/DK0QgO/ATBfZwQdee3TqvCsoqUej/GZrBA01DTf4pbfswRxHsTxN2yH0haImUnMafWHtE4CQ==", + "license": "MIT", "dependencies": { - "minimatch": "^9.0.3", - "semver": "^7.6.0", - "vscode-languageserver-protocol": "3.17.6-next.11" + "minimatch": "^10.0.3", + "semver": "^7.7.1", + "vscode-languageserver-protocol": "3.17.6-next.16" }, "engines": { "vscode": "^1.91.0" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.11.tgz", - "integrity": "sha512-GeJxEp1TiLsp79f8WG5n10wLViXfgFKb99hU9K8m7KDWM95/QFEqWkm79f9LVm54tUK74I91a9EeiQLCS/FABQ==", + "version": "3.17.6-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.16.tgz", + "integrity": "sha512-kQTjXEuyxMbdmmZ3U+Lib3oUl12xEKNc73RtWxPSDS3TFtjVwt98Q1CUzfDA9EUpsA24M46Bl6q3sLe9AUOKyw==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" + "vscode-jsonrpc": "9.0.0-next.11", + "vscode-languageserver-types": "3.17.6-next.6" } }, "node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.5.tgz", - "integrity": "sha512-QFmf3Yl1tCgUQfA77N9Me/LXldJXkIVypQbty2rJ1DNHQkC+iwvm4Z2tXg9czSwlhvv0pD4pbF5mT7WhAglolw==" + "version": "3.17.6-next.6", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.6.tgz", + "integrity": "sha512-aiJY5/yW+xzw7KPNlwi3gQtddq/3EIn5z8X8nCgJfaiAij2R1APKePngv+MUdLdYJBVTLu+Qa0ODsT+pHgYguQ==", + "license": "MIT" }, "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" } } } diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index af75de5386b6..0779d640a7fa 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -23,9 +23,6 @@ "supported": true } }, - "enabledApiProposals": [ - "documentPaste" - ], "scripts": { "compile": "npx gulp compile-extension:css-language-features-client compile-extension:css-language-features-server", "watch": "npx gulp watch-extension:css-language-features-client watch-extension:css-language-features-server", @@ -997,11 +994,11 @@ ] }, "dependencies": { - "vscode-languageclient": "^10.0.0-next.13", - "vscode-uri": "^3.0.8" + "vscode-languageclient": "^10.0.0-next.19", + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/css-language-features/package.nls.json b/extensions/css-language-features/package.nls.json index d6e25a57a43a..057ec214bc2f 100644 --- a/extensions/css-language-features/package.nls.json +++ b/extensions/css-language-features/package.nls.json @@ -35,7 +35,7 @@ "css.format.newlineBetweenRules.desc": "Separate rulesets by a blank line.", "css.format.spaceAroundSelectorSeparator.desc": "Ensure a space character around selector separators '>', '+', '~' (e.g. `a > b`).", "css.format.braceStyle.desc": "Put braces on the same line as rules (`collapse`) or put braces on own line (`expand`).", - "css.format.preserveNewLines.desc": "Whether existing line breaks before elements should be preserved.", + "css.format.preserveNewLines.desc": "Whether existing line breaks before rules and declarations should be preserved.", "css.format.maxPreserveNewLines.desc": "Maximum number of line breaks to be preserved in one chunk, when `#css.format.preserveNewLines#` is enabled.", "less.title": "LESS", "less.completion.triggerPropertyValueCompletion.desc": "By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior.", @@ -69,7 +69,7 @@ "less.format.newlineBetweenRules.desc": "Separate rulesets by a blank line.", "less.format.spaceAroundSelectorSeparator.desc": "Ensure a space character around selector separators '>', '+', '~' (e.g. `a > b`).", "less.format.braceStyle.desc": "Put braces on the same line as rules (`collapse`) or put braces on own line (`expand`).", - "less.format.preserveNewLines.desc": "Whether existing line breaks before elements should be preserved.", + "less.format.preserveNewLines.desc": "Whether existing line breaks before rules and declarations should be preserved.", "less.format.maxPreserveNewLines.desc": "Maximum number of line breaks to be preserved in one chunk, when `#less.format.preserveNewLines#` is enabled.", "scss.title": "SCSS (Sass)", "scss.completion.triggerPropertyValueCompletion.desc": "By default, VS Code triggers property value completion after selecting a CSS property. Use this setting to disable this behavior.", @@ -103,7 +103,7 @@ "scss.format.newlineBetweenRules.desc": "Separate rulesets by a blank line.", "scss.format.spaceAroundSelectorSeparator.desc": "Ensure a space character around selector separators '>', '+', '~' (e.g. `a > b`).", "scss.format.braceStyle.desc": "Put braces on the same line as rules (`collapse`) or put braces on own line (`expand`).", - "scss.format.preserveNewLines.desc": "Whether existing line breaks before elements should be preserved.", + "scss.format.preserveNewLines.desc": "Whether existing line breaks before rules and declarations should be preserved.", "scss.format.maxPreserveNewLines.desc": "Maximum number of line breaks to be preserved in one chunk, when `#scss.format.preserveNewLines#` is enabled.", "css.colorDecorators.enable.deprecationMessage": "The setting `css.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.", "scss.colorDecorators.enable.deprecationMessage": "The setting `scss.colorDecorators.enable` has been deprecated in favor of `editor.colorDecorators`.", diff --git a/extensions/css-language-features/server/package-lock.json b/extensions/css-language-features/server/package-lock.json index 5fa5546e0adb..29a57f1d6361 100644 --- a/extensions/css-language-features/server/package-lock.json +++ b/extensions/css-language-features/server/package-lock.json @@ -10,31 +10,33 @@ "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", - "vscode-css-languageservice": "^6.3.1", - "vscode-languageserver": "^10.0.0-next.11", - "vscode-uri": "^3.0.8" + "vscode-css-languageservice": "^6.3.9", + "vscode-languageserver": "^10.0.0-next.16", + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x" + "@types/mocha": "^10.0.10", + "@types/node": "25.x" }, "engines": { "node": "*" } }, "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@vscode/l10n": { @@ -43,54 +45,60 @@ "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-css-languageservice": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.1.tgz", - "integrity": "sha512-1BzTBuJfwMc3A0uX4JBdJgoxp74cjj4q2mDJdp49yD/GuAq4X0k5WtK6fNcMYr+FfJ9nqgR6lpfCSZDkARJ5qQ==", + "version": "6.3.9", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.9.tgz", + "integrity": "sha512-1tLWfp+TDM5ZuVWht3jmaY5y7O6aZmpeXLoHl5bv1QtRsRKt4xYGRMmdJa5Pqx/FTkgRbsna9R+Gn2xE+evVuA==", + "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "3.17.5", - "vscode-uri": "^3.0.8" + "vscode-uri": "^3.1.0" } }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.6.tgz", - "integrity": "sha512-KCSvUNsFiVciG9iqjJKBZOd66CN3ZKohDlYRmoOi+pd8l15MFLZ8wRG4c+wuzePGba/8WcCG2TM+C/GVlvuaeA==", + "version": "9.0.0-next.11", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.11.tgz", + "integrity": "sha512-u6LElQNbSiE9OugEEmrUKwH6+8BpPz2S5MDHvQUqHL//I4Q8GPikKLOUf856UnbLkZdhxaPrExac1lA3XwpIPA==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageserver": { - "version": "10.0.0-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.11.tgz", - "integrity": "sha512-cmobSrVDYhlh/t02vz/bV8nNpds8mus5HnILULae2iAvOjoaJPnTAp0jJWoYdUqTpIVzT9JV6JMKqLEvdqpeqg==", + "version": "10.0.0-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.16.tgz", + "integrity": "sha512-RbsYDOhddv1NtBCAR7+oVxxCmOpQUHhrtgUE0xz6J+BJGSCkfOqBCyLUIwSjKk2rK9llxUj/pR5aL8QCsXrxow==", + "license": "MIT", "dependencies": { - "vscode-languageserver-protocol": "3.17.6-next.11" + "vscode-languageserver-protocol": "3.17.6-next.16" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.11.tgz", - "integrity": "sha512-GeJxEp1TiLsp79f8WG5n10wLViXfgFKb99hU9K8m7KDWM95/QFEqWkm79f9LVm54tUK74I91a9EeiQLCS/FABQ==", + "version": "3.17.6-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.16.tgz", + "integrity": "sha512-kQTjXEuyxMbdmmZ3U+Lib3oUl12xEKNc73RtWxPSDS3TFtjVwt98Q1CUzfDA9EUpsA24M46Bl6q3sLe9AUOKyw==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" + "vscode-jsonrpc": "9.0.0-next.11", + "vscode-languageserver-types": "3.17.6-next.6" } }, "node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.5.tgz", - "integrity": "sha512-QFmf3Yl1tCgUQfA77N9Me/LXldJXkIVypQbty2rJ1DNHQkC+iwvm4Z2tXg9czSwlhvv0pD4pbF5mT7WhAglolw==" + "version": "3.17.6-next.6", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.6.tgz", + "integrity": "sha512-aiJY5/yW+xzw7KPNlwi3gQtddq/3EIn5z8X8nCgJfaiAij2R1APKePngv+MUdLdYJBVTLu+Qa0ODsT+pHgYguQ==", + "license": "MIT" }, "node_modules/vscode-languageserver-textdocument": { "version": "1.0.12", @@ -103,9 +111,10 @@ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" }, "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" } } } diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 33153ad03cf2..1e6bc0311b8e 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -11,13 +11,13 @@ "browser": "./dist/browser/cssServerMain", "dependencies": { "@vscode/l10n": "^0.0.18", - "vscode-css-languageservice": "^6.3.1", - "vscode-languageserver": "^10.0.0-next.11", - "vscode-uri": "^3.0.8" + "vscode-css-languageservice": "^6.3.9", + "vscode-languageserver": "^10.0.0-next.16", + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x" + "@types/mocha": "^10.0.10", + "@types/node": "25.x" }, "scripts": { "compile": "gulp compile-extension:css-language-features-server", diff --git a/extensions/css/cgmanifest.json b/extensions/css/cgmanifest.json index 0705aca66c44..7b85089b6b91 100644 --- a/extensions/css/cgmanifest.json +++ b/extensions/css/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "microsoft/vscode-css", "repositoryUrl": "https://github.com/microsoft/vscode-css", - "commitHash": "c216f777497265700ff336f739328e5197e012cd" + "commitHash": "a927fe2f73927bf5c25d0b0c4dd0e63d69fd8887" } }, "licenseDetail": [ diff --git a/extensions/css/syntaxes/css.tmLanguage.json b/extensions/css/syntaxes/css.tmLanguage.json index 2096e16e9206..5ba8bc90b738 100644 --- a/extensions/css/syntaxes/css.tmLanguage.json +++ b/extensions/css/syntaxes/css.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-css/commit/c216f777497265700ff336f739328e5197e012cd", + "version": "https://github.com/microsoft/vscode-css/commit/a927fe2f73927bf5c25d0b0c4dd0e63d69fd8887", "name": "CSS", "scopeName": "source.css", "patterns": [ @@ -1421,7 +1421,7 @@ "property-names": { "patterns": [ { - "match": "(?xi) (?!]?[\d;:]*["$#'* ]?[a-zA-Z@^`{}|~])|(:?\x1b\].*?\x07)/g; +const CSI_SEQUENCE = /(?:\x1b\[|\x9b)[=?>!]?[\d;:]*["$#'* ]?[a-zA-Z@^`{}|~]/; +const OSC_SEQUENCE = /(?:\x1b\]|\x9d).*?(?:\x1b\\|\x07|\x9c)/; +const ESC_SEQUENCE = /\x1b(?:[ #%\(\)\*\+\-\.\/]?[a-zA-Z0-9\|}~@])/; +const CONTROL_SEQUENCES = new RegExp('(?:' + [ + CSI_SEQUENCE.source, + OSC_SEQUENCE.source, + ESC_SEQUENCE.source, +].join('|') + ')', 'g'); /** * Froms vs/base/common/strings.ts in core @@ -31,7 +38,7 @@ const CSI_SEQUENCE = /(?:(?:\x1b\[|\x9B)[=?>!]?[\d;:]*["$#'* ]?[a-zA-Z@^`{}|~])| */ function removeAnsiEscapeCodes(str: string): string { if (str) { - str = str.replace(CSI_SEQUENCE, ''); + str = str.replace(CONTROL_SEQUENCES, ''); } return str; diff --git a/extensions/docker/cgmanifest.json b/extensions/docker/cgmanifest.json index dbbf43607eef..8462de7dd728 100644 --- a/extensions/docker/cgmanifest.json +++ b/extensions/docker/cgmanifest.json @@ -6,10 +6,10 @@ "git": { "name": "language-docker", "repositoryUrl": "https://github.com/moby/moby", - "commitHash": "abd39744c6f3ed854500e423f5fabf952165161f" + "commitHash": "c2029cb2574647e4bc28ed58486b8e85883eedb9" } }, - "license": "Apache2", + "license": "Apache-2.0", "description": "The file syntaxes/docker.tmLanguage was included from https://github.com/moby/moby/blob/master/contrib/syntax/textmate/Docker.tmbundle/Syntaxes/Dockerfile.tmLanguage.", "version": "0.0.0" } diff --git a/extensions/docker/syntaxes/docker.tmLanguage.json b/extensions/docker/syntaxes/docker.tmLanguage.json index f7f414636c49..aa5223a31eae 100644 --- a/extensions/docker/syntaxes/docker.tmLanguage.json +++ b/extensions/docker/syntaxes/docker.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/moby/moby/commit/abd39744c6f3ed854500e423f5fabf952165161f", + "version": "https://github.com/moby/moby/commit/c2029cb2574647e4bc28ed58486b8e85883eedb9", "name": "Dockerfile", "scopeName": "source.dockerfile", "patterns": [ @@ -41,6 +41,9 @@ }, "match": "^\\s*(?i:(ONBUILD)\\s+)?(?i:(CMD|ENTRYPOINT))\\s" }, + { + "include": "#string-character-escape" + }, { "begin": "\"", "beginCaptures": { @@ -57,8 +60,7 @@ "name": "string.quoted.double.dockerfile", "patterns": [ { - "match": "\\\\.", - "name": "constant.character.escaped.dockerfile" + "include": "#string-character-escape" } ] }, @@ -78,8 +80,7 @@ "name": "string.quoted.single.dockerfile", "patterns": [ { - "match": "\\\\.", - "name": "constant.character.escaped.dockerfile" + "include": "#string-character-escape" } ] }, @@ -98,5 +99,11 @@ "comment": "comment.line", "match": "^(\\s*)((#).*$\\n?)" } - ] + ], + "repository": { + "string-character-escape": { + "name": "constant.character.escaped.dockerfile", + "match": "\\\\." + } + } } \ No newline at end of file diff --git a/extensions/emmet/package-lock.json b/extensions/emmet/package-lock.json index aa01c4104caa..abac59d730ee 100644 --- a/extensions/emmet/package-lock.json +++ b/extensions/emmet/package-lock.json @@ -10,14 +10,14 @@ "license": "MIT", "dependencies": { "@emmetio/css-parser": "ramya-rao-a/css-parser#vscode", - "@emmetio/html-matcher": "^0.3.3", + "@emmetio/html-matcher": "^1.3.0", "@emmetio/math-expression": "^1.0.5", "@vscode/emmet-helper": "^2.8.8", - "image-size": "~1.0.0", + "image-size": "~2.0.2", "vscode-languageserver-textdocument": "^1.0.1" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.13.0" @@ -48,12 +48,12 @@ } }, "node_modules/@emmetio/html-matcher": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@emmetio/html-matcher/-/html-matcher-0.3.3.tgz", - "integrity": "sha1-C72twIguGFlQ8Dc33G2/j3vZByg= sha512-+aeGmFXoR36nk2qzqPhBnWjnB38BV+dreTh/tsfbWP9kHv7fqRa9XuG1BSQFbPtKzsjUsBvGXkgGU3G8MkMw6A==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@emmetio/html-matcher/-/html-matcher-1.3.0.tgz", + "integrity": "sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==", + "license": "ISC", "dependencies": { - "@emmetio/stream-reader": "^2.0.0", - "@emmetio/stream-reader-utils": "^0.1.0" + "@emmetio/scanner": "^1.0.0" } }, "node_modules/@emmetio/math-expression": { @@ -80,30 +80,33 @@ "integrity": "sha1-JEywLHfsLnT3ipvTGCGKvJxQCmE= sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@vscode/emmet-helper": { - "version": "2.9.3", - "resolved": "https://registry.npmjs.org/@vscode/emmet-helper/-/emmet-helper-2.9.3.tgz", - "integrity": "sha512-rB39LHWWPQYYlYfpv9qCoZOVioPCftKXXqrsyqN1mTWZM6dTnONT63Db+03vgrBbHzJN45IrgS/AGxw9iiqfEw==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/@vscode/emmet-helper/-/emmet-helper-2.11.0.tgz", + "integrity": "sha512-QLxjQR3imPZPQltfbWRnHU6JecWTF1QSWhx3GAKQpslx7y3Dp6sIIXhKjiUJ/BR9FX8PVthjr9PD6pNwOJfAzw==", + "license": "MIT", "dependencies": { "emmet": "^2.4.3", "jsonc-parser": "^2.3.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-languageserver-types": "^3.15.1", - "vscode-uri": "^2.1.2" + "vscode-uri": "^3.0.8" } }, "node_modules/emmet": { - "version": "2.4.7", - "resolved": "https://registry.npmjs.org/emmet/-/emmet-2.4.7.tgz", - "integrity": "sha512-O5O5QNqtdlnQM2bmKHtJgyChcrFMgQuulI+WdiOw2NArzprUqqxUW6bgYtKvzKgrsYpuLWalOkdhNP+1jluhCA==", + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/emmet/-/emmet-2.4.11.tgz", + "integrity": "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==", + "license": "MIT", "workspaces": [ "./packages/scanner", "./packages/abbreviation", @@ -116,57 +119,46 @@ } }, "node_modules/image-size": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.0.0.tgz", - "integrity": "sha512-JLJ6OwBfO1KcA+TvJT+v8gbE6iWbj24LyDNFgFEN0lzegn6cC6a/p3NIDaepMsJjQjlUWqIC7wJv8lBFxPNjcw==", - "dependencies": { - "queue": "6.0.2" - }, + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz", + "integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==", + "license": "MIT", "bin": { "image-size": "bin/image-size.js" }, "engines": { - "node": ">=12.0.0" + "node": ">=16.x" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, "node_modules/jsonc-parser": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==" }, - "node_modules/queue": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", - "dependencies": { - "inherits": "~2.0.3" - } - }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.3.tgz", - "integrity": "sha512-ynEGytvgTb6HVSUwPJIAZgiHQmPCx8bZ8w5um5Lz+q5DjP0Zj8wTFhQpyg8xaMvefDytw2+HH5yzqS+FhsR28A==" + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "license": "MIT" }, "node_modules/vscode-languageserver-types": { - "version": "3.17.3", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz", - "integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==" + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" }, "node_modules/vscode-uri": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.2.tgz", - "integrity": "sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==" + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "license": "MIT" } } } diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index a390a86fc2ec..b4c19e159f87 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -479,14 +479,14 @@ "deps": "npm install @vscode/emmet-helper" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "dependencies": { "@emmetio/css-parser": "ramya-rao-a/css-parser#vscode", - "@emmetio/html-matcher": "^0.3.3", + "@emmetio/html-matcher": "^1.3.0", "@emmetio/math-expression": "^1.0.5", "@vscode/emmet-helper": "^2.8.8", - "image-size": "~1.0.0", + "image-size": "~2.0.2", "vscode-languageserver-textdocument": "^1.0.1" }, "capabilities": { diff --git a/extensions/emmet/src/test/completion.test.ts b/extensions/emmet/src/test/completion.test.ts index 97ac6ecff335..4f74ba92e25e 100644 --- a/extensions/emmet/src/test/completion.test.ts +++ b/extensions/emmet/src/test/completion.test.ts @@ -22,14 +22,14 @@ suite('Tests for completion in CSS embedded in HTML', () => { }); // https://github.com/microsoft/vscode/issues/79766 - test('#79766, correct region determination', async () => { + test('microsoft/vscode#79766, correct region determination', async () => { await testCompletionProvider('html', `
di|
`, [ { label: 'div', documentation: `
|
` } ]); }); // https://github.com/microsoft/vscode/issues/86941 - test('#86941, widows should be completed after width', async () => { + test('microsoft/vscode#86941, widows should be completed after width', async () => { await testCompletionProvider('css', `.foo { wi| }`, [ { label: 'width: ;', documentation: `width: |;` } ]); @@ -56,14 +56,14 @@ suite('Tests for completion in CSS embedded in HTML', () => { }); // https://github.com/microsoft/vscode/issues/117020 - test('#117020, ! at end of abbreviation should have completion', async () => { + test('microsoft/vscode#117020, ! at end of abbreviation should have completion', async () => { await testCompletionProvider('css', `.foo { bdbn!| }`, [ { label: 'border-bottom: none !important;', documentation: `border-bottom: none !important;` } ]); }); // https://github.com/microsoft/vscode/issues/138461 - test('#138461, JSX array noise', async () => { + test('microsoft/vscode#138461, JSX array noise', async () => { await testCompletionProvider('jsx', 'a[i]', undefined); await testCompletionProvider('jsx', 'Component[a b]', undefined); await testCompletionProvider('jsx', '[a, b]', undefined); @@ -71,6 +71,13 @@ suite('Tests for completion in CSS embedded in HTML', () => { { label: '
', documentation: '
|
' } ]); }); + + // https://github.com/microsoft/vscode-emmet-helper/pull/90 + test('microsoft/vscode-emmet-helper#90', async () => { + await testCompletionProvider('html', 'dialog', [ + { label: '', documentation: '|' } + ]); + }); }); interface TestCompletionItem { diff --git a/extensions/extension-editing/package-lock.json b/extensions/extension-editing/package-lock.json index 3fa0c35e2d06..c4b1f5e99162 100644 --- a/extensions/extension-editing/package-lock.json +++ b/extensions/extension-editing/package-lock.json @@ -9,31 +9,51 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "jsonc-parser": "^3.2.0", - "markdown-it": "^12.3.2", - "parse5": "^3.0.2" + "jsonc-parser": "^3.3.1", + "markdown-it": "^14.1.0", + "parse5": "^8.0.0" }, "devDependencies": { - "@types/markdown-it": "0.0.2", - "@types/node": "20.x" + "@types/markdown-it": "14.1.2", + "@types/node": "25.x" }, "engines": { "vscode": "^1.4.0" } }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/markdown-it": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-0.0.2.tgz", - "integrity": "sha1-XZrRnm5lCM3S8llt+G/Qqt5ZhmA= sha512-A2seE+zJYSjGHy7L/v0EN/xRfgv2A60TuXOwI8tt5aZxF4UeoYIkM2jERnNH8w4VFr7oFEm0lElGOao7fZgygQ==", - "dev": true + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/argparse": { @@ -42,69 +62,100 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" }, "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", "dependencies": { - "uc.micro": "^1.0.1" + "uc.micro": "^2.0.0" } }, "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", "dependencies": { "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, "bin": { - "markdown-it": "bin/markdown-it.js" + "markdown-it": "bin/markdown-it.mjs" } }, "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" }, "node_modules/parse5": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.2.tgz", - "integrity": "sha1-Be/1fw70V3+xRKefi5qWemzERRA= sha512-yQW05f47bKFJa0WdnyzP7vh7+B+w8jhVsFBBiaEbIfNDSSt8GADBhcQgsdYxatQ7rVs1nU9cmsYXURGWBH3Siw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "license": "MIT", "dependencies": { - "@types/node": "^6.0.46" + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/parse5/node_modules/@types/node": { - "version": "6.0.78", - "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.78.tgz", - "integrity": "sha512-+vD6E8ixntRzzZukoF3uP1iV+ZjVN3koTcaeK+BEoc/kSfGbLDIGC7RmCaUgVpUfN6cWvfczFRERCyKM9mkvXg==" + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } }, "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/extension-editing/package.json b/extensions/extension-editing/package.json index 184d28e8df0c..03f7f6398836 100644 --- a/extensions/extension-editing/package.json +++ b/extensions/extension-editing/package.json @@ -26,9 +26,9 @@ "watch": "gulp watch-extension:extension-editing" }, "dependencies": { - "jsonc-parser": "^3.2.0", - "markdown-it": "^12.3.2", - "parse5": "^3.0.2" + "jsonc-parser": "^3.3.1", + "markdown-it": "^14.1.0", + "parse5": "^8.0.0" }, "contributes": { "jsonValidation": [ @@ -66,8 +66,8 @@ ] }, "devDependencies": { - "@types/markdown-it": "0.0.2", - "@types/node": "20.x" + "@types/markdown-it": "14.1.2", + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/git-base/package-lock.json b/extensions/git-base/package-lock.json index f4b29739ca63..05a194b412e9 100644 --- a/extensions/git-base/package-lock.json +++ b/extensions/git-base/package-lock.json @@ -9,26 +9,28 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "0.10.x" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/git-base/package.json b/extensions/git-base/package.json index 3c9b07a13e8f..139b8d064fd7 100644 --- a/extensions/git-base/package.json +++ b/extensions/git-base/package.json @@ -104,7 +104,7 @@ ] }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/git-base/src/api/git-base.d.ts b/extensions/git-base/src/api/git-base.d.ts index 53cac4d5c70f..d4ec49df47dc 100644 --- a/extensions/git-base/src/api/git-base.d.ts +++ b/extensions/git-base/src/api/git-base.d.ts @@ -3,11 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, Event, ProviderResult, Uri } from 'vscode'; +import { Command, Disposable, Event, ProviderResult } from 'vscode'; export { ProviderResult } from 'vscode'; export interface API { registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable; + getRemoteSourceActions(url: string): Promise; pickRemoteSource(options: PickRemoteSourceOptions): Promise; } diff --git a/extensions/git/package-lock.json b/extensions/git/package-lock.json index b15b708e618c..37be7ae3b2ce 100644 --- a/extensions/git/package-lock.json +++ b/extensions/git/package-lock.json @@ -9,207 +9,253 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@joaomoreno/unique-names-generator": "^5.1.0", - "@vscode/extension-telemetry": "^0.9.0", - "@vscode/iconv-lite-umd": "0.7.0", + "@joaomoreno/unique-names-generator": "^5.2.0", + "@vscode/extension-telemetry": "^1.2.0", + "@vscode/iconv-lite-umd": "0.7.1", "byline": "^5.0.0", - "file-type": "16.5.4", + "file-type": "21.3.0", "jschardet": "3.1.4", - "picomatch": "2.3.1", - "vscode-uri": "^2.0.0", - "which": "4.0.0" + "picomatch": "4.0.3", + "vscode-uri": "^3.1.0", + "which": "6.0.0" }, "devDependencies": { - "@types/byline": "4.2.31", - "@types/mocha": "^9.1.1", - "@types/node": "20.x", - "@types/picomatch": "2.3.0", - "@types/which": "3.0.0" + "@types/byline": "4.2.36", + "@types/mocha": "^10.0.10", + "@types/node": "25.x", + "@types/picomatch": "4.0.2", + "@types/which": "3.0.4" }, "engines": { "vscode": "^1.5.0" } }, + "node_modules/@borewit/text-codec": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.1.1.tgz", + "integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/@joaomoreno/unique-names-generator": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@joaomoreno/unique-names-generator/-/unique-names-generator-5.1.0.tgz", - "integrity": "sha512-KEVThTpUIKPb7dBKJ9mJ3WYnD1mJZZsEinCSp9CVEPlWbDagurFv1RKRjvvujrLfJzsGc0HkBHS9W8Bughao4A==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@joaomoreno/unique-names-generator/-/unique-names-generator-5.2.0.tgz", + "integrity": "sha512-JEh3qZ85Z6syFvQlhRGRyTPI1M5VticiiP8Xl8EV0XfyfI4Mwzd6Zw28BBrEgUJCYv/cpKCQClVj3J8Tn0KFiA==", + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", + "license": "MIT" + }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } }, "node_modules/@tokenizer/token": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" }, "node_modules/@types/byline": { - "version": "4.2.31", - "resolved": "https://registry.npmjs.org/@types/byline/-/byline-4.2.31.tgz", - "integrity": "sha1-DmH8ucA+BH0hxEllVMcRYperYM0= sha512-TC6Ljn7tALesQMQyTNoMWoM44SNvWtCLkJDrA/TxcwE5ILkWt4zi5wbEokqiDk42S75eykAY1onPImWDybOkmQ==", + "version": "4.2.36", + "resolved": "https://registry.npmjs.org/@types/byline/-/byline-4.2.36.tgz", + "integrity": "sha512-dO55KDSaOSE+3T8TwP66mzn0u/PM/aSedVMr1tby7WBNjfLIuS6IbYXi1mlau49sVSVB+gXKJscWE0JO3tlXDw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@types/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g==", - "dev": true + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/which": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.0.tgz", - "integrity": "sha512-ASCxdbsrwNfSMXALlC3Decif9rwDMu+80KGp5zI2RLRotfMsTv7fHL8W8VDp24wymzDyIFudhUeSCugrgRFfHQ==", - "dev": true + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.4.tgz", + "integrity": "sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w==", + "dev": true, + "license": "MIT" }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.2.0.tgz", + "integrity": "sha512-En6dTwfy5NFzSMibvOpx/lKq2jtgWuR4++KJbi3SpQ2iT8gm+PHo9868/scocW122KDwTxl4ruxZ7i4rHmJJnQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" } }, "node_modules/@vscode/iconv-lite-umd": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz", - "integrity": "sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg==" + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.1.tgz", + "integrity": "sha512-tK6k0DXFHW7q5+GGuGZO+phpAqpxO4WXl+BLc/8/uOk3RsM2ssAL3CQUQDb1TGfwltjsauhN6S4ghYZzs4sPFw==", + "license": "MIT" }, "node_modules/byline": { "version": "5.0.0", @@ -219,17 +265,36 @@ "node": ">=0.10.0" } }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/file-type": { - "version": "16.5.4", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", - "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", + "version": "21.3.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.0.tgz", + "integrity": "sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==", + "license": "MIT", "dependencies": { - "readable-web-to-node-stream": "^3.0.0", - "strtok3": "^6.2.4", - "token-types": "^4.1.1" + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" }, "engines": { - "node": ">=10" + "node": ">=20" }, "funding": { "url": "https://github.com/sindresorhus/file-type?sponsor=1" @@ -252,12 +317,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + ], + "license": "BSD-3-Clause" }, "node_modules/isexe": { "version": "3.1.1", @@ -276,136 +337,88 @@ "node": ">=0.1.90" } }, - "node_modules/peek-readable": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", - "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", - "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "node_modules/strtok3": { + "version": "10.3.4", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", + "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", + "license": "MIT", "dependencies": { - "readable-stream": "^3.6.0" + "@tokenizer/token": "^0.3.0" }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/strtok3": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", - "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", + "node_modules/token-types": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.1.tgz", + "integrity": "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==", + "license": "MIT", "dependencies": { + "@borewit/text-codec": "^0.1.0", "@tokenizer/token": "^0.3.0", - "peek-readable": "^4.1.0" + "ieee754": "^1.2.1" }, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/token-types": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.0.tgz", - "integrity": "sha512-P0rrp4wUpefLncNamWIef62J0v0kQR/GfDVji9WKY7GDCWy5YbVSrKUTam07iWPZQGy0zWNOfstYTykMmPNR7w==", - "dependencies": { - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-uri": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.0.0.tgz", - "integrity": "sha512-lWXWofDSYD8r/TIyu64MdwB4FaSirQ608PP/TzUyslyOeHGwQ0eTHUZeJrK1ILOmwUHaJtV693m2JoUYroUDpw==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" }, "node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", + "license": "ISC", "dependencies": { "isexe": "^3.1.1" }, @@ -413,7 +426,7 @@ "node-which": "bin/which.js" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } } } diff --git a/extensions/git/package.json b/extensions/git/package.json index 9743287dfb99..83249197c4a3 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -21,7 +21,6 @@ "contribSourceControlInputBoxMenu", "contribSourceControlTitleMenu", "contribViewsWelcome", - "diffCommand", "editSessionIdentityProvider", "quickDiffProvider", "quickInputButtonLocation", @@ -32,8 +31,10 @@ "scmSelectedProvider", "scmTextDocument", "scmValidation", + "statusBarItemTooltip", "tabInputMultiDiff", "tabInputTextMerge", + "textEditorDiffInformation", "timeline" ], "categories": [ @@ -446,6 +447,12 @@ "category": "Git", "enablement": "!operationInProgress" }, + { + "command": "git.graph.checkout", + "title": "%command.graphCheckout%", + "category": "Git", + "enablement": "!operationInProgress" + }, { "command": "git.checkoutDetached", "title": "%command.checkoutDetached%", @@ -453,8 +460,8 @@ "enablement": "!operationInProgress" }, { - "command": "git.checkoutRefDetached", - "title": "%command.checkoutRefDetached%", + "command": "git.graph.checkoutDetached", + "title": "%command.graphCheckoutDetached%", "category": "Git", "enablement": "!operationInProgress" }, @@ -476,6 +483,18 @@ "category": "Git", "enablement": "!operationInProgress" }, + { + "command": "git.graph.deleteBranch", + "title": "%command.graphDeleteBranch%", + "category": "Git", + "enablement": "!operationInProgress" + }, + { + "command": "git.deleteRemoteBranch", + "title": "%command.deleteRemoteBranch%", + "category": "Git", + "enablement": "!operationInProgress" + }, { "command": "git.renameBranch", "title": "%command.renameBranch%", @@ -512,6 +531,12 @@ "category": "Git", "enablement": "!operationInProgress" }, + { + "command": "git.graph.deleteTag", + "title": "%command.graphDeleteTag%", + "category": "Git", + "enablement": "!operationInProgress" + }, { "command": "git.deleteRemoteTag", "title": "%command.deleteRemoteTag%", @@ -567,7 +592,7 @@ "title": "%command.pull%", "icon": "$(repo-pull)", "category": "Git", - "enablement": "!operationInProgress" + "enablement": "!operationInProgress && scmCurrentHistoryItemRefInFilter && scmCurrentHistoryItemRefHasRemote" }, { "command": "git.push", @@ -616,7 +641,7 @@ "title": "%command.push%", "icon": "$(repo-push)", "category": "Git", - "enablement": "!operationInProgress" + "enablement": "!operationInProgress && scmCurrentHistoryItemRefInFilter && scmCurrentHistoryItemRefHasRemote" }, { "command": "git.cherryPick", @@ -625,8 +650,8 @@ "enablement": "!operationInProgress" }, { - "command": "git.cherryPickRef", - "title": "%command.cherryPickRef%", + "command": "git.graph.cherryPick", + "title": "%command.graphCherryPick%", "category": "Git", "enablement": "!operationInProgress" }, @@ -905,13 +930,6 @@ "category": "Git", "enablement": "!operationInProgress" }, - { - "command": "git.viewAllChanges", - "title": "%command.viewAllChanges%", - "icon": "$(diff-multiple)", - "category": "Git", - "enablement": "!operationInProgress" - }, { "command": "git.copyCommitId", "title": "%command.timelineCopyCommitId%", @@ -921,6 +939,16 @@ "command": "git.copyCommitMessage", "title": "%command.timelineCopyCommitMessage%", "category": "Git" + }, + { + "command": "git.blame.toggleEditorDecoration", + "title": "%command.blameToggleEditorDecoration%", + "category": "Git" + }, + { + "command": "git.blame.toggleStatusBarItem", + "title": "%command.blameToggleStatusBarItem%", + "category": "Git" } ], "continueEditSession": [ @@ -936,19 +964,19 @@ "command": "git.stageSelectedRanges", "key": "ctrl+k ctrl+alt+s", "mac": "cmd+k cmd+alt+s", - "when": "isInDiffEditor" + "when": "editorTextFocus && resourceScheme == file" }, { "command": "git.unstageSelectedRanges", "key": "ctrl+k ctrl+n", "mac": "cmd+k cmd+n", - "when": "isInDiffEditor" + "when": "editorTextFocus && isInDiffEditor && isInDiffRightEditor && resourceScheme == git" }, { "command": "git.revertSelectedRanges", "key": "ctrl+k ctrl+r", "mac": "cmd+k cmd+r", - "when": "isInDiffEditor" + "when": "editorTextFocus && resourceScheme == file" } ], "menus": { @@ -1019,7 +1047,7 @@ }, { "command": "git.stageSelectedRanges", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == file" }, { "command": "git.stageChange", @@ -1027,7 +1055,7 @@ }, { "command": "git.revertSelectedRanges", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == file" }, { "command": "git.revertChange", @@ -1047,7 +1075,7 @@ }, { "command": "git.unstageSelectedRanges", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == git" }, { "command": "git.clean", @@ -1201,6 +1229,10 @@ "command": "git.deleteBranch", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" }, + { + "command": "git.deleteRemoteBranch", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" + }, { "command": "git.renameBranch", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" @@ -1437,10 +1469,6 @@ "command": "git.viewCommit", "when": "false" }, - { - "command": "git.viewAllChanges", - "when": "false" - }, { "command": "git.stageFile", "when": "false" @@ -1470,12 +1498,32 @@ "when": "false" }, { - "command": "git.checkoutRefDetached", + "command": "git.graph.checkout", "when": "false" }, { - "command": "git.cherryPickRef", + "command": "git.graph.checkoutDetached", "when": "false" + }, + { + "command": "git.graph.deleteBranch", + "when": "false" + }, + { + "command": "git.graph.deleteTag", + "when": "false" + }, + { + "command": "git.graph.cherryPick", + "when": "false" + }, + { + "command": "git.diff.stageHunk", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && diffEditorOriginalUri =~ /^git\\:.*%22ref%22%3A%22~%22%7D$/" + }, + { + "command": "git.diff.stageSelection", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && diffEditorOriginalUri =~ /^git\\:.*%22ref%22%3A%22~%22%7D$/" } ], "scm/title": [ @@ -1968,30 +2016,45 @@ "scm/history/title": [ { "command": "git.fetchAll", - "group": "navigation@999", + "group": "navigation@900", "when": "scmProvider == git" + }, + { + "command": "git.pullRef", + "group": "navigation@901", + "when": "scmProvider == git" + }, + { + "command": "git.pushRef", + "when": "scmProvider == git && scmCurrentHistoryItemRefHasRemote", + "group": "navigation@902" + }, + { + "command": "git.publish", + "when": "scmProvider == git && !scmCurrentHistoryItemRefHasRemote", + "group": "navigation@903" } ], "scm/historyItem/context": [ { - "command": "git.createTag", + "command": "git.graph.checkoutDetached", "when": "scmProvider == git", - "group": "1_create@1" + "group": "1_checkout@2" }, { "command": "git.branch", "when": "scmProvider == git", - "group": "1_create@2" + "group": "2_branch@2" }, { - "command": "git.cherryPickRef", + "command": "git.createTag", "when": "scmProvider == git", - "group": "2_modify@1" + "group": "3_tag@1" }, { - "command": "git.checkoutRefDetached", + "command": "git.graph.cherryPick", "when": "scmProvider == git", - "group": "2_modify@2" + "group": "4_modify@1" }, { "command": "git.copyCommitId", @@ -2004,6 +2067,23 @@ "group": "9_copy@2" } ], + "scm/historyItemRef/context": [ + { + "command": "git.graph.checkout", + "when": "scmProvider == git", + "group": "1_checkout@1" + }, + { + "command": "git.graph.deleteBranch", + "when": "scmProvider == git && scmHistoryItemRef =~ /^refs\\/heads\\/|^refs\\/remotes\\//", + "group": "2_branch@2" + }, + { + "command": "git.graph.deleteTag", + "when": "scmProvider == git && scmHistoryItemRef =~ /^refs\\/tags\\//", + "group": "3_tag@2" + } + ], "editor/title": [ { "command": "git.openFile", @@ -2017,7 +2097,7 @@ }, { "command": "git.openChange", - "group": "navigation", + "group": "navigation@2", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && !isInDiffEditor && !isMergeEditor && resourceScheme == file && scmActiveResourceHasChanges" }, { @@ -2031,47 +2111,67 @@ "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && editorLangId == git-commit" }, { - "command": "git.stageSelectedRanges", + "command": "git.stashApplyEditor", + "alt": "git.stashPopEditor", + "group": "navigation@1", + "when": "config.git.enabled && !git.missing && resourceScheme == git-stash" + }, + { + "command": "git.stashDropEditor", + "group": "navigation@2", + "when": "config.git.enabled && !git.missing && resourceScheme == git-stash" + }, + { + "command": "git.stage", "group": "2_git@1", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && !isInDiffEditor && !isMergeEditor && resourceScheme == file && git.activeResourceHasUnstagedChanges" }, { - "command": "git.unstageSelectedRanges", + "command": "git.unstage", "group": "2_git@2", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && !isInDiffEditor && !isMergeEditor && resourceScheme == file && git.activeResourceHasStagedChanges" }, { - "command": "git.revertSelectedRanges", + "command": "git.stage", + "group": "2_git@1", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && isInDiffRightEditor && !isEmbeddedDiffEditor && resourceScheme == file" + }, + { + "command": "git.stageSelectedRanges", + "group": "2_git@2", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && isInDiffRightEditor && !isEmbeddedDiffEditor && resourceScheme == file" + }, + { + "command": "git.unstage", "group": "2_git@3", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && isInDiffRightEditor && !isEmbeddedDiffEditor && resourceScheme == git" }, { - "command": "git.stashApplyEditor", - "alt": "git.stashPopEditor", - "group": "navigation@1", - "when": "config.git.enabled && !git.missing && resourceScheme == git-stash" + "command": "git.unstageSelectedRanges", + "group": "2_git@4", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && isInDiffRightEditor && !isEmbeddedDiffEditor && resourceScheme == git" }, { - "command": "git.stashDropEditor", - "group": "navigation@2", - "when": "config.git.enabled && !git.missing && resourceScheme == git-stash" + "command": "git.revertSelectedRanges", + "group": "2_git@5", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && isInDiffRightEditor && !isEmbeddedDiffEditor && resourceScheme == file" } ], "editor/context": [ { "command": "git.stageSelectedRanges", "group": "2_git@1", - "when": "isInDiffRightEditor && !isEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && isInDiffRightEditor && !isEmbeddedDiffEditor && resourceScheme == file" }, { "command": "git.unstageSelectedRanges", "group": "2_git@2", - "when": "isInDiffRightEditor && !isEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && isInDiffRightEditor && !isEmbeddedDiffEditor && resourceScheme == git" }, { "command": "git.revertSelectedRanges", "group": "2_git@3", - "when": "isInDiffRightEditor && !isEmbeddedDiffEditor && config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && isInDiffEditor && isInDiffRightEditor && !isEmbeddedDiffEditor && resourceScheme == file" } ], "editor/content": [ @@ -2346,6 +2446,10 @@ "command": "git.deleteBranch", "group": "3_modify@2" }, + { + "command": "git.deleteRemoteBranch", + "group": "3_modify@3" + }, { "command": "git.publish", "group": "4_publish@1" @@ -3164,6 +3268,68 @@ "maximum": 100, "markdownDescription": "%config.similarityThreshold%", "scope": "resource" + }, + "git.blame.editorDecoration.enabled": { + "type": "boolean", + "default": false, + "markdownDescription": "%config.blameEditorDecoration.enabled%" + }, + "git.blame.editorDecoration.template": { + "type": "string", + "default": "${subject}, ${authorName} (${authorDateAgo})", + "markdownDescription": "%config.blameEditorDecoration.template%" + }, + "git.blame.statusBarItem.enabled": { + "type": "boolean", + "default": true, + "markdownDescription": "%config.blameStatusBarItem.enabled%" + }, + "git.blame.statusBarItem.template": { + "type": "string", + "default": "${authorName} (${authorDateAgo})", + "markdownDescription": "%config.blameStatusBarItem.template%" + }, + "git.commitShortHashLength": { + "type": "number", + "default": 7, + "minimum": 7, + "maximum": 40, + "markdownDescription": "%config.commitShortHashLength%", + "scope": "resource" + }, + "git.diagnosticsCommitHook.Enabled": { + "type": "boolean", + "default": false, + "markdownDescription": "%config.diagnosticsCommitHook.Enabled%", + "scope": "resource" + }, + "git.diagnosticsCommitHook.Sources": { + "type": "object", + "additionalProperties": { + "type": "string", + "enum": [ + "error", + "warning", + "information", + "hint", + "none" + ] + }, + "default": { + "*": "error" + }, + "markdownDescription": "%config.diagnosticsCommitHook.Sources%", + "scope": "resource" + }, + "git.discardUntrackedChangesToTrash": { + "type": "boolean", + "default": true, + "markdownDescription": "%config.discardUntrackedChangesToTrash%" + }, + "git.showReferenceDetails": { + "type": "boolean", + "default": false, + "markdownDescription": "%config.showReferenceDetails%" } } }, @@ -3267,6 +3433,16 @@ "highContrast": "#8db9e2", "highContrastLight": "#1258a7" } + }, + { + "id": "git.blame.editorDecorationForeground", + "description": "%colors.blameEditorDecoration%", + "defaults": { + "dark": "editorInlayHint.foreground", + "light": "editorInlayHint.foreground", + "highContrast": "editorInlayHint.foreground", + "highContrastLight": "editorInlayHint.foreground" + } } ], "configurationDefaults": { @@ -3291,22 +3467,22 @@ { "view": "scm", "contents": "%view.workbench.scm.missing%", - "when": "config.git.enabled && git.missing" + "when": "config.git.enabled && git.missing && remoteName != ''" }, { "view": "scm", "contents": "%view.workbench.scm.missing.mac%", - "when": "config.git.enabled && git.missing && isMac" + "when": "config.git.enabled && git.missing && remoteName == '' && isMac" }, { "view": "scm", "contents": "%view.workbench.scm.missing.windows%", - "when": "config.git.enabled && git.missing && isWindows" + "when": "config.git.enabled && git.missing && remoteName == '' && isWindows" }, { "view": "scm", "contents": "%view.workbench.scm.missing.linux%", - "when": "config.git.enabled && git.missing && isLinux" + "when": "config.git.enabled && git.missing && remoteName == '' && isLinux" }, { "view": "scm", @@ -3389,22 +3565,22 @@ ] }, "dependencies": { - "@joaomoreno/unique-names-generator": "^5.1.0", - "@vscode/extension-telemetry": "^0.9.0", - "@vscode/iconv-lite-umd": "0.7.0", + "@joaomoreno/unique-names-generator": "^5.2.0", + "@vscode/extension-telemetry": "^1.2.0", + "@vscode/iconv-lite-umd": "0.7.1", "byline": "^5.0.0", - "file-type": "16.5.4", + "file-type": "21.3.0", "jschardet": "3.1.4", - "picomatch": "2.3.1", - "vscode-uri": "^2.0.0", - "which": "4.0.0" + "picomatch": "4.0.3", + "vscode-uri": "^3.1.0", + "which": "6.0.0" }, "devDependencies": { - "@types/byline": "4.2.31", - "@types/mocha": "^9.1.1", - "@types/node": "20.x", - "@types/picomatch": "2.3.0", - "@types/which": "3.0.0" + "@types/byline": "4.2.36", + "@types/mocha": "^10.0.10", + "@types/node": "25.x", + "@types/picomatch": "4.0.2", + "@types/which": "3.0.4" }, "repository": { "type": "git", diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 78a6b0f2e7a1..52ba38198170 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -62,13 +62,12 @@ "command.undoCommit": "Undo Last Commit", "command.checkout": "Checkout to...", "command.checkoutDetached": "Checkout to (Detached)...", - "command.checkoutRefDetached": "Checkout (Detached)", "command.branch": "Create Branch...", "command.branchFrom": "Create Branch From...", "command.deleteBranch": "Delete Branch...", + "command.deleteRemoteBranch": "Delete Remote Branch...", "command.renameBranch": "Rename Branch...", "command.cherryPick": "Cherry Pick...", - "command.cherryPickRef": "Cherry Pick", "command.cherryPickAbort": "Abort Cherry Pick", "command.merge": "Merge...", "command.mergeAbort": "Abort Merge", @@ -121,11 +120,17 @@ "command.timelineCompareWithSelected": "Compare with Selected", "command.manageUnsafeRepositories": "Manage Unsafe Repositories", "command.openRepositoriesInParentFolders": "Open Repositories In Parent Folders", - "command.viewChanges": "View Changes", - "command.viewStagedChanges": "View Staged Changes", - "command.viewUntrackedChanges": "View Untracked Changes", - "command.viewAllChanges": "View All Changes", - "command.viewCommit": "View Commit", + "command.viewChanges": "Open Changes", + "command.viewStagedChanges": "Open Staged Changes", + "command.viewUntrackedChanges": "Open Untracked Changes", + "command.viewCommit": "Open Commit", + "command.graphCheckout": "Checkout", + "command.graphCheckoutDetached": "Checkout (Detached)", + "command.graphCherryPick": "Cherry Pick", + "command.graphDeleteBranch": "Delete Branch", + "command.graphDeleteTag": "Delete Tag", + "command.blameToggleEditorDecoration": "Toggle Git Blame Editor Decoration", + "command.blameToggleStatusBarItem": "Toggle Git Blame Status Bar Item", "command.api.getRepositories": "Get Repositories", "command.api.getRepositoryState": "Get Repository State", "command.api.getRemoteSources": "Get Remote Sources", @@ -276,6 +281,15 @@ "config.publishBeforeContinueOn.never": "Never publish unpublished Git state when using Continue Working On from a Git repository", "config.publishBeforeContinueOn.prompt": "Prompt to publish unpublished Git state when using Continue Working On from a Git repository", "config.similarityThreshold": "Controls the threshold of the similarity index (the amount of additions/deletions compared to the file's size) for changes in a pair of added/deleted files to be considered a rename. **Note:** Requires Git version `2.18.0` or later.", + "config.blameEditorDecoration.enabled": "Controls whether to show blame information in the editor using editor decorations.", + "config.blameEditorDecoration.template": "Template for the blame information editor decoration. Supported variables:\n\n* `hash`: Commit hash\n\n* `hashShort`: First N characters of the commit hash according to `#git.commitShortHashLength#`\n\n* `subject`: First line of the commit message\n\n* `authorName`: Author name\n\n* `authorEmail`: Author email\n\n* `authorDate`: Author date\n\n* `authorDateAgo`: Time difference between now and the author date\n\n", + "config.blameStatusBarItem.enabled": "Controls whether to show blame information in the status bar.", + "config.blameStatusBarItem.template": "Template for the blame information status bar item. Supported variables:\n\n* `hash`: Commit hash\n\n* `hashShort`: First N characters of the commit hash according to `#git.commitShortHashLength#`\n\n* `subject`: First line of the commit message\n\n* `authorName`: Author name\n\n* `authorEmail`: Author email\n\n* `authorDate`: Author date\n\n* `authorDateAgo`: Time difference between now and the author date\n\n", + "config.commitShortHashLength": "Controls the length of the commit short hash.", + "config.diagnosticsCommitHook.Enabled": "Controls whether to check for unresolved diagnostics before committing.", + "config.diagnosticsCommitHook.Sources": "Controls the list of sources (**Item**) and the minimum severity (**Value**) to be considered before committing. **Note:** To ignore diagnostics from a particular source, add the source to the list and set the minimum severity to `none`.", + "config.discardUntrackedChangesToTrash": "Controls whether discarding untracked changes moves the file(s) to the Recycle Bin (Windows), Trash (macOS, Linux) instead of deleting them permanently. **Note:** This setting has no effect when connected to a remote or when running in Linux as a snap package.", + "config.showReferenceDetails": "Controls whether to show the details of the last commit for Git refs in the checkout, branch, and tag pickers.", "submenu.explorer": "Git", "submenu.commit": "Commit", "submenu.commit.amend": "Amend", @@ -300,10 +314,13 @@ "colors.incomingDeleted": "Color for deleted incoming resource.", "colors.incomingRenamed": "Color for renamed incoming resource.", "colors.incomingModified": "Color for modified incoming resource.", + "colors.blameEditorDecoration": "Color for the blame editor decoration.", "view.workbench.scm.missing.windows": { "message": "[Download Git for Windows](https://git-scm.com/download/win)\nAfter installing, please [reload](command:workbench.action.reloadWindow) (or [troubleshoot](command:git.showOutput)). Additional source control providers can be installed [from the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22).", "comment": [ "{Locked='](command:workbench.action.reloadWindow'}", + "{Locked='](command:git.showOutput'}", + "{Locked='](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22'}", "Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for VS Code", "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" ] @@ -312,6 +329,8 @@ "message": "[Download Git for macOS](https://git-scm.com/download/mac)\nAfter installing, please [reload](command:workbench.action.reloadWindow) (or [troubleshoot](command:git.showOutput)). Additional source control providers can be installed [from the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22).", "comment": [ "{Locked='](command:workbench.action.reloadWindow'}", + "{Locked='](command:git.showOutput'}", + "{Locked='](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22'}", "Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for VS Code", "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" ] @@ -320,11 +339,19 @@ "message": "Source control depends on Git being installed.\n[Download Git for Linux](https://git-scm.com/download/linux)\nAfter installing, please [reload](command:workbench.action.reloadWindow) (or [troubleshoot](command:git.showOutput)). Additional source control providers can be installed [from the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22).", "comment": [ "{Locked='](command:workbench.action.reloadWindow'}", + "{Locked='](command:git.showOutput'}", + "{Locked='](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22'}", "Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for VS Code", "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" ] }, - "view.workbench.scm.missing": "Install Git, a popular source control system, to track code changes and collaborate with others. Learn more in our [Git guides](https://aka.ms/vscode-scm).", + "view.workbench.scm.missing": { + "message": "Install Git, a popular source control system, to track code changes and collaborate with others. Learn more in our [Git guides](https://aka.ms/vscode-scm).", + "comment": [ + "{Locked='](https://aka.ms/vscode-scm'}", + "Please make sure there is no space between the right bracket and left parenthesis: ]( this is an internal syntax for links" + ] + }, "view.workbench.scm.disabled": { "message": "If you would like to use Git features, please enable Git in your [settings](command:workbench.action.openSettings?%5B%22git.enabled%22%5D).\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "comment": [ @@ -334,7 +361,7 @@ ] }, "view.workbench.scm.empty": { - "message": "In order to use Git features, you can open a folder containing a Git repository or clone from a URL.\n[Open Folder](command:vscode.openFolder)\n[Clone Repository](command:git.clone)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", + "message": "In order to use Git features, you can open a folder containing a Git repository or clone from a URL.\n[Open Folder](command:vscode.openFolder)\n[Clone Repository](command:git.cloneRecursive)\nTo learn more about how to use Git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", "comment": [ "{Locked='](command:vscode.openFolder'}", "Do not translate the 'command:*' part inside of the '(..)'. It is an internal command syntax for VS Code", diff --git a/extensions/git/src/actionButton.ts b/extensions/git/src/actionButton.ts index 2fbdaf4f97eb..63eefb1de028 100644 --- a/extensions/git/src/actionButton.ts +++ b/extensions/git/src/actionButton.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Command, Disposable, Event, EventEmitter, SourceControlActionButton, Uri, workspace, l10n } from 'vscode'; +import { Command, Disposable, Event, EventEmitter, SourceControlActionButton, Uri, workspace, l10n, LogOutputChannel } from 'vscode'; import { Branch, RefType, Status } from './api/git'; import { OperationKind } from './operation'; import { CommitCommandsCenter } from './postCommitCommands'; @@ -25,7 +25,8 @@ function isActionButtonStateEqual(state1: ActionButtonState, state2: ActionButto state1.isMergeInProgress === state2.isMergeInProgress && state1.isRebaseInProgress === state2.isRebaseInProgress && state1.isSyncInProgress === state2.isSyncInProgress && - state1.repositoryHasChangesToCommit === state2.repositoryHasChangesToCommit; + state1.repositoryHasChangesToCommit === state2.repositoryHasChangesToCommit && + state1.repositoryHasUnresolvedConflicts === state2.repositoryHasUnresolvedConflicts; } interface ActionButtonState { @@ -36,6 +37,7 @@ interface ActionButtonState { readonly isRebaseInProgress: boolean; readonly isSyncInProgress: boolean; readonly repositoryHasChangesToCommit: boolean; + readonly repositoryHasUnresolvedConflicts: boolean; } export class ActionButton { @@ -49,6 +51,8 @@ export class ActionButton { return; } + this.logger.trace(`[ActionButton][setState] ${JSON.stringify(state)}`); + this._state = state; this._onDidChange.fire(); } @@ -56,8 +60,9 @@ export class ActionButton { private disposables: Disposable[] = []; constructor( - readonly repository: Repository, - readonly postCommitCommandCenter: CommitCommandsCenter) { + private readonly repository: Repository, + private readonly postCommitCommandCenter: CommitCommandsCenter, + private readonly logger: LogOutputChannel) { this._state = { HEAD: undefined, isCheckoutInProgress: false, @@ -65,7 +70,8 @@ export class ActionButton { isMergeInProgress: false, isRebaseInProgress: false, isSyncInProgress: false, - repositoryHasChangesToCommit: false + repositoryHasChangesToCommit: false, + repositoryHasUnresolvedConflicts: false }; repository.onDidRunGitStatus(this.onDidRunGitStatus, this, this.disposables); @@ -102,7 +108,15 @@ export class ActionButton { } // Commit Changes (enabled) -> Publish Branch -> Sync Changes -> Commit Changes (disabled) - return actionButton ?? this.getPublishBranchActionButton() ?? this.getSyncChangesActionButton() ?? this.getCommitActionButton(); + actionButton = actionButton ?? this.getPublishBranchActionButton() ?? this.getSyncChangesActionButton() ?? this.getCommitActionButton(); + + this.logger.trace(`[ActionButton][getButton] ${JSON.stringify({ + command: actionButton?.command.command, + title: actionButton?.command.title, + enabled: actionButton?.enabled + })}`); + + return actionButton; } private getCommitActionButton(): SourceControlActionButton | undefined { @@ -117,7 +131,11 @@ export class ActionButton { return { command: primaryCommand, secondaryCommands: this.getCommitActionButtonSecondaryCommands(), - enabled: (this.state.repositoryHasChangesToCommit || this.state.isRebaseInProgress) && !this.state.isCommitInProgress && !this.state.isMergeInProgress + enabled: ( + this.state.repositoryHasChangesToCommit || + (this.state.isRebaseInProgress && !this.state.repositoryHasUnresolvedConflicts) || + (this.state.isMergeInProgress && !this.state.repositoryHasUnresolvedConflicts)) && + !this.state.isCommitInProgress }; } @@ -132,6 +150,16 @@ export class ActionButton { }; } + // Merge Continue + if (this.state.isMergeInProgress) { + return { + command: 'git.commit', + title: l10n.t('{0} Continue', '$(check)'), + tooltip: this.state.isCommitInProgress ? l10n.t('Continuing Merge...') : l10n.t('Continue Merge'), + arguments: [this.repository.sourceControl, null] + }; + } + // Not a branch (tag, detached) if (this.state.HEAD?.type === RefType.Tag || !this.state.HEAD?.name) { return { @@ -152,6 +180,11 @@ export class ActionButton { return []; } + // Merge Continue + if (this.state.isMergeInProgress) { + return []; + } + // Not a branch (tag, detached) if (this.state.HEAD?.type === RefType.Tag || !this.state.HEAD?.name) { return []; @@ -250,9 +283,10 @@ export class ActionButton { this.state = { ...this.state, HEAD: this.repository.HEAD, - isMergeInProgress: this.repository.mergeGroup.resourceStates.length !== 0, + isMergeInProgress: this.repository.mergeInProgress, isRebaseInProgress: !!this.repository.rebaseCommit, - repositoryHasChangesToCommit: this.repositoryHasChangesToCommit() + repositoryHasChangesToCommit: this.repositoryHasChangesToCommit(), + repositoryHasUnresolvedConflicts: this.repository.mergeGroup.resourceStates.length > 0 }; } diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index 63139af2447d..d715c535a501 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -3,230 +3,259 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-native-private */ + import { Model } from '../model'; import { Repository as BaseRepository, Resource } from '../repository'; -import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, ForcePushMode, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent, FetchOptions, RemoteSourceProvider, RemoteSourcePublisher, PostCommitCommandsProvider, RefQuery, BranchProtectionProvider, InitOptions } from './git'; +import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, ForcePushMode, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, RefType, CredentialsProvider, BranchQuery, PushErrorHandler, PublishEvent, FetchOptions, RemoteSourceProvider, RemoteSourcePublisher, PostCommitCommandsProvider, RefQuery, BranchProtectionProvider, InitOptions, SourceControlHistoryItemDetailsProvider } from './git'; import { Event, SourceControlInputBox, Uri, SourceControl, Disposable, commands, CancellationToken } from 'vscode'; import { combinedDisposable, filterEvent, mapEvent } from '../util'; import { toGitUri } from '../uri'; import { GitExtensionImpl } from './extension'; import { GitBaseApi } from '../git-base'; -import { PickRemoteSourceOptions } from './git-base'; -import { Operation, OperationResult } from '../operation'; +import { PickRemoteSourceOptions } from '../typings/git-base'; +import { OperationKind, OperationResult } from '../operation'; class ApiInputBox implements InputBox { - set value(value: string) { this._inputBox.value = value; } - get value(): string { return this._inputBox.value; } - constructor(private _inputBox: SourceControlInputBox) { } + #inputBox: SourceControlInputBox; + + constructor(inputBox: SourceControlInputBox) { this.#inputBox = inputBox; } + + set value(value: string) { this.#inputBox.value = value; } + get value(): string { return this.#inputBox.value; } } export class ApiChange implements Change { + #resource: Resource; + constructor(resource: Resource) { this.#resource = resource; } - get uri(): Uri { return this.resource.resourceUri; } - get originalUri(): Uri { return this.resource.original; } - get renameUri(): Uri | undefined { return this.resource.renameResourceUri; } - get status(): Status { return this.resource.type; } - - constructor(private readonly resource: Resource) { } + get uri(): Uri { return this.#resource.resourceUri; } + get originalUri(): Uri { return this.#resource.original; } + get renameUri(): Uri | undefined { return this.#resource.renameResourceUri; } + get status(): Status { return this.#resource.type; } } export class ApiRepositoryState implements RepositoryState { + #repository: BaseRepository; + readonly onDidChange: Event; - get HEAD(): Branch | undefined { return this._repository.HEAD; } + constructor(repository: BaseRepository) { + this.#repository = repository; + this.onDidChange = this.#repository.onDidRunGitStatus; + } + + get HEAD(): Branch | undefined { return this.#repository.HEAD; } /** * @deprecated Use ApiRepository.getRefs() instead. */ get refs(): Ref[] { console.warn('Deprecated. Use ApiRepository.getRefs() instead.'); return []; } - get remotes(): Remote[] { return [...this._repository.remotes]; } - get submodules(): Submodule[] { return [...this._repository.submodules]; } - get rebaseCommit(): Commit | undefined { return this._repository.rebaseCommit; } - - get mergeChanges(): Change[] { return this._repository.mergeGroup.resourceStates.map(r => new ApiChange(r)); } - get indexChanges(): Change[] { return this._repository.indexGroup.resourceStates.map(r => new ApiChange(r)); } - get workingTreeChanges(): Change[] { return this._repository.workingTreeGroup.resourceStates.map(r => new ApiChange(r)); } - get untrackedChanges(): Change[] { return this._repository.untrackedGroup.resourceStates.map(r => new ApiChange(r)); } - - readonly onDidChange: Event = this._repository.onDidRunGitStatus; - - constructor(private _repository: BaseRepository) { } + get remotes(): Remote[] { return [...this.#repository.remotes]; } + get submodules(): Submodule[] { return [...this.#repository.submodules]; } + get rebaseCommit(): Commit | undefined { return this.#repository.rebaseCommit; } + + get mergeChanges(): Change[] { return this.#repository.mergeGroup.resourceStates.map(r => new ApiChange(r)); } + get indexChanges(): Change[] { return this.#repository.indexGroup.resourceStates.map(r => new ApiChange(r)); } + get workingTreeChanges(): Change[] { return this.#repository.workingTreeGroup.resourceStates.map(r => new ApiChange(r)); } + get untrackedChanges(): Change[] { return this.#repository.untrackedGroup.resourceStates.map(r => new ApiChange(r)); } } export class ApiRepositoryUIState implements RepositoryUIState { + #sourceControl: SourceControl; + readonly onDidChange: Event; - get selected(): boolean { return this._sourceControl.selected; } - - readonly onDidChange: Event = mapEvent(this._sourceControl.onDidChangeSelection, () => null); + constructor(sourceControl: SourceControl) { + this.#sourceControl = sourceControl; + this.onDidChange = mapEvent(this.#sourceControl.onDidChangeSelection, () => null); + } - constructor(private _sourceControl: SourceControl) { } + get selected(): boolean { return this.#sourceControl.selected; } } export class ApiRepository implements Repository { + #repository: BaseRepository; + + readonly rootUri: Uri; + readonly inputBox: InputBox; + readonly state: RepositoryState; + readonly ui: RepositoryUIState; - readonly rootUri: Uri = Uri.file(this.repository.root); - readonly inputBox: InputBox = new ApiInputBox(this.repository.inputBox); - readonly state: RepositoryState = new ApiRepositoryState(this.repository); - readonly ui: RepositoryUIState = new ApiRepositoryUIState(this.repository.sourceControl); + readonly onDidCommit: Event; + readonly onDidCheckout: Event; - readonly onDidCommit: Event = mapEvent(filterEvent(this.repository.onDidRunOperation, e => e.operation === Operation.Commit), () => null); + constructor(repository: BaseRepository) { + this.#repository = repository; - constructor(readonly repository: BaseRepository) { } + this.rootUri = Uri.file(this.#repository.root); + this.inputBox = new ApiInputBox(this.#repository.inputBox); + this.state = new ApiRepositoryState(this.#repository); + this.ui = new ApiRepositoryUIState(this.#repository.sourceControl); + + this.onDidCommit = mapEvent( + filterEvent(this.#repository.onDidRunOperation, e => e.operation.kind === OperationKind.Commit), () => null); + this.onDidCheckout = mapEvent( + filterEvent(this.#repository.onDidRunOperation, e => e.operation.kind === OperationKind.Checkout || e.operation.kind === OperationKind.CheckoutTracking), () => null); + } apply(patch: string, reverse?: boolean): Promise { - return this.repository.apply(patch, reverse); + return this.#repository.apply(patch, reverse); } getConfigs(): Promise<{ key: string; value: string }[]> { - return this.repository.getConfigs(); + return this.#repository.getConfigs(); } getConfig(key: string): Promise { - return this.repository.getConfig(key); + return this.#repository.getConfig(key); } setConfig(key: string, value: string): Promise { - return this.repository.setConfig(key, value); + return this.#repository.setConfig(key, value); + } + + unsetConfig(key: string): Promise { + return this.#repository.unsetConfig(key); } getGlobalConfig(key: string): Promise { - return this.repository.getGlobalConfig(key); + return this.#repository.getGlobalConfig(key); } getObjectDetails(treeish: string, path: string): Promise<{ mode: string; object: string; size: number }> { - return this.repository.getObjectDetails(treeish, path); + return this.#repository.getObjectDetails(treeish, path); } detectObjectType(object: string): Promise<{ mimetype: string; encoding?: string }> { - return this.repository.detectObjectType(object); + return this.#repository.detectObjectType(object); } buffer(ref: string, filePath: string): Promise { - return this.repository.buffer(ref, filePath); + return this.#repository.buffer(ref, filePath); } show(ref: string, path: string): Promise { - return this.repository.show(ref, path); + return this.#repository.show(ref, path); } getCommit(ref: string): Promise { - return this.repository.getCommit(ref); + return this.#repository.getCommit(ref); } add(paths: string[]) { - return this.repository.add(paths.map(p => Uri.file(p))); + return this.#repository.add(paths.map(p => Uri.file(p))); } revert(paths: string[]) { - return this.repository.revert(paths.map(p => Uri.file(p))); + return this.#repository.revert(paths.map(p => Uri.file(p))); } clean(paths: string[]) { - return this.repository.clean(paths.map(p => Uri.file(p))); + return this.#repository.clean(paths.map(p => Uri.file(p))); } diff(cached?: boolean) { - return this.repository.diff(cached); + return this.#repository.diff(cached); } diffWithHEAD(): Promise; diffWithHEAD(path: string): Promise; diffWithHEAD(path?: string): Promise { - return this.repository.diffWithHEAD(path); + return this.#repository.diffWithHEAD(path); } diffWith(ref: string): Promise; diffWith(ref: string, path: string): Promise; diffWith(ref: string, path?: string): Promise { - return this.repository.diffWith(ref, path); + return this.#repository.diffWith(ref, path); } diffIndexWithHEAD(): Promise; diffIndexWithHEAD(path: string): Promise; diffIndexWithHEAD(path?: string): Promise { - return this.repository.diffIndexWithHEAD(path); + return this.#repository.diffIndexWithHEAD(path); } diffIndexWith(ref: string): Promise; diffIndexWith(ref: string, path: string): Promise; diffIndexWith(ref: string, path?: string): Promise { - return this.repository.diffIndexWith(ref, path); + return this.#repository.diffIndexWith(ref, path); } diffBlobs(object1: string, object2: string): Promise { - return this.repository.diffBlobs(object1, object2); + return this.#repository.diffBlobs(object1, object2); } diffBetween(ref1: string, ref2: string): Promise; diffBetween(ref1: string, ref2: string, path: string): Promise; diffBetween(ref1: string, ref2: string, path?: string): Promise { - return this.repository.diffBetween(ref1, ref2, path); + return this.#repository.diffBetween(ref1, ref2, path); } hashObject(data: string): Promise { - return this.repository.hashObject(data); + return this.#repository.hashObject(data); } createBranch(name: string, checkout: boolean, ref?: string | undefined): Promise { - return this.repository.branch(name, checkout, ref); + return this.#repository.branch(name, checkout, ref); } deleteBranch(name: string, force?: boolean): Promise { - return this.repository.deleteBranch(name, force); + return this.#repository.deleteBranch(name, force); } getBranch(name: string): Promise { - return this.repository.getBranch(name); + return this.#repository.getBranch(name); } getBranches(query: BranchQuery, cancellationToken?: CancellationToken): Promise { - return this.repository.getBranches(query, cancellationToken); + return this.#repository.getBranches(query, cancellationToken); } getBranchBase(name: string): Promise { - return this.repository.getBranchBase(name); + return this.#repository.getBranchBase(name); } setBranchUpstream(name: string, upstream: string): Promise { - return this.repository.setBranchUpstream(name, upstream); + return this.#repository.setBranchUpstream(name, upstream); } getRefs(query: RefQuery, cancellationToken?: CancellationToken): Promise { - return this.repository.getRefs(query, cancellationToken); + return this.#repository.getRefs(query, cancellationToken); } checkIgnore(paths: string[]): Promise> { - return this.repository.checkIgnore(paths); + return this.#repository.checkIgnore(paths); } getMergeBase(ref1: string, ref2: string): Promise { - return this.repository.getMergeBase(ref1, ref2); + return this.#repository.getMergeBase(ref1, ref2); } tag(name: string, message: string, ref?: string | undefined): Promise { - return this.repository.tag({ name, message, ref }); + return this.#repository.tag({ name, message, ref }); } deleteTag(name: string): Promise { - return this.repository.deleteTag(name); + return this.#repository.deleteTag(name); } status(): Promise { - return this.repository.status(); + return this.#repository.status(); } checkout(treeish: string): Promise { - return this.repository.checkout(treeish); + return this.#repository.checkout(treeish); } addRemote(name: string, url: string): Promise { - return this.repository.addRemote(name, url); + return this.#repository.addRemote(name, url); } removeRemote(name: string): Promise { - return this.repository.removeRemote(name); + return this.#repository.removeRemote(name); } renameRemote(name: string, newName: string): Promise { - return this.repository.renameRemote(name, newName); + return this.#repository.renameRemote(name, newName); } fetch(arg0?: FetchOptions | string | undefined, @@ -235,74 +264,102 @@ export class ApiRepository implements Repository { prune?: boolean | undefined ): Promise { if (arg0 !== undefined && typeof arg0 !== 'string') { - return this.repository.fetch(arg0); + return this.#repository.fetch(arg0); } - return this.repository.fetch({ remote: arg0, ref, depth, prune }); + return this.#repository.fetch({ remote: arg0, ref, depth, prune }); } pull(unshallow?: boolean): Promise { - return this.repository.pull(undefined, unshallow); + return this.#repository.pull(undefined, unshallow); } push(remoteName?: string, branchName?: string, setUpstream: boolean = false, force?: ForcePushMode): Promise { - return this.repository.pushTo(remoteName, branchName, setUpstream, force); + return this.#repository.pushTo(remoteName, branchName, setUpstream, force); } blame(path: string): Promise { - return this.repository.blame(path); + return this.#repository.blame(path); } log(options?: LogOptions): Promise { - return this.repository.log(options); + return this.#repository.log(options); } commit(message: string, opts?: CommitOptions): Promise { - return this.repository.commit(message, { ...opts, postCommitCommand: null }); + return this.#repository.commit(message, { ...opts, postCommitCommand: null }); } merge(ref: string): Promise { - return this.repository.merge(ref); + return this.#repository.merge(ref); } mergeAbort(): Promise { - return this.repository.mergeAbort(); + return this.#repository.mergeAbort(); + } + + applyStash(index?: number): Promise { + return this.#repository.applyStash(index); + } + + popStash(index?: number): Promise { + return this.#repository.popStash(index); + } + + dropStash(index?: number): Promise { + return this.#repository.dropStash(index); } } export class ApiGit implements Git { + #model: Model; + + private _env: { [key: string]: string } | undefined; + + constructor(model: Model) { this.#model = model; } - get path(): string { return this._model.git.path; } + get path(): string { return this.#model.git.path; } - constructor(private _model: Model) { } + get env(): { [key: string]: string } { + if (this._env === undefined) { + this._env = Object.freeze(this.#model.git.env); + } + + return this._env; + } } export class ApiImpl implements API { + #model: Model; + readonly git: ApiGit; - readonly git = new ApiGit(this._model); + constructor(model: Model) { + this.#model = model; + this.git = new ApiGit(this.#model); + } get state(): APIState { - return this._model.state; + return this.#model.state; } get onDidChangeState(): Event { - return this._model.onDidChangeState; + return this.#model.onDidChangeState; } get onDidPublish(): Event { - return this._model.onDidPublish; + return this.#model.onDidPublish; } get onDidOpenRepository(): Event { - return mapEvent(this._model.onDidOpenRepository, r => new ApiRepository(r)); + return mapEvent(this.#model.onDidOpenRepository, r => new ApiRepository(r)); } get onDidCloseRepository(): Event { - return mapEvent(this._model.onDidCloseRepository, r => new ApiRepository(r)); + return mapEvent(this.#model.onDidCloseRepository, r => new ApiRepository(r)); } get repositories(): Repository[] { - return this._model.repositories.map(r => new ApiRepository(r)); + return this.#model.repositories.map(r => new ApiRepository(r)); } toGitUri(uri: Uri, ref: string): Uri { @@ -310,14 +367,14 @@ export class ApiImpl implements API { } getRepository(uri: Uri): Repository | null { - const result = this._model.getRepository(uri); + const result = this.#model.getRepository(uri); return result ? new ApiRepository(result) : null; } async init(root: Uri, options?: InitOptions): Promise { const path = root.fsPath; - await this._model.git.init(path, options); - await this._model.openRepository(path); + await this.#model.git.init(path, options); + await this.#model.openRepository(path); return this.getRepository(root) || null; } @@ -326,7 +383,7 @@ export class ApiImpl implements API { return null; } - await this._model.openRepository(root.fsPath); + await this.#model.openRepository(root.fsPath); return this.getRepository(root) || null; } @@ -334,7 +391,7 @@ export class ApiImpl implements API { const disposables: Disposable[] = []; if (provider.publishRepository) { - disposables.push(this._model.registerRemoteSourcePublisher(provider as RemoteSourcePublisher)); + disposables.push(this.#model.registerRemoteSourcePublisher(provider as RemoteSourcePublisher)); } disposables.push(GitBaseApi.getAPI().registerRemoteSourceProvider(provider)); @@ -342,26 +399,28 @@ export class ApiImpl implements API { } registerRemoteSourcePublisher(publisher: RemoteSourcePublisher): Disposable { - return this._model.registerRemoteSourcePublisher(publisher); + return this.#model.registerRemoteSourcePublisher(publisher); } registerCredentialsProvider(provider: CredentialsProvider): Disposable { - return this._model.registerCredentialsProvider(provider); + return this.#model.registerCredentialsProvider(provider); } registerPostCommitCommandsProvider(provider: PostCommitCommandsProvider): Disposable { - return this._model.registerPostCommitCommandsProvider(provider); + return this.#model.registerPostCommitCommandsProvider(provider); } registerPushErrorHandler(handler: PushErrorHandler): Disposable { - return this._model.registerPushErrorHandler(handler); + return this.#model.registerPushErrorHandler(handler); } - registerBranchProtectionProvider(root: Uri, provider: BranchProtectionProvider): Disposable { - return this._model.registerBranchProtectionProvider(root, provider); + registerSourceControlHistoryItemDetailsProvider(provider: SourceControlHistoryItemDetailsProvider): Disposable { + return this.#model.registerSourceControlHistoryItemDetailsProvider(provider); } - constructor(private _model: Model) { } + registerBranchProtectionProvider(root: Uri, provider: BranchProtectionProvider): Disposable { + return this.#model.registerBranchProtectionProvider(root, provider); + } } function getRefType(type: RefType): string { diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index ee45e7a893a3..9562106f9c6d 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Uri, Event, Disposable, ProviderResult, Command, CancellationToken } from 'vscode'; +import { Uri, Event, Disposable, ProviderResult, Command, CancellationToken, SourceControlHistoryItem } from 'vscode'; export { ProviderResult } from 'vscode'; export interface Git { @@ -30,6 +30,7 @@ export interface Ref { readonly type: RefType; readonly name?: string; readonly commit?: string; + readonly commitDetails?: Commit; readonly remote?: string; } @@ -185,6 +186,7 @@ export interface RefQuery { readonly contains?: string; readonly count?: number; readonly pattern?: string | string[]; + readonly includeCommitDetails?: boolean; readonly sort?: 'alphabetically' | 'committerdate'; } @@ -200,10 +202,12 @@ export interface Repository { readonly ui: RepositoryUIState; readonly onDidCommit: Event; + readonly onDidCheckout: Event; getConfigs(): Promise<{ key: string; value: string; }[]>; getConfig(key: string): Promise; setConfig(key: string, value: string): Promise; + unsetConfig(key: string): Promise; getGlobalConfig(key: string): Promise; getObjectDetails(treeish: string, path: string): Promise<{ mode: string, object: string, size: number }>; @@ -266,6 +270,10 @@ export interface Repository { commit(message: string, opts?: CommitOptions): Promise; merge(ref: string): Promise; mergeAbort(): Promise; + + applyStash(index?: number): Promise; + popStash(index?: number): Promise; + dropStash(index?: number): Promise; } export interface RemoteSource { @@ -321,6 +329,23 @@ export interface BranchProtectionProvider { provideBranchProtection(): BranchProtection[]; } +export interface AvatarQueryCommit { + readonly hash: string; + readonly authorName?: string; + readonly authorEmail?: string; +} + +export interface AvatarQuery { + readonly commits: AvatarQueryCommit[]; + readonly size: number; +} + +export interface SourceControlHistoryItemDetailsProvider { + provideAvatar(repository: Repository, query: AvatarQuery): ProviderResult>; + provideHoverCommands(repository: Repository): ProviderResult; + provideMessageLinks(repository: Repository, message: string): ProviderResult; +} + export type APIState = 'uninitialized' | 'initialized'; export interface PublishEvent { @@ -348,6 +373,7 @@ export interface API { registerPostCommitCommandsProvider(provider: PostCommitCommandsProvider): Disposable; registerPushErrorHandler(handler: PushErrorHandler): Disposable; registerBranchProtectionProvider(root: Uri, provider: BranchProtectionProvider): Disposable; + registerSourceControlHistoryItemDetailsProvider(provider: SourceControlHistoryItemDetailsProvider): Disposable; } export interface GitExtension { diff --git a/extensions/git/src/blame.ts b/extensions/git/src/blame.ts new file mode 100644 index 000000000000..12182f1de8d8 --- /dev/null +++ b/extensions/git/src/blame.ts @@ -0,0 +1,786 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DecorationOptions, l10n, Position, Range, TextEditor, TextEditorChange, TextEditorDecorationType, TextEditorChangeKind, ThemeColor, Uri, window, workspace, EventEmitter, ConfigurationChangeEvent, StatusBarItem, StatusBarAlignment, Command, MarkdownString, languages, HoverProvider, CancellationToken, Hover, TextDocument } from 'vscode'; +import { Model } from './model'; +import { dispose, fromNow, getCommitShortHash, IDisposable } from './util'; +import { Repository } from './repository'; +import { throttle } from './decorators'; +import { BlameInformation, Commit } from './git'; +import { fromGitUri, isGitUri, toGitUri } from './uri'; +import { emojify, ensureEmojis } from './emoji'; +import { getWorkingTreeAndIndexDiffInformation, getWorkingTreeDiffInformation } from './staging'; +import { provideSourceControlHistoryItemAvatar, provideSourceControlHistoryItemHoverCommands, provideSourceControlHistoryItemMessageLinks } from './historyItemDetailsProvider'; +import { AvatarQuery, AvatarQueryCommit } from './api/git'; +import { LRUCache } from './cache'; + +const AVATAR_SIZE = 20; + +function lineRangesContainLine(changes: readonly TextEditorChange[], lineNumber: number): boolean { + return changes.some(c => c.modified.startLineNumber <= lineNumber && lineNumber < c.modified.endLineNumberExclusive); +} + +function lineRangeLength(startLineNumber: number, endLineNumberExclusive: number): number { + return endLineNumberExclusive - startLineNumber; +} + +function mapModifiedLineNumberToOriginalLineNumber(lineNumber: number, changes: readonly TextEditorChange[]): number { + if (changes.length === 0) { + return lineNumber; + } + + for (const change of changes) { + // Do not process changes after the line number + if (lineNumber < change.modified.startLineNumber) { + break; + } + + // Map line number to the original line number + if (change.kind === TextEditorChangeKind.Addition) { + // Addition + lineNumber = lineNumber - lineRangeLength(change.modified.startLineNumber, change.modified.endLineNumberExclusive); + } else if (change.kind === TextEditorChangeKind.Deletion) { + // Deletion + lineNumber = lineNumber + lineRangeLength(change.original.startLineNumber, change.original.endLineNumberExclusive); + } else if (change.kind === TextEditorChangeKind.Modification) { + // Modification + const originalRangeLength = lineRangeLength(change.original.startLineNumber, change.original.endLineNumberExclusive); + const modifiedRangeLength = lineRangeLength(change.modified.startLineNumber, change.modified.endLineNumberExclusive); + + if (originalRangeLength !== modifiedRangeLength) { + lineNumber = lineNumber - (modifiedRangeLength - originalRangeLength); + } + } else { + throw new Error('Unexpected change kind'); + } + } + + return lineNumber; +} + +function getEditorDecorationRange(lineNumber: number): Range { + const position = new Position(lineNumber, Number.MAX_SAFE_INTEGER); + return new Range(position, position); +} + +function isResourceSchemeSupported(uri: Uri): boolean { + return uri.scheme === 'file' || isGitUri(uri); +} + +function isResourceBlameInformationEqual(a: ResourceBlameInformation | undefined, b: ResourceBlameInformation | undefined): boolean { + if (a === b) { + return true; + } + + if (!a || !b || + a.resource.toString() !== b.resource.toString() || + a.blameInformation.length !== b.blameInformation.length) { + return false; + } + + for (let index = 0; index < a.blameInformation.length; index++) { + if (a.blameInformation[index].lineNumber !== b.blameInformation[index].lineNumber) { + return false; + } + + const aBlameInformation = a.blameInformation[index].blameInformation; + const bBlameInformation = b.blameInformation[index].blameInformation; + + if (typeof aBlameInformation === 'string' && typeof bBlameInformation === 'string') { + if (aBlameInformation !== bBlameInformation) { + return false; + } + } else if (typeof aBlameInformation !== 'string' && typeof bBlameInformation !== 'string') { + if (aBlameInformation.hash !== bBlameInformation.hash) { + return false; + } + } else { + return false; + } + } + + return true; +} + +type BlameInformationTemplateTokens = { + readonly hash: string; + readonly hashShort: string; + readonly subject: string; + readonly authorName: string; + readonly authorEmail: string; + readonly authorDate: string; + readonly authorDateAgo: string; +}; + +interface ResourceBlameInformation { + readonly resource: Uri; + readonly blameInformation: readonly LineBlameInformation[]; +} + +interface LineBlameInformation { + readonly lineNumber: number; + readonly blameInformation: BlameInformation | string; +} + +class GitBlameInformationCache { + private readonly _cache = new Map>(); + + delete(repository: Repository): boolean { + return this._cache.delete(repository); + } + + get(repository: Repository, resource: Uri, commit: string): BlameInformation[] | undefined { + const key = this._getCacheKey(resource, commit); + return this._cache.get(repository)?.get(key); + } + + set(repository: Repository, resource: Uri, commit: string, blameInformation: BlameInformation[]): void { + if (!this._cache.has(repository)) { + this._cache.set(repository, new LRUCache(100)); + } + + const key = this._getCacheKey(resource, commit); + this._cache.get(repository)!.set(key, blameInformation); + } + + private _getCacheKey(resource: Uri, commit: string): string { + return toGitUri(resource, commit).toString(); + } +} + +export class GitBlameController { + private readonly _subjectMaxLength = 50; + + private readonly _onDidChangeBlameInformation = new EventEmitter(); + public readonly onDidChangeBlameInformation = this._onDidChangeBlameInformation.event; + + private _textEditorBlameInformation: ResourceBlameInformation | undefined; + get textEditorBlameInformation(): ResourceBlameInformation | undefined { + return this._textEditorBlameInformation; + } + private set textEditorBlameInformation(blameInformation: ResourceBlameInformation | undefined) { + if (isResourceBlameInformationEqual(this._textEditorBlameInformation, blameInformation)) { + return; + } + + this._textEditorBlameInformation = blameInformation; + this._onDidChangeBlameInformation.fire(); + } + + private _HEAD: string | undefined; + private readonly _commitInformationCache = new LRUCache(100); + private readonly _repositoryBlameCache = new GitBlameInformationCache(); + + private _editorDecoration: GitBlameEditorDecoration | undefined; + private _statusBarItem: GitBlameStatusBarItem | undefined; + + private _repositoryDisposables = new Map(); + private _enablementDisposables: IDisposable[] = []; + private _disposables: IDisposable[] = []; + + constructor(private readonly _model: Model) { + workspace.onDidChangeConfiguration(this._onDidChangeConfiguration, this, this._disposables); + this._onDidChangeConfiguration(); + } + + formatBlameInformationMessage(documentUri: Uri, template: string, blameInformation: BlameInformation): string { + const subject = blameInformation.subject && blameInformation.subject.length > this._subjectMaxLength + ? `${blameInformation.subject.substring(0, this._subjectMaxLength)}\u2026` + : blameInformation.subject; + + const templateTokens = { + hash: blameInformation.hash, + hashShort: getCommitShortHash(documentUri, blameInformation.hash), + subject: emojify(subject ?? ''), + authorName: blameInformation.authorName ?? '', + authorEmail: blameInformation.authorEmail ?? '', + authorDate: new Date(blameInformation.authorDate ?? new Date()).toLocaleString(), + authorDateAgo: fromNow(blameInformation.authorDate ?? new Date(), true, true) + } satisfies BlameInformationTemplateTokens; + + return template.replace(/\$\{(.+?)\}/g, (_, token) => { + return token in templateTokens ? templateTokens[token as keyof BlameInformationTemplateTokens] : `\${${token}}`; + }); + } + + async getBlameInformationHover(documentUri: Uri, blameInformation: BlameInformation): Promise { + const remoteHoverCommands: Command[] = []; + let commitAvatar: string | undefined; + let commitInformation: Commit | undefined; + let commitMessageWithLinks: string | undefined; + + const repository = this._model.getRepository(documentUri); + if (repository) { + try { + // Commit details + commitInformation = this._commitInformationCache.get(blameInformation.hash); + if (!commitInformation) { + commitInformation = await repository.getCommit(blameInformation.hash); + this._commitInformationCache.set(blameInformation.hash, commitInformation); + } + + // Avatar + const avatarQuery = { + commits: [{ + hash: blameInformation.hash, + authorName: blameInformation.authorName, + authorEmail: blameInformation.authorEmail + } satisfies AvatarQueryCommit], + size: AVATAR_SIZE + } satisfies AvatarQuery; + + const avatarResult = await provideSourceControlHistoryItemAvatar(this._model, repository, avatarQuery); + commitAvatar = avatarResult?.get(blameInformation.hash); + } catch { } + + // Remote hover commands + const unpublishedCommits = await repository.getUnpublishedCommits(); + if (!unpublishedCommits.has(blameInformation.hash)) { + remoteHoverCommands.push(...await provideSourceControlHistoryItemHoverCommands(this._model, repository) ?? []); + } + + // Message links + commitMessageWithLinks = await provideSourceControlHistoryItemMessageLinks( + this._model, repository, commitInformation?.message ?? blameInformation.subject ?? ''); + } + + const markdownString = new MarkdownString(); + markdownString.isTrusted = true; + markdownString.supportHtml = true; + markdownString.supportThemeIcons = true; + + // Author, date + const hash = commitInformation?.hash ?? blameInformation.hash; + const authorName = commitInformation?.authorName ?? blameInformation.authorName; + const authorEmail = commitInformation?.authorEmail ?? blameInformation.authorEmail; + const authorDate = commitInformation?.authorDate ?? blameInformation.authorDate; + const avatar = commitAvatar ? `![${authorName}](${commitAvatar}|width=${AVATAR_SIZE},height=${AVATAR_SIZE})` : '$(account)'; + + if (authorName) { + if (authorEmail) { + const emailTitle = l10n.t('Email'); + markdownString.appendMarkdown(`${avatar} [**${authorName}**](mailto:${authorEmail} "${emailTitle} ${authorName}")`); + } else { + markdownString.appendMarkdown(`${avatar} **${authorName}**`); + } + + if (authorDate) { + const dateString = new Date(authorDate).toLocaleString(undefined, { + year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' + }); + markdownString.appendMarkdown(`, $(history) ${fromNow(authorDate, true, true)} (${dateString})`); + } + + markdownString.appendMarkdown('\n\n'); + } + + // Subject | Message + markdownString.appendMarkdown(`${emojify(commitMessageWithLinks ?? commitInformation?.message ?? blameInformation.subject ?? '')}\n\n`); + markdownString.appendMarkdown(`---\n\n`); + + // Short stats + if (commitInformation?.shortStat) { + markdownString.appendMarkdown(`${commitInformation.shortStat.files === 1 ? + l10n.t('{0} file changed', commitInformation.shortStat.files) : + l10n.t('{0} files changed', commitInformation.shortStat.files)}`); + + if (commitInformation.shortStat.insertions) { + markdownString.appendMarkdown(`, ${commitInformation.shortStat.insertions === 1 ? + l10n.t('{0} insertion{1}', commitInformation.shortStat.insertions, '(+)') : + l10n.t('{0} insertions{1}', commitInformation.shortStat.insertions, '(+)')}`); + } + + if (commitInformation.shortStat.deletions) { + markdownString.appendMarkdown(`, ${commitInformation.shortStat.deletions === 1 ? + l10n.t('{0} deletion{1}', commitInformation.shortStat.deletions, '(-)') : + l10n.t('{0} deletions{1}', commitInformation.shortStat.deletions, '(-)')}`); + } + + markdownString.appendMarkdown(`\n\n---\n\n`); + } + + // Commands + markdownString.appendMarkdown(`[\`$(git-commit) ${getCommitShortHash(documentUri, hash)} \`](command:git.viewCommit?${encodeURIComponent(JSON.stringify([documentUri, hash]))} "${l10n.t('Open Commit')}")`); + markdownString.appendMarkdown(' '); + markdownString.appendMarkdown(`[$(copy)](command:git.copyContentToClipboard?${encodeURIComponent(JSON.stringify(hash))} "${l10n.t('Copy Commit Hash')}")`); + + // Remote hover commands + if (remoteHoverCommands.length > 0) { + markdownString.appendMarkdown('  |  '); + + const remoteCommandsMarkdown = remoteHoverCommands + .map(command => `[${command.title}](command:${command.command}?${encodeURIComponent(JSON.stringify([...command.arguments ?? [], hash]))} "${command.tooltip}")`); + markdownString.appendMarkdown(remoteCommandsMarkdown.join(' ')); + } + + markdownString.appendMarkdown('  |  '); + markdownString.appendMarkdown(`[$(gear)](command:workbench.action.openSettings?%5B%22git.blame%22%5D "${l10n.t('Open Settings')}")`); + + return markdownString; + } + + private _onDidChangeConfiguration(e?: ConfigurationChangeEvent): void { + if (e && + !e.affectsConfiguration('git.blame.editorDecoration.enabled') && + !e.affectsConfiguration('git.blame.statusBarItem.enabled')) { + return; + } + + const config = workspace.getConfiguration('git'); + const editorDecorationEnabled = config.get('blame.editorDecoration.enabled') === true; + const statusBarItemEnabled = config.get('blame.statusBarItem.enabled') === true; + + // Editor decoration + if (editorDecorationEnabled) { + if (!this._editorDecoration) { + this._editorDecoration = new GitBlameEditorDecoration(this); + } + } else { + this._editorDecoration?.dispose(); + this._editorDecoration = undefined; + } + + // StatusBar item + if (statusBarItemEnabled) { + if (!this._statusBarItem) { + this._statusBarItem = new GitBlameStatusBarItem(this); + } + } else { + this._statusBarItem?.dispose(); + this._statusBarItem = undefined; + } + + // Listeners + if (editorDecorationEnabled || statusBarItemEnabled) { + if (this._enablementDisposables.length === 0) { + this._model.onDidOpenRepository(this._onDidOpenRepository, this, this._enablementDisposables); + this._model.onDidCloseRepository(this._onDidCloseRepository, this, this._enablementDisposables); + for (const repository of this._model.repositories) { + this._onDidOpenRepository(repository); + } + + window.onDidChangeActiveTextEditor(e => this._updateTextEditorBlameInformation(e), this, this._enablementDisposables); + window.onDidChangeTextEditorSelection(e => this._updateTextEditorBlameInformation(e.textEditor, 'selection'), this, this._enablementDisposables); + window.onDidChangeTextEditorDiffInformation(e => this._updateTextEditorBlameInformation(e.textEditor), this, this._enablementDisposables); + } + } else { + this._enablementDisposables = dispose(this._enablementDisposables); + } + + this._updateTextEditorBlameInformation(window.activeTextEditor); + } + + private _onDidOpenRepository(repository: Repository): void { + const repositoryDisposables: IDisposable[] = []; + repository.onDidRunGitStatus(() => this._onDidRunGitStatus(repository), this, repositoryDisposables); + + this._repositoryDisposables.set(repository, repositoryDisposables); + } + + private _onDidCloseRepository(repository: Repository): void { + const disposables = this._repositoryDisposables.get(repository); + if (disposables) { + dispose(disposables); + } + + this._repositoryDisposables.delete(repository); + this._repositoryBlameCache.delete(repository); + } + + private _onDidRunGitStatus(repository: Repository): void { + if (!repository.HEAD?.commit || this._HEAD === repository.HEAD.commit) { + return; + } + + this._HEAD = repository.HEAD.commit; + this._updateTextEditorBlameInformation(window.activeTextEditor); + } + + private async _getBlameInformation(resource: Uri, commit: string): Promise { + const repository = this._model.getRepository(resource); + if (!repository) { + return undefined; + } + + const resourceBlameInformation = this._repositoryBlameCache.get(repository, resource, commit); + if (resourceBlameInformation) { + return resourceBlameInformation; + } + + // Ensure that the emojis are loaded as we will need + // access to them when formatting the blame information. + await ensureEmojis(); + + // Get blame information for the resource and cache it + const blameInformation = await repository.blame2(resource.fsPath, commit) ?? []; + this._repositoryBlameCache.set(repository, resource, commit, blameInformation); + + return blameInformation; + } + + @throttle + private async _updateTextEditorBlameInformation(textEditor: TextEditor | undefined, reason?: 'selection'): Promise { + if (textEditor) { + if (!textEditor.diffInformation || textEditor !== window.activeTextEditor) { + return; + } + } else { + this.textEditorBlameInformation = undefined; + return; + } + + const repository = this._model.getRepository(textEditor.document.uri); + if (!repository || !repository.HEAD?.commit) { + return; + } + + // Only support resources with `file` and `git` schemes + if (!isResourceSchemeSupported(textEditor.document.uri)) { + this.textEditorBlameInformation = undefined; + return; + } + + // Do not show blame information when there is a single selection and it is at the beginning + // of the file [0, 0, 0, 0] unless the user explicitly navigates the cursor there. We do this + // to avoid showing blame information when the editor is not focused. + if (reason !== 'selection' && textEditor.selections.length === 1 && + textEditor.selections[0].start.line === 0 && textEditor.selections[0].start.character === 0 && + textEditor.selections[0].end.line === 0 && textEditor.selections[0].end.character === 0) { + this.textEditorBlameInformation = undefined; + return; + } + + let allChanges: readonly TextEditorChange[]; + let workingTreeChanges: readonly TextEditorChange[]; + let workingTreeAndIndexChanges: readonly TextEditorChange[] | undefined; + + if (isGitUri(textEditor.document.uri)) { + const { ref } = fromGitUri(textEditor.document.uri); + + // For the following scenarios we can discard the diff information + // 1) Commit - Resource in the multi-file diff editor when viewing the details of a commit. + // 2) HEAD - Resource on the left-hand side of the diff editor when viewing a resource from the index. + // 3) ~ - Resource on the left-hand side of the diff editor when viewing a resource from the working tree. + if (/^[0-9a-f]{40}$/i.test(ref) || ref === 'HEAD' || ref === '~') { + workingTreeChanges = allChanges = []; + workingTreeAndIndexChanges = undefined; + } else if (ref === '') { + // Resource on the right-hand side of the diff editor when viewing a resource from the index. + const diffInformationWorkingTreeAndIndex = getWorkingTreeAndIndexDiffInformation(textEditor); + + // Working tree + index diff information is present and it is stale. Diff information + // may be stale when the selection changes because of a content change and the diff + // information is not yet updated. + if (diffInformationWorkingTreeAndIndex && diffInformationWorkingTreeAndIndex.isStale) { + this.textEditorBlameInformation = undefined; + return; + } + + workingTreeChanges = []; + workingTreeAndIndexChanges = allChanges = diffInformationWorkingTreeAndIndex?.changes ?? []; + } else { + throw new Error(`Unexpected ref: ${ref}`); + } + } else { + // Working tree diff information. Diff Editor (Working Tree) -> Text Editor + const diffInformationWorkingTree = getWorkingTreeDiffInformation(textEditor); + + // Working tree diff information is not present or it is stale. Diff information + // may be stale when the selection changes because of a content change and the diff + // information is not yet updated. + if (!diffInformationWorkingTree || diffInformationWorkingTree.isStale) { + this.textEditorBlameInformation = undefined; + return; + } + + // Working tree + index diff information + const diffInformationWorkingTreeAndIndex = getWorkingTreeAndIndexDiffInformation(textEditor); + + // Working tree + index diff information is present and it is stale. Diff information + // may be stale when the selection changes because of a content change and the diff + // information is not yet updated. + if (diffInformationWorkingTreeAndIndex && diffInformationWorkingTreeAndIndex.isStale) { + this.textEditorBlameInformation = undefined; + return; + } + + workingTreeChanges = diffInformationWorkingTree.changes; + workingTreeAndIndexChanges = diffInformationWorkingTreeAndIndex?.changes; + + // For staged resources, we provide an additional "original resource" so that the editor + // diff information contains both the changes that are in the working tree and the changes + // that are in the working tree + index. + allChanges = workingTreeAndIndexChanges ?? workingTreeChanges; + } + + let commit: string; + if (!isGitUri(textEditor.document.uri)) { + // Resource with the `file` scheme + commit = repository.HEAD.commit; + } else { + // Resource with the `git` scheme + const { ref } = fromGitUri(textEditor.document.uri); + commit = /^[0-9a-f]{40}$/i.test(ref) ? ref : repository.HEAD.commit; + } + + // Git blame information + const resourceBlameInformation = await this._getBlameInformation(textEditor.document.uri, commit); + if (!resourceBlameInformation) { + return; + } + + const lineBlameInformation: LineBlameInformation[] = []; + for (const lineNumber of new Set(textEditor.selections.map(s => s.active.line))) { + // Check if the line is contained in the working tree diff information + if (lineRangesContainLine(workingTreeChanges, lineNumber + 1)) { + if (reason === 'selection') { + // Only show the `Not Committed Yet` message upon selection change due to navigation + lineBlameInformation.push({ lineNumber, blameInformation: l10n.t('Not Committed Yet') }); + } + continue; + } + + // Check if the line is contained in the working tree + index diff information + if (lineRangesContainLine(workingTreeAndIndexChanges ?? [], lineNumber + 1)) { + lineBlameInformation.push({ lineNumber, blameInformation: l10n.t('Not Committed Yet (Staged)') }); + continue; + } + + // Map the line number to the git blame ranges using the diff information + const lineNumberWithDiff = mapModifiedLineNumberToOriginalLineNumber(lineNumber + 1, allChanges); + const blameInformation = resourceBlameInformation.find(blameInformation => { + return blameInformation.ranges.find(range => { + return lineNumberWithDiff >= range.startLineNumber && lineNumberWithDiff <= range.endLineNumber; + }); + }); + + if (blameInformation) { + lineBlameInformation.push({ lineNumber, blameInformation }); + } + } + + this.textEditorBlameInformation = { + resource: textEditor.document.uri, + blameInformation: lineBlameInformation + }; + } + + dispose() { + for (const disposables of this._repositoryDisposables.values()) { + dispose(disposables); + } + this._repositoryDisposables.clear(); + + this._disposables = dispose(this._disposables); + } +} + +class GitBlameEditorDecoration implements HoverProvider { + private _decoration: TextEditorDecorationType; + + private _hoverDisposable: IDisposable | undefined; + private _disposables: IDisposable[] = []; + + constructor(private readonly _controller: GitBlameController) { + this._decoration = window.createTextEditorDecorationType({ + after: { + color: new ThemeColor('git.blame.editorDecorationForeground') + } + }); + this._disposables.push(this._decoration); + + workspace.onDidChangeConfiguration(this._onDidChangeConfiguration, this, this._disposables); + window.onDidChangeActiveTextEditor(this._onDidChangeActiveTextEditor, this, this._disposables); + this._controller.onDidChangeBlameInformation(() => this._onDidChangeBlameInformation(), this, this._disposables); + + this._onDidChangeConfiguration(); + } + + async provideHover(document: TextDocument, position: Position, token: CancellationToken): Promise { + if (token.isCancellationRequested) { + return undefined; + } + + const textEditor = window.activeTextEditor; + if (!textEditor) { + return undefined; + } + + // Position must be at the end of the line + if (position.character !== document.lineAt(position.line).range.end.character) { + return undefined; + } + + // Get blame information + const blameInformation = this._controller.textEditorBlameInformation?.blameInformation; + const lineBlameInformation = blameInformation?.find(blame => blame.lineNumber === position.line); + + if (!lineBlameInformation || typeof lineBlameInformation.blameInformation === 'string') { + return undefined; + } + + const contents = await this._controller.getBlameInformationHover(textEditor.document.uri, lineBlameInformation.blameInformation); + + if (!contents || token.isCancellationRequested) { + return undefined; + } + + return { range: getEditorDecorationRange(position.line), contents: [contents] }; + } + + private _onDidChangeConfiguration(e?: ConfigurationChangeEvent): void { + if (e && + !e.affectsConfiguration('git.commitShortHashLength') && + !e.affectsConfiguration('git.blame.editorDecoration.template')) { + return; + } + + this._registerHoverProvider(); + this._onDidChangeBlameInformation(); + } + + private _onDidChangeActiveTextEditor(): void { + // Clear decorations + for (const editor of window.visibleTextEditors) { + if (editor !== window.activeTextEditor) { + editor.setDecorations(this._decoration, []); + } + } + + // Register hover provider + this._registerHoverProvider(); + } + + private _onDidChangeBlameInformation(): void { + const textEditor = window.activeTextEditor; + if (!textEditor) { + return; + } + + // Get blame information + const blameInformation = this._controller.textEditorBlameInformation?.blameInformation; + if (!blameInformation || blameInformation.length === 0) { + textEditor.setDecorations(this._decoration, []); + return; + } + + // Set decorations for the editor + const config = workspace.getConfiguration('git'); + const template = config.get('blame.editorDecoration.template', '${subject}, ${authorName} (${authorDateAgo})'); + + const decorations = blameInformation.map(blame => { + const contentText = typeof blame.blameInformation !== 'string' + ? this._controller.formatBlameInformationMessage(textEditor.document.uri, template, blame.blameInformation) + : blame.blameInformation; + + return this._createDecoration(blame.lineNumber, contentText); + }); + + textEditor.setDecorations(this._decoration, decorations); + } + + private _createDecoration(lineNumber: number, contentText: string): DecorationOptions { + return { + range: getEditorDecorationRange(lineNumber), + renderOptions: { + after: { + contentText, + margin: '0 0 0 50px' + } + }, + }; + } + + private _registerHoverProvider(): void { + this._hoverDisposable?.dispose(); + + if (window.activeTextEditor && isResourceSchemeSupported(window.activeTextEditor.document.uri)) { + this._hoverDisposable = languages.registerHoverProvider({ + pattern: window.activeTextEditor.document.uri.fsPath + }, this); + } + } + + dispose() { + this._hoverDisposable?.dispose(); + this._hoverDisposable = undefined; + + this._disposables = dispose(this._disposables); + } +} + +class GitBlameStatusBarItem { + private _statusBarItem: StatusBarItem; + private _disposables: IDisposable[] = []; + + constructor(private readonly _controller: GitBlameController) { + this._statusBarItem = window.createStatusBarItem('git.blame', StatusBarAlignment.Right, 200); + this._statusBarItem.name = l10n.t('Git Blame Information'); + this._disposables.push(this._statusBarItem); + + workspace.onDidChangeConfiguration(this._onDidChangeConfiguration, this, this._disposables); + this._controller.onDidChangeBlameInformation(() => this._onDidChangeBlameInformation(), this, this._disposables); + } + + private _onDidChangeConfiguration(e: ConfigurationChangeEvent): void { + if (!e.affectsConfiguration('git.commitShortHashLength') && + !e.affectsConfiguration('git.blame.statusBarItem.template')) { + return; + } + + this._onDidChangeBlameInformation(); + } + + private async _onDidChangeBlameInformation(): Promise { + if (!window.activeTextEditor) { + this._statusBarItem.hide(); + return; + } + + const blameInformation = this._controller.textEditorBlameInformation?.blameInformation; + if (!blameInformation || blameInformation.length === 0) { + this._statusBarItem.hide(); + return; + } + + if (typeof blameInformation[0].blameInformation === 'string') { + this._statusBarItem.text = `$(git-commit) ${blameInformation[0].blameInformation}`; + this._statusBarItem.tooltip = l10n.t('Git Blame Information'); + this._statusBarItem.command = undefined; + } else { + const config = workspace.getConfiguration('git'); + const template = config.get('blame.statusBarItem.template', '${authorName} (${authorDateAgo})'); + + this._statusBarItem.text = `$(git-commit) ${this._controller.formatBlameInformationMessage( + window.activeTextEditor.document.uri, template, blameInformation[0].blameInformation)}`; + + this._statusBarItem.tooltip2 = (cancellationToken: CancellationToken) => { + return this._provideTooltip(window.activeTextEditor!.document.uri, + blameInformation[0].blameInformation as BlameInformation, cancellationToken); + }; + + this._statusBarItem.command = { + title: l10n.t('Open Commit'), + command: 'git.viewCommit', + arguments: [window.activeTextEditor.document.uri, blameInformation[0].blameInformation.hash] + } satisfies Command; + } + + this._statusBarItem.show(); + } + + private async _provideTooltip(uri: Uri, blameInformation: BlameInformation, cancellationToken: CancellationToken): Promise { + if (cancellationToken.isCancellationRequested) { + return undefined; + } + + const tooltip = await this._controller.getBlameInformationHover(uri, blameInformation); + return cancellationToken.isCancellationRequested ? undefined : tooltip; + } + + dispose() { + this._disposables = dispose(this._disposables); + } +} diff --git a/extensions/git/src/cache.ts b/extensions/git/src/cache.ts new file mode 100644 index 000000000000..df0c0df5561a --- /dev/null +++ b/extensions/git/src/cache.ts @@ -0,0 +1,485 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +interface Item { + previous: Item | undefined; + next: Item | undefined; + key: K; + value: V; +} + +const enum Touch { + None = 0, + AsOld = 1, + AsNew = 2 +} + +class LinkedMap implements Map { + + readonly [Symbol.toStringTag] = 'LinkedMap'; + + private _map: Map>; + private _head: Item | undefined; + private _tail: Item | undefined; + private _size: number; + + private _state: number; + + constructor() { + this._map = new Map>(); + this._head = undefined; + this._tail = undefined; + this._size = 0; + this._state = 0; + } + + clear(): void { + this._map.clear(); + this._head = undefined; + this._tail = undefined; + this._size = 0; + this._state++; + } + + isEmpty(): boolean { + return !this._head && !this._tail; + } + + get size(): number { + return this._size; + } + + get first(): V | undefined { + return this._head?.value; + } + + get last(): V | undefined { + return this._tail?.value; + } + + has(key: K): boolean { + return this._map.has(key); + } + + get(key: K, touch: Touch = Touch.None): V | undefined { + const item = this._map.get(key); + if (!item) { + return undefined; + } + if (touch !== Touch.None) { + this.touch(item, touch); + } + return item.value; + } + + set(key: K, value: V, touch: Touch = Touch.None): this { + let item = this._map.get(key); + if (item) { + item.value = value; + if (touch !== Touch.None) { + this.touch(item, touch); + } + } else { + item = { key, value, next: undefined, previous: undefined }; + switch (touch) { + case Touch.None: + this.addItemLast(item); + break; + case Touch.AsOld: + this.addItemFirst(item); + break; + case Touch.AsNew: + this.addItemLast(item); + break; + default: + this.addItemLast(item); + break; + } + this._map.set(key, item); + this._size++; + } + return this; + } + + delete(key: K): boolean { + return !!this.remove(key); + } + + remove(key: K): V | undefined { + const item = this._map.get(key); + if (!item) { + return undefined; + } + this._map.delete(key); + this.removeItem(item); + this._size--; + return item.value; + } + + shift(): V | undefined { + if (!this._head && !this._tail) { + return undefined; + } + if (!this._head || !this._tail) { + throw new Error('Invalid list'); + } + const item = this._head; + this._map.delete(item.key); + this.removeItem(item); + this._size--; + return item.value; + } + + forEach(callbackfn: (value: V, key: K, map: LinkedMap) => void, thisArg?: any): void { + const state = this._state; + let current = this._head; + while (current) { + if (thisArg) { + callbackfn.bind(thisArg)(current.value, current.key, this); + } else { + callbackfn(current.value, current.key, this); + } + if (this._state !== state) { + throw new Error(`LinkedMap got modified during iteration.`); + } + current = current.next; + } + } + + keys(): IterableIterator { + const map = this; + const state = this._state; + let current = this._head; + const iterator: IterableIterator = { + [Symbol.iterator]() { + return iterator; + }, + next(): IteratorResult { + if (map._state !== state) { + throw new Error(`LinkedMap got modified during iteration.`); + } + if (current) { + const result = { value: current.key, done: false }; + current = current.next; + return result; + } else { + return { value: undefined, done: true }; + } + } + }; + return iterator; + } + + values(): IterableIterator { + const map = this; + const state = this._state; + let current = this._head; + const iterator: IterableIterator = { + [Symbol.iterator]() { + return iterator; + }, + next(): IteratorResult { + if (map._state !== state) { + throw new Error(`LinkedMap got modified during iteration.`); + } + if (current) { + const result = { value: current.value, done: false }; + current = current.next; + return result; + } else { + return { value: undefined, done: true }; + } + } + }; + return iterator; + } + + entries(): IterableIterator<[K, V]> { + const map = this; + const state = this._state; + let current = this._head; + const iterator: IterableIterator<[K, V]> = { + [Symbol.iterator]() { + return iterator; + }, + next(): IteratorResult<[K, V]> { + if (map._state !== state) { + throw new Error(`LinkedMap got modified during iteration.`); + } + if (current) { + const result: IteratorResult<[K, V]> = { value: [current.key, current.value], done: false }; + current = current.next; + return result; + } else { + return { value: undefined, done: true }; + } + } + }; + return iterator; + } + + [Symbol.iterator](): IterableIterator<[K, V]> { + return this.entries(); + } + + protected trimOld(newSize: number) { + if (newSize >= this.size) { + return; + } + if (newSize === 0) { + this.clear(); + return; + } + let current = this._head; + let currentSize = this.size; + while (current && currentSize > newSize) { + this._map.delete(current.key); + current = current.next; + currentSize--; + } + this._head = current; + this._size = currentSize; + if (current) { + current.previous = undefined; + } + this._state++; + } + + protected trimNew(newSize: number) { + if (newSize >= this.size) { + return; + } + if (newSize === 0) { + this.clear(); + return; + } + let current = this._tail; + let currentSize = this.size; + while (current && currentSize > newSize) { + this._map.delete(current.key); + current = current.previous; + currentSize--; + } + this._tail = current; + this._size = currentSize; + if (current) { + current.next = undefined; + } + this._state++; + } + + private addItemFirst(item: Item): void { + // First time Insert + if (!this._head && !this._tail) { + this._tail = item; + } else if (!this._head) { + throw new Error('Invalid list'); + } else { + item.next = this._head; + this._head.previous = item; + } + this._head = item; + this._state++; + } + + private addItemLast(item: Item): void { + // First time Insert + if (!this._head && !this._tail) { + this._head = item; + } else if (!this._tail) { + throw new Error('Invalid list'); + } else { + item.previous = this._tail; + this._tail.next = item; + } + this._tail = item; + this._state++; + } + + private removeItem(item: Item): void { + if (item === this._head && item === this._tail) { + this._head = undefined; + this._tail = undefined; + } + else if (item === this._head) { + // This can only happen if size === 1 which is handled + // by the case above. + if (!item.next) { + throw new Error('Invalid list'); + } + item.next.previous = undefined; + this._head = item.next; + } + else if (item === this._tail) { + // This can only happen if size === 1 which is handled + // by the case above. + if (!item.previous) { + throw new Error('Invalid list'); + } + item.previous.next = undefined; + this._tail = item.previous; + } + else { + const next = item.next; + const previous = item.previous; + if (!next || !previous) { + throw new Error('Invalid list'); + } + next.previous = previous; + previous.next = next; + } + item.next = undefined; + item.previous = undefined; + this._state++; + } + + private touch(item: Item, touch: Touch): void { + if (!this._head || !this._tail) { + throw new Error('Invalid list'); + } + if ((touch !== Touch.AsOld && touch !== Touch.AsNew)) { + return; + } + + if (touch === Touch.AsOld) { + if (item === this._head) { + return; + } + + const next = item.next; + const previous = item.previous; + + // Unlink the item + if (item === this._tail) { + // previous must be defined since item was not head but is tail + // So there are more than on item in the map + previous!.next = undefined; + this._tail = previous; + } + else { + // Both next and previous are not undefined since item was neither head nor tail. + next!.previous = previous; + previous!.next = next; + } + + // Insert the node at head + item.previous = undefined; + item.next = this._head; + this._head.previous = item; + this._head = item; + this._state++; + } else if (touch === Touch.AsNew) { + if (item === this._tail) { + return; + } + + const next = item.next; + const previous = item.previous; + + // Unlink the item. + if (item === this._head) { + // next must be defined since item was not tail but is head + // So there are more than on item in the map + next!.previous = undefined; + this._head = next; + } else { + // Both next and previous are not undefined since item was neither head nor tail. + next!.previous = previous; + previous!.next = next; + } + item.next = undefined; + item.previous = this._tail; + this._tail.next = item; + this._tail = item; + this._state++; + } + } + + toJSON(): [K, V][] { + const data: [K, V][] = []; + + this.forEach((value, key) => { + data.push([key, value]); + }); + + return data; + } + + fromJSON(data: [K, V][]): void { + this.clear(); + + for (const [key, value] of data) { + this.set(key, value); + } + } +} + +abstract class Cache extends LinkedMap { + + protected _limit: number; + protected _ratio: number; + + constructor(limit: number, ratio: number = 1) { + super(); + this._limit = limit; + this._ratio = Math.min(Math.max(0, ratio), 1); + } + + get limit(): number { + return this._limit; + } + + set limit(limit: number) { + this._limit = limit; + this.checkTrim(); + } + + get ratio(): number { + return this._ratio; + } + + set ratio(ratio: number) { + this._ratio = Math.min(Math.max(0, ratio), 1); + this.checkTrim(); + } + + override get(key: K, touch: Touch = Touch.AsNew): V | undefined { + return super.get(key, touch); + } + + peek(key: K): V | undefined { + return super.get(key, Touch.None); + } + + override set(key: K, value: V): this { + super.set(key, value, Touch.AsNew); + return this; + } + + protected checkTrim() { + if (this.size > this._limit) { + this.trim(Math.round(this._limit * this._ratio)); + } + } + + protected abstract trim(newSize: number): void; +} + +export class LRUCache extends Cache { + + constructor(limit: number, ratio: number = 1) { + super(limit, ratio); + } + + protected override trim(newSize: number) { + this.trimOld(newSize); + } + + override set(key: K, value: V): this { + super.set(key, value); + this.checkTrim(); + return this; + } +} diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index a85322a055ec..1094a438b07f 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -5,20 +5,20 @@ import * as os from 'os'; import * as path from 'path'; -import { Command, commands, Disposable, LineChange, MessageOptions, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity, TabInputText, TabInputTextMerge, QuickPickItemKind, TextDocument, LogOutputChannel, l10n, Memento, UIKind, QuickInputButton, ThemeIcon, SourceControlHistoryItem, SourceControl, InputBoxValidationMessage, Tab, TabInputNotebook, QuickInputButtonLocation, SourceControlHistoryItemRef } from 'vscode'; +import { Command, commands, Disposable, MessageOptions, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity, TabInputText, TabInputTextMerge, QuickPickItemKind, TextDocument, LogOutputChannel, l10n, Memento, UIKind, QuickInputButton, ThemeIcon, SourceControlHistoryItem, SourceControl, InputBoxValidationMessage, Tab, TabInputNotebook, QuickInputButtonLocation, languages } from 'vscode'; import TelemetryReporter from '@vscode/extension-telemetry'; import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator'; import { ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourcePublisher, Remote } from './api/git'; import { Git, Stash } from './git'; import { Model } from './model'; import { GitResourceGroup, Repository, Resource, ResourceGroupType } from './repository'; -import { DiffEditorSelectionHunkToolbarContext, applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineChange, toLineRanges } from './staging'; +import { DiffEditorSelectionHunkToolbarContext, LineChange, applyLineChanges, getIndexDiffInformation, getModifiedRange, getWorkingTreeDiffInformation, intersectDiffWithRange, invertLineChange, toLineChanges, toLineRanges } from './staging'; import { fromGitUri, toGitUri, isGitUri, toMergeUris, toMultiFileDiffEditorUris } from './uri'; -import { dispose, grep, isDefined, isDescendant, pathEquals, relativePath } from './util'; +import { DiagnosticSeverityConfig, dispose, fromNow, getCommitShortHash, grep, isDefined, isDescendant, isLinuxSnap, isRemote, isWindows, pathEquals, relativePath, toDiagnosticSeverity, truncate } from './util'; import { GitTimelineItem } from './timelineProvider'; import { ApiRepository } from './api/api1'; import { getRemoteSourceActions, pickRemoteSource } from './remoteSource'; -import { RemoteSourceAction } from './api/git-base'; +import { RemoteSourceAction } from './typings/git-base'; abstract class CheckoutCommandItem implements QuickPickItem { abstract get label(): string; @@ -73,6 +73,10 @@ class RefItem implements QuickPickItem { } get description(): string { + if (this.ref.commitDetails?.authorDate) { + return fromNow(this.ref.commitDetails.authorDate, true, true); + } + switch (this.ref.type) { case RefType.Head: return this.shortCommit; @@ -85,6 +89,14 @@ class RefItem implements QuickPickItem { } } + get detail(): string | undefined { + if (this.ref.commitDetails?.authorName && this.ref.commitDetails?.message) { + return `${this.ref.commitDetails?.authorName} | ${this.ref.commitDetails?.message}`; + } + + return undefined; + } + get refName(): string | undefined { return this.ref.name; } get refRemote(): string | undefined { return this.ref.remote; } get shortCommit(): string { return (this.ref.commit || '').substr(0, 8); } @@ -155,8 +167,11 @@ class CheckoutTagItem extends RefItem { class BranchDeleteItem extends RefItem { async run(repository: Repository, force?: boolean): Promise { - if (this.ref.name) { - await repository.deleteBranch(this.ref.name, force); + if (this.ref.type === RefType.Head && this.refName) { + await repository.deleteBranch(this.refName, force); + } else if (this.ref.type === RefType.RemoteHead && this.refRemote && this.refName) { + const refName = this.refName.substring(this.refRemote.length + 1); + await repository.deleteRemoteRef(this.refRemote, refName, { force }); } } } @@ -178,7 +193,7 @@ class RemoteTagDeleteItem extends RefItem { async run(repository: Repository, remote: string): Promise { if (this.ref.name) { - await repository.deleteRemoteTag(remote, this.ref.name); + await repository.deleteRemoteRef(remote, this.ref.name); } } } @@ -272,7 +287,6 @@ class StashItem implements QuickPickItem { interface ScmCommandOptions { repository?: boolean; - diff?: boolean; } interface ScmCommand { @@ -309,7 +323,7 @@ async function categorizeResourceByResolution(resources: Resource[]): Promise<{ const isBothAddedOrModified = (s: Resource) => s.type === Status.BOTH_MODIFIED || s.type === Status.BOTH_ADDED; const isAnyDeleted = (s: Resource) => s.type === Status.DELETED_BY_THEM || s.type === Status.DELETED_BY_US; const possibleUnresolved = merge.filter(isBothAddedOrModified); - const promises = possibleUnresolved.map(s => grep(s.resourceUri.fsPath, /^<{7}|^={7}|^>{7}/)); + const promises = possibleUnresolved.map(s => grep(s.resourceUri.fsPath, /^<{7}\s|^={7}$|^>{7}\s/)); const unresolvedBothModified = await Promise.all(promises); const resolved = possibleUnresolved.filter((_s, i) => !unresolvedBothModified[i]); const deletionConflicts = merge.filter(s => isAnyDeleted(s)); @@ -324,6 +338,8 @@ async function categorizeResourceByResolution(resources: Resource[]): Promise<{ async function createCheckoutItems(repository: Repository, detached = false): Promise { const config = workspace.getConfiguration('git'); const checkoutTypeConfig = config.get('checkoutType'); + const showRefDetails = config.get('showReferenceDetails') === true; + let checkoutTypes: string[]; if (checkoutTypeConfig === 'all' || !checkoutTypeConfig || checkoutTypeConfig.length === 0) { @@ -339,7 +355,7 @@ async function createCheckoutItems(repository: Repository, detached = false): Pr checkoutTypes = checkoutTypes.filter(t => t !== 'tags'); } - const refs = await repository.getRefs(); + const refs = await repository.getRefs({ includeCommitDetails: showRefDetails }); const refProcessors = checkoutTypes.map(type => getCheckoutRefProcessor(repository, type)) .filter(p => !!p) as RefProcessor[]; @@ -614,6 +630,87 @@ class CommandErrorOutputTextDocumentContentProvider implements TextDocumentConte } } +async function evaluateDiagnosticsCommitHook(repository: Repository, options: CommitOptions): Promise { + const config = workspace.getConfiguration('git', Uri.file(repository.root)); + const enabled = config.get('diagnosticsCommitHook.Enabled', false) === true; + const sourceSeverity = config.get>('diagnosticsCommitHook.Sources', { '*': 'error' }); + + if (!enabled) { + return true; + } + + const changes: Uri[] = []; + if (repository.indexGroup.resourceStates.length > 0) { + // Staged files + changes.push(...repository.indexGroup.resourceStates.map(r => r.resourceUri)); + } else if (options.all === 'tracked') { + // Tracked files + changes.push(...repository.workingTreeGroup.resourceStates + .filter(r => r.type !== Status.UNTRACKED && r.type !== Status.IGNORED) + .map(r => r.resourceUri)); + } else { + // All files + changes.push(...repository.workingTreeGroup.resourceStates.map(r => r.resourceUri)); + changes.push(...repository.untrackedGroup.resourceStates.map(r => r.resourceUri)); + } + + const diagnostics = languages.getDiagnostics(); + const changesDiagnostics = diagnostics.filter(([uri, diags]) => { + // File + if (uri.scheme !== 'file' || !changes.find(c => pathEquals(c.fsPath, uri.fsPath))) { + return false; + } + + // Diagnostics + return diags.find(d => { + // No source or ignored source + if (!d.source || (Object.keys(sourceSeverity).includes(d.source) && sourceSeverity[d.source] === 'none')) { + return false; + } + + // Source severity + if (Object.keys(sourceSeverity).includes(d.source) && + d.severity <= toDiagnosticSeverity(sourceSeverity[d.source])) { + return true; + } + + // Wildcard severity + if (Object.keys(sourceSeverity).includes('*') && + d.severity <= toDiagnosticSeverity(sourceSeverity['*'])) { + return true; + } + + return false; + }); + }); + + if (changesDiagnostics.length === 0) { + return true; + } + + // Show dialog + const commit = l10n.t('Commit Anyway'); + const view = l10n.t('View Problems'); + + const message = changesDiagnostics.length === 1 + ? l10n.t('The following file has unresolved diagnostics: \'{0}\'.\n\nHow would you like to proceed?', path.basename(changesDiagnostics[0][0].fsPath)) + : l10n.t('There are {0} files that have unresolved diagnostics.\n\nHow would you like to proceed?', changesDiagnostics.length); + + const choice = await window.showWarningMessage(message, { modal: true }, commit, view); + + // Commit Anyway + if (choice === commit) { + return true; + } + + // View Problems + if (choice === view) { + commands.executeCommand('workbench.panel.markers.view.focus'); + } + + return false; +} + export class CommandCenter { private disposables: Disposable[]; @@ -628,12 +725,7 @@ export class CommandCenter { ) { this.disposables = Commands.map(({ commandId, key, method, options }) => { const command = this.createCommand(commandId, key, method, options); - - if (options.diff) { - return commands.registerDiffInformationCommand(commandId, command); - } else { - return commands.registerCommand(commandId, command); - } + return commands.registerCommand(commandId, command); }); this.disposables.push(workspace.registerTextDocumentContentProvider('git-output', this.commandErrors)); @@ -1341,6 +1433,10 @@ export class CommandCenter { } await repository.move(from, to); + + // Close active editor and open the renamed file + await commands.executeCommand('workbench.action.closeActiveEditor'); + await commands.executeCommand('vscode.open', Uri.file(path.join(repository.root, to)), { viewColumn: ViewColumn.Active }); } @command('git.stage') @@ -1525,16 +1621,20 @@ export class CommandCenter { } @command('git.diff.stageHunk') - async diffStageHunk(changes: DiffEditorSelectionHunkToolbarContext): Promise { + async diffStageHunk(changes: DiffEditorSelectionHunkToolbarContext | undefined): Promise { this.diffStageHunkOrSelection(changes); } @command('git.diff.stageSelection') - async diffStageSelection(changes: DiffEditorSelectionHunkToolbarContext): Promise { + async diffStageSelection(changes: DiffEditorSelectionHunkToolbarContext | undefined): Promise { this.diffStageHunkOrSelection(changes); } - async diffStageHunkOrSelection(changes: DiffEditorSelectionHunkToolbarContext): Promise { + async diffStageHunkOrSelection(changes: DiffEditorSelectionHunkToolbarContext | undefined): Promise { + if (!changes) { + return; + } + let modifiedUri = changes.modifiedUri; if (!modifiedUri) { const textEditor = window.activeTextEditor; @@ -1551,20 +1651,32 @@ export class CommandCenter { await this.runByRepository(modifiedUri, async (repository, resource) => await repository.stage(resource, result)); } - @command('git.stageSelectedRanges', { diff: true }) - async stageSelectedChanges(changes: LineChange[]): Promise { + @command('git.stageSelectedRanges') + async stageSelectedChanges(): Promise { const textEditor = window.activeTextEditor; if (!textEditor) { return; } + const workingTreeDiffInformation = getWorkingTreeDiffInformation(textEditor); + if (!workingTreeDiffInformation) { + return; + } + + const workingTreeLineChanges = toLineChanges(workingTreeDiffInformation); + + this.logger.trace(`[CommandCenter][stageSelectedChanges] diffInformation: ${JSON.stringify(workingTreeDiffInformation)}`); + this.logger.trace(`[CommandCenter][stageSelectedChanges] diffInformation changes: ${JSON.stringify(workingTreeLineChanges)}`); + const modifiedDocument = textEditor.document; const selectedLines = toLineRanges(textEditor.selections, modifiedDocument); - const selectedChanges = changes - .map(diff => selectedLines.reduce((result, range) => result || intersectDiffWithRange(modifiedDocument, diff, range), null)) + const selectedChanges = workingTreeLineChanges + .map(change => selectedLines.reduce((result, range) => result || intersectDiffWithRange(modifiedDocument, change, range), null)) .filter(d => !!d) as LineChange[]; + this.logger.trace(`[CommandCenter][stageSelectedChanges] selectedChanges: ${JSON.stringify(selectedChanges)}`); + if (!selectedChanges.length) { window.showInformationMessage(l10n.t('The selection range does not contain any changes.')); return; @@ -1733,26 +1845,38 @@ export class CommandCenter { textEditor.selections = [new Selection(firstStagedLine, 0, firstStagedLine, 0)]; } - @command('git.revertSelectedRanges', { diff: true }) - async revertSelectedRanges(changes: LineChange[]): Promise { + @command('git.revertSelectedRanges') + async revertSelectedRanges(): Promise { const textEditor = window.activeTextEditor; if (!textEditor) { return; } + const workingTreeDiffInformation = getWorkingTreeDiffInformation(textEditor); + if (!workingTreeDiffInformation) { + return; + } + + const workingTreeLineChanges = toLineChanges(workingTreeDiffInformation); + + this.logger.trace(`[CommandCenter][revertSelectedRanges] diffInformation: ${JSON.stringify(workingTreeDiffInformation)}`); + this.logger.trace(`[CommandCenter][revertSelectedRanges] diffInformation changes: ${JSON.stringify(workingTreeLineChanges)}`); + const modifiedDocument = textEditor.document; const selections = textEditor.selections; - const selectedChanges = changes.filter(change => { + const selectedChanges = workingTreeLineChanges.filter(change => { const modifiedRange = getModifiedRange(modifiedDocument, change); return selections.every(selection => !selection.intersection(modifiedRange)); }); - if (selectedChanges.length === changes.length) { + if (selectedChanges.length === workingTreeLineChanges.length) { window.showInformationMessage(l10n.t('The selection range does not contain any changes.')); return; } + this.logger.trace(`[CommandCenter][revertSelectedRanges] selectedChanges: ${JSON.stringify(selectedChanges)}`); + const selectionsBeforeRevert = textEditor.selections; await this._revertChanges(textEditor, selectedChanges); textEditor.selections = selectionsBeforeRevert; @@ -1810,8 +1934,8 @@ export class CommandCenter { await repository.revert([]); } - @command('git.unstageSelectedRanges', { diff: true }) - async unstageSelectedRanges(diffs: LineChange[]): Promise { + @command('git.unstageSelectedRanges') + async unstageSelectedRanges(): Promise { const textEditor = window.activeTextEditor; if (!textEditor) { @@ -1831,12 +1955,33 @@ export class CommandCenter { return; } - const originalUri = toGitUri(modifiedUri, 'HEAD'); + const repository = this.model.getRepository(modifiedUri); + if (!repository) { + return; + } + + const resource = repository.indexGroup.resourceStates + .find(r => pathEquals(r.resourceUri.fsPath, modifiedUri.fsPath)); + if (!resource) { + return; + } + + const indexDiffInformation = getIndexDiffInformation(textEditor); + if (!indexDiffInformation) { + return; + } + + const indexLineChanges = toLineChanges(indexDiffInformation); + + this.logger.trace(`[CommandCenter][unstageSelectedRanges] diffInformation: ${JSON.stringify(indexDiffInformation)}`); + this.logger.trace(`[CommandCenter][unstageSelectedRanges] diffInformation changes: ${JSON.stringify(indexLineChanges)}`); + + const originalUri = toGitUri(resource.original, 'HEAD'); const originalDocument = await workspace.openTextDocument(originalUri); const selectedLines = toLineRanges(textEditor.selections, modifiedDocument); - const selectedDiffs = diffs - .map(diff => selectedLines.reduce((result, range) => result || intersectDiffWithRange(modifiedDocument, diff, range), null)) - .filter(d => !!d) as LineChange[]; + const selectedDiffs = indexLineChanges + .map(change => selectedLines.reduce((result, range) => result || intersectDiffWithRange(modifiedDocument, change, range), null)) + .filter(c => !!c) as LineChange[]; if (!selectedDiffs.length) { window.showInformationMessage(l10n.t('The selection range does not contain any changes.')); @@ -1844,9 +1989,12 @@ export class CommandCenter { } const invertedDiffs = selectedDiffs.map(invertLineChange); - const result = applyLineChanges(modifiedDocument, originalDocument, invertedDiffs); - await this.runByRepository(modifiedUri, async (repository, resource) => await repository.stage(resource, result)); + this.logger.trace(`[CommandCenter][unstageSelectedRanges] selectedDiffs: ${JSON.stringify(selectedDiffs)}`); + this.logger.trace(`[CommandCenter][unstageSelectedRanges] invertedDiffs: ${JSON.stringify(invertedDiffs)}`); + + const result = applyLineChanges(modifiedDocument, originalDocument, invertedDiffs); + await repository.stage(modifiedUri, result); } @command('git.unstageFile') @@ -1906,49 +2054,39 @@ export class CommandCenter { return; } - const untrackedCount = scmResources.reduce((s, r) => s + (r.type === Status.UNTRACKED ? 1 : 0), 0); - let message: string; - let yes = l10n.t('Discard Changes'); + await this._cleanAll(scmResources); + } - if (scmResources.length === 1) { - if (untrackedCount > 0) { - message = l10n.t('Are you sure you want to DELETE {0}?\nThis is IRREVERSIBLE!\nThis file will be FOREVER LOST if you proceed.', path.basename(scmResources[0].resourceUri.fsPath)); - yes = l10n.t('Delete file'); - } else { - if (scmResources[0].type === Status.DELETED) { - yes = l10n.t('Restore file'); - message = l10n.t('Are you sure you want to restore {0}?', path.basename(scmResources[0].resourceUri.fsPath)); - } else { - message = l10n.t('Are you sure you want to discard changes in {0}?', path.basename(scmResources[0].resourceUri.fsPath)); - } - } - } else { - if (scmResources.every(resource => resource.type === Status.DELETED)) { - yes = l10n.t('Restore files'); - message = l10n.t('Are you sure you want to restore {0} files?', scmResources.length); - } else { - message = l10n.t('Are you sure you want to discard changes in {0} files?', scmResources.length); - } + @command('git.cleanAll', { repository: true }) + async cleanAll(repository: Repository): Promise { + await this._cleanAll(repository.workingTreeGroup.resourceStates); + } - if (untrackedCount > 0) { - message = `${message}\n\n${l10n.t('This will DELETE {0} untracked files!\nThis is IRREVERSIBLE!\nThese files will be FOREVER LOST.', untrackedCount)}`; - } + @command('git.cleanAllTracked', { repository: true }) + async cleanAllTracked(repository: Repository): Promise { + const resources = repository.workingTreeGroup.resourceStates + .filter(r => r.type !== Status.UNTRACKED && r.type !== Status.IGNORED); + + if (resources.length === 0) { + return; } - const pick = await window.showWarningMessage(message, { modal: true }, yes); + await this._cleanTrackedChanges(resources); + } - if (pick !== yes) { + @command('git.cleanAllUntracked', { repository: true }) + async cleanAllUntracked(repository: Repository): Promise { + const resources = [...repository.workingTreeGroup.resourceStates, ...repository.untrackedGroup.resourceStates] + .filter(r => r.type === Status.UNTRACKED || r.type === Status.IGNORED); + + if (resources.length === 0) { return; } - const resources = scmResources.map(r => r.resourceUri); - await this.runByRepository(resources, async (repository, resources) => repository.clean(resources)); + await this._cleanUntrackedChanges(resources); } - @command('git.cleanAll', { repository: true }) - async cleanAll(repository: Repository): Promise { - let resources = repository.workingTreeGroup.resourceStates; - + private async _cleanAll(resources: Resource[]): Promise { if (resources.length === 0) { return; } @@ -1957,24 +2095,25 @@ export class CommandCenter { const untrackedResources = resources.filter(r => r.type === Status.UNTRACKED || r.type === Status.IGNORED); if (untrackedResources.length === 0) { - await this._cleanTrackedChanges(repository, resources); - } else if (resources.length === 1) { - await this._cleanUntrackedChange(repository, resources[0]); + // Tracked files only + await this._cleanTrackedChanges(resources); } else if (trackedResources.length === 0) { - await this._cleanUntrackedChanges(repository, resources); - } else { // resources.length > 1 && untrackedResources.length > 0 && trackedResources.length > 0 - const untrackedMessage = untrackedResources.length === 1 - ? l10n.t('The following untracked file will be DELETED FROM DISK if discarded: {0}.', path.basename(untrackedResources[0].resourceUri.fsPath)) - : l10n.t('There are {0} untracked files which will be DELETED FROM DISK if discarded.', untrackedResources.length); + // Untracked files only + await this._cleanUntrackedChanges(resources); + } else { + // Tracked & Untracked files + const [untrackedMessage, untrackedMessageDetail] = this.getDiscardUntrackedChangesDialogDetails(untrackedResources); - const message = l10n.t('{0}\n\nThis is IRREVERSIBLE, your current working set will be FOREVER LOST.', untrackedMessage, resources.length); + const trackedMessage = trackedResources.length === 1 + ? l10n.t('\n\nAre you sure you want to discard changes in \'{0}\'?', path.basename(trackedResources[0].resourceUri.fsPath)) + : l10n.t('\n\nAre you sure you want to discard ALL changes in {0} files?', trackedResources.length); const yesTracked = trackedResources.length === 1 - ? l10n.t('Discard 1 Tracked File', trackedResources.length) - : l10n.t('Discard {0} Tracked Files', trackedResources.length); + ? l10n.t('Discard 1 Tracked File') + : l10n.t('Discard All {0} Tracked Files', trackedResources.length); const yesAll = l10n.t('Discard All {0} Files', resources.length); - const pick = await window.showWarningMessage(message, { modal: true }, yesTracked, yesAll); + const pick = await window.showWarningMessage(`${untrackedMessage} ${untrackedMessageDetail}${trackedMessage}\n\nThis is IRREVERSIBLE!\nYour current working set will be FOREVER LOST if you proceed.`, { modal: true }, yesTracked, yesAll); if (pick === yesTracked) { resources = trackedResources; @@ -1982,76 +2121,85 @@ export class CommandCenter { return; } - await repository.clean(resources.map(r => r.resourceUri)); + const resourceUris = resources.map(r => r.resourceUri); + await this.runByRepository(resourceUris, async (repository, resources) => repository.clean(resources)); } } - @command('git.cleanAllTracked', { repository: true }) - async cleanAllTracked(repository: Repository): Promise { - const resources = repository.workingTreeGroup.resourceStates - .filter(r => r.type !== Status.UNTRACKED && r.type !== Status.IGNORED); + private async _cleanTrackedChanges(resources: Resource[]): Promise { + const allResourcesDeleted = resources.every(r => r.type === Status.DELETED); - if (resources.length === 0) { - return; - } + const message = allResourcesDeleted + ? resources.length === 1 + ? l10n.t('Are you sure you want to restore \'{0}\'?', path.basename(resources[0].resourceUri.fsPath)) + : l10n.t('Are you sure you want to restore ALL {0} files?', resources.length) + : resources.length === 1 + ? l10n.t('Are you sure you want to discard changes in \'{0}\'?', path.basename(resources[0].resourceUri.fsPath)) + : l10n.t('Are you sure you want to discard ALL changes in {0} files?\n\nThis is IRREVERSIBLE!\nYour current working set will be FOREVER LOST if you proceed.', resources.length); - await this._cleanTrackedChanges(repository, resources); - } + const yes = allResourcesDeleted + ? resources.length === 1 + ? l10n.t('Restore File') + : l10n.t('Restore All {0} Files', resources.length) + : resources.length === 1 + ? l10n.t('Discard File') + : l10n.t('Discard All {0} Files', resources.length); - @command('git.cleanAllUntracked', { repository: true }) - async cleanAllUntracked(repository: Repository): Promise { - const resources = [...repository.workingTreeGroup.resourceStates, ...repository.untrackedGroup.resourceStates] - .filter(r => r.type === Status.UNTRACKED || r.type === Status.IGNORED); - - if (resources.length === 0) { - return; - } - - if (resources.length === 1) { - await this._cleanUntrackedChange(repository, resources[0]); - } else { - await this._cleanUntrackedChanges(repository, resources); - } - } - - private async _cleanTrackedChanges(repository: Repository, resources: Resource[]): Promise { - const message = resources.length === 1 - ? l10n.t('Are you sure you want to discard changes in {0}?', path.basename(resources[0].resourceUri.fsPath)) - : l10n.t('Are you sure you want to discard ALL changes in {0} files?\nThis is IRREVERSIBLE!\nYour current working set will be FOREVER LOST if you proceed.', resources.length); - const yes = resources.length === 1 - ? l10n.t('Discard 1 File') - : l10n.t('Discard All {0} Files', resources.length); const pick = await window.showWarningMessage(message, { modal: true }, yes); if (pick !== yes) { return; } - await repository.clean(resources.map(r => r.resourceUri)); + const resourceUris = resources.map(r => r.resourceUri); + await this.runByRepository(resourceUris, async (repository, resources) => repository.clean(resources)); } - private async _cleanUntrackedChange(repository: Repository, resource: Resource): Promise { - const message = l10n.t('Are you sure you want to DELETE {0}?\nThis is IRREVERSIBLE!\nThis file will be FOREVER LOST if you proceed.', path.basename(resource.resourceUri.fsPath)); - const yes = l10n.t('Delete file'); - const pick = await window.showWarningMessage(message, { modal: true }, yes); + private async _cleanUntrackedChanges(resources: Resource[]): Promise { + const [message, messageDetail, primaryAction] = this.getDiscardUntrackedChangesDialogDetails(resources); + const pick = await window.showWarningMessage(message, { detail: messageDetail, modal: true }, primaryAction); - if (pick !== yes) { + if (pick !== primaryAction) { return; } - await repository.clean([resource.resourceUri]); + const resourceUris = resources.map(r => r.resourceUri); + await this.runByRepository(resourceUris, async (repository, resources) => repository.clean(resources)); } - private async _cleanUntrackedChanges(repository: Repository, resources: Resource[]): Promise { - const message = l10n.t('Are you sure you want to DELETE {0} files?\nThis is IRREVERSIBLE!\nThese files will be FOREVER LOST if you proceed.', resources.length); - const yes = l10n.t('Delete Files'); - const pick = await window.showWarningMessage(message, { modal: true }, yes); + private getDiscardUntrackedChangesDialogDetails(resources: Resource[]): [string, string, string] { + const config = workspace.getConfiguration('git'); + const discardUntrackedChangesToTrash = config.get('discardUntrackedChangesToTrash', true) && !isRemote && !isLinuxSnap; - if (pick !== yes) { - return; - } + const messageWarning = !discardUntrackedChangesToTrash + ? resources.length === 1 + ? '\n\nThis is IRREVERSIBLE!\nThis file will be FOREVER LOST if you proceed.' + : '\n\nThis is IRREVERSIBLE!\nThese files will be FOREVER LOST if you proceed.' + : ''; - await repository.clean(resources.map(r => r.resourceUri)); + const message = resources.length === 1 + ? l10n.t('Are you sure you want to DELETE the following untracked file: \'{0}\'?{1}', path.basename(resources[0].resourceUri.fsPath), messageWarning) + : l10n.t('Are you sure you want to DELETE the {0} untracked files?{1}', resources.length, messageWarning); + + const messageDetail = discardUntrackedChangesToTrash + ? isWindows + ? resources.length === 1 + ? 'You can restore this file from the Recycle Bin.' + : 'You can restore these files from the Recycle Bin.' + : resources.length === 1 + ? 'You can restore this file from the Trash.' + : 'You can restore these files from the Trash.' + : ''; + + const primaryAction = discardUntrackedChangesToTrash + ? isWindows + ? l10n.t('Move to Recycle Bin') + : l10n.t('Move to Trash') + : resources.length === 1 + ? l10n.t('Delete File') + : l10n.t('Delete All {0} Files', resources.length); + + return [message, messageDetail, primaryAction]; } private async smartCommit( @@ -2173,6 +2321,8 @@ export class CommandCenter { // amend allows changing only the commit message && !opts.amend && !opts.empty + // merge not in progress + && !repository.mergeInProgress // rebase not in progress && repository.rebaseCommit === undefined ) { @@ -2220,7 +2370,13 @@ export class CommandCenter { opts.all = 'tracked'; } - // Branch protection + // Diagnostics commit hook + const diagnosticsResult = await evaluateDiagnosticsCommitHook(repository, opts); + if (!diagnosticsResult) { + return; + } + + // Branch protection commit hook const branchProtectionPrompt = config.get<'alwaysCommit' | 'alwaysCommitToNewBranch' | 'alwaysPrompt'>('branchProtectionPrompt')!; if (repository.isBranchProtected() && (branchProtectionPrompt === 'alwaysPrompt' || branchProtectionPrompt === 'alwaysCommitToNewBranch')) { const commitToNewBranch = l10n.t('Commit to a New Branch'); @@ -2496,13 +2652,38 @@ export class CommandCenter { return this._checkout(repository, { treeish }); } + @command('git.graph.checkout', { repository: true }) + async checkout2(repository: Repository, historyItem?: SourceControlHistoryItem, historyItemRefId?: string): Promise { + const historyItemRef = historyItem?.references?.find(r => r.id === historyItemRefId); + if (!historyItemRef) { + return; + } + + const config = workspace.getConfiguration('git', Uri.file(repository.root)); + const pullBeforeCheckout = config.get('pullBeforeCheckout', false) === true; + + // Branch, tag + if (historyItemRef.id.startsWith('refs/heads/') || historyItemRef.id.startsWith('refs/tags/')) { + await repository.checkout(historyItemRef.name, { pullBeforeCheckout }); + return; + } + + // Remote branch + const branches = await repository.findTrackingBranches(historyItemRef.name); + if (branches.length > 0) { + await repository.checkout(branches[0].name!, { pullBeforeCheckout }); + } else { + await repository.checkoutTracking(historyItemRef.name); + } + } + @command('git.checkoutDetached', { repository: true }) async checkoutDetached(repository: Repository, treeish?: string): Promise { return this._checkout(repository, { detached: true, treeish }); } - @command('git.checkoutRefDetached', { repository: true }) - async checkoutRefDetached(repository: Repository, historyItem?: SourceControlHistoryItemRef): Promise { + @command('git.graph.checkoutDetached', { repository: true }) + async checkoutDetached2(repository: Repository, historyItem?: SourceControlHistoryItem): Promise { if (!historyItem) { return false; } @@ -2529,6 +2710,7 @@ export class CommandCenter { const quickPick = window.createQuickPick(); quickPick.busy = true; quickPick.sortByLabel = false; + quickPick.matchOnDetail = true; quickPick.placeholder = opts?.detached ? l10n.t('Select a branch to checkout in detached mode') : l10n.t('Select a branch or tag to checkout'); @@ -2603,7 +2785,7 @@ export class CommandCenter { await this.cleanAll(repository); await item.run(repository, opts); } else if (choice === stash || choice === migrate) { - if (await this._stash(repository)) { + if (await this._stash(repository, true)) { await item.run(repository, opts); if (choice === migrate) { @@ -2749,9 +2931,12 @@ export class CommandCenter { private async _branch(repository: Repository, defaultName?: string, from = false, target?: string): Promise { target = target ?? 'HEAD'; + const config = workspace.getConfiguration('git'); + const showRefDetails = config.get('showReferenceDetails') === true; + if (from) { const getRefPicks = async () => { - const refs = await repository.getRefs(); + const refs = await repository.getRefs({ includeCommitDetails: showRefDetails }); const refProcessors = new RefItemsProcessor([ new RefProcessor(RefType.Head), new RefProcessor(RefType.RemoteHead), @@ -2807,19 +2992,90 @@ export class CommandCenter { } @command('git.deleteBranch', { repository: true }) - async deleteBranch(repository: Repository, name: string, force?: boolean): Promise { + async deleteBranch(repository: Repository, name: string | undefined, force?: boolean): Promise { + await this._deleteBranch(repository, undefined, name, { remote: false, force }); + } + + @command('git.graph.deleteBranch', { repository: true }) + async deleteBranch2(repository: Repository, historyItem?: SourceControlHistoryItem, historyItemRefId?: string): Promise { + const historyItemRef = historyItem?.references?.find(r => r.id === historyItemRefId); + if (!historyItemRef) { + return; + } + + // Local branch + if (historyItemRef.id.startsWith('refs/heads/')) { + if (historyItemRef.id === repository.historyProvider.currentHistoryItemRef?.id) { + window.showInformationMessage(l10n.t('The active branch cannot be deleted.')); + return; + } + + await this._deleteBranch(repository, undefined, historyItemRef.name, { remote: false }); + return; + } + + // Remote branch + if (historyItemRef.id === repository.historyProvider.currentHistoryItemRemoteRef?.id) { + window.showInformationMessage(l10n.t('The remote branch of the active branch cannot be deleted.')); + return; + } + + const index = historyItemRef.name.indexOf('/'); + if (index === -1) { + return; + } + + const remoteName = historyItemRef.name.substring(0, index); + const refName = historyItemRef.name.substring(index + 1); + + await this._deleteBranch(repository, remoteName, refName, { remote: true }); + } + + @command('git.deleteRemoteBranch', { repository: true }) + async deleteRemoteBranch(repository: Repository): Promise { + await this._deleteBranch(repository, undefined, undefined, { remote: true }); + } + + private async _deleteBranch(repository: Repository, remote: string | undefined, name: string | undefined, options: { remote: boolean; force?: boolean }): Promise { let run: (force?: boolean) => Promise; - if (typeof name === 'string') { - run = force => repository.deleteBranch(name, force); + + const config = workspace.getConfiguration('git'); + const showRefDetails = config.get('showReferenceDetails') === true; + + if (!options.remote && typeof name === 'string') { + // Local branch + run = force => repository.deleteBranch(name!, force); + } else if (options.remote && typeof remote === 'string' && typeof name === 'string') { + // Remote branch + run = force => repository.deleteRemoteRef(remote, name!, { force }); } else { const getBranchPicks = async () => { - const refs = await repository.getRefs({ pattern: 'refs/heads' }); - const currentHead = repository.HEAD && repository.HEAD.name; + const pattern = options.remote ? 'refs/remotes' : 'refs/heads'; + const refs = await repository.getRefs({ pattern, includeCommitDetails: showRefDetails }); + + const refsToExclude: string[] = []; + if (options.remote) { + refsToExclude.push('origin/HEAD'); - return refs.filter(ref => ref.name !== currentHead).map(ref => new BranchDeleteItem(ref)); + if (repository.HEAD?.upstream) { + // Current branch's upstream + refsToExclude.push(`${repository.HEAD.upstream.remote}/${repository.HEAD.upstream.name}`); + } + } else { + if (repository.HEAD?.name) { + // Current branch + refsToExclude.push(repository.HEAD.name); + } + } + + return refs.filter(ref => ref.name && !refsToExclude.includes(ref.name)) + .map(ref => new BranchDeleteItem(ref)); }; - const placeHolder = l10n.t('Select a branch to delete'); + const placeHolder = !options.remote + ? l10n.t('Select a branch to delete') + : l10n.t('Select a remote branch to delete'); + const choice = await this.pickRef(getBranchPicks(), placeHolder); if (!choice || !choice.refName) { @@ -2830,7 +3086,7 @@ export class CommandCenter { } try { - await run(force); + await run(options.force); } catch (err) { if (err.gitErrorCode !== GitErrorCodes.BranchNotFullyMerged) { throw err; @@ -2873,8 +3129,11 @@ export class CommandCenter { @command('git.merge', { repository: true }) async merge(repository: Repository): Promise { + const config = workspace.getConfiguration('git'); + const showRefDetails = config.get('showReferenceDetails') === true; + const getQuickPickItems = async (): Promise => { - const refs = await repository.getRefs(); + const refs = await repository.getRefs({ includeCommitDetails: showRefDetails }); const itemsProcessor = new RefItemsProcessor([ new RefProcessor(RefType.Head, MergeItem), new RefProcessor(RefType.RemoteHead, MergeItem), @@ -2899,8 +3158,11 @@ export class CommandCenter { @command('git.rebase', { repository: true }) async rebase(repository: Repository): Promise { + const config = workspace.getConfiguration('git'); + const showRefDetails = config.get('showReferenceDetails') === true; + const getQuickPickItems = async (): Promise => { - const refs = await repository.getRefs(); + const refs = await repository.getRefs({ includeCommitDetails: showRefDetails }); const itemsProcessor = new RebaseItemsProcessors(repository); return itemsProcessor.processRefs(refs); @@ -2938,20 +3200,32 @@ export class CommandCenter { @command('git.deleteTag', { repository: true }) async deleteTag(repository: Repository): Promise { + const config = workspace.getConfiguration('git'); + const showRefDetails = config.get('showReferenceDetails') === true; + const tagPicks = async (): Promise => { - const remoteTags = await repository.getRefs({ pattern: 'refs/tags' }); + const remoteTags = await repository.getRefs({ pattern: 'refs/tags', includeCommitDetails: showRefDetails }); return remoteTags.length === 0 ? [{ label: l10n.t('$(info) This repository has no tags.') }] : remoteTags.map(ref => new TagDeleteItem(ref)); }; const placeHolder = l10n.t('Select a tag to delete'); const choice = await this.pickRef(tagPicks(), placeHolder); - if (choice instanceof TagDeleteItem) { await choice.run(repository); } } + @command('git.graph.deleteTag', { repository: true }) + async deleteTag2(repository: Repository, historyItem?: SourceControlHistoryItem, historyItemRefId?: string): Promise { + const historyItemRef = historyItem?.references?.find(r => r.id === historyItemRefId); + if (!historyItemRef) { + return; + } + + await repository.deleteTag(historyItemRef.name); + } + @command('git.deleteRemoteTag', { repository: true }) async deleteRemoteTag(repository: Repository): Promise { const remotePicks = repository.remotes @@ -3310,11 +3584,12 @@ export class CommandCenter { await repository.cherryPick(hash); } - @command('git.cherryPickRef', { repository: true }) - async cherryPickRef(repository: Repository, historyItem?: SourceControlHistoryItem): Promise { + @command('git.graph.cherryPick', { repository: true }) + async cherryPick2(repository: Repository, historyItem?: SourceControlHistoryItem): Promise { if (!historyItem) { return; } + await repository.cherryPick(historyItem.id); } @@ -3974,16 +4249,12 @@ export class CommandCenter { } const commit = await repository.getCommit(item.ref); - const commitParentId = commit.parents.length > 0 ? commit.parents[0] : `${commit.hash}^`; - const changes = await repository.diffBetween(commitParentId, commit.hash); - - const title = `${item.shortRef} - ${commit.message}`; - const multiDiffSourceUri = toGitUri(Uri.file(repository.root), commit.hash, { scheme: 'git-commit' }); + const commitParentId = commit.parents.length > 0 ? commit.parents[0] : await repository.getEmptyTree(); + const changes = await repository.diffTrees(commitParentId, commit.hash); + const resources = changes.map(c => toMultiFileDiffEditorUris(c, commitParentId, commit.hash)); - const resources: { originalUri: Uri | undefined; modifiedUri: Uri | undefined }[] = []; - for (const change of changes) { - resources.push(toMultiFileDiffEditorUris(change, commitParentId, commit.hash)); - } + const title = `${item.shortRef} - ${truncate(commit.message)}`; + const multiDiffSourceUri = Uri.from({ scheme: 'scm-history-item', path: `${repository.root}/${commitParentId}..${commit.hash}` }); return { command: '_workbench.openMultiDiffEditor', @@ -4218,77 +4489,67 @@ export class CommandCenter { }); } - @command('git.viewCommit', { repository: true }) - async viewCommit(repository: Repository, historyItem1: SourceControlHistoryItem, historyItem2?: SourceControlHistoryItem): Promise { - if (!repository || !historyItem1) { + @command('git.copyCommitId', { repository: true }) + async copyCommitId(repository: Repository, historyItem: SourceControlHistoryItem): Promise { + if (!repository || !historyItem) { return; } - if (historyItem2) { - const mergeBase = await repository.getMergeBase(historyItem1.id, historyItem2.id); - if (!mergeBase || (mergeBase !== historyItem1.id && mergeBase !== historyItem2.id)) { - return; - } - } - - let title: string | undefined; - let historyItemParentId: string | undefined; + env.clipboard.writeText(historyItem.id); + } - // If historyItem2 is not provided, we are viewing a single commit. If historyItem2 is - // provided, we are viewing a range and we have to include both start and end commits. - // TODO@lszomoru - handle the case when historyItem2 is the first commit in the repository - if (!historyItem2) { - const commit = await repository.getCommit(historyItem1.id); - title = `${historyItem1.id.substring(0, 8)} - ${commit.message}`; - historyItemParentId = historyItem1.parentIds.length > 0 ? historyItem1.parentIds[0] : `${historyItem1.id}^`; - } else { - title = l10n.t('All Changes ({0} ↔ {1})', historyItem2.id.substring(0, 8), historyItem1.id.substring(0, 8)); - historyItemParentId = historyItem2.parentIds.length > 0 ? historyItem2.parentIds[0] : `${historyItem2.id}^`; + @command('git.copyCommitMessage', { repository: true }) + async copyCommitMessage(repository: Repository, historyItem: SourceControlHistoryItem): Promise { + if (!repository || !historyItem) { + return; } - const multiDiffSourceUri = toGitUri(Uri.file(repository.root), `${historyItemParentId}..${historyItem1.id}`, { scheme: 'git-commit', }); - - await this._viewChanges(repository, historyItem1.id, historyItemParentId, multiDiffSourceUri, title); + env.clipboard.writeText(historyItem.message); } - @command('git.viewAllChanges', { repository: true }) - async viewAllChanges(repository: Repository, historyItem: SourceControlHistoryItem): Promise { - if (!repository || !historyItem) { + @command('git.viewCommit', { repository: true }) + async viewCommit(repository: Repository, historyItemId: string): Promise { + if (!repository || !historyItemId) { return; } - const modifiedShortRef = historyItem.id.substring(0, 8); - const originalShortRef = historyItem.parentIds.length > 0 ? historyItem.parentIds[0].substring(0, 8) : `${modifiedShortRef}^`; - const title = l10n.t('All Changes ({0} ↔ {1})', originalShortRef, modifiedShortRef); + const rootUri = Uri.file(repository.root); + const commit = await repository.getCommit(historyItemId); + const title = `${getCommitShortHash(rootUri, historyItemId)} - ${truncate(commit.message)}`; + const historyItemParentId = commit.parents.length > 0 ? commit.parents[0] : await repository.getEmptyTree(); - const multiDiffSourceUri = toGitUri(Uri.file(repository.root), historyItem.id, { scheme: 'git-changes' }); - - await this._viewChanges(repository, modifiedShortRef, originalShortRef, multiDiffSourceUri, title); - } + const multiDiffSourceUri = Uri.from({ scheme: 'scm-history-item', path: `${repository.root}/${historyItemParentId}..${historyItemId}` }); - async _viewChanges(repository: Repository, historyItemId: string, historyItemParentId: string, multiDiffSourceUri: Uri, title: string): Promise { - const changes = await repository.diffBetween(historyItemParentId, historyItemId); + const changes = await repository.diffTrees(historyItemParentId, historyItemId); const resources = changes.map(c => toMultiFileDiffEditorUris(c, historyItemParentId, historyItemId)); await commands.executeCommand('_workbench.openMultiDiffEditor', { multiDiffSourceUri, title, resources }); } - @command('git.copyCommitId', { repository: true }) - async copyCommitId(repository: Repository, historyItem: SourceControlHistoryItem): Promise { - if (!repository || !historyItem) { + @command('git.copyContentToClipboard') + async copyContentToClipboard(content: string): Promise { + if (typeof content !== 'string') { return; } - env.clipboard.writeText(historyItem.id); + env.clipboard.writeText(content); } - @command('git.copyCommitMessage', { repository: true }) - async copyCommitMessage(repository: Repository, historyItem: SourceControlHistoryItem): Promise { - if (!repository || !historyItem) { - return; - } + @command('git.blame.toggleEditorDecoration') + toggleBlameEditorDecoration(): void { + this._toggleBlameSetting('blame.editorDecoration.enabled'); + } - env.clipboard.writeText(historyItem.message); + @command('git.blame.toggleStatusBarItem') + toggleBlameStatusBarItem(): void { + this._toggleBlameSetting('blame.statusBarItem.enabled'); + } + + private _toggleBlameSetting(setting: string): void { + const config = workspace.getConfiguration('git'); + const enabled = config.get(setting) === true; + + config.update(setting, !enabled, true); } private createCommand(id: string, key: string, method: Function, options: ScmCommandOptions): (...args: any[]) => any { @@ -4377,15 +4638,17 @@ export class CommandCenter { message = l10n.t('Can\'t force push refs to remote. The tip of the remote-tracking branch has been updated since the last checkout. Try running "Pull" first to pull the latest changes from the remote branch first.'); break; case GitErrorCodes.Conflict: - message = l10n.t('There are merge conflicts. Resolve them before committing.'); + message = l10n.t('There are merge conflicts. Please resolve them before committing your changes.'); type = 'warning'; + choices.clear(); choices.set(l10n.t('Show Changes'), () => commands.executeCommand('workbench.view.scm')); options.modal = false; break; case GitErrorCodes.StashConflict: - message = l10n.t('There were merge conflicts while applying the stash.'); - choices.set(l10n.t('Show Changes'), () => commands.executeCommand('workbench.view.scm')); + message = l10n.t('There are merge conflicts while applying the stash. Please resolve them before committing your changes.'); type = 'warning'; + choices.clear(); + choices.set(l10n.t('Show Changes'), () => commands.executeCommand('workbench.view.scm')); options.modal = false; break; case GitErrorCodes.AuthenticationFailed: { diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index 548f3aa2a1c4..2e72a1e41144 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -118,11 +118,11 @@ class GitDecorationProvider implements FileDecorationProvider { private onDidRunGitStatus(): void { const newDecorations = new Map(); - this.collectSubmoduleDecorationData(newDecorations); this.collectDecorationData(this.repository.indexGroup, newDecorations); this.collectDecorationData(this.repository.untrackedGroup, newDecorations); this.collectDecorationData(this.repository.workingTreeGroup, newDecorations); this.collectDecorationData(this.repository.mergeGroup, newDecorations); + this.collectSubmoduleDecorationData(newDecorations); const uris = new Set([...this.decorations.keys()].concat([...newDecorations.keys()])); this.decorations = newDecorations; @@ -273,6 +273,7 @@ class GitIncomingChangesFileDecorationProvider implements FileDecorationProvider export class GitDecorations { + private enabled = false; private disposables: Disposable[] = []; private modelDisposables: Disposable[] = []; private providers = new Map(); @@ -286,13 +287,19 @@ export class GitDecorations { } private update(): void { - const enabled = workspace.getConfiguration('git').get('decorations.enabled'); + const config = workspace.getConfiguration('git'); + const enabled = config.get('decorations.enabled') === true; + if (this.enabled === enabled) { + return; + } if (enabled) { this.enable(); } else { this.disable(); } + + this.enabled = enabled; } private enable(): void { diff --git a/extensions/git/src/decorators.ts b/extensions/git/src/decorators.ts index b1a25d4fd919..f89ff2327e95 100644 --- a/extensions/git/src/decorators.ts +++ b/extensions/git/src/decorators.ts @@ -98,4 +98,4 @@ export function debounce(delay: number): Function { this[timerKey] = setTimeout(() => fn.apply(this, args), delay); }; }); -} \ No newline at end of file +} diff --git a/extensions/git/src/fileSystemProvider.ts b/extensions/git/src/fileSystemProvider.ts index af80924ae138..19928863832a 100644 --- a/extensions/git/src/fileSystemProvider.ts +++ b/extensions/git/src/fileSystemProvider.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { workspace, Uri, Disposable, Event, EventEmitter, window, FileSystemProvider, FileChangeEvent, FileStat, FileType, FileChangeType, FileSystemError } from 'vscode'; +import { workspace, Uri, Disposable, Event, EventEmitter, window, FileSystemProvider, FileChangeEvent, FileStat, FileType, FileChangeType, FileSystemError, LogOutputChannel } from 'vscode'; import { debounce, throttle } from './decorators'; import { fromGitUri, toGitUri } from './uri'; import { Model, ModelChangeEvent, OriginalResourceChangeEvent } from './model'; @@ -18,7 +18,7 @@ interface CacheRow { const THREE_MINUTES = 1000 * 60 * 3; const FIVE_MINUTES = 1000 * 60 * 5; -function sanitizeRef(ref: string, path: string, repository: Repository): string { +function sanitizeRef(ref: string, path: string, submoduleOf: string | undefined, repository: Repository): string { if (ref === '~') { const fileUri = Uri.file(path); const uriString = fileUri.toString(); @@ -30,6 +30,11 @@ function sanitizeRef(ref: string, path: string, repository: Repository): string return `:${ref[1]}`; } + // Submodule HEAD + if (submoduleOf && (ref === 'index' || ref === 'wt')) { + return 'HEAD'; + } + return ref; } @@ -43,7 +48,7 @@ export class GitFileSystemProvider implements FileSystemProvider { private mtime = new Date().getTime(); private disposables: Disposable[] = []; - constructor(private model: Model) { + constructor(private readonly model: Model, private readonly logger: LogOutputChannel) { this.disposables.push( model.onDidChangeRepository(this.onDidChangeRepository, this), model.onDidChangeOriginalResource(this.onDidChangeOriginalResource, this), @@ -136,17 +141,24 @@ export class GitFileSystemProvider implements FileSystemProvider { const { submoduleOf, path, ref } = fromGitUri(uri); const repository = submoduleOf ? this.model.getRepository(submoduleOf) : this.model.getRepository(uri); if (!repository) { + this.logger.warn(`[GitFileSystemProvider][stat] Repository not found - ${uri.toString()}`); throw FileSystemError.FileNotFound(); } - let size = 0; try { - const details = await repository.getObjectDetails(sanitizeRef(ref, path, repository), path); - size = details.size; + const details = await repository.getObjectDetails(sanitizeRef(ref, path, submoduleOf, repository), path); + return { type: FileType.File, size: details.size, mtime: this.mtime, ctime: 0 }; } catch { - // noop + // Empty tree + if (ref === await repository.getEmptyTree()) { + this.logger.warn(`[GitFileSystemProvider][stat] Empty tree - ${uri.toString()}`); + return { type: FileType.File, size: 0, mtime: this.mtime, ctime: 0 }; + } + + // File does not exist in git. This could be because the file is untracked or ignored + this.logger.warn(`[GitFileSystemProvider][stat] File not found - ${uri.toString()}`); + throw FileSystemError.FileNotFound(); } - return { type: FileType.File, size: size, mtime: this.mtime, ctime: 0 }; } readDirectory(): Thenable<[string, FileType][]> { @@ -181,6 +193,7 @@ export class GitFileSystemProvider implements FileSystemProvider { const repository = this.model.getRepository(uri); if (!repository) { + this.logger.warn(`[GitFileSystemProvider][readFile] Repository not found - ${uri.toString()}`); throw FileSystemError.FileNotFound(); } @@ -190,9 +203,17 @@ export class GitFileSystemProvider implements FileSystemProvider { this.cache.set(uri.toString(), cacheValue); try { - return await repository.buffer(sanitizeRef(ref, path, repository), path); - } catch (err) { - return new Uint8Array(0); + return await repository.buffer(sanitizeRef(ref, path, submoduleOf, repository), path); + } catch { + // Empty tree + if (ref === await repository.getEmptyTree()) { + this.logger.warn(`[GitFileSystemProvider][readFile] Empty tree - ${uri.toString()}`); + return new Uint8Array(0); + } + + // File does not exist in git. This could be because the file is untracked or ignored + this.logger.warn(`[GitFileSystemProvider][readFile] File not found - ${uri.toString()}`); + throw FileSystemError.FileNotFound(); } } diff --git a/extensions/git/src/git-base.ts b/extensions/git/src/git-base.ts index 6cef535cfa54..437a126c9f3c 100644 --- a/extensions/git/src/git-base.ts +++ b/extensions/git/src/git-base.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { extensions } from 'vscode'; -import { API as GitBaseAPI, GitBaseExtension } from './api/git-base'; +import { API as GitBaseAPI, GitBaseExtension } from './typings/git-base'; export class GitBaseApi { diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index bb5cb7102daf..4f64607aa6a1 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -12,10 +12,10 @@ import which from 'which'; import { EventEmitter } from 'events'; import * as iconv from '@vscode/iconv-lite-umd'; import * as filetype from 'file-type'; -import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter, Versions, isWindows, pathEquals, isMacintosh, isDescendant } from './util'; +import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter, Versions, isWindows, pathEquals, isMacintosh, isDescendant, relativePath } from './util'; import { CancellationError, CancellationToken, ConfigurationChangeEvent, LogOutputChannel, Progress, Uri, workspace } from 'vscode'; import { detectEncoding } from './encoding'; -import { Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, RefQuery, InitOptions } from './api/git'; +import { Commit as ApiCommit, Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, RefQuery, InitOptions } from './api/git'; import * as byline from 'byline'; import { StringDecoder } from 'string_decoder'; @@ -62,6 +62,7 @@ export interface LogFileOptions { /** Optional. Specifies whether to start retrieving log entries in reverse order. */ readonly reverse?: boolean; readonly sortByAuthorDate?: boolean; + readonly shortStats?: boolean; } function parseVersion(raw: string): string { @@ -315,7 +316,7 @@ export interface IGitOptions { gitPath: string; userAgent: string; version: string; - env?: any; + env?: { [key: string]: string }; } function getGitErrorCode(stderr: string): string | undefined { @@ -354,6 +355,10 @@ function sanitizePath(path: string): string { return path.replace(/^([a-z]):\\/i, (_, letter) => `${letter.toUpperCase()}:\\`); } +function sanitizeRelativePath(from: string, to: string): string { + return path.isAbsolute(to) ? relativePath(from, to).replace(/\\/g, '/') : to; +} + const COMMIT_FORMAT = '%H%n%aN%n%aE%n%at%n%ct%n%P%n%D%n%B'; const STASH_FORMAT = '%H%n%P%n%gd%n%gs'; @@ -369,7 +374,8 @@ export class Git { readonly path: string; readonly userAgent: string; readonly version: string; - private env: any; + readonly env: { [key: string]: string }; + private commandsToLog: string[] = []; private _onOutput = new EventEmitter(); @@ -502,7 +508,7 @@ export class Git { const repoUri = Uri.file(repositoryRootPath); const pathUri = Uri.file(pathInsidePossibleRepository); if (repoUri.authority.length !== 0 && pathUri.authority.length === 0) { - const match = /(?<=^\/?)([a-zA-Z])(?=:\/)/.exec(pathUri.path); + const match = /^[\/]?([a-zA-Z])[:\/]/.exec(pathUri.path); if (match !== null) { const [, letter] = match; @@ -1054,9 +1060,144 @@ function parseGitChanges(repositoryRoot: string, raw: string): Change[] { return result; } +export interface BlameInformation { + readonly hash: string; + readonly subject?: string; + readonly authorName?: string; + readonly authorEmail?: string; + readonly authorDate?: number; + readonly ranges: { + readonly startLineNumber: number; + readonly endLineNumber: number; + }[]; +} + +function parseGitBlame(data: string): BlameInformation[] { + const lineSeparator = /\r?\n/; + const commitRegex = /^([0-9a-f]{40})/gm; + + const blameInformation = new Map(); + + let commitHash: string | undefined = undefined; + let authorName: string | undefined = undefined; + let authorEmail: string | undefined = undefined; + let authorTime: number | undefined = undefined; + let message: string | undefined = undefined; + let startLineNumber: number | undefined = undefined; + let endLineNumber: number | undefined = undefined; + + for (const line of data.split(lineSeparator)) { + // Commit + const commitMatch = line.match(commitRegex); + if (!commitHash && commitMatch) { + const segments = line.split(' '); + + commitHash = commitMatch[0]; + startLineNumber = Number(segments[2]); + endLineNumber = Number(segments[2]) + Number(segments[3]) - 1; + } + + // Commit properties + if (commitHash && line.startsWith('author ')) { + authorName = line.substring('author '.length); + } + if (commitHash && line.startsWith('author-mail ')) { + authorEmail = line.substring('author-mail <'.length, line.length - 1); + } + if (commitHash && line.startsWith('author-time ')) { + authorTime = Number(line.substring('author-time '.length)) * 1000; + } + if (commitHash && line.startsWith('summary ')) { + message = line.substring('summary '.length); + } + + // Commit end + if (commitHash && startLineNumber && endLineNumber && line.startsWith('filename ')) { + const existingCommit = blameInformation.get(commitHash); + if (existingCommit) { + existingCommit.ranges.push({ startLineNumber, endLineNumber }); + blameInformation.set(commitHash, existingCommit); + } else { + blameInformation.set(commitHash, { + hash: commitHash, authorName, authorEmail, authorDate: authorTime, subject: message, ranges: [{ startLineNumber, endLineNumber }] + }); + } + + commitHash = authorName = authorEmail = authorTime = message = startLineNumber = endLineNumber = undefined; + } + } + + return Array.from(blameInformation.values()); +} + +const REFS_FORMAT = '%(refname)%00%(objectname)%00%(*objectname)'; +const REFS_WITH_DETAILS_FORMAT = `${REFS_FORMAT}%00%(parent)%00%(*parent)%00%(authorname)%00%(*authorname)%00%(authordate:unix)%00%(*authordate:unix)%00%(subject)%00%(*subject)`; + +const headRegex = /^refs\/heads\/([^ ]+)$/; +const remoteHeadRegex = /^refs\/remotes\/([^/]+)\/([^ ]+)$/; +const tagRegex = /^refs\/tags\/([^ ]+)$/; + +function parseRefs(data: string, includeCommitDetails: boolean): Ref[] { + const refs: Ref[] = []; + const refRegex = !includeCommitDetails + ? /^(.*)\0([0-9a-f]{40})\0([0-9a-f]{40})?$/gm + : /^(.*)\0([0-9a-f]{40})\0([0-9a-f]{40})?\0(.*)\0(.*)\0(.*)\0(.*)\0(.*)\0(.*)\0(.*)\0(.*)$/gm; + + let ref: string | undefined; + let commitHash: string | undefined; + let tagCommitHash: string | undefined; + let commitParents: string | undefined; + let tagCommitParents: string | undefined; + let commitSubject: string | undefined; + let tagCommitSubject: string | undefined; + let authorName: string | undefined; + let tagAuthorName: string | undefined; + let authorDate: string | undefined; + let tagAuthorDate: string | undefined; + + let match: RegExpExecArray | null; + let refMatch: RegExpExecArray | null; + + do { + match = refRegex.exec(data); + if (match === null) { + break; + } + + let commitDetails: ApiCommit | undefined = undefined; + [, ref, commitHash, tagCommitHash, commitParents, tagCommitParents, authorName, tagAuthorName, authorDate, tagAuthorDate, commitSubject, tagCommitSubject] = match; + + if (includeCommitDetails) { + const parents = tagCommitParents || commitParents; + const subject = tagCommitSubject || commitSubject; + const author = tagAuthorName || authorName; + const date = tagAuthorDate || authorDate; + + commitDetails = { + hash: commitHash, + message: subject, + parents: parents ? parents.split(' ') : [], + authorName: author, + authorDate: date ? new Date(Number(date) * 1000) : undefined + } satisfies ApiCommit; + } + + if (refMatch = headRegex.exec(ref)) { + refs.push({ name: refMatch[1], commit: commitHash, commitDetails, type: RefType.Head }); + } else if (refMatch = remoteHeadRegex.exec(ref)) { + refs.push({ name: `${refMatch[1]}/${refMatch[2]}`, remote: refMatch[1], commit: commitHash, commitDetails, type: RefType.RemoteHead }); + } else if (refMatch = tagRegex.exec(ref)) { + refs.push({ name: refMatch[1], commit: tagCommitHash ?? commitHash, commitDetails, type: RefType.Tag }); + } + } while (true); + + return refs; +} + export interface PullOptions { - unshallow?: boolean; - tags?: boolean; + readonly unshallow?: boolean; + readonly tags?: boolean; + readonly autoStash?: boolean; readonly cancellationToken?: CancellationToken; } @@ -1095,11 +1236,11 @@ export class Repository { return this.git.spawn(args, options); } - async config(scope: string, key: string, value: any = null, options: SpawnOptions = {}): Promise { - const args = ['config']; + async config(command: string, scope: string, key: string, value: any = null, options: SpawnOptions = {}): Promise { + const args = ['config', `--${command}`]; if (scope) { - args.push('--' + scope); + args.push(`--${scope}`); } args.push(key); @@ -1214,6 +1355,10 @@ export class Repository { } } + if (options?.shortStats) { + args.push('--shortstat'); + } + if (options?.sortByAuthorDate) { args.push('--author-date-order'); } @@ -1253,8 +1398,8 @@ export class Repository { .filter(entry => !!entry); } - async bufferString(object: string, encoding: string = 'utf8', autoGuessEncoding = false, candidateGuessEncodings: string[] = []): Promise { - const stdout = await this.buffer(object); + async bufferString(ref: string, filePath: string, encoding: string = 'utf8', autoGuessEncoding = false, candidateGuessEncodings: string[] = []): Promise { + const stdout = await this.buffer(ref, filePath); if (autoGuessEncoding) { encoding = detectEncoding(stdout, candidateGuessEncodings) || encoding; @@ -1265,8 +1410,9 @@ export class Repository { return iconv.decode(stdout, encoding); } - async buffer(object: string): Promise { - const child = this.stream(['show', '--textconv', object]); + async buffer(ref: string, filePath: string): Promise { + const relativePath = sanitizeRelativePath(this.repositoryRoot, filePath); + const child = this.stream(['show', '--textconv', `${ref}:${relativePath}`]); if (!child.stdout) { return Promise.reject('Can\'t open file from git'); @@ -1291,14 +1437,17 @@ export class Repository { } async getObjectDetails(treeish: string, path: string): Promise<{ mode: string; object: string; size: number }> { - if (!treeish) { // index + if (!treeish || treeish === ':1' || treeish === ':2' || treeish === ':3') { // index const elements = await this.lsfiles(path); if (elements.length === 0) { throw new GitError({ message: 'Path not known by git', gitErrorCode: GitErrorCodes.UnknownPath }); } - const { mode, object } = elements[0]; + const { mode, object } = treeish !== '' + ? elements.find(e => e.stage === treeish.substring(1)) ?? elements[0] + : elements[0]; + const catFile = await this.exec(['cat-file', '-s', object]); const size = parseInt(catFile.stdout); @@ -1312,13 +1461,20 @@ export class Repository { } const { mode, object, size } = elements[0]; - return { mode, object, size: parseInt(size) }; + return { mode, object, size: parseInt(size) || 0 }; } - async lstree(treeish: string, path?: string): Promise { - const args = ['ls-tree', '-l', treeish]; + async lstree(treeish: string, path?: string, options?: { recursive?: boolean }): Promise { + const args = ['ls-tree', '-l']; + + if (options?.recursive) { + args.push('-r'); + } + + args.push(treeish); + if (path) { - args.push('--', sanitizePath(path)); + args.push('--', sanitizeRelativePath(this.repositoryRoot, path)); } const { stdout } = await this.exec(args); @@ -1326,15 +1482,24 @@ export class Repository { } async lsfiles(path: string): Promise { - const { stdout } = await this.exec(['ls-files', '--stage', '--', sanitizePath(path)]); + const args = ['ls-files', '--stage']; + const relativePath = sanitizeRelativePath(this.repositoryRoot, path); + + if (relativePath) { + args.push('--', relativePath); + } + + const { stdout } = await this.exec(args); return parseLsFiles(stdout); } - async getGitRelativePath(ref: string, relativePath: string): Promise { - const relativePathLowercase = relativePath.toLowerCase(); - const dirname = path.posix.dirname(relativePath) + '/'; - const elements: { file: string }[] = ref ? await this.lstree(ref, dirname) : await this.lsfiles(dirname); - const element = elements.filter(file => file.file.toLowerCase() === relativePathLowercase)[0]; + async getGitFilePath(ref: string, filePath: string): Promise { + const elements: { file: string }[] = ref + ? await this.lstree(ref, undefined, { recursive: true }) + : await this.lsfiles(this.repositoryRoot); + + const relativePathLowercase = sanitizeRelativePath(this.repositoryRoot, filePath).toLowerCase(); + const element = elements.find(file => file.file.toLowerCase() === relativePathLowercase); if (!element) { throw new GitError({ @@ -1342,7 +1507,7 @@ export class Repository { }); } - return element.file; + return path.join(this.repositoryRoot, element.file); } async detectObjectType(object: string): Promise<{ mimetype: string; encoding?: string }> { @@ -1422,7 +1587,7 @@ export class Repository { return await this.diffFiles(false); } - const args = ['diff', '--', sanitizePath(path)]; + const args = ['diff', '--', sanitizeRelativePath(this.repositoryRoot, path)]; const result = await this.exec(args); return result.stdout; } @@ -1435,7 +1600,7 @@ export class Repository { return await this.diffFiles(false, ref); } - const args = ['diff', ref, '--', sanitizePath(path)]; + const args = ['diff', ref, '--', sanitizeRelativePath(this.repositoryRoot, path)]; const result = await this.exec(args); return result.stdout; } @@ -1448,7 +1613,7 @@ export class Repository { return await this.diffFiles(true); } - const args = ['diff', '--cached', '--', sanitizePath(path)]; + const args = ['diff', '--cached', '--', sanitizeRelativePath(this.repositoryRoot, path)]; const result = await this.exec(args); return result.stdout; } @@ -1461,7 +1626,7 @@ export class Repository { return await this.diffFiles(true, ref); } - const args = ['diff', '--cached', ref, '--', sanitizePath(path)]; + const args = ['diff', '--cached', ref, '--', sanitizeRelativePath(this.repositoryRoot, path)]; const result = await this.exec(args); return result.stdout; } @@ -1481,7 +1646,7 @@ export class Repository { return await this.diffFiles(false, range); } - const args = ['diff', range, '--', sanitizePath(path)]; + const args = ['diff', range, '--', sanitizeRelativePath(this.repositoryRoot, path)]; const result = await this.exec(args); return result.stdout.trim(); @@ -1516,8 +1681,14 @@ export class Repository { return parseGitChanges(this.repositoryRoot, gitResult.stdout); } - async diffTrees(treeish1: string, treeish2?: string): Promise { - const args = ['diff-tree', '-r', '--name-status', '-z', '--diff-filter=ADMR', treeish1]; + async diffTrees(treeish1: string, treeish2?: string, options?: { similarityThreshold?: number }): Promise { + const args = ['diff-tree', '-r', '--name-status', '-z', '--diff-filter=ADMR']; + + if (options?.similarityThreshold) { + args.push(`--find-renames=${options.similarityThreshold}%`); + } + + args.push(treeish1); if (treeish2) { args.push(treeish2); @@ -1567,7 +1738,7 @@ export class Repository { } if (paths && paths.length) { - for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { + for (const chunk of splitInChunks(paths.map(p => sanitizeRelativePath(this.repositoryRoot, p)), MAX_CLI_LENGTH)) { await this.exec([...args, '--', ...chunk]); } } else { @@ -1582,14 +1753,15 @@ export class Repository { return; } - args.push(...paths.map(sanitizePath)); + args.push(...paths.map(p => sanitizeRelativePath(this.repositoryRoot, p))); await this.exec(args); } - async stage(path: string, data: string): Promise { - const child = this.stream(['hash-object', '--stdin', '-w', '--path', sanitizePath(path)], { stdio: [null, null, null] }); - child.stdin!.end(data, 'utf8'); + async stage(path: string, data: string, encoding: string): Promise { + const relativePath = sanitizeRelativePath(this.repositoryRoot, path); + const child = this.stream(['hash-object', '--stdin', '-w', '--path', relativePath], { stdio: [null, null, null] }); + child.stdin!.end(iconv.encode(data, encoding)); const { exitCode, stdout } = await exec(child); const hash = stdout.toString('utf8'); @@ -1617,7 +1789,7 @@ export class Repository { add = '--add'; } - await this.exec(['update-index', add, '--cacheinfo', mode, hash, path]); + await this.exec(['update-index', add, '--cacheinfo', mode, hash, relativePath]); } async checkout(treeish: string, paths: string[], opts: { track?: boolean; detached?: boolean } = Object.create(null)): Promise { @@ -1637,7 +1809,7 @@ export class Repository { try { if (paths && paths.length > 0) { - for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { + for (const chunk of splitInChunks(paths.map(p => sanitizeRelativePath(this.repositoryRoot, p)), MAX_CLI_LENGTH)) { await this.exec([...args, '--', ...chunk]); } } else { @@ -1831,8 +2003,14 @@ export class Repository { await this.exec(args); } - async deleteRemoteTag(remoteName: string, tagName: string): Promise { - const args = ['push', '--delete', remoteName, tagName]; + async deleteRemoteRef(remoteName: string, refName: string, options?: { force?: boolean }): Promise { + const args = ['push', remoteName, '--delete']; + + if (options?.force) { + args.push('--force'); + } + + args.push(refName); await this.exec(args); } @@ -1845,7 +2023,7 @@ export class Repository { const args = ['clean', '-f', '-q']; for (const paths of groups) { - for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { + for (const chunk of splitInChunks(paths.map(p => sanitizeRelativePath(this.repositoryRoot, p)), MAX_CLI_LENGTH)) { promises.push(limiter.queue(() => this.exec([...args, '--', ...chunk]))); } } @@ -1885,7 +2063,7 @@ export class Repository { try { if (paths && paths.length > 0) { - for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { + for (const chunk of splitInChunks(paths.map(p => sanitizeRelativePath(this.repositoryRoot, p)), MAX_CLI_LENGTH)) { await this.exec([...args, '--', ...chunk]); } } else { @@ -1992,6 +2170,11 @@ export class Repository { args.push('--unshallow'); } + // --auto-stash option is only available `git pull --merge` starting with git 2.27.0 + if (options.autoStash && this._git.compareGitVersionTo('2.27.0') !== -1) { + args.push('--autostash'); + } + if (rebase) { args.push('-r'); } @@ -2125,7 +2308,7 @@ export class Repository { async blame(path: string): Promise { try { - const args = ['blame', sanitizePath(path)]; + const args = ['blame', '--', sanitizeRelativePath(this.repositoryRoot, path)]; const result = await this.exec(args); return result.stdout.trim(); } catch (err) { @@ -2137,6 +2320,25 @@ export class Repository { } } + async blame2(path: string, ref?: string): Promise { + try { + const args = ['blame', '--root', '--incremental']; + + if (ref) { + args.push(ref); + } + + args.push('--', sanitizeRelativePath(this.repositoryRoot, path)); + + const result = await this.exec(args); + + return parseGitBlame(result.stdout.trim()); + } + catch (err) { + return undefined; + } + } + async createStash(message?: string, includeUntracked?: boolean, staged?: boolean): Promise { try { const args = ['stash', 'push']; @@ -2340,7 +2542,9 @@ export class Repository { // Upstream commit if (HEAD && HEAD.upstream) { - const ref = `refs/remotes/${HEAD.upstream.remote}/${HEAD.upstream.name}`; + const ref = HEAD.upstream.remote !== '.' + ? `refs/remotes/${HEAD.upstream.remote}/${HEAD.upstream.name}` + : `refs/heads/${HEAD.upstream.name}`; const commit = await this.revParse(ref); HEAD = { ...HEAD, upstream: { ...HEAD.upstream, commit } }; } @@ -2445,7 +2649,7 @@ export class Repository { args.push('--sort', `-${query.sort}`); } - args.push('--format', '%(refname) %(objectname) %(*objectname)'); + args.push('--format', query.includeCommitDetails ? REFS_WITH_DETAILS_FORMAT : REFS_FORMAT); if (query.pattern) { const patterns = Array.isArray(query.pattern) ? query.pattern : [query.pattern]; @@ -2459,25 +2663,7 @@ export class Repository { } const result = await this.exec(args, { cancellationToken }); - - const fn = (line: string): Ref | null => { - let match: RegExpExecArray | null; - - if (match = /^refs\/heads\/([^ ]+) ([0-9a-f]{40}) ([0-9a-f]{40})?$/.exec(line)) { - return { name: match[1], commit: match[2], type: RefType.Head }; - } else if (match = /^refs\/remotes\/([^/]+)\/([^ ]+) ([0-9a-f]{40}) ([0-9a-f]{40})?$/.exec(line)) { - return { name: `${match[1]}/${match[2]}`, commit: match[3], type: RefType.RemoteHead, remote: match[1] }; - } else if (match = /^refs\/tags\/([^ ]+) ([0-9a-f]{40}) ([0-9a-f]{40})?$/.exec(line)) { - return { name: match[1], commit: match[3] ?? match[2], type: RefType.Tag }; - } - - return null; - }; - - return result.stdout.split('\n') - .filter(line => !!line) - .map(fn) - .filter(ref => !!ref) as Ref[]; + return parseRefs(result.stdout, query.includeCommitDetails === true); } async getRemoteRefs(remote: string, opts?: { heads?: boolean; tags?: boolean; cancellationToken?: CancellationToken }): Promise { @@ -2631,7 +2817,7 @@ export class Repository { return { type: RefType.Head, name: branchName, - upstream: upstream ? { + upstream: upstream !== '' && status !== '[gone]' ? { name: upstreamRef ? upstreamRef.substring(11) : upstream.substring(index + 1), remote: remoteName ? remoteName : upstream.substring(0, index) } : undefined, @@ -2674,8 +2860,8 @@ export class Repository { return Promise.reject(new Error(`No such branch: ${name}.`)); } - async getDefaultBranch(): Promise { - const result = await this.exec(['symbolic-ref', '--short', 'refs/remotes/origin/HEAD']); + async getDefaultBranch(remoteName: string): Promise { + const result = await this.exec(['symbolic-ref', '--short', `refs/remotes/${remoteName}/HEAD`]); if (!result.stdout || result.stderr) { throw new Error('No default branch'); } @@ -2735,7 +2921,7 @@ export class Repository { } async getCommit(ref: string): Promise { - const result = await this.exec(['show', '-s', '--decorate=full', '--shortstat', `--format=${COMMIT_FORMAT}`, '-z', ref]); + const result = await this.exec(['show', '-s', '--decorate=full', '--shortstat', `--format=${COMMIT_FORMAT}`, '-z', ref, '--']); const commits = parseGitCommits(result.stdout); if (commits.length === 0) { return Promise.reject('bad commit format'); @@ -2743,6 +2929,15 @@ export class Repository { return commits[0]; } + async revList(ref1: string, ref2: string): Promise { + const result = await this.exec(['rev-list', `${ref1}..${ref2}`]); + if (result.stderr) { + return []; + } + + return result.stdout.trim().split('\n'); + } + async revParse(ref: string): Promise { try { const result = await fs.readFile(path.join(this.dotGit.path, ref), 'utf8'); @@ -2765,7 +2960,7 @@ export class Repository { async updateSubmodules(paths: string[]): Promise { const args = ['submodule', 'update']; - for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { + for (const chunk of splitInChunks(paths.map(p => sanitizeRelativePath(this.repositoryRoot, p)), MAX_CLI_LENGTH)) { await this.exec([...args, '--', ...chunk]); } } diff --git a/extensions/git/src/gitEditor.ts b/extensions/git/src/gitEditor.ts index 6291e5152a72..38001136956e 100644 --- a/extensions/git/src/gitEditor.ts +++ b/extensions/git/src/gitEditor.ts @@ -67,7 +67,7 @@ export class GitEditor implements IIPCHandler, ITerminalEnvironmentProvider { } export class GitEditorDocumentLinkProvider implements DocumentLinkProvider { - private readonly _regex = /^#\s+(modified|new file|deleted|renamed|copied|type change):\s+(?.*?)(?:\s+->\s+(?.*))*$/gm; + constructor(private readonly _model: Model) { } diff --git a/extensions/git/src/historyItemDetailsProvider.ts b/extensions/git/src/historyItemDetailsProvider.ts new file mode 100644 index 000000000000..be0e2b337f8f --- /dev/null +++ b/extensions/git/src/historyItemDetailsProvider.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Command, Disposable } from 'vscode'; +import { AvatarQuery, SourceControlHistoryItemDetailsProvider } from './api/git'; +import { Repository } from './repository'; +import { ApiRepository } from './api/api1'; + +export interface ISourceControlHistoryItemDetailsProviderRegistry { + registerSourceControlHistoryItemDetailsProvider(provider: SourceControlHistoryItemDetailsProvider): Disposable; + getSourceControlHistoryItemDetailsProviders(): SourceControlHistoryItemDetailsProvider[]; +} + +export async function provideSourceControlHistoryItemAvatar( + registry: ISourceControlHistoryItemDetailsProviderRegistry, + repository: Repository, + query: AvatarQuery +): Promise | undefined> { + for (const provider of registry.getSourceControlHistoryItemDetailsProviders()) { + const result = await provider.provideAvatar(new ApiRepository(repository), query); + + if (result) { + return result; + } + } + + return undefined; +} + +export async function provideSourceControlHistoryItemHoverCommands( + registry: ISourceControlHistoryItemDetailsProviderRegistry, + repository: Repository +): Promise { + for (const provider of registry.getSourceControlHistoryItemDetailsProviders()) { + const result = await provider.provideHoverCommands(new ApiRepository(repository)); + + if (result) { + return result; + } + } + + return undefined; +} + +export async function provideSourceControlHistoryItemMessageLinks( + registry: ISourceControlHistoryItemDetailsProviderRegistry, + repository: Repository, + message: string +): Promise { + for (const provider of registry.getSourceControlHistoryItemDetailsProviders()) { + const result = await provider.provideMessageLinks( + new ApiRepository(repository), message); + + if (result) { + return result; + } + } + + return undefined; +} diff --git a/extensions/git/src/historyProvider.ts b/extensions/git/src/historyProvider.ts index 896c46851c49..8e5a8f9d2f14 100644 --- a/extensions/git/src/historyProvider.ts +++ b/extensions/git/src/historyProvider.ts @@ -6,20 +6,23 @@ import { Disposable, Event, EventEmitter, FileDecoration, FileDecorationProvider, SourceControlHistoryItem, SourceControlHistoryItemChange, SourceControlHistoryOptions, SourceControlHistoryProvider, ThemeIcon, Uri, window, LogOutputChannel, SourceControlHistoryItemRef, l10n, SourceControlHistoryItemRefsChangeEvent } from 'vscode'; import { Repository, Resource } from './repository'; -import { IDisposable, deltaHistoryItemRefs, dispose, filterEvent } from './util'; -import { toGitUri } from './uri'; -import { Branch, LogOptions, Ref, RefType } from './api/git'; +import { IDisposable, deltaHistoryItemRefs, dispose, filterEvent, getCommitShortHash } from './util'; +import { toMultiFileDiffEditorUris } from './uri'; +import { AvatarQuery, AvatarQueryCommit, Branch, LogOptions, Ref, RefType } from './api/git'; import { emojify, ensureEmojis } from './emoji'; import { Commit } from './git'; import { OperationKind, OperationResult } from './operation'; +import { ISourceControlHistoryItemDetailsProviderRegistry, provideSourceControlHistoryItemAvatar, provideSourceControlHistoryItemMessageLinks } from './historyItemDetailsProvider'; + +function toSourceControlHistoryItemRef(repository: Repository, ref: Ref): SourceControlHistoryItemRef { + const rootUri = Uri.file(repository.root); -function toSourceControlHistoryItemRef(ref: Ref): SourceControlHistoryItemRef { switch (ref.type) { case RefType.RemoteHead: return { id: `refs/remotes/${ref.name}`, name: ref.name ?? '', - description: ref.commit ? l10n.t('Remote branch at {0}', ref.commit.substring(0, 8)) : undefined, + description: ref.commit ? l10n.t('Remote branch at {0}', getCommitShortHash(rootUri, ref.commit)) : undefined, revision: ref.commit, icon: new ThemeIcon('cloud'), category: l10n.t('remote branches') @@ -28,7 +31,7 @@ function toSourceControlHistoryItemRef(ref: Ref): SourceControlHistoryItemRef { return { id: `refs/tags/${ref.name}`, name: ref.name ?? '', - description: ref.commit ? l10n.t('Tag at {0}', ref.commit.substring(0, 8)) : undefined, + description: ref.commit ? l10n.t('Tag at {0}', getCommitShortHash(rootUri, ref.commit)) : undefined, revision: ref.commit, icon: new ThemeIcon('tag'), category: l10n.t('tags') @@ -37,7 +40,7 @@ function toSourceControlHistoryItemRef(ref: Ref): SourceControlHistoryItemRef { return { id: `refs/heads/${ref.name}`, name: ref.name ?? '', - description: ref.commit ? ref.commit.substring(0, 8) : undefined, + description: ref.commit ? getCommitShortHash(rootUri, ref.commit) : undefined, revision: ref.commit, icon: new ThemeIcon('git-branch'), category: l10n.t('branches') @@ -88,13 +91,17 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec readonly onDidChangeHistoryItemRefs: Event = this._onDidChangeHistoryItemRefs.event; private _HEAD: Branch | undefined; - private historyItemRefs: SourceControlHistoryItemRef[] = []; + private _historyItemRefs: SourceControlHistoryItemRef[] = []; private historyItemDecorations = new Map(); private disposables: Disposable[] = []; - constructor(protected readonly repository: Repository, private readonly logger: LogOutputChannel) { + constructor( + private historyItemDetailProviderRegistry: ISourceControlHistoryItemDetailsProviderRegistry, + private readonly repository: Repository, + private readonly logger: LogOutputChannel + ) { const onDidRunWriteOperation = filterEvent(repository.onDidRunOperation, e => !e.operation.readOnly); this.disposables.push(onDidRunWriteOperation(this.onDidRunWriteOperation, this)); @@ -110,6 +117,14 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec return; } + // Refs (alphabetically) + const historyItemRefs = this.repository.refs + .map(ref => toSourceControlHistoryItemRef(this.repository, ref)) + .sort((a, b) => a.id.localeCompare(b.id)); + + const delta = deltaHistoryItemRefs(this._historyItemRefs, historyItemRefs); + this._historyItemRefs = historyItemRefs; + let historyItemRefId = ''; let historyItemRefName = ''; @@ -121,18 +136,34 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec historyItemRefName = this.repository.HEAD.name; // Remote - this._currentHistoryItemRemoteRef = this.repository.HEAD.upstream ? { - id: `refs/remotes/${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`, - name: `${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`, - revision: this.repository.HEAD.upstream.commit, - icon: new ThemeIcon('cloud') - } : undefined; + if (this.repository.HEAD.upstream) { + if (this.repository.HEAD.upstream.remote === '.') { + // Local branch + this._currentHistoryItemRemoteRef = { + id: `refs/heads/${this.repository.HEAD.upstream.name}`, + name: this.repository.HEAD.upstream.name, + revision: this.repository.HEAD.upstream.commit, + icon: new ThemeIcon('gi-branch') + }; + } else { + // Remote branch + this._currentHistoryItemRemoteRef = { + id: `refs/remotes/${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`, + name: `${this.repository.HEAD.upstream.remote}/${this.repository.HEAD.upstream.name}`, + revision: this.repository.HEAD.upstream.commit, + icon: new ThemeIcon('cloud') + }; + } + } else { + this._currentHistoryItemRemoteRef = undefined; + } - // Base - compute only if the branch has changed + // Base if (this._HEAD?.name !== this.repository.HEAD.name) { + // Compute base if the branch has changed const mergeBase = await this.resolveHEADMergeBase(); - this._currentHistoryItemBaseRef = mergeBase && + this._currentHistoryItemBaseRef = mergeBase && mergeBase.name && mergeBase.remote && (mergeBase.remote !== this.repository.HEAD.upstream?.remote || mergeBase.name !== this.repository.HEAD.upstream?.name) ? { id: `refs/remotes/${mergeBase.remote}/${mergeBase.name}`, @@ -140,6 +171,17 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec revision: mergeBase.commit, icon: new ThemeIcon('cloud') } : undefined; + } else { + // Update base revision if it has changed + const mergeBaseModified = delta.modified + .find(ref => ref.id === this._currentHistoryItemBaseRef?.id); + + if (this._currentHistoryItemBaseRef && mergeBaseModified) { + this._currentHistoryItemBaseRef = { + ...this._currentHistoryItemBaseRef, + revision: mergeBaseModified.revision + }; + } } } else { // Detached commit @@ -176,18 +218,10 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec this.logger.trace(`[GitHistoryProvider][onDidRunWriteOperation] currentHistoryItemRemoteRef: ${JSON.stringify(this._currentHistoryItemRemoteRef)}`); this.logger.trace(`[GitHistoryProvider][onDidRunWriteOperation] currentHistoryItemBaseRef: ${JSON.stringify(this._currentHistoryItemBaseRef)}`); - // Refs (alphabetically) - const historyItemRefs = this.repository.refs - .map(ref => toSourceControlHistoryItemRef(ref)) - .sort((a, b) => a.id.localeCompare(b.id)); - // Auto-fetch const silent = result.operation.kind === OperationKind.Fetch && result.operation.showProgress === false; - const delta = deltaHistoryItemRefs(this.historyItemRefs, historyItemRefs); this._onDidChangeHistoryItemRefs.fire({ ...delta, silent }); - this.historyItemRefs = historyItemRefs; - const deltaLog = { added: delta.added.map(ref => ref.id), modified: delta.modified.map(ref => ref.id), @@ -207,13 +241,13 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec for (const ref of refs) { switch (ref.type) { case RefType.RemoteHead: - remoteBranches.push(toSourceControlHistoryItemRef(ref)); + remoteBranches.push(toSourceControlHistoryItemRef(this.repository, ref)); break; case RefType.Tag: - tags.push(toSourceControlHistoryItemRef(ref)); + tags.push(toSourceControlHistoryItemRef(this.repository, ref)); break; default: - branches.push(toSourceControlHistoryItemRef(ref)); + branches.push(toSourceControlHistoryItemRef(this.repository, ref)); break; } } @@ -248,23 +282,51 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec const commits = await this.repository.log({ ...logOptions, silent: true }); + // Avatars + const avatarQuery = { + commits: commits.map(c => ({ + hash: c.hash, + authorName: c.authorName, + authorEmail: c.authorEmail + } satisfies AvatarQueryCommit)), + size: 20 + } satisfies AvatarQuery; + + const commitAvatars = await provideSourceControlHistoryItemAvatar( + this.historyItemDetailProviderRegistry, this.repository, avatarQuery); + await ensureEmojis(); - return commits.map(commit => { + const historyItems: SourceControlHistoryItem[] = []; + for (const commit of commits) { + const message = emojify(commit.message); + const messageWithLinks = await provideSourceControlHistoryItemMessageLinks( + this.historyItemDetailProviderRegistry, this.repository, message) ?? message; + + const newLineIndex = message.indexOf('\n'); + const subject = newLineIndex !== -1 + ? `${message.substring(0, newLineIndex)}\u2026` + : message; + + const avatarUrl = commitAvatars?.get(commit.hash); const references = this._resolveHistoryItemRefs(commit); - return { + historyItems.push({ id: commit.hash, parentIds: commit.parents, - message: emojify(commit.message), + subject, + message: messageWithLinks, author: commit.authorName, - icon: new ThemeIcon('git-commit'), - displayId: commit.hash.substring(0, 8), + authorEmail: commit.authorEmail, + authorIcon: avatarUrl ? Uri.parse(avatarUrl) : new ThemeIcon('account'), + displayId: getCommitShortHash(Uri.file(this.repository.root), commit.hash), timestamp: commit.authorDate?.getTime(), statistics: commit.shortStat ?? { files: 0, insertions: 0, deletions: 0 }, references: references.length !== 0 ? references : undefined - }; - }); + } satisfies SourceControlHistoryItem); + } + + return historyItems; } catch (err) { this.logger.error(`[GitHistoryProvider][provideHistoryItems] Failed to get history items with options '${JSON.stringify(options)}': ${err}`); return []; @@ -286,10 +348,8 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec // History item change historyItemChanges.push({ uri: historyItemUri, - originalUri: toGitUri(change.originalUri, historyItemParentId), - modifiedUri: toGitUri(change.uri, historyItemId), - renameUri: change.renameUri, - }); + ...toMultiFileDiffEditorUris(change, historyItemParentId, historyItemId) + } satisfies SourceControlHistoryItemChange); // History item change decoration const letter = Resource.getStatusLetter(change.status); @@ -348,12 +408,17 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec const references: SourceControlHistoryItemRef[] = []; for (const ref of commit.refNames) { + if (ref === 'refs/remotes/origin/HEAD') { + continue; + } + switch (true) { case ref.startsWith('HEAD -> refs/heads/'): references.push({ id: ref.substring('HEAD -> '.length), name: ref.substring('HEAD -> refs/heads/'.length), revision: commit.hash, + category: l10n.t('branches'), icon: new ThemeIcon('target') }); break; @@ -362,6 +427,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec id: ref, name: ref.substring('refs/heads/'.length), revision: commit.hash, + category: l10n.t('branches'), icon: new ThemeIcon('git-branch') }); break; @@ -370,6 +436,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec id: ref, name: ref.substring('refs/remotes/'.length), revision: commit.hash, + category: l10n.t('remote branches'), icon: new ThemeIcon('cloud') }); break; @@ -378,6 +445,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec id: ref.substring('tag: '.length), name: ref.substring('tag: refs/tags/'.length), revision: commit.hash, + category: l10n.t('tags'), icon: new ThemeIcon('tag') }); break; diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 25772be7ea75..8205d8de69f9 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -26,6 +26,8 @@ import { GitEditor, GitEditorDocumentLinkProvider } from './gitEditor'; import { GitPostCommitCommandsProvider } from './postCommitCommands'; import { GitEditSessionIdentityProvider } from './editSessionIdentityProvider'; import { GitCommitInputBoxCodeActionsProvider, GitCommitInputBoxDiagnosticsManager } from './diagnostics'; +import { GitBlameController } from './blame'; +import { StagedResourceQuickDiffProvider } from './repository'; const deactivateTasks: { (): Promise }[] = []; @@ -110,14 +112,16 @@ async function createModel(context: ExtensionContext, logger: LogOutputChannel, const cc = new CommandCenter(git, model, context.globalState, logger, telemetryReporter); disposables.push( cc, - new GitFileSystemProvider(model), + new GitFileSystemProvider(model, logger), new GitDecorations(model), + new GitBlameController(model), new GitTimelineProvider(model, cc), new GitEditSessionIdentityProvider(model), + new StagedResourceQuickDiffProvider(model), new TerminalShellExecutionManager(model, logger) ); - const postCommitCommandsProvider = new GitPostCommitCommandsProvider(); + const postCommitCommandsProvider = new GitPostCommitCommandsProvider(model); model.registerPostCommitCommandsProvider(postCommitCommandsProvider); const diagnosticsManager = new GitCommitInputBoxDiagnosticsManager(model); diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 48e66d5e9ff6..d17d118de1c9 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -12,13 +12,14 @@ import { Git } from './git'; import * as path from 'path'; import * as fs from 'fs'; import { fromGitUri } from './uri'; -import { APIState as State, CredentialsProvider, PushErrorHandler, PublishEvent, RemoteSourcePublisher, PostCommitCommandsProvider, BranchProtectionProvider } from './api/git'; +import { APIState as State, CredentialsProvider, PushErrorHandler, PublishEvent, RemoteSourcePublisher, PostCommitCommandsProvider, BranchProtectionProvider, SourceControlHistoryItemDetailsProvider } from './api/git'; import { Askpass } from './askpass'; import { IPushErrorHandlerRegistry } from './pushError'; import { ApiRepository } from './api/api1'; import { IRemoteSourcePublisherRegistry } from './remotePublisher'; import { IPostCommitCommandsProviderRegistry } from './postCommitCommands'; import { IBranchProtectionProviderRegistry } from './branchProtection'; +import { ISourceControlHistoryItemDetailsProviderRegistry } from './historyItemDetailsProvider'; class RepositoryPick implements QuickPickItem { @memoize get label(): string { @@ -170,7 +171,7 @@ class UnsafeRepositoriesManager { } } -export class Model implements IRepositoryResolver, IBranchProtectionProviderRegistry, IRemoteSourcePublisherRegistry, IPostCommitCommandsProviderRegistry, IPushErrorHandlerRegistry { +export class Model implements IRepositoryResolver, IBranchProtectionProviderRegistry, IRemoteSourcePublisherRegistry, IPostCommitCommandsProviderRegistry, IPushErrorHandlerRegistry, ISourceControlHistoryItemDetailsProviderRegistry { private _onDidOpenRepository = new EventEmitter(); readonly onDidOpenRepository: Event = this._onDidOpenRepository.event; @@ -236,6 +237,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi readonly onDidChangeBranchProtectionProviders = this._onDidChangeBranchProtectionProviders.event; private pushErrorHandlers = new Set(); + private historyItemDetailsProviders = new Set(); private _unsafeRepositoriesManager: UnsafeRepositoriesManager; get unsafeRepositories(): string[] { @@ -272,6 +274,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi workspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables); window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables); + window.onDidChangeActiveTextEditor(this.onDidChangeActiveTextEditor, this, this.disposables); workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables); const fsWatcher = workspace.createFileSystemWatcher('**'); @@ -519,6 +522,31 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi } } + private onDidChangeActiveTextEditor(): void { + const textEditor = window.activeTextEditor; + + if (textEditor === undefined) { + commands.executeCommand('setContext', 'git.activeResourceHasUnstagedChanges', false); + commands.executeCommand('setContext', 'git.activeResourceHasStagedChanges', false); + return; + } + + const repository = this.getRepository(textEditor.document.uri); + if (!repository) { + commands.executeCommand('setContext', 'git.activeResourceHasUnstagedChanges', false); + commands.executeCommand('setContext', 'git.activeResourceHasStagedChanges', false); + return; + } + + const indexResource = repository.indexGroup.resourceStates + .find(resource => pathEquals(resource.resourceUri.fsPath, textEditor.document.uri.fsPath)); + const workingTreeResource = repository.workingTreeGroup.resourceStates + .find(resource => pathEquals(resource.resourceUri.fsPath, textEditor.document.uri.fsPath)); + + commands.executeCommand('setContext', 'git.activeResourceHasStagedChanges', indexResource !== undefined); + commands.executeCommand('setContext', 'git.activeResourceHasUnstagedChanges', workingTreeResource !== undefined); + } + @sequentialize async openRepository(repoPath: string, openIfClosed = false): Promise { this.logger.trace(`[Model][openRepository] Repository: ${repoPath}`); @@ -607,7 +635,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi // Open repository const [dotGit, repositoryRootRealPath] = await Promise.all([this.git.getRepositoryDotGit(repositoryRoot), this.getRepositoryRootRealPath(repositoryRoot)]); - const repository = new Repository(this.git.open(repositoryRoot, repositoryRootRealPath, dotGit, this.logger), this, this, this, this, this, this.globalState, this.logger, this.telemetryReporter); + const repository = new Repository(this.git.open(repositoryRoot, repositoryRootRealPath, dotGit, this.logger), this, this, this, this, this, this, this.globalState, this.logger, this.telemetryReporter); this.open(repository); this._closedRepositoriesManager.deleteRepository(repository.root); @@ -727,8 +755,10 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi const statusListener = repository.onDidRunGitStatus(() => { checkForSubmodules(); updateMergeChanges(); + this.onDidChangeActiveTextEditor(); }); checkForSubmodules(); + this.onDidChangeActiveTextEditor(); const updateOperationInProgressContext = () => { let operationInProgress = false; @@ -843,7 +873,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi } if (hint instanceof ApiRepository) { - return this.openRepositories.filter(r => r.repository === hint.repository)[0]; + hint = hint.rootUri; } if (typeof hint === 'string') { @@ -974,6 +1004,15 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi return [...this.pushErrorHandlers]; } + registerSourceControlHistoryItemDetailsProvider(provider: SourceControlHistoryItemDetailsProvider): Disposable { + this.historyItemDetailsProviders.add(provider); + return toDisposable(() => this.historyItemDetailsProviders.delete(provider)); + } + + getSourceControlHistoryItemDetailsProviders(): SourceControlHistoryItemDetailsProvider[] { + return [...this.historyItemDetailsProviders]; + } + getUnsafeRepositoryPath(repository: string): string | undefined { return this._unsafeRepositoriesManager.getRepositoryPath(repository); } diff --git a/extensions/git/src/operation.ts b/extensions/git/src/operation.ts index 294c72f32061..d8b2773f1c28 100644 --- a/extensions/git/src/operation.ts +++ b/extensions/git/src/operation.ts @@ -20,7 +20,7 @@ export const enum OperationKind { Config = 'Config', DeleteBranch = 'DeleteBranch', DeleteRef = 'DeleteRef', - DeleteRemoteTag = 'DeleteRemoteTag', + DeleteRemoteRef = 'DeleteRemoteRef', DeleteTag = 'DeleteTag', Diff = 'Diff', Fetch = 'Fetch', @@ -66,7 +66,7 @@ export const enum OperationKind { export type Operation = AddOperation | ApplyOperation | BlameOperation | BranchOperation | CheckIgnoreOperation | CherryPickOperation | CheckoutOperation | CheckoutTrackingOperation | CleanOperation | CommitOperation | ConfigOperation | DeleteBranchOperation | - DeleteRefOperation | DeleteRemoteTagOperation | DeleteTagOperation | DiffOperation | FetchOperation | FindTrackingBranchesOperation | + DeleteRefOperation | DeleteRemoteRefOperation | DeleteTagOperation | DiffOperation | FetchOperation | FindTrackingBranchesOperation | GetBranchOperation | GetBranchesOperation | GetCommitTemplateOperation | GetObjectDetailsOperation | GetObjectFilesOperation | GetRefsOperation | GetRemoteRefsOperation | HashObjectOperation | IgnoreOperation | LogOperation | LogFileOperation | MergeOperation | MergeAbortOperation | MergeBaseOperation | MoveOperation | PostCommitCommandOperation | PullOperation | PushOperation | RemoteOperation | RenameBranchOperation | @@ -88,7 +88,7 @@ export type CommitOperation = BaseOperation & { kind: OperationKind.Commit }; export type ConfigOperation = BaseOperation & { kind: OperationKind.Config }; export type DeleteBranchOperation = BaseOperation & { kind: OperationKind.DeleteBranch }; export type DeleteRefOperation = BaseOperation & { kind: OperationKind.DeleteRef }; -export type DeleteRemoteTagOperation = BaseOperation & { kind: OperationKind.DeleteRemoteTag }; +export type DeleteRemoteRefOperation = BaseOperation & { kind: OperationKind.DeleteRemoteRef }; export type DeleteTagOperation = BaseOperation & { kind: OperationKind.DeleteTag }; export type DiffOperation = BaseOperation & { kind: OperationKind.Diff }; export type FetchOperation = BaseOperation & { kind: OperationKind.Fetch }; @@ -134,7 +134,7 @@ export type TagOperation = BaseOperation & { kind: OperationKind.Tag }; export const Operation = { Add: (showProgress: boolean): AddOperation => ({ kind: OperationKind.Add, blocking: false, readOnly: false, remote: false, retry: false, showProgress }), Apply: { kind: OperationKind.Apply, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as ApplyOperation, - Blame: { kind: OperationKind.Blame, blocking: false, readOnly: true, remote: false, retry: false, showProgress: true } as BlameOperation, + Blame: (showProgress: boolean) => ({ kind: OperationKind.Blame, blocking: false, readOnly: true, remote: false, retry: false, showProgress } as BlameOperation), Branch: { kind: OperationKind.Branch, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as BranchOperation, CheckIgnore: { kind: OperationKind.CheckIgnore, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as CheckIgnoreOperation, CherryPick: { kind: OperationKind.CherryPick, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as CherryPickOperation, @@ -145,7 +145,7 @@ export const Operation = { Config: (readOnly: boolean) => ({ kind: OperationKind.Config, blocking: false, readOnly, remote: false, retry: false, showProgress: false } as ConfigOperation), DeleteBranch: { kind: OperationKind.DeleteBranch, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as DeleteBranchOperation, DeleteRef: { kind: OperationKind.DeleteRef, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as DeleteRefOperation, - DeleteRemoteTag: { kind: OperationKind.DeleteRemoteTag, blocking: false, readOnly: false, remote: true, retry: false, showProgress: true } as DeleteRemoteTagOperation, + DeleteRemoteRef: { kind: OperationKind.DeleteRemoteRef, blocking: false, readOnly: false, remote: true, retry: false, showProgress: true } as DeleteRemoteRefOperation, DeleteTag: { kind: OperationKind.DeleteTag, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as DeleteTagOperation, Diff: { kind: OperationKind.Diff, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as DiffOperation, Fetch: (showProgress: boolean) => ({ kind: OperationKind.Fetch, blocking: false, readOnly: false, remote: true, retry: true, showProgress } as FetchOperation), diff --git a/extensions/git/src/postCommitCommands.ts b/extensions/git/src/postCommitCommands.ts index d4e227b6db76..69a18114a41e 100644 --- a/extensions/git/src/postCommitCommands.ts +++ b/extensions/git/src/postCommitCommands.ts @@ -5,7 +5,7 @@ import { Command, commands, Disposable, Event, EventEmitter, Memento, Uri, workspace, l10n } from 'vscode'; import { PostCommitCommandsProvider } from './api/git'; -import { Repository } from './repository'; +import { IRepositoryResolver, Repository } from './repository'; import { ApiRepository } from './api/api1'; import { dispose } from './util'; import { OperationKind } from './operation'; @@ -18,17 +18,23 @@ export interface IPostCommitCommandsProviderRegistry { } export class GitPostCommitCommandsProvider implements PostCommitCommandsProvider { + constructor(private readonly _repositoryResolver: IRepositoryResolver) { } + getCommands(apiRepository: ApiRepository): Command[] { - const config = workspace.getConfiguration('git', Uri.file(apiRepository.repository.root)); + const repository = this._repositoryResolver.getRepository(apiRepository.rootUri); + if (!repository) { + return []; + } + + const config = workspace.getConfiguration('git', Uri.file(repository.root)); // Branch protection - const isBranchProtected = apiRepository.repository.isBranchProtected(); + const isBranchProtected = repository.isBranchProtected(); const branchProtectionPrompt = config.get<'alwaysCommit' | 'alwaysCommitToNewBranch' | 'alwaysPrompt'>('branchProtectionPrompt')!; const alwaysPrompt = isBranchProtected && branchProtectionPrompt === 'alwaysPrompt'; const alwaysCommitToNewBranch = isBranchProtected && branchProtectionPrompt === 'alwaysCommitToNewBranch'; // Icon - const repository = apiRepository.repository; const isCommitInProgress = repository.operations.isRunning(OperationKind.Commit) || repository.operations.isRunning(OperationKind.PostCommitCommand); const icon = isCommitInProgress ? '$(sync~spin)' : alwaysPrompt ? '$(lock)' : alwaysCommitToNewBranch ? '$(git-branch)' : undefined; diff --git a/extensions/git/src/remoteSource.ts b/extensions/git/src/remoteSource.ts index 4fdd6f06c1d1..eb63e5db81f6 100644 --- a/extensions/git/src/remoteSource.ts +++ b/extensions/git/src/remoteSource.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { PickRemoteSourceOptions, PickRemoteSourceResult } from './api/git-base'; +import { PickRemoteSourceOptions, PickRemoteSourceResult } from './typings/git-base'; import { GitBaseApi } from './git-base'; export async function pickRemoteSource(options: PickRemoteSourceOptions & { branch?: false | undefined }): Promise; diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 6302a55e891d..f32d915d7151 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -7,14 +7,15 @@ import TelemetryReporter from '@vscode/extension-telemetry'; import * as fs from 'fs'; import * as path from 'path'; import picomatch from 'picomatch'; -import { CancellationError, CancellationToken, CancellationTokenSource, Command, commands, Disposable, Event, EventEmitter, FileDecoration, l10n, LogLevel, LogOutputChannel, Memento, ProgressLocation, ProgressOptions, RelativePattern, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, TabInputNotebookDiff, TabInputTextDiff, TabInputTextMultiDiff, ThemeColor, Uri, window, workspace, WorkspaceEdit } from 'vscode'; +import * as iconv from '@vscode/iconv-lite-umd'; +import { CancellationError, CancellationToken, CancellationTokenSource, Command, commands, Disposable, Event, EventEmitter, FileDecoration, l10n, LogLevel, LogOutputChannel, Memento, ProgressLocation, ProgressOptions, QuickDiffProvider, RelativePattern, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, TabInputNotebookDiff, TabInputTextDiff, TabInputTextMultiDiff, ThemeColor, Uri, window, workspace, WorkspaceEdit } from 'vscode'; import { ActionButton } from './actionButton'; import { ApiRepository } from './api/api1'; import { Branch, BranchQuery, Change, CommitOptions, FetchOptions, ForcePushMode, GitErrorCodes, LogOptions, Ref, RefQuery, RefType, Remote, Status } from './api/git'; import { AutoFetcher } from './autofetch'; import { GitBranchProtectionProvider, IBranchProtectionProviderRegistry } from './branchProtection'; import { debounce, memoize, throttle } from './decorators'; -import { Repository as BaseRepository, Commit, GitError, LogFileOptions, LsTreeElement, PullOptions, Stash, Submodule } from './git'; +import { Repository as BaseRepository, BlameInformation, Commit, GitError, LogFileOptions, LsTreeElement, PullOptions, Stash, Submodule } from './git'; import { GitHistoryProvider } from './historyProvider'; import { Operation, OperationKind, OperationManager, OperationResult } from './operation'; import { CommitCommandsCenter, IPostCommitCommandsProviderRegistry } from './postCommitCommands'; @@ -22,8 +23,10 @@ import { IPushErrorHandlerRegistry } from './pushError'; import { IRemoteSourcePublisherRegistry } from './remotePublisher'; import { StatusBarCommands } from './statusbar'; import { toGitUri } from './uri'; -import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent, pathEquals, relativePath } from './util'; +import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, getCommitShortHash, IDisposable, isDescendant, isLinuxSnap, isRemote, Limiter, onceEvent, pathEquals, relativePath } from './util'; import { IFileWatcher, watch } from './watch'; +import { detectEncoding } from './encoding'; +import { ISourceControlHistoryItemDetailsProviderRegistry } from './historyItemDetailsProvider'; const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); @@ -558,13 +561,11 @@ class ResourceCommandResolver { switch (resource.type) { case Status.INDEX_MODIFIED: case Status.INDEX_RENAMED: - case Status.INDEX_ADDED: case Status.INTENT_TO_RENAME: case Status.TYPE_CHANGED: return { original: toGitUri(resource.original, 'HEAD') }; case Status.MODIFIED: - case Status.UNTRACKED: return { original: toGitUri(resource.resourceUri, '~') }; case Status.DELETED_BY_US: @@ -841,6 +842,7 @@ export class Repository implements Disposable { private isRepositoryHuge: false | { limit: number } = false; private didWarnAboutLimit = false; + private unpublishedCommits: Set | undefined = undefined; private branchProtection = new Map(); private commitCommandCenter: CommitCommandsCenter; private resourceCommandResolver = new ResourceCommandResolver(this); @@ -854,6 +856,7 @@ export class Repository implements Disposable { remoteSourcePublisherRegistry: IRemoteSourcePublisherRegistry, postCommitCommandsProviderRegistry: IPostCommitCommandsProviderRegistry, private readonly branchProtectionProviderRegistry: IBranchProtectionProviderRegistry, + historyItemDetailProviderRegistry: ISourceControlHistoryItemDetailsProviderRegistry, globalState: Memento, private readonly logger: LogOutputChannel, private telemetryReporter: TelemetryReporter @@ -892,7 +895,7 @@ export class Repository implements Disposable { this._sourceControl.quickDiffProvider = this; - this._historyProvider = new GitHistoryProvider(this, logger); + this._historyProvider = new GitHistoryProvider(historyItemDetailProviderRegistry, this, logger); this._sourceControl.historyProvider = this._historyProvider; this.disposables.push(this._historyProvider); @@ -984,9 +987,9 @@ export class Repository implements Disposable { this.commitCommandCenter = new CommitCommandsCenter(globalState, this, postCommitCommandsProviderRegistry); this.disposables.push(this.commitCommandCenter); - const actionButton = new ActionButton(this, this.commitCommandCenter); + const actionButton = new ActionButton(this, this.commitCommandCenter, this.logger); this.disposables.push(actionButton); - actionButton.onDidChange(() => this._sourceControl.actionButton = actionButton.button); + actionButton.onDidChange(() => this._sourceControl.actionButton = actionButton.button, this, this.disposables); this._sourceControl.actionButton = actionButton.button; const progressManager = new ProgressManager(this); @@ -1021,7 +1024,7 @@ export class Repository implements Disposable { * Quick diff label */ get label(): string { - return l10n.t('Git local working changes'); + return l10n.t('Git local changes (working tree)'); } provideOriginalResource(uri: Uri): Uri | undefined { @@ -1031,11 +1034,34 @@ export class Repository implements Disposable { // Ignore path that is not inside the current repository if (this.repositoryResolver.getRepository(uri) !== this) { + this.logger.trace(`[Repository][provideOriginalResource] Resource is not part of the repository: ${uri.toString()}`); return undefined; } // Ignore path that is inside a merge group - if (this.mergeGroup.resourceStates.some(r => r.resourceUri.path === uri.path)) { + if (this.mergeGroup.resourceStates.some(r => pathEquals(r.resourceUri.fsPath, uri.fsPath))) { + this.logger.trace(`[Repository][provideOriginalResource] Resource is part of a merge group: ${uri.toString()}`); + return undefined; + } + + // Ignore path that is untracked + if (this.untrackedGroup.resourceStates.some(r => pathEquals(r.resourceUri.path, uri.path)) || + this.workingTreeGroup.resourceStates.some(r => pathEquals(r.resourceUri.path, uri.path) && r.type === Status.UNTRACKED)) { + this.logger.trace(`[Repository][provideOriginalResource] Resource is untracked: ${uri.toString()}`); + return undefined; + } + + const activeTabInput = window.tabGroups.activeTabGroup.activeTab?.input; + + // Ignore file that is on the right-hand side of a diff editor + if (activeTabInput instanceof TabInputTextDiff && pathEquals(activeTabInput.modified.fsPath, uri.fsPath)) { + this.logger.trace(`[Repository][provideOriginalResource] Resource is on the right-hand side of a diff editor: ${uri.toString()}`); + return undefined; + } + + // Ignore file that is on the right -hand side of a multi-file diff editor + if (activeTabInput instanceof TabInputTextMultiDiff && activeTabInput.textDiffs.some(diff => pathEquals(diff.modified.fsPath, uri.fsPath))) { + this.logger.trace(`[Repository][provideOriginalResource] Resource is on the right-hand side of a multi-file diff editor: ${uri.toString()}`); return undefined; } @@ -1057,15 +1083,19 @@ export class Repository implements Disposable { } getConfig(key: string): Promise { - return this.run(Operation.Config(true), () => this.repository.config('local', key)); + return this.run(Operation.Config(true), () => this.repository.config('get', 'local', key)); } getGlobalConfig(key: string): Promise { - return this.run(Operation.Config(true), () => this.repository.config('global', key)); + return this.run(Operation.Config(true), () => this.repository.config('get', 'global', key)); } setConfig(key: string, value: string): Promise { - return this.run(Operation.Config(false), () => this.repository.config('local', key, value)); + return this.run(Operation.Config(false), () => this.repository.config('add', 'local', key, value)); + } + + unsetConfig(key: string): Promise { + return this.run(Operation.Config(false), () => this.repository.config('unset', 'local', key)); } log(options?: LogOptions & { silent?: boolean }): Promise { @@ -1136,7 +1166,10 @@ export class Repository implements Disposable { } diffTrees(treeish1: string, treeish2?: string): Promise { - return this.run(Operation.Diff, () => this.repository.diffTrees(treeish1, treeish2)); + const scopedConfig = workspace.getConfiguration('git', Uri.file(this.root)); + const similarityThreshold = scopedConfig.get('similarityThreshold', 50); + + return this.run(Operation.Diff, () => this.repository.diffTrees(treeish1, treeish2, { similarityThreshold })); } getMergeBase(ref1: string, ref2: string, ...refs: string[]): Promise { @@ -1190,9 +1223,18 @@ export class Repository implements Disposable { } async stage(resource: Uri, contents: string): Promise { - const path = relativePath(this.repository.root, resource.fsPath).replace(/\\/g, '/'); await this.run(Operation.Stage, async () => { - await this.repository.stage(path, contents); + const configFiles = workspace.getConfiguration('files', Uri.file(resource.fsPath)); + let encoding = configFiles.get('encoding') ?? 'utf8'; + const autoGuessEncoding = configFiles.get('autoGuessEncoding') === true; + const candidateGuessEncodings = configFiles.get('candidateGuessEncodings') ?? []; + + if (autoGuessEncoding) { + encoding = detectEncoding(Buffer.from(contents), candidateGuessEncodings) ?? encoding; + } + + encoding = iconv.encodingExists(encoding) ? encoding : 'utf8'; + await this.repository.stage(resource.fsPath, contents, encoding); this._onDidChangeOriginalResource.fire(resource); this.closeDiffEditors([], [...resource.fsPath]); @@ -1317,6 +1359,9 @@ export class Repository implements Disposable { } async clean(resources: Uri[]): Promise { + const config = workspace.getConfiguration('git'); + const discardUntrackedChangesToTrash = config.get('discardUntrackedChangesToTrash', true) && !isRemote && !isLinuxSnap; + await this.run( Operation.Clean(!this.optimisticUpdateEnabled()), async () => { @@ -1354,7 +1399,14 @@ export class Repository implements Disposable { } }); - await this.repository.clean(toClean); + if (discardUntrackedChangesToTrash) { + const limiter = new Limiter(5); + await Promise.all(toClean.map(fsPath => limiter.queue( + async () => await workspace.fs.delete(Uri.file(fsPath), { useTrash: true })))); + } else { + await this.repository.clean(toClean); + } + try { await this.repository.checkout('', toCheckout); } catch (err) { @@ -1417,7 +1469,10 @@ export class Repository implements Disposable { } async deleteBranch(name: string, force?: boolean): Promise { - await this.run(Operation.DeleteBranch, () => this.repository.deleteBranch(name, force)); + return this.run(Operation.DeleteBranch, async () => { + await this.repository.deleteBranch(name, force); + await this.repository.config('unset', 'local', `branch.${name}.vscode-merge-base`); + }); } async renameBranch(name: string): Promise { @@ -1464,7 +1519,7 @@ export class Repository implements Disposable { async getBranches(query: BranchQuery = {}, cancellationToken?: CancellationToken): Promise { return await this.run(Operation.GetBranches, async () => { const refs = await this.getRefs(query, cancellationToken); - return refs.filter(value => (value.type === RefType.Head || value.type === RefType.RemoteHead) && (query.remote || !value.remote)); + return refs.filter(value => value.type === RefType.Head || (value.type === RefType.RemoteHead && query.remote)); }); } @@ -1477,7 +1532,11 @@ export class Repository implements Disposable { try { const mergeBase = await this.getConfig(mergeBaseConfigKey); const branchFromConfig = mergeBase !== '' ? await this.getBranch(mergeBase) : undefined; - if (branchFromConfig) { + + // There was a brief period of time when we would consider local branches as a valid + // merge base. Since then we have fixed the issue and only remote branches can be used + // as a merge base so we are adding an additional check. + if (branchFromConfig && branchFromConfig.remote) { return branchFromConfig; } } catch (err) { } @@ -1539,8 +1598,13 @@ export class Repository implements Disposable { } private async getDefaultBranch(): Promise { + const defaultRemote = this.getDefaultRemote(); + if (!defaultRemote) { + return undefined; + } + try { - const defaultBranch = await this.repository.getDefaultBranch(); + const defaultBranch = await this.repository.getDefaultBranch(defaultRemote.name); return defaultBranch; } catch (err) { @@ -1603,12 +1667,12 @@ export class Repository implements Disposable { await this.run(Operation.DeleteTag, () => this.repository.deleteTag(name)); } - async deleteRemoteTag(remoteName: string, tagName: string): Promise { - await this.run(Operation.DeleteRemoteTag, () => this.repository.deleteRemoteTag(remoteName, tagName)); + async deleteRemoteRef(remoteName: string, refName: string, options?: { force?: boolean }): Promise { + await this.run(Operation.DeleteRemoteRef, () => this.repository.deleteRemoteRef(remoteName, refName, options)); } async checkout(treeish: string, opts?: { detached?: boolean; pullBeforeCheckout?: boolean }): Promise { - const refLabel = opts?.detached ? treeish.substring(0, 8) : treeish; + const refLabel = opts?.detached ? getCommitShortHash(Uri.file(this.root), treeish) : treeish; await this.run(Operation.Checkout(refLabel), async () => { @@ -1626,7 +1690,7 @@ export class Repository implements Disposable { } async checkoutTracking(treeish: string, opts: { detached?: boolean } = {}): Promise { - const refLabel = opts.detached ? treeish.substring(0, 8) : treeish; + const refLabel = opts.detached ? getCommitShortHash(Uri.file(this.root), treeish) : treeish; await this.run(Operation.CheckoutTracking(refLabel), () => this.repository.checkout(treeish, [], { ...opts, track: true })); } @@ -1655,6 +1719,14 @@ export class Repository implements Disposable { await this.run(Operation.DeleteRef, () => this.repository.deleteRef(ref)); } + getDefaultRemote(): Remote | undefined { + if (this.remotes.length === 0) { + return undefined; + } + + return this.remotes.find(r => r.name === 'origin') ?? this.remotes[0]; + } + async addRemote(name: string, url: string): Promise { await this.run(Operation.Remote, () => this.repository.addRemote(name, url)); } @@ -1726,6 +1798,7 @@ export class Repository implements Disposable { await this.run(Operation.Pull, async () => { await this.maybeAutoStash(async () => { const config = workspace.getConfiguration('git', Uri.file(this.root)); + const autoStash = config.get('autoStash'); const fetchOnPull = config.get('fetchOnPull'); const tags = config.get('pullTags'); @@ -1735,7 +1808,7 @@ export class Repository implements Disposable { } if (await this.checkIfMaybeRebased(this.HEAD?.name)) { - await this._pullAndHandleTagConflict(rebase, remote, branch, { unshallow, tags }); + await this._pullAndHandleTagConflict(rebase, remote, branch, { unshallow, tags, autoStash }); } }); }); @@ -1783,7 +1856,11 @@ export class Repository implements Disposable { } async blame(path: string): Promise { - return await this.run(Operation.Blame, () => this.repository.blame(path)); + return await this.run(Operation.Blame(true), () => this.repository.blame(path)); + } + + async blame2(path: string, ref?: string): Promise { + return await this.run(Operation.Blame(false), () => this.repository.blame2(path, ref)); } @throttle @@ -1805,6 +1882,7 @@ export class Repository implements Disposable { await this.run(Operation.Sync, async () => { await this.maybeAutoStash(async () => { const config = workspace.getConfiguration('git', Uri.file(this.root)); + const autoStash = config.get('autoStash'); const fetchOnPull = config.get('fetchOnPull'); const tags = config.get('pullTags'); const followTags = config.get('followTagsWhenSync'); @@ -1817,7 +1895,7 @@ export class Repository implements Disposable { } if (await this.checkIfMaybeRebased(this.HEAD?.name)) { - await this._pullAndHandleTagConflict(rebase, remoteName, pullBranch, { tags, cancellationToken }); + await this._pullAndHandleTagConflict(rebase, remoteName, pullBranch, { tags, cancellationToken, autoStash }); } }; @@ -1898,18 +1976,17 @@ export class Repository implements Disposable { async show(ref: string, filePath: string): Promise { return await this.run(Operation.Show, async () => { - const path = relativePath(this.repository.root, filePath).replace(/\\/g, '/'); const configFiles = workspace.getConfiguration('files', Uri.file(filePath)); const defaultEncoding = configFiles.get('encoding'); const autoGuessEncoding = configFiles.get('autoGuessEncoding'); const candidateGuessEncodings = configFiles.get('candidateGuessEncodings'); try { - return await this.repository.bufferString(`${ref}:${path}`, defaultEncoding, autoGuessEncoding, candidateGuessEncodings); + return await this.repository.bufferString(ref, filePath, defaultEncoding, autoGuessEncoding, candidateGuessEncodings); } catch (err) { if (err.gitErrorCode === GitErrorCodes.WrongCase) { - const gitRelativePath = await this.repository.getGitRelativePath(ref, path); - return await this.repository.bufferString(`${ref}:${gitRelativePath}`, defaultEncoding, autoGuessEncoding, candidateGuessEncodings); + const gitFilePath = await this.repository.getGitFilePath(ref, filePath); + return await this.repository.bufferString(ref, gitFilePath, defaultEncoding, autoGuessEncoding, candidateGuessEncodings); } throw err; @@ -1918,18 +1995,15 @@ export class Repository implements Disposable { } async buffer(ref: string, filePath: string): Promise { - return this.run(Operation.Show, () => { - const path = relativePath(this.repository.root, filePath).replace(/\\/g, '/'); - return this.repository.buffer(`${ref}:${path}`); - }); + return this.run(Operation.Show, () => this.repository.buffer(ref, filePath)); } getObjectFiles(ref: string): Promise { return this.run(Operation.GetObjectFiles, () => this.repository.lstree(ref)); } - getObjectDetails(ref: string, filePath: string): Promise<{ mode: string; object: string; size: number }> { - return this.run(Operation.GetObjectDetails, () => this.repository.getObjectDetails(ref, filePath)); + getObjectDetails(ref: string, path: string): Promise<{ mode: string; object: string; size: number }> { + return this.run(Operation.GetObjectDetails, () => this.repository.getObjectDetails(ref, path)); } detectObjectType(object: string): Promise<{ mimetype: string; encoding?: string }> { @@ -2200,6 +2274,17 @@ export class Repository implements Disposable { this.isCherryPickInProgress(), this.getInputTemplate()]); + // Reset the list of unpublished commits if HEAD has + // changed (ex: checkout, fetch, pull, push, publish, etc.). + // The list of unpublished commits will be computed lazily + // on demand. + if (this.HEAD?.name !== HEAD?.name || + this.HEAD?.commit !== HEAD?.commit || + this.HEAD?.ahead !== HEAD?.ahead || + this.HEAD?.upstream !== HEAD?.upstream) { + this.unpublishedCommits = undefined; + } + this._HEAD = HEAD; this._remotes = remotes!; this._submodules = submodules!; @@ -2307,24 +2392,26 @@ export class Repository implements Disposable { const yes = { title: l10n.t('Yes') }; const no = { title: l10n.t('No') }; - const result = await window.showWarningMessage(`${gitWarn} ${addKnown}`, yes, no, neverAgain); - if (result === yes) { - this.ignore([Uri.file(folderPath)]); - } else { + window.showWarningMessage(`${gitWarn} ${addKnown}`, yes, no, neverAgain).then(result => { + if (result === yes) { + this.ignore([Uri.file(folderPath)]); + } else { + if (result === neverAgain) { + config.update('ignoreLimitWarning', true, false); + } + + this.didWarnAboutLimit = true; + } + }); + } else { + const ok = { title: l10n.t('OK') }; + window.showWarningMessage(gitWarn, ok, neverAgain).then(result => { if (result === neverAgain) { config.update('ignoreLimitWarning', true, false); } this.didWarnAboutLimit = true; - } - } else { - const ok = { title: l10n.t('OK') }; - const result = await window.showWarningMessage(gitWarn, ok, neverAgain); - if (result === neverAgain) { - config.update('ignoreLimitWarning', true, false); - } - - this.didWarnAboutLimit = true; + }); } } @@ -2441,17 +2528,22 @@ export class Repository implements Disposable { private async maybeAutoStash(runOperation: () => Promise): Promise { const config = workspace.getConfiguration('git', Uri.file(this.root)); const shouldAutoStash = config.get('autoStash') - && this.workingTreeGroup.resourceStates.some(r => r.type !== Status.UNTRACKED && r.type !== Status.IGNORED); + && this.repository.git.compareGitVersionTo('2.27.0') < 0 + && (this.indexGroup.resourceStates.length > 0 + || this.workingTreeGroup.resourceStates.some( + r => r.type !== Status.UNTRACKED && r.type !== Status.IGNORED)); if (!shouldAutoStash) { return await runOperation(); } await this.repository.createStash(undefined, true); - const result = await runOperation(); - await this.repository.popStash(); - - return result; + try { + const result = await runOperation(); + return result; + } finally { + await this.repository.popStash(); + } } private onFileChange(_uri: Uri): void { @@ -2517,7 +2609,7 @@ export class Repository implements Disposable { return head + (this.workingTreeGroup.resourceStates.length + this.untrackedGroup.resourceStates.length > 0 ? '*' : '') + (this.indexGroup.resourceStates.length > 0 ? '+' : '') - + (this.mergeGroup.resourceStates.length > 0 ? '!' : ''); + + (this.mergeInProgress || !!this.rebaseCommit ? '!' : ''); } get syncLabel(): string { @@ -2672,7 +2764,71 @@ export class Repository implements Disposable { return false; } + async getUnpublishedCommits(): Promise> { + if (this.unpublishedCommits) { + return this.unpublishedCommits; + } + + if (!this.HEAD?.name) { + this.unpublishedCommits = new Set(); + return this.unpublishedCommits; + } + + if (this.HEAD.upstream) { + // Upstream + if (this.HEAD.ahead === 0) { + this.unpublishedCommits = new Set(); + } else { + const ref1 = `${this.HEAD.upstream.remote}/${this.HEAD.upstream.name}`; + const ref2 = this.HEAD.name; + + const revList = await this.repository.revList(ref1, ref2); + this.unpublishedCommits = new Set(revList); + } + } else if (this.historyProvider.currentHistoryItemBaseRef) { + // Base + const ref1 = this.historyProvider.currentHistoryItemBaseRef.id; + const ref2 = this.HEAD.name; + + const revList = await this.repository.revList(ref1, ref2); + this.unpublishedCommits = new Set(revList); + } else { + this.unpublishedCommits = new Set(); + } + + return this.unpublishedCommits; + } + dispose(): void { this.disposables = dispose(this.disposables); } } + +export class StagedResourceQuickDiffProvider implements QuickDiffProvider { + readonly visible: boolean = false; + + private _disposables: IDisposable[] = []; + + constructor(private readonly _repositoryResolver: IRepositoryResolver) { + this._disposables.push(window.registerQuickDiffProvider({ scheme: 'file' }, this, l10n.t('Git local changes (working tree + index)'))); + } + + provideOriginalResource(uri: Uri): Uri | undefined { + // Ignore resources outside a repository + const repository = this._repositoryResolver.getRepository(uri); + if (!repository) { + return undefined; + } + + // Ignore resources that are not in the index group + if (!repository.indexGroup.resourceStates.some(r => pathEquals(r.resourceUri.fsPath, uri.fsPath))) { + return undefined; + } + + return toGitUri(uri, 'HEAD', { replaceFileExtension: true }); + } + + dispose() { + this._disposables = dispose(this._disposables); + } +} diff --git a/extensions/git/src/staging.ts b/extensions/git/src/staging.ts index 2dcc6d54487a..ec7232bec44a 100644 --- a/extensions/git/src/staging.ts +++ b/extensions/git/src/staging.ts @@ -3,7 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TextDocument, Range, LineChange, Selection, Uri } from 'vscode'; +import { TextDocument, Range, Selection, Uri, TextEditor, TextEditorDiffInformation } from 'vscode'; +import { fromGitUri, isGitUri } from './uri'; + +export interface LineChange { + readonly originalStartLineNumber: number; + readonly originalEndLineNumber: number; + readonly modifiedStartLineNumber: number; + readonly modifiedEndLineNumber: number; +} export function applyLineChanges(original: TextDocument, modified: TextDocument, diffs: LineChange[]): string { const result: string[] = []; @@ -143,6 +151,60 @@ export function invertLineChange(diff: LineChange): LineChange { }; } +export function toLineChanges(diffInformation: TextEditorDiffInformation): LineChange[] { + return diffInformation.changes.map(x => { + let originalStartLineNumber: number; + let originalEndLineNumber: number; + let modifiedStartLineNumber: number; + let modifiedEndLineNumber: number; + + if (x.original.startLineNumber === x.original.endLineNumberExclusive) { + // Insertion + originalStartLineNumber = x.original.startLineNumber - 1; + originalEndLineNumber = 0; + } else { + originalStartLineNumber = x.original.startLineNumber; + originalEndLineNumber = x.original.endLineNumberExclusive - 1; + } + + if (x.modified.startLineNumber === x.modified.endLineNumberExclusive) { + // Deletion + modifiedStartLineNumber = x.modified.startLineNumber - 1; + modifiedEndLineNumber = 0; + } else { + modifiedStartLineNumber = x.modified.startLineNumber; + modifiedEndLineNumber = x.modified.endLineNumberExclusive - 1; + } + + return { + originalStartLineNumber, + originalEndLineNumber, + modifiedStartLineNumber, + modifiedEndLineNumber + }; + }); +} + +export function getIndexDiffInformation(textEditor: TextEditor): TextEditorDiffInformation | undefined { + // Diff Editor (Index) + return textEditor.diffInformation?.find(diff => + diff.original && isGitUri(diff.original) && fromGitUri(diff.original).ref === 'HEAD' && + diff.modified && isGitUri(diff.modified) && fromGitUri(diff.modified).ref === ''); +} + +export function getWorkingTreeDiffInformation(textEditor: TextEditor): TextEditorDiffInformation | undefined { + // Working tree diff information. Diff Editor (Working Tree) -> Text Editor + return getDiffInformation(textEditor, '~') ?? getDiffInformation(textEditor, ''); +} + +export function getWorkingTreeAndIndexDiffInformation(textEditor: TextEditor): TextEditorDiffInformation | undefined { + return getDiffInformation(textEditor, 'HEAD'); +} + +function getDiffInformation(textEditor: TextEditor, ref: string): TextEditorDiffInformation | undefined { + return textEditor.diffInformation?.find(diff => diff.original && isGitUri(diff.original) && fromGitUri(diff.original).ref === ref); +} + export interface DiffEditorSelectionHunkToolbarContext { mapping: unknown; /** diff --git a/extensions/git/src/test/smoke.test.ts b/extensions/git/src/test/smoke.test.ts index b9a3ddfd0638..c0e15b03ebeb 100644 --- a/extensions/git/src/test/smoke.test.ts +++ b/extensions/git/src/test/smoke.test.ts @@ -140,8 +140,8 @@ suite('git smoke test', function () { assert.strictEqual(repository.state.indexChanges.length, 0); - await repository.commit('third commit', { all: true }); - + await commands.executeCommand('git.stageAll'); + await repository.commit('third commit'); assert.strictEqual(repository.state.workingTreeChanges.length, 0); assert.strictEqual(repository.state.indexChanges.length, 0); }); diff --git a/extensions/git/src/timelineProvider.ts b/extensions/git/src/timelineProvider.ts index 5788ecc53dd7..90d8d28c3965 100644 --- a/extensions/git/src/timelineProvider.ts +++ b/extensions/git/src/timelineProvider.ts @@ -3,13 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationToken, ConfigurationChangeEvent, Disposable, env, Event, EventEmitter, MarkdownString, ThemeIcon, Timeline, TimelineChangeEvent, TimelineItem, TimelineOptions, TimelineProvider, Uri, workspace, l10n } from 'vscode'; +import { CancellationToken, ConfigurationChangeEvent, Disposable, env, Event, EventEmitter, MarkdownString, ThemeIcon, Timeline, TimelineChangeEvent, TimelineItem, TimelineOptions, TimelineProvider, Uri, workspace, l10n, Command } from 'vscode'; import { Model } from './model'; import { Repository, Resource } from './repository'; import { debounce } from './decorators'; import { emojify, ensureEmojis } from './emoji'; import { CommandCenter } from './commands'; import { OperationKind, OperationResult } from './operation'; +import { getCommitShortHash } from './util'; +import { CommitShortStat } from './git'; +import { provideSourceControlHistoryItemAvatar, provideSourceControlHistoryItemHoverCommands, provideSourceControlHistoryItemMessageLinks } from './historyItemDetailsProvider'; +import { AvatarQuery, AvatarQueryCommit } from './api/git'; + +const AVATAR_SIZE = 20; export class GitTimelineItem extends TimelineItem { static is(item: TimelineItem): item is GitTimelineItem { @@ -48,18 +54,60 @@ export class GitTimelineItem extends TimelineItem { return this.shortenRef(this.previousRef); } - setItemDetails(author: string, email: string | undefined, date: string, message: string): void { + setItemDetails(uri: Uri, hash: string | undefined, avatar: string | undefined, author: string, email: string | undefined, date: string, message: string, shortStat?: CommitShortStat, remoteSourceCommands: Command[] = []): void { this.tooltip = new MarkdownString('', true); + this.tooltip.isTrusted = true; + this.tooltip.supportHtml = true; + + const avatarMarkdown = avatar + ? `![${author}](${avatar}|width=${AVATAR_SIZE},height=${AVATAR_SIZE})` + : '$(account)'; if (email) { const emailTitle = l10n.t('Email'); - this.tooltip.appendMarkdown(`$(account) [**${author}**](mailto:${email} "${emailTitle} ${author}")\n\n`); + this.tooltip.appendMarkdown(`${avatarMarkdown} [**${author}**](mailto:${email} "${emailTitle} ${author}")`); } else { - this.tooltip.appendMarkdown(`$(account) **${author}**\n\n`); + this.tooltip.appendMarkdown(`${avatarMarkdown} **${author}**`); } - this.tooltip.appendMarkdown(`$(history) ${date}\n\n`); - this.tooltip.appendMarkdown(message); + this.tooltip.appendMarkdown(`, $(history) ${date}\n\n`); + this.tooltip.appendMarkdown(`${message}\n\n`); + + if (shortStat) { + this.tooltip.appendMarkdown(`---\n\n`); + + const labels: string[] = []; + if (shortStat.insertions) { + labels.push(`${shortStat.insertions === 1 ? + l10n.t('{0} insertion{1}', shortStat.insertions, '(+)') : + l10n.t('{0} insertions{1}', shortStat.insertions, '(+)')}`); + } + + if (shortStat.deletions) { + labels.push(`${shortStat.deletions === 1 ? + l10n.t('{0} deletion{1}', shortStat.deletions, '(-)') : + l10n.t('{0} deletions{1}', shortStat.deletions, '(-)')}`); + } + + this.tooltip.appendMarkdown(`${labels.join(', ')}\n\n`); + } + + if (hash) { + this.tooltip.appendMarkdown(`---\n\n`); + + this.tooltip.appendMarkdown(`[\`$(git-commit) ${getCommitShortHash(uri, hash)} \`](command:git.viewCommit?${encodeURIComponent(JSON.stringify([uri, hash]))} "${l10n.t('Open Commit')}")`); + this.tooltip.appendMarkdown(' '); + this.tooltip.appendMarkdown(`[$(copy)](command:git.copyContentToClipboard?${encodeURIComponent(JSON.stringify(hash))} "${l10n.t('Copy Commit Hash')}")`); + + // Remote commands + if (remoteSourceCommands.length > 0) { + this.tooltip.appendMarkdown('  |  '); + + const remoteCommandsMarkdown = remoteSourceCommands + .map(command => `[${command.title}](command:${command.command}?${encodeURIComponent(JSON.stringify([...command.arguments ?? [], hash]))} "${command.tooltip}")`); + this.tooltip.appendMarkdown(remoteCommandsMarkdown.join(' ')); + } + } } private shortenRef(ref: string): string { @@ -153,6 +201,7 @@ export class GitTimelineProvider implements TimelineProvider { maxEntries: limit, hash: options.cursor, follow: true, + shortStats: true, // sortByAuthorDate: true }); @@ -173,18 +222,39 @@ export class GitTimelineProvider implements TimelineProvider { const openComparison = l10n.t('Open Comparison'); - const items = commits.map((c, i) => { + const emptyTree = await repo.getEmptyTree(); + const unpublishedCommits = await repo.getUnpublishedCommits(); + const remoteHoverCommands = await provideSourceControlHistoryItemHoverCommands(this.model, repo); + + const avatarQuery = { + commits: commits.map(c => ({ + hash: c.hash, + authorName: c.authorName, + authorEmail: c.authorEmail + }) satisfies AvatarQueryCommit), + size: 20 + } satisfies AvatarQuery; + const avatars = await provideSourceControlHistoryItemAvatar(this.model, repo, avatarQuery); + + const items: GitTimelineItem[] = []; + for (let index = 0; index < commits.length; index++) { + const c = commits[index]; + const date = dateType === 'authored' ? c.authorDate : c.commitDate; const message = emojify(c.message); - const item = new GitTimelineItem(c.hash, commits[i + 1]?.hash ?? `${c.hash}^`, message, date?.getTime() ?? 0, c.hash, 'git:file:commit'); + const previousRef = commits[index + 1]?.hash ?? emptyTree; + const item = new GitTimelineItem(c.hash, previousRef, message, date?.getTime() ?? 0, c.hash, 'git:file:commit'); item.iconPath = new ThemeIcon('git-commit'); if (showAuthor) { item.description = c.authorName; } - item.setItemDetails(c.authorName!, c.authorEmail, dateFormatter.format(date), message); + const commitRemoteSourceCommands = !unpublishedCommits.has(c.hash) ? remoteHoverCommands : []; + const messageWithLinks = await provideSourceControlHistoryItemMessageLinks(this.model, repo, message) ?? message; + + item.setItemDetails(uri, c.hash, avatars?.get(c.hash), c.authorName!, c.authorEmail, dateFormatter.format(date), messageWithLinks, c.shortStat, commitRemoteSourceCommands); const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri); if (cmd) { @@ -195,8 +265,8 @@ export class GitTimelineProvider implements TimelineProvider { }; } - return item; - }); + items.push(item); + } if (options.cursor === undefined) { const you = l10n.t('You'); @@ -209,7 +279,7 @@ export class GitTimelineProvider implements TimelineProvider { // TODO@eamodio: Replace with a better icon -- reflecting its status maybe? item.iconPath = new ThemeIcon('git-commit'); item.description = ''; - item.setItemDetails(you, undefined, dateFormatter.format(date), Resource.getStatusText(index.type)); + item.setItemDetails(uri, undefined, undefined, you, undefined, dateFormatter.format(date), Resource.getStatusText(index.type)); const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri); if (cmd) { @@ -231,7 +301,7 @@ export class GitTimelineProvider implements TimelineProvider { const item = new GitTimelineItem('', index ? '~' : 'HEAD', l10n.t('Uncommitted Changes'), date.getTime(), 'working', 'git:file:working'); item.iconPath = new ThemeIcon('circle-outline'); item.description = ''; - item.setItemDetails(you, undefined, dateFormatter.format(date), Resource.getStatusText(working.type)); + item.setItemDetails(uri, undefined, undefined, you, undefined, dateFormatter.format(date), Resource.getStatusText(working.type)); const cmd = this.commands.resolveTimelineOpenDiffCommand(item, uri); if (cmd) { diff --git a/extensions/git/src/api/git-base.d.ts b/extensions/git/src/typings/git-base.d.ts similarity index 97% rename from extensions/git/src/api/git-base.d.ts rename to extensions/git/src/typings/git-base.d.ts index 1eeb17399010..d4ec49df47dc 100644 --- a/extensions/git/src/api/git-base.d.ts +++ b/extensions/git/src/typings/git-base.d.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, Event, ProviderResult, Uri } from 'vscode'; +import { Command, Disposable, Event, ProviderResult } from 'vscode'; export { ProviderResult } from 'vscode'; export interface API { - pickRemoteSource(options: PickRemoteSourceOptions): Promise; - getRemoteSourceActions(url: string): Promise; registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable; + getRemoteSourceActions(url: string): Promise; + pickRemoteSource(options: PickRemoteSourceOptions): Promise; } export interface GitBaseExtension { diff --git a/extensions/git/src/typings/vscode.proposed.canonicalUriProvider.d.ts b/extensions/git/src/typings/vscode.proposed.canonicalUriProvider.d.ts deleted file mode 100644 index 84ee599797d9..000000000000 --- a/extensions/git/src/typings/vscode.proposed.canonicalUriProvider.d.ts +++ /dev/null @@ -1,47 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'vscode' { - - // https://github.com/microsoft/vscode/issues/180582 - - export namespace workspace { - /** - * - * @param scheme The URI scheme that this provider can provide canonical URIs for. - * A canonical URI represents the conversion of a resource's alias into a source of truth URI. - * Multiple aliases may convert to the same source of truth URI. - * @param provider A provider which can convert URIs of scheme @param scheme to - * a canonical URI which is stable across machines. - */ - export function registerCanonicalUriProvider(scheme: string, provider: CanonicalUriProvider): Disposable; - - /** - * - * @param uri The URI to provide a canonical URI for. - * @param token A cancellation token for the request. - */ - export function getCanonicalUri(uri: Uri, options: CanonicalUriRequestOptions, token: CancellationToken): ProviderResult; - } - - export interface CanonicalUriProvider { - /** - * - * @param uri The URI to provide a canonical URI for. - * @param options Options that the provider should honor in the URI it returns. - * @param token A cancellation token for the request. - * @returns The canonical URI for the requested URI or undefined if no canonical URI can be provided. - */ - provideCanonicalUri(uri: Uri, options: CanonicalUriRequestOptions, token: CancellationToken): ProviderResult; - } - - export interface CanonicalUriRequestOptions { - /** - * - * The desired scheme of the canonical URI. - */ - targetScheme: string; - } -} diff --git a/extensions/git/src/typings/vscode.proposed.editSessionIdentityProvider.d.ts b/extensions/git/src/typings/vscode.proposed.editSessionIdentityProvider.d.ts deleted file mode 100644 index e09d0e142b48..000000000000 --- a/extensions/git/src/typings/vscode.proposed.editSessionIdentityProvider.d.ts +++ /dev/null @@ -1,71 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'vscode' { - - // https://github.com/microsoft/vscode/issues/157734 - - export namespace workspace { - /** - * An event that is emitted when an edit session identity is about to be requested. - */ - export const onWillCreateEditSessionIdentity: Event; - - /** - * - * @param scheme The URI scheme that this provider can provide edit session identities for. - * @param provider A provider which can convert URIs for workspace folders of scheme @param scheme to - * an edit session identifier which is stable across machines. This enables edit sessions to be resolved. - */ - export function registerEditSessionIdentityProvider(scheme: string, provider: EditSessionIdentityProvider): Disposable; - } - - export interface EditSessionIdentityProvider { - /** - * - * @param workspaceFolder The workspace folder to provide an edit session identity for. - * @param token A cancellation token for the request. - * @returns A string representing the edit session identity for the requested workspace folder. - */ - provideEditSessionIdentity(workspaceFolder: WorkspaceFolder, token: CancellationToken): ProviderResult; - - /** - * - * @param identity1 An edit session identity. - * @param identity2 A second edit session identity to compare to @param identity1. - * @param token A cancellation token for the request. - * @returns An {@link EditSessionIdentityMatch} representing the edit session identity match confidence for the provided identities. - */ - provideEditSessionIdentityMatch(identity1: string, identity2: string, token: CancellationToken): ProviderResult; - } - - export enum EditSessionIdentityMatch { - Complete = 100, - Partial = 50, - None = 0 - } - - export interface EditSessionIdentityWillCreateEvent { - - /** - * A cancellation token. - */ - readonly token: CancellationToken; - - /** - * The workspace folder to create an edit session identity for. - */ - readonly workspaceFolder: WorkspaceFolder; - - /** - * Allows to pause the event until the provided thenable resolves. - * - * *Note:* This function can only be called during event dispatch. - * - * @param thenable A thenable that delays saving. - */ - waitUntil(thenable: Thenable): void; - } -} diff --git a/extensions/git/src/uri.ts b/extensions/git/src/uri.ts index 169abd1bc35d..8b04fabe583e 100644 --- a/extensions/git/src/uri.ts +++ b/extensions/git/src/uri.ts @@ -64,12 +64,24 @@ export function toMergeUris(uri: Uri): { base: Uri; ours: Uri; theirs: Uri } { export function toMultiFileDiffEditorUris(change: Change, originalRef: string, modifiedRef: string): { originalUri: Uri | undefined; modifiedUri: Uri | undefined } { switch (change.status) { case Status.INDEX_ADDED: - return { originalUri: undefined, modifiedUri: toGitUri(change.uri, modifiedRef) }; + return { + originalUri: undefined, + modifiedUri: toGitUri(change.uri, modifiedRef) + }; case Status.DELETED: - return { originalUri: toGitUri(change.uri, originalRef), modifiedUri: undefined }; + return { + originalUri: toGitUri(change.uri, originalRef), + modifiedUri: undefined + }; case Status.INDEX_RENAMED: - return { originalUri: toGitUri(change.originalUri, originalRef), modifiedUri: toGitUri(change.uri, modifiedRef) }; + return { + originalUri: toGitUri(change.originalUri, originalRef), + modifiedUri: toGitUri(change.uri, modifiedRef) + }; default: - return { originalUri: toGitUri(change.uri, originalRef), modifiedUri: toGitUri(change.uri, modifiedRef) }; + return { + originalUri: toGitUri(change.uri, originalRef), + modifiedUri: toGitUri(change.uri, modifiedRef) + }; } } diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index b8764f43d614..7dd1cbafbdf0 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Disposable, EventEmitter, SourceControlHistoryItemRef } from 'vscode'; +import { Event, Disposable, EventEmitter, SourceControlHistoryItemRef, l10n, workspace, Uri, DiagnosticSeverity, env } from 'vscode'; import { dirname, sep, relative } from 'path'; import { Readable } from 'stream'; import { promises as fs, createReadStream } from 'fs'; @@ -11,6 +11,9 @@ import byline from 'byline'; export const isMacintosh = process.platform === 'darwin'; export const isWindows = process.platform === 'win32'; +export const isRemote = env.remoteName !== undefined; +export const isLinux = process.platform === 'linux'; +export const isLinuxSnap = isLinux && !!process.env['SNAP'] && !!process.env['SNAP_REVISION']; export function log(...args: any[]): void { console.log.apply(console, ['git:', ...args]); @@ -288,6 +291,10 @@ export function detectUnicodeEncoding(buffer: Buffer): Encoding | null { return null; } +export function truncate(value: string, maxLength = 20): string { + return value.length <= maxLength ? value : `${value.substring(0, maxLength)}\u2026`; +} + function normalizePath(path: string): string { // Windows & Mac are currently being handled // as case insensitive file systems in VS Code. @@ -568,3 +575,215 @@ export function deltaHistoryItemRefs(before: SourceControlHistoryItemRef[], afte return { added, modified, removed }; } + +const minute = 60; +const hour = minute * 60; +const day = hour * 24; +const week = day * 7; +const month = day * 30; +const year = day * 365; + +/** + * Create a l10n.td difference of the time between now and the specified date. + * @param date The date to generate the difference from. + * @param appendAgoLabel Whether to append the " ago" to the end. + * @param useFullTimeWords Whether to use full words (eg. seconds) instead of + * shortened (eg. secs). + * @param disallowNow Whether to disallow the string "now" when the difference + * is less than 30 seconds. + */ +export function fromNow(date: number | Date, appendAgoLabel?: boolean, useFullTimeWords?: boolean, disallowNow?: boolean): string { + if (typeof date !== 'number') { + date = date.getTime(); + } + + const seconds = Math.round((new Date().getTime() - date) / 1000); + if (seconds < -30) { + return l10n.t('in {0}', fromNow(new Date().getTime() + seconds * 1000, false)); + } + + if (!disallowNow && seconds < 30) { + return l10n.t('now'); + } + + let value: number; + if (seconds < minute) { + value = seconds; + + if (appendAgoLabel) { + if (value === 1) { + return useFullTimeWords + ? l10n.t('{0} second ago', value) + : l10n.t('{0} sec ago', value); + } else { + return useFullTimeWords + ? l10n.t('{0} seconds ago', value) + : l10n.t('{0} secs ago', value); + } + } else { + if (value === 1) { + return useFullTimeWords + ? l10n.t('{0} second', value) + : l10n.t('{0} sec', value); + } else { + return useFullTimeWords + ? l10n.t('{0} seconds', value) + : l10n.t('{0} secs', value); + } + } + } + + if (seconds < hour) { + value = Math.floor(seconds / minute); + if (appendAgoLabel) { + if (value === 1) { + return useFullTimeWords + ? l10n.t('{0} minute ago', value) + : l10n.t('{0} min ago', value); + } else { + return useFullTimeWords + ? l10n.t('{0} minutes ago', value) + : l10n.t('{0} mins ago', value); + } + } else { + if (value === 1) { + return useFullTimeWords + ? l10n.t('{0} minute', value) + : l10n.t('{0} min', value); + } else { + return useFullTimeWords + ? l10n.t('{0} minutes', value) + : l10n.t('{0} mins', value); + } + } + } + + if (seconds < day) { + value = Math.floor(seconds / hour); + if (appendAgoLabel) { + if (value === 1) { + return useFullTimeWords + ? l10n.t('{0} hour ago', value) + : l10n.t('{0} hr ago', value); + } else { + return useFullTimeWords + ? l10n.t('{0} hours ago', value) + : l10n.t('{0} hrs ago', value); + } + } else { + if (value === 1) { + return useFullTimeWords + ? l10n.t('{0} hour', value) + : l10n.t('{0} hr', value); + } else { + return useFullTimeWords + ? l10n.t('{0} hours', value) + : l10n.t('{0} hrs', value); + } + } + } + + if (seconds < week) { + value = Math.floor(seconds / day); + if (appendAgoLabel) { + return value === 1 + ? l10n.t('{0} day ago', value) + : l10n.t('{0} days ago', value); + } else { + return value === 1 + ? l10n.t('{0} day', value) + : l10n.t('{0} days', value); + } + } + + if (seconds < month) { + value = Math.floor(seconds / week); + if (appendAgoLabel) { + if (value === 1) { + return useFullTimeWords + ? l10n.t('{0} week ago', value) + : l10n.t('{0} wk ago', value); + } else { + return useFullTimeWords + ? l10n.t('{0} weeks ago', value) + : l10n.t('{0} wks ago', value); + } + } else { + if (value === 1) { + return useFullTimeWords + ? l10n.t('{0} week', value) + : l10n.t('{0} wk', value); + } else { + return useFullTimeWords + ? l10n.t('{0} weeks', value) + : l10n.t('{0} wks', value); + } + } + } + + if (seconds < year) { + value = Math.floor(seconds / month); + if (appendAgoLabel) { + if (value === 1) { + return useFullTimeWords + ? l10n.t('{0} month ago', value) + : l10n.t('{0} mo ago', value); + } else { + return useFullTimeWords + ? l10n.t('{0} months ago', value) + : l10n.t('{0} mos ago', value); + } + } else { + if (value === 1) { + return useFullTimeWords + ? l10n.t('{0} month', value) + : l10n.t('{0} mo', value); + } else { + return useFullTimeWords + ? l10n.t('{0} months', value) + : l10n.t('{0} mos', value); + } + } + } + + value = Math.floor(seconds / year); + if (appendAgoLabel) { + if (value === 1) { + return useFullTimeWords + ? l10n.t('{0} year ago', value) + : l10n.t('{0} yr ago', value); + } else { + return useFullTimeWords + ? l10n.t('{0} years ago', value) + : l10n.t('{0} yrs ago', value); + } + } else { + if (value === 1) { + return useFullTimeWords + ? l10n.t('{0} year', value) + : l10n.t('{0} yr', value); + } else { + return useFullTimeWords + ? l10n.t('{0} years', value) + : l10n.t('{0} yrs', value); + } + } +} + +export function getCommitShortHash(scope: Uri, hash: string): string { + const config = workspace.getConfiguration('git', scope); + const shortHashLength = config.get('commitShortHashLength', 7); + return hash.substring(0, shortHashLength); +} + +export type DiagnosticSeverityConfig = 'error' | 'warning' | 'information' | 'hint' | 'none'; + +export function toDiagnosticSeverity(value: DiagnosticSeverityConfig): DiagnosticSeverity { + return value === 'error' + ? DiagnosticSeverity.Error + : value === 'warning' + ? DiagnosticSeverity.Warning + : value === 'information' + ? DiagnosticSeverity.Information + : DiagnosticSeverity.Hint; +} diff --git a/extensions/git/tsconfig.json b/extensions/git/tsconfig.json index 75fb8217df75..c79be520be97 100644 --- a/extensions/git/tsconfig.json +++ b/extensions/git/tsconfig.json @@ -10,7 +10,10 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.diffCommand.d.ts", + "../../src/vscode-dts/vscode.proposed.canonicalUriProvider.d.ts", + "../../src/vscode-dts/vscode.proposed.editSessionIdentityProvider.d.ts", + "../../src/vscode-dts/vscode.proposed.quickDiffProvider.d.ts", + "../../src/vscode-dts/vscode.proposed.quickInputButtonLocation.d.ts", "../../src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts", "../../src/vscode-dts/vscode.proposed.scmActionButton.d.ts", "../../src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts", @@ -18,10 +21,11 @@ "../../src/vscode-dts/vscode.proposed.scmValidation.d.ts", "../../src/vscode-dts/vscode.proposed.scmMultiDiffEditor.d.ts", "../../src/vscode-dts/vscode.proposed.scmTextDocument.d.ts", + "../../src/vscode-dts/vscode.proposed.statusBarItemTooltip.d.ts", "../../src/vscode-dts/vscode.proposed.tabInputMultiDiff.d.ts", "../../src/vscode-dts/vscode.proposed.tabInputTextMerge.d.ts", + "../../src/vscode-dts/vscode.proposed.textEditorDiffInformation.d.ts", "../../src/vscode-dts/vscode.proposed.timeline.d.ts", - "../../src/vscode-dts/vscode.proposed.quickInputButtonLocation.d.ts", "../types/lib.textEncoder.d.ts" ] } diff --git a/extensions/github-authentication/extension.webpack.config.js b/extensions/github-authentication/extension.webpack.config.js index df3adb4ee7a3..d356151d68c5 100644 --- a/extensions/github-authentication/extension.webpack.config.js +++ b/extensions/github-authentication/extension.webpack.config.js @@ -13,5 +13,5 @@ module.exports = withDefaults({ context: __dirname, entry: { extension: './src/extension.ts', - } + }, }); diff --git a/extensions/github-authentication/package-lock.json b/extensions/github-authentication/package-lock.json index c150fa7acc36..472a81ae21c3 100644 --- a/extensions/github-authentication/package-lock.json +++ b/extensions/github-authentication/package-lock.json @@ -9,166 +9,180 @@ "version": "0.0.2", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", - "node-fetch": "2.6.7", + "@vscode/extension-telemetry": "^1.2.0", + "node-fetch": "3.3.2", "vscode-tas-client": "^0.1.84" }, "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x", - "@types/node-fetch": "^2.5.7" + "@types/mocha": "^10.0.10", + "@types/node": "25.x", + "@types/node-fetch": "^2.6.13" }, "engines": { "vscode": "^1.41.0" } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", + "license": "MIT" }, "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@types/node-fetch": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz", - "integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==", + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", - "form-data": "^3.0.0" + "form-data": "^4.0.4" } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.2.0.tgz", + "integrity": "sha512-En6dTwfy5NFzSMibvOpx/lKq2jtgWuR4++KJbi3SpQ2iT8gm+PHo9868/scocW122KDwTxl4ruxZ7i4rHmJJnQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" @@ -177,14 +191,30 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k= sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -192,67 +222,314 @@ "node": ">= 0.8" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk= sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/form-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", - "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { "node": ">= 6" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, + "license": "MIT", "dependencies": { - "mime-db": "1.44.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", "dependencies": { - "whatwg-url": "^5.0.0" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" }, "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" } }, "node_modules/tas-client": { @@ -260,16 +537,12 @@ "resolved": "https://registry.npmjs.org/tas-client/-/tas-client-0.2.33.tgz", "integrity": "sha512-V+uqV66BOQnWxvI6HjDnE4VkInmYZUQ4dgB7gzaDyFyFSK1i1nF/j7DpS9UbQAgV9NaF1XpcyuavnM1qOeiEIg==" }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-tas-client": { "version": "0.1.84", @@ -282,18 +555,13 @@ "vscode": "^1.85.0" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0= sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" } } } diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index 7fcbc7f151ce..4928e15f51ba 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -39,20 +39,12 @@ } ], "configuration": [{ - "title": "GitHub Enterprise Server Authentication Provider", + "title": "%config.github-enterprise.title%", "properties": { "github-enterprise.uri": { "type": "string", - "description": "GitHub Enterprise Server URI" - } - } - }, - { - "title": "GitHub Authentication", - "properties": { - "github.experimental.multipleAccounts": { - "type": "boolean", - "description": "Experimental support for multiple GitHub accounts" + "markdownDescription": "%config.github-enterprise.uri.description%", + "pattern": "^(?:$|(https?)://(?!github\\.com).*)" } } } @@ -69,14 +61,14 @@ "vscode:prepublish": "npm run compile" }, "dependencies": { - "node-fetch": "2.6.7", - "@vscode/extension-telemetry": "^0.9.0", + "node-fetch": "3.3.2", + "@vscode/extension-telemetry": "^1.2.0", "vscode-tas-client": "^0.1.84" }, "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x", - "@types/node-fetch": "^2.5.7" + "@types/mocha": "^10.0.10", + "@types/node": "25.x", + "@types/node-fetch": "^2.6.13" }, "repository": { "type": "git", diff --git a/extensions/github-authentication/package.nls.json b/extensions/github-authentication/package.nls.json index 592a413b9a50..e326f00f5c91 100644 --- a/extensions/github-authentication/package.nls.json +++ b/extensions/github-authentication/package.nls.json @@ -1,4 +1,6 @@ { "displayName": "GitHub Authentication", - "description": "GitHub Authentication Provider" + "description": "GitHub Authentication Provider", + "config.github-enterprise.title": "GHE.com & GitHub Enterprise Server Authentication", + "config.github-enterprise.uri.description": "The URI for your GHE.com or GitHub Enterprise Server instance.\n\nExamples:\n* GHE.com: `https://octocat.ghe.com`\n* GitHub Enterprise Server: `https://github.octocat.com`\n\n> **Note:** This should _not_ be set to a GitHub.com URI. If your account exists on GitHub.com or is a GitHub Enterprise Managed User, you do not need any additional configuration and can simply log in to GitHub." } diff --git a/extensions/github-authentication/src/config.ts b/extensions/github-authentication/src/config.ts index 30b9dd662653..8cd3a9ed7931 100644 --- a/extensions/github-authentication/src/config.ts +++ b/extensions/github-authentication/src/config.ts @@ -10,6 +10,10 @@ export interface IConfig { } // For easy access to mixin client ID and secret +// +// NOTE: GitHub client secrets cannot be secured when running in a native client so in other words, the client secret is +// not really a secret... so we allow the client secret in code. It is brought in before we publish VS Code. Reference: +// https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/best-practices-for-creating-an-oauth-app#client-secrets export const Config: IConfig = { gitHubClientId: '01ab8ac9400c4e429b23' }; diff --git a/extensions/github-authentication/src/flows.ts b/extensions/github-authentication/src/flows.ts index a2497b2b0b2e..8920ea5d3578 100644 --- a/extensions/github-authentication/src/flows.ts +++ b/extensions/github-authentication/src/flows.ts @@ -105,8 +105,6 @@ async function exchangeCodeForToken( headers: { Accept: 'application/json', 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': body.toString() - }, body: body.toString() }); diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index 8a363d7dd335..d67fc99e08b2 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -99,7 +99,6 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid private readonly _keychain: Keychain; private readonly _accountsSeen = new Set(); private readonly _disposable: vscode.Disposable | undefined; - private _supportsMultipleAccounts = false; private _sessionsPromise: Promise; @@ -136,24 +135,10 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid return sessions; }); - this._supportsMultipleAccounts = this._shouldSupportMultipleAccounts(); - this._disposable = vscode.Disposable.from( this._telemetryReporter, - vscode.authentication.registerAuthenticationProvider(type, this._githubServer.friendlyName, this, { supportsMultipleAccounts: this._supportsMultipleAccounts }), - this.context.secrets.onDidChange(() => this.checkForUpdates()), - vscode.workspace.onDidChangeConfiguration(async e => { - if (e.affectsConfiguration('github.experimental.multipleAccounts')) { - const newValue = this._shouldSupportMultipleAccounts(); - if (newValue === this._supportsMultipleAccounts) { - return; - } - const result = await vscode.window.showInformationMessage(vscode.l10n.t('Please reload the window to apply the new setting.'), { modal: true }, vscode.l10n.t('Reload Window')); - if (result) { - vscode.commands.executeCommand('workbench.action.reloadWindow'); - } - } - }) + vscode.authentication.registerAuthenticationProvider(type, this._githubServer.friendlyName, this, { supportsMultipleAccounts: true }), + this.context.secrets.onDidChange(() => this.checkForUpdates()) ); } @@ -251,9 +236,6 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid const sessionPromises = sessionData.map(async (session: SessionData): Promise => { // For GitHub scope list, order doesn't matter so we immediately sort the scopes const scopesStr = [...session.scopes].sort().join(' '); - if (!this._supportsMultipleAccounts && scopesSeen.has(scopesStr)) { - return undefined; - } let userInfo: { id: string; accountName: string } | undefined; if (!session.account) { try { @@ -339,11 +321,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid const session = await this.tokenToSession(token, scopes); this.afterSessionLoad(session); - const sessionIndex = sessions.findIndex( - this._supportsMultipleAccounts - ? s => s.account.id === session.account.id && arrayEquals([...s.scopes].sort(), sortedScopes) - : s => s.id === session.id || arrayEquals([...s.scopes].sort(), sortedScopes) - ); + const sessionIndex = sessions.findIndex(s => s.account.id === session.account.id && arrayEquals([...s.scopes].sort(), sortedScopes)); const removed = new Array(); if (sessionIndex > -1) { removed.push(...sessions.splice(sessionIndex, 1, session)); @@ -421,26 +399,4 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid throw e; } } - - private _shouldSupportMultipleAccounts(): boolean { - // First check if there is a setting value to allow user to override the default - const inspect = vscode.workspace.getConfiguration('github.experimental').inspect('multipleAccounts'); - if (inspect?.workspaceFolderValue !== undefined) { - this._logger.trace(`Acquired multi-account enablement value from 'workspaceFolderValue'. Value: ${inspect.workspaceFolderValue}`); - return inspect.workspaceFolderValue; - } - if (inspect?.workspaceValue !== undefined) { - this._logger.trace(`Acquired multi-account enablement value from 'workspaceValue'. Value: ${inspect.workspaceValue}`); - return inspect.workspaceValue; - } - if (inspect?.globalValue !== undefined) { - this._logger.trace(`Acquired multi-account enablement value from 'globalValue'. Value: ${inspect.globalValue}`); - return inspect.globalValue; - } - - const value = vscode.env.uriScheme !== 'vscode'; - this._logger.trace(`Acquired multi-account enablement value from default. Value: ${value} because of uriScheme: ${vscode.env.uriScheme}`); - // If no setting or experiment value is found, default to false on stable and true on insiders - return value; - } } diff --git a/extensions/github-authentication/src/node/fetch.ts b/extensions/github-authentication/src/node/fetch.ts index 58718078e699..ca344d436b11 100644 --- a/extensions/github-authentication/src/node/fetch.ts +++ b/extensions/github-authentication/src/node/fetch.ts @@ -2,6 +2,11 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import fetch from 'node-fetch'; -export const fetching = fetch; +let _fetch: typeof fetch; +try { + _fetch = require('electron').net.fetch; +} catch { + _fetch = fetch; +} +export const fetching = _fetch; diff --git a/extensions/github/package-lock.json b/extensions/github/package-lock.json index 41de66d1a9ba..6072610911ed 100644 --- a/extensions/github/package-lock.json +++ b/extensions/github/package-lock.json @@ -9,411 +9,352 @@ "version": "0.0.1", "license": "MIT", "dependencies": { - "@octokit/graphql": "5.0.5", - "@octokit/graphql-schema": "14.4.0", - "@octokit/rest": "19.0.4", - "@vscode/extension-telemetry": "^0.9.0", + "@octokit/graphql": "9.0.3", + "@octokit/graphql-schema": "15.26.1", + "@octokit/rest": "22.0.1", + "@vscode/extension-telemetry": "^1.2.0", "tunnel": "^0.0.6" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.41.0" } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", + "license": "MIT" }, "node_modules/@octokit/auth-token": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-3.0.1.tgz", - "integrity": "sha512-/USkK4cioY209wXRpund6HZzHo9GmjakpV9ycOkpMcMxMk7QVcVFVyCMtzvXYiHsB2crgDgrtNYSELYFBXhhaA==", - "dependencies": { - "@octokit/types": "^7.0.0" - }, + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", + "license": "MIT", "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/auth-token/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==" - }, - "node_modules/@octokit/auth-token/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dependencies": { - "@octokit/openapi-types": "^13.6.0" + "node": ">= 20" } }, "node_modules/@octokit/core": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-4.0.5.tgz", - "integrity": "sha512-4R3HeHTYVHCfzSAi0C6pbGXV8UDI5Rk+k3G7kLVNckswN9mvpOzW9oENfjfH3nEmzg8y3AmKmzs8Sg6pLCeOCA==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", + "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", + "license": "MIT", "dependencies": { - "@octokit/auth-token": "^3.0.0", - "@octokit/graphql": "^5.0.0", - "@octokit/request": "^6.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^7.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "9.0.3", + "@octokit/request": "^10.0.6", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "before-after-hook": "^4.0.0", + "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/core/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==" - }, - "node_modules/@octokit/core/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dependencies": { - "@octokit/openapi-types": "^13.6.0" + "node": ">= 20" } }, "node_modules/@octokit/endpoint": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-7.0.1.tgz", - "integrity": "sha512-/wTXAJwt0HzJ2IeE4kQXO+mBScfzyCkI0hMtkIaqyXd9zg76OpOfNQfHL9FlaxAV2RsNiOXZibVWloy8EexENg==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.2.tgz", + "integrity": "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ==", + "license": "MIT", "dependencies": { - "@octokit/types": "^7.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/endpoint/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==" - }, - "node_modules/@octokit/endpoint/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dependencies": { - "@octokit/openapi-types": "^13.6.0" + "node": ">= 20" } }, "node_modules/@octokit/graphql": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.5.tgz", - "integrity": "sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz", + "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", + "license": "MIT", "dependencies": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "universal-user-agent": "^6.0.0" + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/@octokit/graphql-schema": { - "version": "14.4.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql-schema/-/graphql-schema-14.4.0.tgz", - "integrity": "sha512-+O6/dsLlR6V9gv+t1lqsN+x73TLwyQWZpd3M8/eYnuny7VaznV9TAyUxf18tX8WBBS5IqtlLDk1nG+aSTPRZzQ==", + "version": "15.26.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql-schema/-/graphql-schema-15.26.1.tgz", + "integrity": "sha512-RFDC2MpRBd4AxSRvUeBIVeBU7ojN/SxDfALUd7iVYOSeEK3gZaqR2MGOysj4Zh2xj2RY5fQAUT+Oqq7hWTraMA==", + "license": "MIT", "dependencies": { "graphql": "^16.0.0", "graphql-tag": "^2.10.3" } }, "node_modules/@octokit/openapi-types": { - "version": "17.1.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-17.1.0.tgz", - "integrity": "sha512-rnI26BAITDZTo5vqFOmA7oX4xRd18rO+gcK4MiTpJmsRMxAw0JmevNjPsjpry1bb9SVNo56P/0kbiyXXa4QluA==" + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", + "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", + "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-4.2.0.tgz", - "integrity": "sha512-8otLCIK9esfmOCY14CBnG/xPqv0paf14rc+s9tHpbOpeFwrv5CnECKW1qdqMAT60ngAa9eB1bKQ+l2YCpi0HPQ==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", + "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", + "license": "MIT", "dependencies": { - "@octokit/types": "^7.2.0" + "@octokit/types": "^16.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" }, "peerDependencies": { - "@octokit/core": ">=4" - } - }, - "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==" - }, - "node_modules/@octokit/plugin-paginate-rest/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dependencies": { - "@octokit/openapi-types": "^13.6.0" + "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz", + "integrity": "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, "peerDependencies": { - "@octokit/core": ">=3" + "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-6.4.0.tgz", - "integrity": "sha512-YP4eUqZ6vORy/eZOTdil1ZSrMt0kv7i/CVw+HhC2C0yJN+IqTc/rot957JQ7JfyeJD6HZOjLg6Jp1o9cPhI9KA==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", + "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", + "license": "MIT", "dependencies": { - "@octokit/types": "^7.2.0", - "deprecation": "^2.3.1" + "@octokit/types": "^16.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" }, "peerDependencies": { - "@octokit/core": ">=3" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==" - }, - "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dependencies": { - "@octokit/openapi-types": "^13.6.0" + "@octokit/core": ">=6" } }, "node_modules/@octokit/request": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-6.2.1.tgz", - "integrity": "sha512-gYKRCia3cpajRzDSU+3pt1q2OcuC6PK8PmFIyxZDWCzRXRSIBH8jXjFJ8ZceoygBIm0KsEUg4x1+XcYBz7dHPQ==", + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.7.tgz", + "integrity": "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==", + "license": "MIT", "dependencies": { - "@octokit/endpoint": "^7.0.0", - "@octokit/request-error": "^3.0.0", - "@octokit/types": "^7.0.0", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" + "@octokit/endpoint": "^11.0.2", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "fast-content-type-parse": "^3.0.0", + "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/@octokit/request-error": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-3.0.1.tgz", - "integrity": "sha512-ym4Bp0HTP7F3VFssV88WD1ZyCIRoE8H35pXSKwLeMizcdZAYc/t6N9X9Yr9n6t3aG9IH75XDnZ6UeZph0vHMWQ==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz", + "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", + "license": "MIT", "dependencies": { - "@octokit/types": "^7.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" + "@octokit/types": "^16.0.0" }, "engines": { - "node": ">= 14" - } - }, - "node_modules/@octokit/request-error/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==" - }, - "node_modules/@octokit/request-error/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dependencies": { - "@octokit/openapi-types": "^13.6.0" - } - }, - "node_modules/@octokit/request/node_modules/@octokit/openapi-types": { - "version": "13.6.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-13.6.0.tgz", - "integrity": "sha512-bxftLwoZ2J6zsU1rzRvk0O32j7lVB0NWWn+P5CDHn9zPzytasR3hdAeXlTngRDkqv1LyEeuy5psVnDkmOSwrcQ==" - }, - "node_modules/@octokit/request/node_modules/@octokit/types": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-7.2.0.tgz", - "integrity": "sha512-pYQ/a1U6mHptwhGyp6SvsiM4bWP2s3V95olUeTxas85D/2kN78yN5C8cGN+P4LwJSWUqIEyvq0Qn2WUn6NQRjw==", - "dependencies": { - "@octokit/openapi-types": "^13.6.0" + "node": ">= 20" } }, "node_modules/@octokit/rest": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-19.0.4.tgz", - "integrity": "sha512-LwG668+6lE8zlSYOfwPj4FxWdv/qFXYBpv79TWIQEpBLKA9D/IMcWsF/U9RGpA3YqMVDiTxpgVpEW3zTFfPFTA==", + "version": "22.0.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.1.tgz", + "integrity": "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==", + "license": "MIT", "dependencies": { - "@octokit/core": "^4.0.0", - "@octokit/plugin-paginate-rest": "^4.0.0", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^6.0.0" + "@octokit/core": "^7.0.6", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/plugin-request-log": "^6.0.0", + "@octokit/plugin-rest-endpoint-methods": "^17.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/@octokit/types": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-9.2.0.tgz", - "integrity": "sha512-xySzJG4noWrIBFyMu4lg4tu9vAgNg9S0aoLRONhAEz6ueyi1evBzb40HitIosaYS4XOexphG305IVcLrIX/30g==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz", + "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", + "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^17.1.0" + "@octokit/openapi-types": "^27.0.0" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.2.0.tgz", + "integrity": "sha512-En6dTwfy5NFzSMibvOpx/lKq2jtgWuR4++KJbi3SpQ2iT8gm+PHo9868/scocW122KDwTxl4ruxZ7i4rHmJJnQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" } }, "node_modules/before-after-hook": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", - "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==" - }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", + "license": "Apache-2.0" + }, + "node_modules/fast-content-type-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" }, "node_modules/graphql": { "version": "16.8.1", @@ -437,46 +378,6 @@ "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" } }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E= sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, "node_modules/tslib": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", @@ -491,34 +392,17 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==" - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0= sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", + "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", + "license": "ISC" } } } diff --git a/extensions/github/package.json b/extensions/github/package.json index ece19e32f54a..6d37c512fc6f 100644 --- a/extensions/github/package.json +++ b/extensions/github/package.json @@ -27,33 +27,46 @@ } }, "enabledApiProposals": [ - "contribShareMenu", - "contribEditSessions", "canonicalUriProvider", - "shareProvider" + "contribEditSessions", + "contribShareMenu", + "contribSourceControlHistoryItemMenu", + "scmHistoryProvider", + "shareProvider", + "timeline" ], "contributes": { "commands": [ { "command": "github.publish", - "title": "Publish to GitHub" + "title": "%command.publish%" }, { "command": "github.copyVscodeDevLink", - "title": "Copy vscode.dev Link" + "title": "%command.copyVscodeDevLink%" }, { "command": "github.copyVscodeDevLinkFile", - "title": "Copy vscode.dev Link" + "title": "%command.copyVscodeDevLink%" }, { "command": "github.copyVscodeDevLinkWithoutRange", - "title": "Copy vscode.dev Link" + "title": "%command.copyVscodeDevLink%" }, { "command": "github.openOnVscodeDev", - "title": "Open in vscode.dev", + "title": "%command.openOnVscodeDev%", "icon": "$(globe)" + }, + { + "command": "github.graph.openOnGitHub", + "title": "%command.openOnGitHub%", + "icon": "$(github)" + }, + { + "command": "github.timeline.openOnGitHub", + "title": "%command.openOnGitHub%", + "icon": "$(github)" } ], "continueEditSession": [ @@ -69,7 +82,11 @@ "commandPalette": [ { "command": "github.publish", - "when": "git-base.gitEnabled && remoteName != 'codespaces'" + "when": "git-base.gitEnabled && workspaceFolderCount != 0 && remoteName != 'codespaces'" + }, + { + "command": "github.graph.openOnGitHub", + "when": "false" }, { "command": "github.copyVscodeDevLink", @@ -86,6 +103,10 @@ { "command": "github.openOnVscodeDev", "when": "false" + }, + { + "command": "github.timeline.openOnGitHub", + "when": "false" } ], "file/share": [ @@ -127,6 +148,27 @@ "when": "github.hasGitHubRepo && resourceScheme != untitled && remoteName != 'codespaces'", "group": "0_vscode@0" } + ], + "scm/historyItem/context": [ + { + "command": "github.graph.openOnGitHub", + "when": "github.hasGitHubRepo", + "group": "0_view@2" + } + ], + "scm/historyItem/hover": [ + { + "command": "github.graph.openOnGitHub", + "when": "github.hasGitHubRepo", + "group": "1_open@1" + } + ], + "timeline/item/context": [ + { + "command": "github.timeline.openOnGitHub", + "group": "1_actions@3", + "when": "github.hasGitHubRepo && timelineItem =~ /git:file:commit\\b/" + } ] }, "configuration": [ @@ -153,6 +195,12 @@ ], "default": "https", "description": "%config.gitProtocol%" + }, + "github.showAvatar": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%config.showAvatar%" } } } @@ -179,14 +227,14 @@ "watch": "gulp watch-extension:github" }, "dependencies": { - "@octokit/graphql": "5.0.5", - "@octokit/graphql-schema": "14.4.0", - "@octokit/rest": "19.0.4", + "@octokit/graphql": "9.0.3", + "@octokit/graphql-schema": "15.26.1", + "@octokit/rest": "22.0.1", "tunnel": "^0.0.6", - "@vscode/extension-telemetry": "^0.9.0" + "@vscode/extension-telemetry": "^1.2.0" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/github/package.nls.json b/extensions/github/package.nls.json index 5ead7903af20..40271bea980e 100644 --- a/extensions/github/package.nls.json +++ b/extensions/github/package.nls.json @@ -1,9 +1,14 @@ { "displayName": "GitHub", "description": "GitHub features for VS Code", + "command.copyVscodeDevLink": "Copy vscode.dev Link", + "command.publish": "Publish to GitHub", + "command.openOnGitHub": "Open on GitHub", + "command.openOnVscodeDev": "Open in vscode.dev", "config.branchProtection": "Controls whether to query repository rules for GitHub repositories", "config.gitAuthentication": "Controls whether to enable automatic GitHub authentication for git commands within VS Code.", "config.gitProtocol": "Controls which protocol is used to clone a GitHub repository", + "config.showAvatar": "Controls whether to show the GitHub avatar of the commit author in various hovers (ex: Git blame, Timeline, Source Control Graph, etc.)", "welcome.publishFolder": { "message": "You can directly publish this folder to a GitHub repository. Once published, you'll have access to source control features powered by Git and GitHub.\n[$(github) Publish to GitHub](command:github.publish)", "comment": [ diff --git a/extensions/github/src/branchProtection.ts b/extensions/github/src/branchProtection.ts index a7d18c41b9f8..52b4fbfae222 100644 --- a/extensions/github/src/branchProtection.ts +++ b/extensions/github/src/branchProtection.ts @@ -48,7 +48,7 @@ const REPOSITORY_RULESETS_QUERY = ` } `; -export class GithubBranchProtectionProviderManager { +export class GitHubBranchProtectionProviderManager { private readonly disposables = new DisposableStore(); private readonly providerDisposables = new DisposableStore(); @@ -61,7 +61,7 @@ export class GithubBranchProtectionProviderManager { if (enabled) { for (const repository of this.gitAPI.repositories) { - this.providerDisposables.add(this.gitAPI.registerBranchProtectionProvider(repository.rootUri, new GithubBranchProtectionProvider(repository, this.globalState, this.logger, this.telemetryReporter))); + this.providerDisposables.add(this.gitAPI.registerBranchProtectionProvider(repository.rootUri, new GitHubBranchProtectionProvider(repository, this.globalState, this.logger, this.telemetryReporter))); } } else { this.providerDisposables.dispose(); @@ -77,7 +77,7 @@ export class GithubBranchProtectionProviderManager { private readonly telemetryReporter: TelemetryReporter) { this.disposables.add(this.gitAPI.onDidOpenRepository(repository => { if (this._enabled) { - this.providerDisposables.add(gitAPI.registerBranchProtectionProvider(repository.rootUri, new GithubBranchProtectionProvider(repository, this.globalState, this.logger, this.telemetryReporter))); + this.providerDisposables.add(gitAPI.registerBranchProtectionProvider(repository.rootUri, new GitHubBranchProtectionProvider(repository, this.globalState, this.logger, this.telemetryReporter))); } })); @@ -102,7 +102,7 @@ export class GithubBranchProtectionProviderManager { } -export class GithubBranchProtectionProvider implements BranchProtectionProvider { +export class GitHubBranchProtectionProvider implements BranchProtectionProvider { private readonly _onDidChangeBranchProtection = new EventEmitter(); onDidChangeBranchProtection = this._onDidChangeBranchProtection.event; @@ -173,12 +173,12 @@ export class GithubBranchProtectionProvider implements BranchProtectionProvider } // Repository details - this.logger.trace(`Fetching repository details for "${repository.owner}/${repository.repo}".`); + this.logger.trace(`[GitHubBranchProtectionProvider][updateRepositoryBranchProtection] Fetching repository details for "${repository.owner}/${repository.repo}".`); const repositoryDetails = await this.getRepositoryDetails(repository.owner, repository.repo); // Check repository write permission if (repositoryDetails.viewerPermission !== 'ADMIN' && repositoryDetails.viewerPermission !== 'MAINTAIN' && repositoryDetails.viewerPermission !== 'WRITE') { - this.logger.trace(`Skipping branch protection for "${repository.owner}/${repository.repo}" due to missing repository write permission.`); + this.logger.trace(`[GitHubBranchProtectionProvider][updateRepositoryBranchProtection] Skipping branch protection for "${repository.owner}/${repository.repo}" due to missing repository write permission.`); continue; } @@ -201,7 +201,7 @@ export class GithubBranchProtectionProvider implements BranchProtectionProvider // Save branch protection to global state await this.globalState.update(this.globalStateKey, branchProtection); - this.logger.trace(`Branch protection for "${this.repository.rootUri.toString()}": ${JSON.stringify(branchProtection)}.`); + this.logger.trace(`[GitHubBranchProtectionProvider][updateRepositoryBranchProtection] Branch protection for "${this.repository.rootUri.toString()}": ${JSON.stringify(branchProtection)}.`); /* __GDPR__ "branchProtection" : { @@ -211,7 +211,7 @@ export class GithubBranchProtectionProvider implements BranchProtectionProvider */ this.telemetryReporter.sendTelemetryEvent('branchProtection', undefined, { rulesetCount: this.branchProtection.length }); } catch (err) { - this.logger.warn(`Failed to update repository branch protection: ${err.message}`); + this.logger.warn(`[GitHubBranchProtectionProvider][updateRepositoryBranchProtection] Failed to update repository branch protection: ${err.message}`); if (err instanceof AuthenticationError) { // A GitHub authentication session could be missing if the user has not yet diff --git a/extensions/github/src/commands.ts b/extensions/github/src/commands.ts index 1f1504521f83..4e5587c09b53 100644 --- a/extensions/github/src/commands.ts +++ b/extensions/github/src/commands.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { API as GitAPI } from './typings/git'; +import { API as GitAPI, RefType, Repository } from './typings/git'; import { publishRepository } from './publish'; -import { DisposableStore } from './util'; -import { LinkContext, getLink, getVscodeDevHost } from './links'; +import { DisposableStore, getRepositoryFromUrl } from './util'; +import { LinkContext, getCommitLink, getLink, getVscodeDevHost } from './links'; async function copyVscodeDevLink(gitAPI: GitAPI, useSelection: boolean, context: LinkContext, includeRange = true) { try { @@ -34,6 +34,29 @@ async function openVscodeDevLink(gitAPI: GitAPI): Promise { + // Get the unique remotes that contain the commit + const branches = await repository.getBranches({ contains: commit, remote: true }); + const remoteNames = new Set(branches.filter(b => b.type === RefType.RemoteHead && b.remote).map(b => b.remote!)); + + // GitHub remotes that contain the commit + const remotes = repository.state.remotes + .filter(r => remoteNames.has(r.name) && r.fetchUrl && getRepositoryFromUrl(r.fetchUrl)); + + if (remotes.length === 0) { + vscode.window.showInformationMessage(vscode.l10n.t('No GitHub remotes found that contain this commit.')); + return; + } + + // upstream -> origin -> first + const remote = remotes.find(r => r.name === 'upstream') + ?? remotes.find(r => r.name === 'origin') + ?? remotes[0]; + + const link = getCommitLink(remote.fetchUrl!, commit); + vscode.env.openExternal(vscode.Uri.parse(link)); +} + export function registerCommands(gitAPI: GitAPI): vscode.Disposable { const disposables = new DisposableStore(); @@ -57,6 +80,37 @@ export function registerCommands(gitAPI: GitAPI): vscode.Disposable { return copyVscodeDevLink(gitAPI, true, context, false); })); + disposables.add(vscode.commands.registerCommand('github.openOnGitHub', async (url: string, historyItemId: string) => { + const link = getCommitLink(url, historyItemId); + vscode.env.openExternal(vscode.Uri.parse(link)); + })); + + disposables.add(vscode.commands.registerCommand('github.graph.openOnGitHub', async (repository: vscode.SourceControl, historyItem: vscode.SourceControlHistoryItem) => { + if (!repository || !historyItem) { + return; + } + + const apiRepository = gitAPI.repositories.find(r => r.rootUri.fsPath === repository.rootUri?.fsPath); + if (!apiRepository) { + return; + } + + await openOnGitHub(apiRepository, historyItem.id); + })); + + disposables.add(vscode.commands.registerCommand('github.timeline.openOnGitHub', async (item: vscode.TimelineItem, uri: vscode.Uri) => { + if (!item.id || !uri) { + return; + } + + const apiRepository = gitAPI.getRepository(uri); + if (!apiRepository) { + return; + } + + await openOnGitHub(apiRepository, item.id); + })); + disposables.add(vscode.commands.registerCommand('github.openOnVscodeDev', async () => { return openVscodeDevLink(gitAPI); })); diff --git a/extensions/github/src/extension.ts b/extensions/github/src/extension.ts index 1827768312e5..de0349ba9d3a 100644 --- a/extensions/github/src/extension.ts +++ b/extensions/github/src/extension.ts @@ -13,9 +13,10 @@ import { DisposableStore, repositoryHasGitHubRemote } from './util'; import { GithubPushErrorHandler } from './pushErrorHandler'; import { GitBaseExtension } from './typings/git-base'; import { GithubRemoteSourcePublisher } from './remoteSourcePublisher'; -import { GithubBranchProtectionProviderManager } from './branchProtection'; +import { GitHubBranchProtectionProviderManager } from './branchProtection'; import { GitHubCanonicalUriProvider } from './canonicalUriProvider'; import { VscodeDevShareProvider } from './shareProviders'; +import { GitHubSourceControlHistoryItemDetailsProvider } from './historyItemDetailsProvider'; export function activate(context: ExtensionContext): void { const disposables: Disposable[] = []; @@ -97,9 +98,10 @@ function initializeGitExtension(context: ExtensionContext, telemetryReporter: Te disposables.add(registerCommands(gitAPI)); disposables.add(new GithubCredentialProviderManager(gitAPI)); - disposables.add(new GithubBranchProtectionProviderManager(gitAPI, context.globalState, logger, telemetryReporter)); + disposables.add(new GitHubBranchProtectionProviderManager(gitAPI, context.globalState, logger, telemetryReporter)); disposables.add(gitAPI.registerPushErrorHandler(new GithubPushErrorHandler(telemetryReporter))); disposables.add(gitAPI.registerRemoteSourcePublisher(new GithubRemoteSourcePublisher(gitAPI))); + disposables.add(gitAPI.registerSourceControlHistoryItemDetailsProvider(new GitHubSourceControlHistoryItemDetailsProvider(gitAPI, logger))); disposables.add(new GitHubCanonicalUriProvider(gitAPI)); disposables.add(new VscodeDevShareProvider(gitAPI)); setGitHubContext(gitAPI, disposables); diff --git a/extensions/github/src/historyItemDetailsProvider.ts b/extensions/github/src/historyItemDetailsProvider.ts new file mode 100644 index 000000000000..b31126e2ada2 --- /dev/null +++ b/extensions/github/src/historyItemDetailsProvider.ts @@ -0,0 +1,332 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { authentication, Command, l10n, LogOutputChannel, workspace } from 'vscode'; +import { Commit, Repository as GitHubRepository, Maybe } from '@octokit/graphql-schema'; +import { API, AvatarQuery, AvatarQueryCommit, Repository, SourceControlHistoryItemDetailsProvider } from './typings/git'; +import { DisposableStore, getRepositoryDefaultRemote, getRepositoryDefaultRemoteUrl, getRepositoryFromUrl, groupBy, sequentialize } from './util'; +import { AuthenticationError, getOctokitGraphql } from './auth'; +import { getAvatarLink } from './links'; + +const ISSUE_EXPRESSION = /(([A-Za-z0-9_.\-]+)\/([A-Za-z0-9_.\-]+))?(#|GH-)([1-9][0-9]*)($|\b)/g; + +const ASSIGNABLE_USERS_QUERY = ` + query assignableUsers($owner: String!, $repo: String!) { + repository(owner: $owner, name: $repo) { + assignableUsers(first: 100) { + nodes { + id + login + name + email + avatarUrl + } + } + } + } +`; + +const COMMIT_AUTHOR_QUERY = ` + query commitAuthor($owner: String!, $repo: String!, $commit: String!) { + repository(owner: $owner, name: $repo) { + object(expression: $commit) { + ... on Commit { + author { + name + email + avatarUrl + user { + id + login + } + } + } + } + } + } +`; + +interface GitHubRepositoryStore { + readonly users: GitHubUser[]; + readonly commits: Set; +} + +interface GitHubUser { + readonly id: string; + readonly login: string; + readonly name?: Maybe; + readonly email: string; + readonly avatarUrl: string; +} + +function getUserIdFromNoReplyEmail(email: string | undefined): string | undefined { + const match = email?.match(/^([0-9]+)\+[^@]+@users\.noreply\.github\.com$/); + return match?.[1]; +} + +function compareAvatarQuery(a: AvatarQueryCommit, b: AvatarQueryCommit): number { + // Email + const emailComparison = (a.authorEmail ?? '').localeCompare(b.authorEmail ?? ''); + if (emailComparison !== 0) { + return emailComparison; + } + + // Name + return (a.authorName ?? '').localeCompare(b.authorName ?? ''); +} + +export class GitHubSourceControlHistoryItemDetailsProvider implements SourceControlHistoryItemDetailsProvider { + private _isUserAuthenticated = true; + private readonly _store = new Map(); + private readonly _disposables = new DisposableStore(); + + constructor(private readonly _gitAPI: API, private readonly _logger: LogOutputChannel) { + this._disposables.add(this._gitAPI.onDidCloseRepository(repository => this._onDidCloseRepository(repository))); + + this._disposables.add(authentication.onDidChangeSessions(e => { + if (e.provider.id === 'github') { + this._isUserAuthenticated = true; + } + })); + + this._disposables.add(workspace.onDidChangeConfiguration(e => { + if (!e.affectsConfiguration('github.showAvatar')) { + return; + } + + this._store.clear(); + })); + } + + async provideAvatar(repository: Repository, query: AvatarQuery): Promise | undefined> { + this._logger.trace(`[GitHubSourceControlHistoryItemDetailsProvider][provideAvatar] Avatar resolution for ${query.commits.length} commit(s) in ${repository.rootUri.fsPath}.`); + + const config = workspace.getConfiguration('github', repository.rootUri); + const showAvatar = config.get('showAvatar', true) === true; + + if (!this._isUserAuthenticated || !showAvatar) { + this._logger.trace(`[GitHubSourceControlHistoryItemDetailsProvider][provideAvatar] Avatar resolution is disabled. (${showAvatar === false ? 'setting' : 'auth'})`); + return undefined; + } + + const descriptor = getRepositoryDefaultRemote(repository); + if (!descriptor) { + this._logger.trace(`[GitHubSourceControlHistoryItemDetailsProvider][provideAvatar] Repository does not have a GitHub remote.`); + return undefined; + } + + + try { + const logs = { cached: 0, email: 0, github: 0, incomplete: 0 }; + + // Warm up the in-memory cache with the first page + // (100 users) from this list of assignable users + await this._loadAssignableUsers(descriptor); + + const repositoryStore = this._store.get(this._getRepositoryKey(descriptor)); + if (!repositoryStore) { + return undefined; + } + + // Group the query by author + const authorQuery = groupBy(query.commits, compareAvatarQuery); + + const results = new Map(); + await Promise.all(authorQuery.map(async commits => { + if (commits.length === 0) { + return; + } + + // Query the in-memory cache for the user + const avatarUrl = repositoryStore.users.find( + user => user.email === commits[0].authorEmail || user.name === commits[0].authorName)?.avatarUrl; + + // Cache hit + if (avatarUrl) { + // Add avatar for each commit + logs.cached += commits.length; + commits.forEach(({ hash }) => results.set(hash, `${avatarUrl}&s=${query.size}`)); + return; + } + + // Check if any of the commit are being tracked in the list + // of known commits that have incomplte author information + if (commits.some(({ hash }) => repositoryStore.commits.has(hash))) { + commits.forEach(({ hash }) => results.set(hash, undefined)); + return; + } + + // Try to extract the user identifier from GitHub no-reply emails + const userIdFromEmail = getUserIdFromNoReplyEmail(commits[0].authorEmail); + if (userIdFromEmail) { + logs.email += commits.length; + const avatarUrl = getAvatarLink(userIdFromEmail, query.size); + commits.forEach(({ hash }) => results.set(hash, avatarUrl)); + return; + } + + // Get the commit details + const commitAuthor = await this._getCommitAuthor(descriptor, commits[0].hash); + if (!commitAuthor) { + // The commit has incomplete author information, so + // we should not try to query the authors details again + logs.incomplete += commits.length; + for (const { hash } of commits) { + repositoryStore.commits.add(hash); + results.set(hash, undefined); + } + return; + } + + // Save the user to the cache + repositoryStore.users.push(commitAuthor); + + // Add avatar for each commit + logs.github += commits.length; + commits.forEach(({ hash }) => results.set(hash, `${commitAuthor.avatarUrl}&s=${query.size}`)); + })); + + this._logger.trace(`[GitHubSourceControlHistoryItemDetailsProvider][provideAvatar] Avatar resolution for ${query.commits.length} commit(s) in ${repository.rootUri.fsPath} complete: ${JSON.stringify(logs)}.`); + + return results; + } catch (err) { + // A GitHub authentication session could be missing if the user has not yet + // signed in with their GitHub account or they have signed out. Disable the + // avatar resolution until the user signes in with their GitHub account. + if (err instanceof AuthenticationError) { + this._isUserAuthenticated = false; + } + + return undefined; + } + } + + async provideHoverCommands(repository: Repository): Promise { + const url = getRepositoryDefaultRemoteUrl(repository); + if (!url) { + return undefined; + } + + return [{ + title: l10n.t('{0} Open on GitHub', '$(github)'), + tooltip: l10n.t('Open on GitHub'), + command: 'github.openOnGitHub', + arguments: [url] + }]; + } + + async provideMessageLinks(repository: Repository, message: string): Promise { + const descriptor = getRepositoryDefaultRemote(repository); + if (!descriptor) { + return undefined; + } + + return message.replace( + ISSUE_EXPRESSION, + (match, _group1, owner: string | undefined, repo: string | undefined, _group2, number: string | undefined) => { + if (!number || Number.isNaN(parseInt(number))) { + return match; + } + + const label = owner && repo + ? `${owner}/${repo}#${number}` + : `#${number}`; + + owner = owner ?? descriptor.owner; + repo = repo ?? descriptor.repo; + + return `[${label}](https://github.com/${owner}/${repo}/issues/${number})`; + }); + } + + private _onDidCloseRepository(repository: Repository) { + for (const remote of repository.state.remotes) { + if (!remote.fetchUrl) { + continue; + } + + const repository = getRepositoryFromUrl(remote.fetchUrl); + if (!repository) { + continue; + } + + this._store.delete(this._getRepositoryKey(repository)); + } + } + + @sequentialize + private async _loadAssignableUsers(descriptor: { owner: string; repo: string }): Promise { + if (this._store.has(this._getRepositoryKey(descriptor))) { + return; + } + + this._logger.trace(`[GitHubSourceControlHistoryItemDetailsProvider][_loadAssignableUsers] Querying assignable user(s) for ${descriptor.owner}/${descriptor.repo}.`); + + try { + const graphql = await getOctokitGraphql(); + const { repository } = await graphql<{ repository: GitHubRepository }>(ASSIGNABLE_USERS_QUERY, descriptor); + + const users: GitHubUser[] = []; + for (const node of repository.assignableUsers.nodes ?? []) { + if (!node) { + continue; + } + + users.push({ + id: node.id, + login: node.login, + name: node.name, + email: node.email, + avatarUrl: node.avatarUrl, + } satisfies GitHubUser); + } + + this._store.set(this._getRepositoryKey(descriptor), { users, commits: new Set() }); + this._logger.trace(`[GitHubSourceControlHistoryItemDetailsProvider][_loadAssignableUsers] Successfully queried assignable user(s) for ${descriptor.owner}/${descriptor.repo}: ${users.length} user(s).`); + } catch (err) { + this._logger.warn(`[GitHubSourceControlHistoryItemDetailsProvider][_loadAssignableUsers] Failed to load assignable user(s) for ${descriptor.owner}/${descriptor.repo}: ${err}`); + throw err; + } + } + + private async _getCommitAuthor(descriptor: { owner: string; repo: string }, commit: string): Promise { + this._logger.trace(`[GitHubSourceControlHistoryItemDetailsProvider][_getCommitAuthor] Querying commit author for ${descriptor.owner}/${descriptor.repo}/${commit}.`); + + try { + const graphql = await getOctokitGraphql(); + const { repository } = await graphql<{ repository: GitHubRepository }>(COMMIT_AUTHOR_QUERY, { ...descriptor, commit }); + + const commitAuthor = (repository.object as Commit).author; + if (!commitAuthor?.user?.id || !commitAuthor.user?.login || + !commitAuthor?.name || !commitAuthor?.email || !commitAuthor?.avatarUrl) { + this._logger.info(`[GitHubSourceControlHistoryItemDetailsProvider][_getCommitAuthor] Incomplete commit author for ${descriptor.owner}/${descriptor.repo}/${commit}: ${JSON.stringify(repository.object)}`); + + return undefined; + } + + const user = { + id: commitAuthor.user.id, + login: commitAuthor.user.login, + name: commitAuthor.name, + email: commitAuthor.email, + avatarUrl: commitAuthor.avatarUrl, + } satisfies GitHubUser; + + this._logger.trace(`[GitHubSourceControlHistoryItemDetailsProvider][_getCommitAuthor] Successfully queried commit author for ${descriptor.owner}/${descriptor.repo}/${commit}: ${user.login}.`); + return user; + } catch (err) { + this._logger.warn(`[GitHubSourceControlHistoryItemDetailsProvider][_getCommitAuthor] Failed to get commit author for ${descriptor.owner}/${descriptor.repo}/${commit}: ${err}`); + throw err; + } + } + + private _getRepositoryKey(descriptor: { owner: string; repo: string }): string { + return `${descriptor.owner}/${descriptor.repo}`; + } + + dispose(): void { + this._disposables.dispose(); + } +} diff --git a/extensions/github/src/links.ts b/extensions/github/src/links.ts index 911f0e5376bf..fdcac0c5cfdc 100644 --- a/extensions/github/src/links.ts +++ b/extensions/github/src/links.ts @@ -176,6 +176,10 @@ export async function getLink(gitAPI: GitAPI, useSelection: boolean, shouldEnsur return `${uriWithoutFileSegments}${fileSegments}`; } +export function getAvatarLink(userId: string, size: number): string { + return `https://avatars.githubusercontent.com/u/${userId}?s=${size}`; +} + export function getBranchLink(url: string, branch: string, hostPrefix: string = 'https://github.com') { const repo = getRepositoryFromUrl(url); if (!repo) { @@ -186,6 +190,15 @@ export function getBranchLink(url: string, branch: string, hostPrefix: string = return `${hostPrefix}/${repo.owner}/${repo.repo}/tree/${branch}`; } +export function getCommitLink(url: string, hash: string, hostPrefix: string = 'https://github.com') { + const repo = getRepositoryFromUrl(url); + if (!repo) { + throw new Error('Invalid repository URL provided'); + } + + return `${hostPrefix}/${repo.owner}/${repo.repo}/commit/${hash}`; +} + export function getVscodeDevHost(): string { return `https://${vscode.env.appName.toLowerCase().includes('insiders') ? 'insiders.' : ''}vscode.dev/github`; } diff --git a/extensions/github/src/typings/git-base.d.ts b/extensions/github/src/typings/git-base.d.ts index 53cac4d5c70f..d4ec49df47dc 100644 --- a/extensions/github/src/typings/git-base.d.ts +++ b/extensions/github/src/typings/git-base.d.ts @@ -3,11 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, Event, ProviderResult, Uri } from 'vscode'; +import { Command, Disposable, Event, ProviderResult } from 'vscode'; export { ProviderResult } from 'vscode'; export interface API { registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable; + getRemoteSourceActions(url: string): Promise; pickRemoteSource(options: PickRemoteSourceOptions): Promise; } diff --git a/extensions/github/src/typings/git.d.ts b/extensions/github/src/typings/git.d.ts index 7ac67937a47b..e600b767c7cd 100644 --- a/extensions/github/src/typings/git.d.ts +++ b/extensions/github/src/typings/git.d.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Uri, Event, Disposable, ProviderResult, Command } from 'vscode'; +import { Uri, Event, Disposable, ProviderResult, Command, SourceControlHistoryItem } from 'vscode'; export { ProviderResult } from 'vscode'; export interface Git { @@ -289,6 +289,23 @@ export interface BranchProtectionProvider { provideBranchProtection(): BranchProtection[]; } +export interface AvatarQueryCommit { + readonly hash: string; + readonly authorName?: string; + readonly authorEmail?: string; +} + +export interface AvatarQuery { + readonly commits: AvatarQueryCommit[]; + readonly size: number; +} + +export interface SourceControlHistoryItemDetailsProvider { + provideAvatar(repository: Repository, query: AvatarQuery): Promise | undefined>; + provideHoverCommands(repository: Repository): Promise; + provideMessageLinks(repository: Repository, message: string): Promise; +} + export type APIState = 'uninitialized' | 'initialized'; export interface PublishEvent { @@ -316,6 +333,7 @@ export interface API { registerPostCommitCommandsProvider(provider: PostCommitCommandsProvider): Disposable; registerPushErrorHandler(handler: PushErrorHandler): Disposable; registerBranchProtectionProvider(root: Uri, provider: BranchProtectionProvider): Disposable; + registerSourceControlHistoryItemDetailsProvider(provider: SourceControlHistoryItemDetailsProvider): Disposable; } export interface GitExtension { diff --git a/extensions/github/src/typings/vscode.proposed.canonicalUriProvider.d.ts b/extensions/github/src/typings/vscode.proposed.canonicalUriProvider.d.ts deleted file mode 100644 index 84ee599797d9..000000000000 --- a/extensions/github/src/typings/vscode.proposed.canonicalUriProvider.d.ts +++ /dev/null @@ -1,47 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'vscode' { - - // https://github.com/microsoft/vscode/issues/180582 - - export namespace workspace { - /** - * - * @param scheme The URI scheme that this provider can provide canonical URIs for. - * A canonical URI represents the conversion of a resource's alias into a source of truth URI. - * Multiple aliases may convert to the same source of truth URI. - * @param provider A provider which can convert URIs of scheme @param scheme to - * a canonical URI which is stable across machines. - */ - export function registerCanonicalUriProvider(scheme: string, provider: CanonicalUriProvider): Disposable; - - /** - * - * @param uri The URI to provide a canonical URI for. - * @param token A cancellation token for the request. - */ - export function getCanonicalUri(uri: Uri, options: CanonicalUriRequestOptions, token: CancellationToken): ProviderResult; - } - - export interface CanonicalUriProvider { - /** - * - * @param uri The URI to provide a canonical URI for. - * @param options Options that the provider should honor in the URI it returns. - * @param token A cancellation token for the request. - * @returns The canonical URI for the requested URI or undefined if no canonical URI can be provided. - */ - provideCanonicalUri(uri: Uri, options: CanonicalUriRequestOptions, token: CancellationToken): ProviderResult; - } - - export interface CanonicalUriRequestOptions { - /** - * - * The desired scheme of the canonical URI. - */ - targetScheme: string; - } -} diff --git a/extensions/github/src/typings/vscode.proposed.shareProvider.d.ts b/extensions/github/src/typings/vscode.proposed.shareProvider.d.ts deleted file mode 100644 index 6470557cac1c..000000000000 --- a/extensions/github/src/typings/vscode.proposed.shareProvider.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -// https://github.com/microsoft/vscode/issues/176316 - -declare module 'vscode' { - export interface TreeItem { - shareableItem?: ShareableItem; - } - - export interface ShareableItem { - resourceUri: Uri; - selection?: Range; - } - - export interface ShareProvider { - readonly id: string; - readonly label: string; - readonly priority: number; - - provideShare(item: ShareableItem, token: CancellationToken): ProviderResult; - } - - export namespace window { - export function registerShareProvider(selector: DocumentSelector, provider: ShareProvider): Disposable; - } -} diff --git a/extensions/github/src/util.ts b/extensions/github/src/util.ts index 3d8bf4a40bef..4c8a032405d9 100644 --- a/extensions/github/src/util.ts +++ b/extensions/github/src/util.ts @@ -23,6 +23,54 @@ export class DisposableStore { } } +function decorate(decorator: (fn: Function, key: string) => Function): Function { + return (_target: any, key: string, descriptor: any) => { + let fnKey: string | null = null; + let fn: Function | null = null; + + if (typeof descriptor.value === 'function') { + fnKey = 'value'; + fn = descriptor.value; + } else if (typeof descriptor.get === 'function') { + fnKey = 'get'; + fn = descriptor.get; + } + + if (!fn || !fnKey) { + throw new Error('not supported'); + } + + descriptor[fnKey] = decorator(fn, key); + }; +} + +function _sequentialize(fn: Function, key: string): Function { + const currentKey = `__$sequence$${key}`; + + return function (this: any, ...args: any[]) { + const currentPromise = this[currentKey] as Promise || Promise.resolve(null); + const run = async () => await fn.apply(this, args); + this[currentKey] = currentPromise.then(run, run); + return this[currentKey]; + }; +} + +export const sequentialize = decorate(_sequentialize); + +export function groupBy(data: ReadonlyArray, compare: (a: T, b: T) => number): T[][] { + const result: T[][] = []; + let currentGroup: T[] | undefined = undefined; + for (const element of data.slice(0).sort(compare)) { + if (!currentGroup || compare(currentGroup[0], element) !== 0) { + currentGroup = [element]; + result.push(currentGroup); + } else { + currentGroup.push(element); + } + } + return result; +} + export function getRepositoryFromUrl(url: string): { owner: string; repo: string } | undefined { const match = /^https:\/\/github\.com\/([^/]+)\/([^/]+?)(\.git)?$/i.exec(url) || /^git@github\.com:([^/]+)\/([^/]+?)(\.git)?$/i.exec(url); @@ -37,3 +85,24 @@ export function getRepositoryFromQuery(query: string): { owner: string; repo: st export function repositoryHasGitHubRemote(repository: Repository) { return !!repository.state.remotes.find(remote => remote.fetchUrl ? getRepositoryFromUrl(remote.fetchUrl) : undefined); } + +export function getRepositoryDefaultRemoteUrl(repository: Repository): string | undefined { + const remotes = repository.state.remotes + .filter(remote => remote.fetchUrl && getRepositoryFromUrl(remote.fetchUrl)); + + if (remotes.length === 0) { + return undefined; + } + + // upstream -> origin -> first + const remote = remotes.find(remote => remote.name === 'upstream') + ?? remotes.find(remote => remote.name === 'origin') + ?? remotes[0]; + + return remote.fetchUrl; +} + +export function getRepositoryDefaultRemote(repository: Repository): { owner: string; repo: string } | undefined { + const fetchUrl = getRepositoryDefaultRemoteUrl(repository); + return fetchUrl ? getRepositoryFromUrl(fetchUrl) : undefined; +} diff --git a/extensions/github/tsconfig.json b/extensions/github/tsconfig.json index d7aed1836eed..8435c0d09e88 100644 --- a/extensions/github/tsconfig.json +++ b/extensions/github/tsconfig.json @@ -9,6 +9,10 @@ }, "include": [ "src/**/*", - "../../src/vscode-dts/vscode.d.ts" + "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.canonicalUriProvider.d.ts", + "../../src/vscode-dts/vscode.proposed.scmHistoryProvider.d.ts", + "../../src/vscode-dts/vscode.proposed.shareProvider.d.ts", + "../../src/vscode-dts/vscode.proposed.timeline.d.ts" ] } diff --git a/extensions/go/cgmanifest.json b/extensions/go/cgmanifest.json index ae7228fa1aea..a6dbd5d1bf01 100644 --- a/extensions/go/cgmanifest.json +++ b/extensions/go/cgmanifest.json @@ -6,12 +6,12 @@ "git": { "name": "go-syntax", "repositoryUrl": "https://github.com/worlpaker/go-syntax", - "commitHash": "ce0d1d19653dc264d8e53f97cb45cc8d1689f221" + "commitHash": "fbdaec061157e98dda185c0ce771ce6a2c793045" } }, "license": "MIT", "description": "The file syntaxes/go.tmLanguage.json is from https://github.com/worlpaker/go-syntax, which in turn was derived from https://github.com/jeff-hykin/better-go-syntax.", - "version": "0.7.7" + "version": "0.7.9" } ], "version": 1 diff --git a/extensions/go/language-configuration.json b/extensions/go/language-configuration.json index a5e06a56bad1..9238bf3529b0 100644 --- a/extensions/go/language-configuration.json +++ b/extensions/go/language-configuration.json @@ -1,28 +1,86 @@ { "comments": { "lineComment": "//", - "blockComment": [ "/*", "*/" ] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - { "open": "`", "close": "`", "notIn": ["string"]}, - { "open": "\"", "close": "\"", "notIn": ["string"]}, - { "open": "'", "close": "'", "notIn": ["string", "comment"]} + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + { + "open": "`", + "close": "`", + "notIn": [ + "string" + ] + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string", + "comment" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"], - ["`", "`"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "'", + "'" + ], + [ + "`", + "`" + ] ], "indentationRules": { "increaseIndentPattern": "^.*(\\bcase\\b.*:|\\bdefault\\b:|(\\b(func|if|else|switch|select|for|struct)\\b.*)?{[^}\"'`]*|\\([^)\"'`]*)$", @@ -33,5 +91,20 @@ "start": "^\\s*//\\s*#?region\\b", "end": "^\\s*//\\s*#?endregion\\b" } - } -} \ No newline at end of file + }, + "onEnterRules": [ + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, + ] +} diff --git a/extensions/go/syntaxes/go.tmLanguage.json b/extensions/go/syntaxes/go.tmLanguage.json index 8ae31fdbe31c..db17cad3f911 100644 --- a/extensions/go/syntaxes/go.tmLanguage.json +++ b/extensions/go/syntaxes/go.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/worlpaker/go-syntax/commit/ce0d1d19653dc264d8e53f97cb45cc8d1689f221", + "version": "https://github.com/worlpaker/go-syntax/commit/fbdaec061157e98dda185c0ce771ce6a2c793045", "name": "Go", "scopeName": "source.go", "patterns": [ @@ -97,7 +97,10 @@ "comment": "all statements related to variables", "patterns": [ { - "include": "#var_const_assignment" + "include": "#const_assignment" + }, + { + "include": "#var_assignment" }, { "include": "#variable_assignment" @@ -1370,7 +1373,53 @@ } }, { - "include": "#parameter-variable-types" + "begin": "(?:([\\w\\.\\*]+)?(\\[))", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#type-declarations" + }, + { + "match": "(?:\\w+)", + "name": "entity.name.type.go" + } + ] + }, + "2": { + "name": "punctuation.definition.begin.bracket.square.go" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "punctuation.definition.end.bracket.square.go" + } + }, + "patterns": [ + { + "include": "#generic_param_types" + } + ] + }, + { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.definition.begin.bracket.round.go" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.end.bracket.round.go" + } + }, + "patterns": [ + { + "include": "#function_param_types" + } + ] }, { "comment": "other types", @@ -1483,7 +1532,53 @@ } }, { - "include": "#parameter-variable-types" + "begin": "(?:([\\w\\.\\*]+)?(\\[))", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#type-declarations" + }, + { + "match": "(?:\\w+)", + "name": "entity.name.type.go" + } + ] + }, + "2": { + "name": "punctuation.definition.begin.bracket.square.go" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "punctuation.definition.end.bracket.square.go" + } + }, + "patterns": [ + { + "include": "#generic_param_types" + } + ] + }, + { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.definition.begin.bracket.round.go" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.end.bracket.round.go" + } + }, + "patterns": [ + { + "include": "#function_param_types" + } + ] }, { "comment": "other types", @@ -2553,12 +2648,12 @@ } ] }, - "var_const_assignment": { - "comment": "variable assignment with var and const keyword", + "var_assignment": { + "comment": "variable assignment with var keyword", "patterns": [ { - "comment": "var and const with single type assignment", - "match": "(?:(?<=\\bvar\\b|\\bconst\\b)(?:\\s*)(\\b[\\w\\.]+(?:\\,\\s*[\\w\\.]+)*)(?:\\s*)((?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+(?:\\([^\\)]+\\))?)?(?!(?:[\\[\\]\\*]+)?\\b(?:struct|func|map)\\b)(?:[\\w\\.\\[\\]\\*]+(?:\\,\\s*[\\w\\.\\[\\]\\*]+)*)?(?:\\s*)(?:\\=)?)?)", + "comment": "single assignment", + "match": "(?:(?<=\\bvar\\b)(?:\\s*)(\\b[\\w\\.]+(?:\\,\\s*[\\w\\.]+)*)(?:\\s*)((?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+(?:\\([^\\)]+\\))?)?(?!(?:[\\[\\]\\*]+)?\\b(?:struct|func|map)\\b)(?:[\\w\\.\\[\\]\\*]+(?:\\,\\s*[\\w\\.\\[\\]\\*]+)*)?(?:\\s*)(?:\\=)?)?)", "captures": { "1": { "patterns": [ @@ -2604,8 +2699,8 @@ } }, { - "comment": "var and const with multi type assignment", - "begin": "(?:(?<=\\bvar\\b|\\bconst\\b)(?:\\s*)(\\())", + "comment": "multi assignment", + "begin": "(?:(?<=\\bvar\\b)(?:\\s*)(\\())", "beginCaptures": { "1": { "name": "punctuation.definition.begin.bracket.round.go" @@ -2671,6 +2766,124 @@ } ] }, + "const_assignment": { + "comment": "constant assignment with const keyword", + "patterns": [ + { + "comment": "single assignment", + "match": "(?:(?<=\\bconst\\b)(?:\\s*)(\\b[\\w\\.]+(?:\\,\\s*[\\w\\.]+)*)(?:\\s*)((?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+(?:\\([^\\)]+\\))?)?(?!(?:[\\[\\]\\*]+)?\\b(?:struct|func|map)\\b)(?:[\\w\\.\\[\\]\\*]+(?:\\,\\s*[\\w\\.\\[\\]\\*]+)*)?(?:\\s*)(?:\\=)?)?)", + "captures": { + "1": { + "patterns": [ + { + "include": "#delimiters" + }, + { + "match": "\\w+", + "name": "variable.other.constant.go" + } + ] + }, + "2": { + "patterns": [ + { + "include": "#type-declarations-without-brackets" + }, + { + "include": "#generic_types" + }, + { + "match": "\\(", + "name": "punctuation.definition.begin.bracket.round.go" + }, + { + "match": "\\)", + "name": "punctuation.definition.end.bracket.round.go" + }, + { + "match": "\\[", + "name": "punctuation.definition.begin.bracket.square.go" + }, + { + "match": "\\]", + "name": "punctuation.definition.end.bracket.square.go" + }, + { + "match": "\\w+", + "name": "entity.name.type.go" + } + ] + } + } + }, + { + "comment": "multi assignment", + "begin": "(?:(?<=\\bconst\\b)(?:\\s*)(\\())", + "beginCaptures": { + "1": { + "name": "punctuation.definition.begin.bracket.round.go" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.end.bracket.round.go" + } + }, + "patterns": [ + { + "match": "(?:(?:^\\s*)(\\b[\\w\\.]+(?:\\,\\s*[\\w\\.]+)*)(?:\\s*)((?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+(?:\\([^\\)]+\\))?)?(?!(?:[\\[\\]\\*]+)?\\b(?:struct|func|map)\\b)(?:[\\w\\.\\[\\]\\*]+(?:\\,\\s*[\\w\\.\\[\\]\\*]+)*)?(?:\\s*)(?:\\=)?)?)", + "captures": { + "1": { + "patterns": [ + { + "include": "#delimiters" + }, + { + "match": "\\w+", + "name": "variable.other.constant.go" + } + ] + }, + "2": { + "patterns": [ + { + "include": "#type-declarations-without-brackets" + }, + { + "include": "#generic_types" + }, + { + "match": "\\(", + "name": "punctuation.definition.begin.bracket.round.go" + }, + { + "match": "\\)", + "name": "punctuation.definition.end.bracket.round.go" + }, + { + "match": "\\[", + "name": "punctuation.definition.begin.bracket.square.go" + }, + { + "match": "\\]", + "name": "punctuation.definition.end.bracket.square.go" + }, + { + "match": "\\w+", + "name": "entity.name.type.go" + } + ] + } + } + }, + { + "include": "$self" + } + ] + } + ] + }, "variable_assignment": { "comment": "variable assignment", "patterns": [ diff --git a/extensions/groovy/language-configuration.json b/extensions/groovy/language-configuration.json index a81a8864a512..39e5fd4092c0 100644 --- a/extensions/groovy/language-configuration.json +++ b/extensions/groovy/language-configuration.json @@ -1,25 +1,88 @@ { "comments": { "lineComment": "//", - "blockComment": [ "/*", "*/" ] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - { "open": "\"", "close": "\"", "notIn": ["string"] }, - { "open": "'", "close": "'", "notIn": ["string"] } + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "'", + "'" + ] + ], + "onEnterRules": [ + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, ] } diff --git a/extensions/grunt/package-lock.json b/extensions/grunt/package-lock.json index f0431ef8c852..9762d1fc3e98 100644 --- a/extensions/grunt/package-lock.json +++ b/extensions/grunt/package-lock.json @@ -9,26 +9,28 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "*" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/grunt/package.json b/extensions/grunt/package.json index ae533cc0e47f..2c54ec24feb8 100644 --- a/extensions/grunt/package.json +++ b/extensions/grunt/package.json @@ -19,7 +19,7 @@ }, "dependencies": {}, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "main": "./out/main", "activationEvents": [ diff --git a/extensions/gulp/package-lock.json b/extensions/gulp/package-lock.json index 9311609f962c..9703ac99541e 100644 --- a/extensions/gulp/package-lock.json +++ b/extensions/gulp/package-lock.json @@ -9,26 +9,28 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "*" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/gulp/package.json b/extensions/gulp/package.json index 0c19b6884778..b1f5beff55fb 100644 --- a/extensions/gulp/package.json +++ b/extensions/gulp/package.json @@ -18,7 +18,7 @@ }, "dependencies": {}, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "main": "./out/main", "activationEvents": [ diff --git a/extensions/html-language-features/cgmanifest.json b/extensions/html-language-features/cgmanifest.json index 7f57d61af72f..8a0745255f7b 100644 --- a/extensions/html-language-features/cgmanifest.json +++ b/extensions/html-language-features/cgmanifest.json @@ -107,7 +107,7 @@ "", "END OF TERMS AND CONDITIONS" ], - "license": "Apache2", + "license": "Apache-2.0", "version": "1.2.4" } ], diff --git a/extensions/html-language-features/package-lock.json b/extensions/html-language-features/package-lock.json index 52fbd6326671..8eb22bbe9ffc 100644 --- a/extensions/html-language-features/package-lock.json +++ b/extensions/html-language-features/package-lock.json @@ -9,198 +9,206 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", - "vscode-languageclient": "^10.0.0-next.13", - "vscode-uri": "^3.0.8" + "@vscode/extension-telemetry": "^1.2.0", + "vscode-languageclient": "^10.0.0-next.19", + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.77.0" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.2.0.tgz", + "integrity": "sha512-En6dTwfy5NFzSMibvOpx/lKq2jtgWuR4++KJbi3SpQ2iT8gm+PHo9868/scocW122KDwTxl4ruxZ7i4rHmJJnQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" } }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c= sha512-9Y0g0Q8rmSt+H33DfKv7FOc3v+iRI+o1lbzt8jGcIosYW37IIW/2XVYq5NPdmaD5NQ59Nk26Kl/vZbwW9Fr8vg==" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -209,55 +217,56 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.6.tgz", - "integrity": "sha512-KCSvUNsFiVciG9iqjJKBZOd66CN3ZKohDlYRmoOi+pd8l15MFLZ8wRG4c+wuzePGba/8WcCG2TM+C/GVlvuaeA==", + "version": "9.0.0-next.11", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.11.tgz", + "integrity": "sha512-u6LElQNbSiE9OugEEmrUKwH6+8BpPz2S5MDHvQUqHL//I4Q8GPikKLOUf856UnbLkZdhxaPrExac1lA3XwpIPA==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageclient": { - "version": "10.0.0-next.13", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.13.tgz", - "integrity": "sha512-KLsOMJoYpkk36PIgcOjyZ4AekOfzp4kdWdRRbVKeVvSIrwrn/4RSZr0NlD6EvUBBJSsJW4WDrYY7Y3znkqa6+w==", + "version": "10.0.0-next.19", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.19.tgz", + "integrity": "sha512-sJtO8y0Dxs4ue/DK0QgO/ATBfZwQdee3TqvCsoqUej/GZrBA01DTf4pbfswRxHsTxN2yH0haImUnMafWHtE4CQ==", + "license": "MIT", "dependencies": { - "minimatch": "^9.0.3", - "semver": "^7.6.0", - "vscode-languageserver-protocol": "3.17.6-next.11" + "minimatch": "^10.0.3", + "semver": "^7.7.1", + "vscode-languageserver-protocol": "3.17.6-next.16" }, "engines": { "vscode": "^1.91.0" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.11.tgz", - "integrity": "sha512-GeJxEp1TiLsp79f8WG5n10wLViXfgFKb99hU9K8m7KDWM95/QFEqWkm79f9LVm54tUK74I91a9EeiQLCS/FABQ==", + "version": "3.17.6-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.16.tgz", + "integrity": "sha512-kQTjXEuyxMbdmmZ3U+Lib3oUl12xEKNc73RtWxPSDS3TFtjVwt98Q1CUzfDA9EUpsA24M46Bl6q3sLe9AUOKyw==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" + "vscode-jsonrpc": "9.0.0-next.11", + "vscode-languageserver-types": "3.17.6-next.6" } }, "node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.5.tgz", - "integrity": "sha512-QFmf3Yl1tCgUQfA77N9Me/LXldJXkIVypQbty2rJ1DNHQkC+iwvm4Z2tXg9czSwlhvv0pD4pbF5mT7WhAglolw==" + "version": "3.17.6-next.6", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.6.tgz", + "integrity": "sha512-aiJY5/yW+xzw7KPNlwi3gQtddq/3EIn5z8X8nCgJfaiAij2R1APKePngv+MUdLdYJBVTLu+Qa0ODsT+pHgYguQ==", + "license": "MIT" }, "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" } } } diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 60b9718e6c38..8fd3626c57b0 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -258,12 +258,12 @@ ] }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", - "vscode-languageclient": "^10.0.0-next.13", - "vscode-uri": "^3.0.8" + "@vscode/extension-telemetry": "^1.2.0", + "vscode-languageclient": "^10.0.0-next.19", + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/html-language-features/server/package-lock.json b/extensions/html-language-features/server/package-lock.json index c0b4c6bd8e84..4d7c697e1f51 100644 --- a/extensions/html-language-features/server/package-lock.json +++ b/extensions/html-language-features/server/package-lock.json @@ -10,33 +10,35 @@ "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", - "vscode-css-languageservice": "^6.3.1", - "vscode-html-languageservice": "^5.3.1", - "vscode-languageserver": "^10.0.0-next.11", + "vscode-css-languageservice": "^6.3.9", + "vscode-html-languageservice": "^5.6.1", + "vscode-languageserver": "^10.0.0-next.16", "vscode-languageserver-textdocument": "^1.0.12", "vscode-uri": "^3.0.8" }, "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x" + "@types/mocha": "^10.0.10", + "@types/node": "25.x" }, "engines": { "node": "*" } }, "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.12.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.5.tgz", - "integrity": "sha512-BD+BjQ9LS/D8ST9p5uqBxghlN+S42iuNxjsUGjeZobe/ciXzk2qb1B6IXc6AnRLS+yFJRpN2IPEHMzwspfDJNw==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@vscode/l10n": { @@ -45,65 +47,72 @@ "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-css-languageservice": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.1.tgz", - "integrity": "sha512-1BzTBuJfwMc3A0uX4JBdJgoxp74cjj4q2mDJdp49yD/GuAq4X0k5WtK6fNcMYr+FfJ9nqgR6lpfCSZDkARJ5qQ==", + "version": "6.3.9", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.9.tgz", + "integrity": "sha512-1tLWfp+TDM5ZuVWht3jmaY5y7O6aZmpeXLoHl5bv1QtRsRKt4xYGRMmdJa5Pqx/FTkgRbsna9R+Gn2xE+evVuA==", + "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "3.17.5", - "vscode-uri": "^3.0.8" + "vscode-uri": "^3.1.0" } }, "node_modules/vscode-html-languageservice": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.3.1.tgz", - "integrity": "sha512-ysUh4hFeW/WOWz/TO9gm08xigiSsV/FOAZ+DolgJfeLftna54YdmZ4A+lIn46RbdO3/Qv5QHTn1ZGqmrXQhZyA==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.6.1.tgz", + "integrity": "sha512-5Mrqy5CLfFZUgkyhNZLA1Ye5g12Cb/v6VM7SxUzZUaRKWMDz4md+y26PrfRTSU0/eQAl3XpO9m2og+GGtDMuaA==", + "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "^3.17.5", - "vscode-uri": "^3.0.8" + "vscode-uri": "^3.1.0" } }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.6.tgz", - "integrity": "sha512-KCSvUNsFiVciG9iqjJKBZOd66CN3ZKohDlYRmoOi+pd8l15MFLZ8wRG4c+wuzePGba/8WcCG2TM+C/GVlvuaeA==", + "version": "9.0.0-next.11", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.11.tgz", + "integrity": "sha512-u6LElQNbSiE9OugEEmrUKwH6+8BpPz2S5MDHvQUqHL//I4Q8GPikKLOUf856UnbLkZdhxaPrExac1lA3XwpIPA==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageserver": { - "version": "10.0.0-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.11.tgz", - "integrity": "sha512-cmobSrVDYhlh/t02vz/bV8nNpds8mus5HnILULae2iAvOjoaJPnTAp0jJWoYdUqTpIVzT9JV6JMKqLEvdqpeqg==", + "version": "10.0.0-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.16.tgz", + "integrity": "sha512-RbsYDOhddv1NtBCAR7+oVxxCmOpQUHhrtgUE0xz6J+BJGSCkfOqBCyLUIwSjKk2rK9llxUj/pR5aL8QCsXrxow==", + "license": "MIT", "dependencies": { - "vscode-languageserver-protocol": "3.17.6-next.11" + "vscode-languageserver-protocol": "3.17.6-next.16" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.11.tgz", - "integrity": "sha512-GeJxEp1TiLsp79f8WG5n10wLViXfgFKb99hU9K8m7KDWM95/QFEqWkm79f9LVm54tUK74I91a9EeiQLCS/FABQ==", + "version": "3.17.6-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.16.tgz", + "integrity": "sha512-kQTjXEuyxMbdmmZ3U+Lib3oUl12xEKNc73RtWxPSDS3TFtjVwt98Q1CUzfDA9EUpsA24M46Bl6q3sLe9AUOKyw==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" + "vscode-jsonrpc": "9.0.0-next.11", + "vscode-languageserver-types": "3.17.6-next.6" } }, "node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.5.tgz", - "integrity": "sha512-QFmf3Yl1tCgUQfA77N9Me/LXldJXkIVypQbty2rJ1DNHQkC+iwvm4Z2tXg9czSwlhvv0pD4pbF5mT7WhAglolw==" + "version": "3.17.6-next.6", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.6.tgz", + "integrity": "sha512-aiJY5/yW+xzw7KPNlwi3gQtddq/3EIn5z8X8nCgJfaiAij2R1APKePngv+MUdLdYJBVTLu+Qa0ODsT+pHgYguQ==", + "license": "MIT" }, "node_modules/vscode-languageserver-textdocument": { "version": "1.0.12", @@ -116,9 +125,10 @@ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" }, "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" } } } diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index cbf778a2c4cf..34d2016e15ea 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -10,15 +10,15 @@ "main": "./out/node/htmlServerMain", "dependencies": { "@vscode/l10n": "^0.0.18", - "vscode-css-languageservice": "^6.3.1", - "vscode-html-languageservice": "^5.3.1", - "vscode-languageserver": "^10.0.0-next.11", + "vscode-css-languageservice": "^6.3.9", + "vscode-html-languageservice": "^5.6.1", + "vscode-languageserver": "^10.0.0-next.16", "vscode-languageserver-textdocument": "^1.0.12", "vscode-uri": "^3.0.8" }, "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x" + "@types/mocha": "^10.0.10", + "@types/node": "25.x" }, "scripts": { "compile": "npx gulp compile-extension:html-language-features-server", diff --git a/extensions/html-language-features/server/src/modes/embeddedSupport.ts b/extensions/html-language-features/server/src/modes/embeddedSupport.ts index 26ef68439da9..a6874e043b44 100644 --- a/extensions/html-language-features/server/src/modes/embeddedSupport.ts +++ b/extensions/html-language-features/server/src/modes/embeddedSupport.ts @@ -198,6 +198,17 @@ function updateContent(c: EmbeddedRegion, content: string): string { if (!c.attributeValue && c.languageId === 'javascript') { return content.replace(``, ` */`); } + if (c.languageId === 'css') { + const quoteEscape = /("|")/g; + return content.replace(quoteEscape, (match, _, offset) => { + const spaces = ' '.repeat(match.length - 1); + const afterChar = content[offset + match.length]; + if (!afterChar || afterChar.includes(' ')) { + return `${spaces}"`; + } + return `"${spaces}`; + }); + } return content; } diff --git a/extensions/html-language-features/server/src/modes/javascriptMode.ts b/extensions/html-language-features/server/src/modes/javascriptMode.ts index d820810c9203..3c864d53b4e7 100644 --- a/extensions/html-language-features/server/src/modes/javascriptMode.ts +++ b/extensions/html-language-features/server/src/modes/javascriptMode.ts @@ -12,20 +12,35 @@ import { } from './languageModes'; import { getWordAtText, isWhitespaceOnly, repeat } from '../utils/strings'; import { HTMLDocumentRegions } from './embeddedSupport'; +import { normalize, sep } from 'path'; import * as ts from 'typescript'; import { getSemanticTokens, getSemanticTokenLegend } from './javascriptSemanticTokens'; +import { statSync } from 'fs'; const JS_WORD_REGEX = /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g; +/** TypeScript does not handle schemes on file references, so normalize and remove the schemes when communicating with tsserver */ +function deschemeURI(uri: string) { + if (!uri.startsWith('file://')) { + return uri; + } + // This is replicating the logic in TypeScriptServiceClient.normalizedPath + const newPath = normalize(uri.replace('file://', '')); + + // Both \ and / must be escaped in regular expressions + return newPath.replace(new RegExp('\\' + sep, 'g'), '/'); +} + function getLanguageServiceHost(scriptKind: ts.ScriptKind) { const compilerOptions: ts.CompilerOptions = { allowNonTsExtensions: true, allowJs: true, lib: ['lib.es2020.full.d.ts'], target: ts.ScriptTarget.Latest, moduleResolution: ts.ModuleResolutionKind.Classic, experimentalDecorators: false }; let currentTextDocument = TextDocument.create('init', 'javascript', 1, ''); - const jsLanguageService = import(/* webpackChunkName: "javascriptLibs" */ './javascriptLibs.js').then(libs => { + let currentWorkspace: Workspace = undefined!; + const jsLanguageService = import(/* webpackChunkName: "javascriptLibs" */ './javascriptLibs').then(libs => { const host: ts.LanguageServiceHost = { getCompilationSettings: () => compilerOptions, - getScriptFileNames: () => [currentTextDocument.uri, 'jquery'], + getScriptFileNames: () => [deschemeURI(currentTextDocument.uri), 'jquery'], getScriptKind: (fileName) => { if (fileName === currentTextDocument.uri) { return scriptKind; @@ -33,15 +48,26 @@ function getLanguageServiceHost(scriptKind: ts.ScriptKind) { return fileName.substr(fileName.length - 2) === 'ts' ? ts.ScriptKind.TS : ts.ScriptKind.JS; }, getScriptVersion: (fileName: string) => { - if (fileName === currentTextDocument.uri) { + // Let the outer TextDocument give the version + if (fileName === deschemeURI(currentTextDocument.uri)) { return String(currentTextDocument.version); } - return '1'; // default lib an jquery.d.ts are static + // Default libs and jquery.d.ts are static. + // Include node_modules as a perf win + if (fileName.startsWith('lib.') || fileName === 'jquery' || fileName.includes('node_modules')) { + return '1'; + } + // Unsure how this could occur, but better to not raise with statSync + if (!ts.sys.fileExists(fileName)) { return '1'; } + // Use mtime from the fs + return String(statSync(fileName).mtimeMs); }, getScriptSnapshot: (fileName: string) => { let text = ''; - if (fileName === currentTextDocument.uri) { + if (fileName === deschemeURI(currentTextDocument.uri)) { text = currentTextDocument.getText(); + } else if (ts.sys.fileExists(fileName)) { + text = ts.sys.readFile(fileName, 'utf8')!; } else { text = libs.loadLibrary(fileName); } @@ -51,37 +77,24 @@ function getLanguageServiceHost(scriptKind: ts.ScriptKind) { getChangeRange: () => undefined }; }, - getCurrentDirectory: () => '', - getDefaultLibFileName: (_options: ts.CompilerOptions) => 'es2020.full', - readFile: (path: string, _encoding?: string | undefined): string | undefined => { - if (path === currentTextDocument.uri) { - return currentTextDocument.getText(); - } else { - return libs.loadLibrary(path); - } - }, - fileExists: (path: string): boolean => { - if (path === currentTextDocument.uri) { - return true; - } else { - return !!libs.loadLibrary(path); - } + getCurrentDirectory: () => { + const workspace = currentWorkspace && currentWorkspace.folders.find(ws => deschemeURI(currentTextDocument.uri).startsWith(deschemeURI(ws.uri))); + return workspace ? deschemeURI(workspace.uri) : ''; }, - directoryExists: (path: string): boolean => { - // typescript tries to first find libraries in node_modules/@types and node_modules/@typescript - // there's no node_modules in our setup - if (path.startsWith('node_modules')) { - return false; - } - return true; - - } + getDefaultLibFileName: (_options: ts.CompilerOptions) => 'es6', + fileExists: ts.sys.fileExists, + readFile: ts.sys.readFile, + readDirectory: ts.sys.readDirectory, + directoryExists: ts.sys.directoryExists, + getDirectories: ts.sys.getDirectories, }; + return ts.createLanguageService(host); }); return { - async getLanguageService(jsDocument: TextDocument): Promise { + async getLanguageService(jsDocument: TextDocument, workspace: Workspace): Promise { currentTextDocument = jsDocument; + currentWorkspace = workspace; return jsLanguageService; }, getCompilationSettings() { @@ -118,10 +131,12 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache !ignoredErrors.includes(d.code)).map((diag: ts.Diagnostic): Diagnostic => { + const languageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + const syntaxDiagnostics: ts.Diagnostic[] = languageService.getSyntacticDiagnostics(filePath); + const semanticDiagnostics = languageService.getSemanticDiagnostics(filePath); + return syntaxDiagnostics.concat(semanticDiagnostics).map((diag: ts.Diagnostic): Diagnostic => { return { range: convertRange(jsDocument, diag), severity: DiagnosticSeverity.Error, @@ -132,9 +147,12 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const offset = jsDocument.offsetAt(position); - const completions = jsLanguageService.getCompletionsAtPosition(jsDocument.uri, offset, { includeExternalModuleExports: false, includeInsertTextCompletions: false }); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let offset = jsDocument.offsetAt(position); + let completions = jsLanguageService.getCompletionsAtPosition(filePath, offset, { includeExternalModuleExports: false, includeInsertTextCompletions: false }); + if (!completions) { return { isIncomplete: false, items: [] }; } @@ -160,22 +178,25 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { - if (isCompletionItemData(item.data)) { - const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const details = jsLanguageService.getCompletionEntryDetails(jsDocument.uri, item.data.offset, item.label, undefined, undefined, undefined, undefined); - if (details) { - item.detail = ts.displayPartsToString(details.displayParts); - item.documentation = ts.displayPartsToString(details.documentation); - delete item.data; - } + const jsDocument = jsDocuments.get(document); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + // @ts-expect-error until 4.3 protocol update + let details = jsLanguageService.getCompletionEntryDetails(filePath, item.data.offset, item.label, undefined, undefined, undefined, undefined); + if (details) { + item.detail = ts.displayPartsToString(details.displayParts); + item.documentation = ts.displayPartsToString(details.documentation); + delete item.data; } return item; }, async doHover(document: TextDocument, position: Position): Promise { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const info = jsLanguageService.getQuickInfoAtPosition(jsDocument.uri, jsDocument.offsetAt(position)); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let info = jsLanguageService.getQuickInfoAtPosition(filePath, jsDocument.offsetAt(position)); if (info) { const contents = ts.displayPartsToString(info.displayParts); return { @@ -187,8 +208,10 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const signHelp = jsLanguageService.getSignatureHelpItems(jsDocument.uri, jsDocument.offsetAt(position), undefined); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let signHelp = jsLanguageService.getSignatureHelpItems(filePath, jsDocument.offsetAt(position), undefined); if (signHelp) { const ret: SignatureHelp = { activeSignature: signHelp.selectedItemIndex, @@ -225,13 +248,15 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { @@ -247,8 +272,10 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const highlights = jsLanguageService.getDocumentHighlights(jsDocument.uri, jsDocument.offsetAt(position), [jsDocument.uri]); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + const highlights = jsLanguageService.getDocumentHighlights(filePath, jsDocument.offsetAt(position), [filePath]); const out: DocumentHighlight[] = []; for (const entry of highlights || []) { for (const highlight of entry.highlightSpans) { @@ -262,8 +289,10 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const items = jsLanguageService.getNavigationBarItems(jsDocument.uri); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let items = jsLanguageService.getNavigationBarItems(filePath); if (items) { const result: SymbolInformation[] = []; const existing = Object.create(null); @@ -299,8 +328,10 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const definition = jsLanguageService.getDefinitionAtPosition(jsDocument.uri, jsDocument.offsetAt(position)); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let definition = jsLanguageService.getDefinitionAtPosition(filePath, jsDocument.offsetAt(position)); if (definition) { return definition.filter(d => d.fileName === jsDocument.uri).map(d => { return { @@ -313,10 +344,12 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const references = jsLanguageService.getReferencesAtPosition(jsDocument.uri, jsDocument.offsetAt(position)); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let references = jsLanguageService.getReferencesAtPosition(filePath, jsDocument.offsetAt(position)); if (references) { - return references.filter(d => d.fileName === jsDocument.uri).map(d => { + return references.filter(d => d.fileName === filePath).map(d => { return { uri: document.uri, range: convertRange(jsDocument, d.textSpan) @@ -327,17 +360,20 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + function convertSelectionRange(selectionRange: ts.SelectionRange): SelectionRange { const parent = selectionRange.parent ? convertSelectionRange(selectionRange.parent) : undefined; return SelectionRange.create(convertRange(jsDocument, selectionRange.textSpan), parent); } - const range = jsLanguageService.getSmartSelectionRange(jsDocument.uri, jsDocument.offsetAt(position)); + const range = jsLanguageService.getSmartSelectionRange(filePath, jsDocument.offsetAt(position)); return convertSelectionRange(range); }, async format(document: TextDocument, range: Range, formatParams: FormattingOptions, settings: Settings = globalSettings): Promise { const jsDocument = documentRegions.get(document).getEmbeddedDocument('javascript', true); - const jsLanguageService = await host.getLanguageService(jsDocument); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); const formatterSettings = settings && settings.javascript && settings.javascript.format; @@ -350,7 +386,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const spans = jsLanguageService.getOutliningSpans(jsDocument.uri); - const ranges: FoldingRange[] = []; - for (const span of spans) { - const curr = convertRange(jsDocument, span.textSpan); - const startLine = curr.start.line; - const endLine = curr.end.line; + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let spans = jsLanguageService.getOutliningSpans(filePath); + let ranges: FoldingRange[] = []; + for (let span of spans) { + let curr = convertRange(jsDocument, span.textSpan); + let startLine = curr.start.line; + let endLine = curr.end.line; if (startLine < endLine) { const foldingRange: FoldingRange = { startLine, endLine }; const match = document.getText(curr).match(/^\s*\/(?:(\/\s*#(?:end)?region\b)|(\*|\/))/); @@ -396,8 +434,10 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - return [...getSemanticTokens(jsLanguageService, jsDocument, jsDocument.uri)]; + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + return getSemanticTokens(jsLanguageService, jsDocument, filePath); }, getSemanticTokenLegend(): { types: string[]; modifiers: string[] } { return getSemanticTokenLegend(); diff --git a/extensions/html-language-features/server/src/test/completions.test.ts b/extensions/html-language-features/server/src/test/completions.test.ts index fbad266e2dea..6cc0e0d3377f 100644 --- a/extensions/html-language-features/server/src/test/completions.test.ts +++ b/extensions/html-language-features/server/src/test/completions.test.ts @@ -94,6 +94,21 @@ suite('HTML Completion', () => { }); }); +suite('JSDoc Imports', () => { + const fixtureRoot = path.resolve(__dirname, '../../src/test/jsdocImportFixtures'); + const fixtureWorkspace = { name: 'fixture', uri: URI.file(fixtureRoot).toString() }; + const indexHtmlUri = URI.file(path.resolve(fixtureRoot, 'index.html')).toString(); + + test('Imports across files', async () => { + await testCompletionFor('', { + items: [ + { label: 'other', }, + { label: 'property', }, + ] + }, indexHtmlUri, [fixtureWorkspace] ); + }); +}); + suite('HTML Path Completion', () => { const triggerSuggestCommand = { title: 'Suggest', diff --git a/extensions/html-language-features/server/src/test/embedded.test.ts b/extensions/html-language-features/server/src/test/embedded.test.ts index 87698f397188..abba5b5858e7 100644 --- a/extensions/html-language-features/server/src/test/embedded.test.ts +++ b/extensions/html-language-features/server/src/test/embedded.test.ts @@ -128,4 +128,12 @@ suite('HTML Embedded Support', () => { assertEmbeddedLanguageContent('
', 'javascript', ' return;\n foo(); '); }); + test('Script content - HTML escape characters', function (): any { + assertEmbeddedLanguageContent('
', 'css', ' __{font-family: " Arial "} '); + assertEmbeddedLanguageContent('
', 'css', ' __{font-family: " Arial "} '); + assertEmbeddedLanguageContent('
', 'css', ' __{font-family: " Arial "} '); + assertEmbeddedLanguageContent('
', 'css', ' __{font-family: " Arial " } '); + assertEmbeddedLanguageContent('
', 'css', ' __{font-family: Arial} '); + }); + }); diff --git a/extensions/html-language-features/server/src/test/jsdocImportFixtures/index.html b/extensions/html-language-features/server/src/test/jsdocImportFixtures/index.html new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/extensions/html-language-features/server/src/test/jsdocImportFixtures/index.html @@ -0,0 +1 @@ + diff --git a/src/vs/workbench/contrib/notebook/browser/notebookAccessibility.ts b/extensions/html-language-features/server/src/test/jsdocImportFixtures/index.js similarity index 99% rename from src/vs/workbench/contrib/notebook/browser/notebookAccessibility.ts rename to extensions/html-language-features/server/src/test/jsdocImportFixtures/index.js index 11a10ed5950c..a4a092d83492 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookAccessibility.ts +++ b/extensions/html-language-features/server/src/test/jsdocImportFixtures/index.js @@ -2,4 +2,3 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - diff --git a/src/vscode-dts/vscode.proposed.contribIssueReporter.d.ts b/extensions/html-language-features/server/src/test/jsdocImportFixtures/jsDocTypes.ts similarity index 71% rename from src/vscode-dts/vscode.proposed.contribIssueReporter.d.ts rename to extensions/html-language-features/server/src/test/jsdocImportFixtures/jsDocTypes.ts index 522d33344678..938dd5d7b2d5 100644 --- a/src/vscode-dts/vscode.proposed.contribIssueReporter.d.ts +++ b/extensions/html-language-features/server/src/test/jsdocImportFixtures/jsDocTypes.ts @@ -3,5 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// empty placeholder declaration for the `issue reporter`-submenu contribution point -// https://github.com/microsoft/vscode/issues/196863 +export type SomeType = { + property: string + other: number +}; diff --git a/extensions/ipynb/.vscode/launch.json b/extensions/ipynb/.vscode/launch.json index 30130a429d58..eaf7cac3cbd6 100644 --- a/extensions/ipynb/.vscode/launch.json +++ b/extensions/ipynb/.vscode/launch.json @@ -14,6 +14,47 @@ ], "request": "launch", "type": "extensionHost" - } + }, + { + // Run this first: https://github.com/microsoft/vscode-jupyter/blob/main/src/test/datascience/setupTestEnvs.cmd + // Then specify either a grep below or mark a test as 'test.only' to run the test that's failing. + "name": "Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out/test/index.node.js" + ], + "env": { + "VSC_JUPYTER_FORCE_LOGGING": "1", + "VSC_JUPYTER_CI_TEST_GREP": "", // Leave as `VSCode Notebook` to run only Notebook tests. + "VSC_JUPYTER_CI_TEST_INVERT_GREP": "", // Initialize this to invert the grep (exclude tests with value defined in grep). + "CI_PYTHON_PATH": "", // Update with path to real python interpereter used for testing. + "VSC_JUPYTER_CI_RUN_NON_PYTHON_NB_TEST": "", // Initialize this to run tests again Julia & other kernels. + // "TF_BUILD": "", // Set to anything to force full logging + "TEST_FILES_SUFFIX": "*.vscode.test,*.vscode.common.test", + "VSC_JUPYTER_REMOTE_NATIVE_TEST": "false", // Change to `true` to run the Native Notebook tests with remote jupyter connections. + "VSC_JUPYTER_NON_RAW_NATIVE_TEST": "false", // Change to `true` to run the Native Notebook tests with non-raw kernels (i.e. local jupyter server). + "XVSC_JUPYTER_INSTRUMENT_CODE_FOR_COVERAGE": "1", + "XVSC_JUPYTER_INSTRUMENT_CODE_FOR_COVERAGE_HTML": "1", //Enable to get full coverage repor (in coverage folder). + "VSC_JUPYTER_EXPOSE_SVC": "1" + // "VSC_JUPYTER_CI_IS_CONDA": "true" // Enable to run conda tests + }, + "sourceMaps": true, + "outFiles": [ + "${workspaceFolder}/out/**/*.js", + "!${workspaceFolder}/**/node_modules**/*" + ], + "preLaunchTask": "Compile", + "skipFiles": [ + "/**" + ], + "presentation": { + "group": "2_tests", + "order": 6 + } + }, + ] } \ No newline at end of file diff --git a/extensions/ipynb/package-lock.json b/extensions/ipynb/package-lock.json index 7042d6a22b2e..3061d126538f 100644 --- a/extensions/ipynb/package-lock.json +++ b/extensions/ipynb/package-lock.json @@ -10,11 +10,11 @@ "license": "MIT", "dependencies": { "@enonic/fnv-plus": "^1.3.0", - "detect-indent": "^6.0.0" + "detect-indent": "^7.0.2" }, "devDependencies": { - "@jupyterlab/nbformat": "^3.2.9", - "@types/markdown-it": "12.2.3" + "@jupyterlab/nbformat": "^4.5.2", + "@types/markdown-it": "14.1.2" }, "engines": { "vscode": "^1.57.0" @@ -26,51 +26,67 @@ "integrity": "sha512-BCN9uNWH8AmiP7BXBJqEinUY9KXalmRzo+L0cB/mQsmFfzODxwQrbvxCHXUNH2iP+qKkWYtB4vyy8N62PViMFw==" }, "node_modules/@jupyterlab/nbformat": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@jupyterlab/nbformat/-/nbformat-3.4.3.tgz", - "integrity": "sha512-i/yADrwhhAJJCUOTa+fEBMyJO7fvX9Y73I0B7V6dQhGcrmrEKLC3wk4yOo63+jRntd5+dupbiOtz3w1ncIXwIA==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/@jupyterlab/nbformat/-/nbformat-4.5.2.tgz", + "integrity": "sha512-1qNgz3T2FmV7cnRUmw8ApSPvlthI7nxQzXDRlpNQmQHTuGHnNJkyahqXaoO6pI5vt+5RK6jRCvME75xFdVwZ+w==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/coreutils": "^1.11.0" + "@lumino/coreutils": "^2.2.2" } }, + "node_modules/@lumino/algorithm": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lumino/algorithm/-/algorithm-2.0.4.tgz", + "integrity": "sha512-gddBhESPqu25KWLeAK9Kz8tS9Ph7P45i0CNG7Ia4XMhK9PHLtTsBdJTC9jP+MqhbzC8zDT/4ekvYRV9ojRPj7Q==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/@lumino/coreutils": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@lumino/coreutils/-/coreutils-1.12.0.tgz", - "integrity": "sha512-DSglh4ylmLi820CNx9soJmDJCpUgymckdWeGWuN0Ash5g60oQvrQDfosVxEhzmNvtvXv45WZEqSBzDP6E5SEmQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@lumino/coreutils/-/coreutils-2.2.2.tgz", + "integrity": "sha512-zaKJaK7rawPATn2BGHkbMrR6oK3s9PxNe9KreLwWF2dB4ZBHDiEmNLRyHRorfJ7XqVOEXAsAAj0jFn+qJPC/4Q==", "dev": true, - "peerDependencies": { - "crypto": "1.0.1" + "license": "BSD-3-Clause", + "dependencies": { + "@lumino/algorithm": "^2.0.4" } }, "node_modules/@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", - "dev": true + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", "dev": true, + "license": "MIT", "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" + "@types/linkify-it": "^5", + "@types/mdurl": "^2" } }, "node_modules/@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" }, "node_modules/detect-indent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", - "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.2.tgz", + "integrity": "sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } } } diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index 4209ddc130ac..c0ca5cfad1ba 100644 --- a/extensions/ipynb/package.json +++ b/extensions/ipynb/package.json @@ -10,7 +10,6 @@ "vscode": "^1.57.0" }, "enabledApiProposals": [ - "documentPaste", "diffContentOptions" ], "activationEvents": [ @@ -44,7 +43,7 @@ "type": "boolean", "scope": "resource", "markdownDescription": "%ipynb.experimental.serialization%", - "default": false, + "default": true, "tags": [ "experimental" ] @@ -72,6 +71,12 @@ "title": "%copyCellOutput.title%", "category": "Notebook" }, + { + "command": "notebook.cellOutput.addToChat", + "title": "%addCellOutputToChat.title%", + "category": "Notebook", + "enablement": "chatIsEnabled" + }, { "command": "notebook.cellOutput.openInTextEditor", "title": "%openCellOutput.title%", @@ -131,12 +136,18 @@ "webview/context": [ { "command": "notebook.cellOutput.copy", - "when": "webviewId == 'notebook.output' && webviewSection == 'image'" + "when": "webviewId == 'notebook.output' && webviewSection == 'image'", + "group": "context@1" }, { "command": "notebook.cellOutput.copy", "when": "webviewId == 'notebook.output' && webviewSection == 'text'" }, + { + "command": "notebook.cellOutput.addToChat", + "when": "webviewId == 'notebook.output' && webviewSection == 'image'", + "group": "context@2" + }, { "command": "notebook.cellOutput.openInTextEditor", "when": "webviewId == 'notebook.output' && webviewSection == 'text'" @@ -151,11 +162,11 @@ }, "dependencies": { "@enonic/fnv-plus": "^1.3.0", - "detect-indent": "^6.0.0" + "detect-indent": "^7.0.2" }, "devDependencies": { - "@jupyterlab/nbformat": "^3.2.9", - "@types/markdown-it": "12.2.3" + "@jupyterlab/nbformat": "^4.5.2", + "@types/markdown-it": "14.1.2" }, "repository": { "type": "git", diff --git a/extensions/ipynb/package.nls.json b/extensions/ipynb/package.nls.json index 61eb74e242df..85ca7c5f2b3f 100644 --- a/extensions/ipynb/package.nls.json +++ b/extensions/ipynb/package.nls.json @@ -8,6 +8,7 @@ "openIpynbInNotebookEditor.title": "Open IPYNB File In Notebook Editor", "cleanInvalidImageAttachment.title": "Clean Invalid Image Attachment Reference", "copyCellOutput.title": "Copy Cell Output", + "addCellOutputToChat.title": "Add Cell Output to Chat", "openCellOutput.title": "Open Cell Output in Text Editor", "markdownAttachmentRenderer.displayName": { "message": "Markdown-It ipynb Cell Attachment renderer", diff --git a/extensions/ipynb/src/common.ts b/extensions/ipynb/src/common.ts index 3fda0bc74f41..dbd3ea1a618d 100644 --- a/extensions/ipynb/src/common.ts +++ b/extensions/ipynb/src/common.ts @@ -62,6 +62,6 @@ export interface CellMetadata { /** * The code cell's prompt number. Will be null if the cell has not been run. */ - execution_count?: number; + execution_count?: number | null; } diff --git a/extensions/ipynb/src/constants.ts b/extensions/ipynb/src/constants.ts index 9a82ccfae390..b72185968a81 100644 --- a/extensions/ipynb/src/constants.ts +++ b/extensions/ipynb/src/constants.ts @@ -5,7 +5,7 @@ import type { DocumentSelector } from 'vscode'; -export const defaultNotebookFormat = { major: 4, minor: 2 }; +export const defaultNotebookFormat = { major: 4, minor: 5 }; export const ATTACHMENT_CLEANUP_COMMANDID = 'ipynb.cleanInvalidImageAttachment'; export const JUPYTER_NOTEBOOK_MARKDOWN_SELECTOR: DocumentSelector = { notebookType: 'jupyter-notebook', language: 'markdown' }; diff --git a/extensions/ipynb/src/deserializers.ts b/extensions/ipynb/src/deserializers.ts index de467f660776..ad99a4fcea82 100644 --- a/extensions/ipynb/src/deserializers.ts +++ b/extensions/ipynb/src/deserializers.ts @@ -149,8 +149,12 @@ function getNotebookCellMetadata(cell: nbformat.IBaseCell): { // We put this only for VSC to display in diff view. // Else we don't use this. const cellMetadata: CellMetadata = {}; - if (cell.cell_type === 'code' && typeof cell['execution_count'] === 'number') { - cellMetadata.execution_count = cell['execution_count']; + if (cell.cell_type === 'code') { + if (typeof cell['execution_count'] === 'number') { + cellMetadata.execution_count = cell['execution_count']; + } else { + cellMetadata.execution_count = null; + } } if (cell['metadata']) { diff --git a/extensions/ipynb/src/helper.ts b/extensions/ipynb/src/helper.ts index beab091f5c69..6d67b7d529fa 100644 --- a/extensions/ipynb/src/helper.ts +++ b/extensions/ipynb/src/helper.ts @@ -147,13 +147,15 @@ export interface ITask { /** * Copied from src/vs/base/common/uuid.ts */ -export function generateUuid() { - // use `randomValues` if possible - function getRandomValues(bucket: Uint8Array): Uint8Array { - for (let i = 0; i < bucket.length; i++) { - bucket[i] = Math.floor(Math.random() * 256); - } - return bucket; +export function generateUuid(): string { + // use `randomUUID` if possible + if (typeof crypto.randomUUID === 'function') { + // see https://developer.mozilla.org/en-US/docs/Web/API/Window/crypto + // > Although crypto is available on all windows, the returned Crypto object only has one + // > usable feature in insecure contexts: the getRandomValues() method. + // > In general, you should use this API only in secure contexts. + + return crypto.randomUUID.bind(crypto)(); } // prep-work @@ -164,7 +166,7 @@ export function generateUuid() { } // get data - getRandomValues(_data); + crypto.getRandomValues(_data); // set version bits _data[6] = (_data[6] & 0x0f) | 0x40; diff --git a/extensions/ipynb/src/ipynbMain.ts b/extensions/ipynb/src/ipynbMain.ts index e4fe302d1882..cc55d39e112a 100644 --- a/extensions/ipynb/src/ipynbMain.ts +++ b/extensions/ipynb/src/ipynbMain.ts @@ -8,6 +8,7 @@ import { activate as keepNotebookModelStoreInSync } from './notebookModelStoreSy import { notebookImagePasteSetup } from './notebookImagePaste'; import { AttachmentCleaner } from './notebookAttachmentCleaner'; import { serializeNotebookToString } from './serializers'; +import { defaultNotebookFormat } from './constants'; // From {nbformat.INotebookMetadata} in @jupyterlab/coreutils type NotebookMetadata = { @@ -86,8 +87,8 @@ export function activate(context: vscode.ExtensionContext, serializer: vscode.No data.metadata = { cells: [], metadata: {}, - nbformat: 4, - nbformat_minor: 2 + nbformat: defaultNotebookFormat.major, + nbformat_minor: defaultNotebookFormat.minor, }; const doc = await vscode.workspace.openNotebookDocument('jupyter-notebook', data); await vscode.window.showNotebookDocument(doc); diff --git a/extensions/ipynb/src/notebookImagePaste.ts b/extensions/ipynb/src/notebookImagePaste.ts index 5e188dd554a8..70a24e9bf2d2 100644 --- a/extensions/ipynb/src/notebookImagePaste.ts +++ b/extensions/ipynb/src/notebookImagePaste.ts @@ -48,7 +48,7 @@ function getImageMimeType(uri: vscode.Uri): string | undefined { class DropOrPasteEditProvider implements vscode.DocumentPasteEditProvider, vscode.DocumentDropEditProvider { - public static readonly kind = vscode.DocumentDropOrPasteEditKind.Empty.append('markdown', 'image', 'attachment'); + public static readonly kind = vscode.DocumentDropOrPasteEditKind.Empty.append('markdown', 'link', 'image', 'attachment'); async provideDocumentPasteEdits( document: vscode.TextDocument, @@ -68,7 +68,7 @@ class DropOrPasteEditProvider implements vscode.DocumentPasteEditProvider, vscod } const pasteEdit = new vscode.DocumentPasteEdit(insert.insertText, vscode.l10n.t('Insert Image as Attachment'), DropOrPasteEditProvider.kind); - pasteEdit.yieldTo = [vscode.DocumentDropOrPasteEditKind.Empty.append('text')]; + pasteEdit.yieldTo = [vscode.DocumentDropOrPasteEditKind.Text]; pasteEdit.additionalEdit = insert.additionalEdit; return [pasteEdit]; } @@ -85,7 +85,7 @@ class DropOrPasteEditProvider implements vscode.DocumentPasteEditProvider, vscod } const dropEdit = new vscode.DocumentDropEdit(insert.insertText); - dropEdit.yieldTo = [vscode.DocumentDropOrPasteEditKind.Empty.append('text')]; + dropEdit.yieldTo = [vscode.DocumentDropOrPasteEditKind.Text]; dropEdit.additionalEdit = insert.additionalEdit; dropEdit.title = vscode.l10n.t('Insert Image as Attachment'); return dropEdit; diff --git a/extensions/ipynb/src/notebookModelStoreSync.ts b/extensions/ipynb/src/notebookModelStoreSync.ts index 451085718c6f..1d83d980b2da 100644 --- a/extensions/ipynb/src/notebookModelStoreSync.ts +++ b/extensions/ipynb/src/notebookModelStoreSync.ts @@ -14,7 +14,7 @@ const noop = () => { }; /** - * Code here is used to ensure the Notebook Model is in sync the the ipynb JSON file. + * Code here is used to ensure the Notebook Model is in sync the ipynb JSON file. * E.g. assume you add a new cell, this new cell will not have any metadata at all. * However when we save the ipynb, the metadata will be an empty object `{}`. * Now thats completely different from the metadata os being `empty/undefined` in the model. @@ -110,7 +110,7 @@ function trackAndUpdateCellMetadata(notebook: NotebookDocument, updates: { cell: updates.forEach(({ cell, metadata }) => { const newMetadata = { ...cell.metadata, ...metadata }; if (!metadata.execution_count && newMetadata.execution_count) { - delete newMetadata.execution_count; + newMetadata.execution_count = null; } if (!metadata.attachments && newMetadata.attachments) { delete newMetadata.attachments; @@ -123,6 +123,7 @@ function trackAndUpdateCellMetadata(notebook: NotebookDocument, updates: { cell: promise.then(clean, clean); } +const pendingCellUpdates = new WeakSet(); function onDidChangeNotebookCells(e: NotebookDocumentChangeEventEx) { if (!isSupportedNotebook(e.notebook)) { return; @@ -150,8 +151,26 @@ function onDidChangeNotebookCells(e: NotebookDocumentChangeEventEx) { metadata.execution_count = e.executionSummary.executionOrder; metadataUpdated = true; } else if (!e.executionSummary && !e.metadata && e.outputs?.length === 0 && currentMetadata.execution_count) { - // Clear all. - delete metadata.execution_count; + // Clear all (user hit clear all). + // NOTE: At this point we're updating the `execution_count` in metadata to `null`. + // Thus this is a change in metadata, which we will need to update in the model. + metadata.execution_count = null; + metadataUpdated = true; + // Note: We will get another event for this, see below for the check. + // track the fact that we're expecting an update for this cell. + pendingCellUpdates.add(e.cell); + } else if ((!e.executionSummary || (!e.executionSummary?.executionOrder && !e.executionSummary?.success && !e.executionSummary?.timing)) + && !e.metadata && !e.outputs && currentMetadata.execution_count && pendingCellUpdates.has(e.cell)) { + // This is a result of the cell being cleared (i.e. we perfomed an update request and this is now the update event). + metadata.execution_count = null; + metadataUpdated = true; + pendingCellUpdates.delete(e.cell); + } else if (!e.executionSummary?.executionOrder && !e.executionSummary?.success && !e.executionSummary?.timing + && !e.metadata && !e.outputs && currentMetadata.execution_count && !pendingCellUpdates.has(e.cell)) { + // This is a result of the cell without outupts but has execution count being cleared + // Create two cells, one that produces output and one that doesn't. Run both and then clear the output or all cells. + // This condition will be satisfied for first cell without outputs. + metadata.execution_count = null; metadataUpdated = true; } diff --git a/extensions/ipynb/src/notebookSerializer.ts b/extensions/ipynb/src/notebookSerializer.ts index 6eb1fab75d80..486ce55b9e74 100644 --- a/extensions/ipynb/src/notebookSerializer.ts +++ b/extensions/ipynb/src/notebookSerializer.ts @@ -8,7 +8,7 @@ import detectIndent from 'detect-indent'; import * as vscode from 'vscode'; import { getPreferredLanguage, jupyterNotebookModelToNotebookData } from './deserializers'; import * as fnv from '@enonic/fnv-plus'; -import { serializeNotebookToString } from './serializers'; +import { serializeNotebookToBytes } from './serializers'; export abstract class NotebookSerializerBase extends vscode.Disposable implements vscode.NotebookSerializer { protected disposed: boolean = false; @@ -81,8 +81,7 @@ export abstract class NotebookSerializerBase extends vscode.Disposable implement return new Uint8Array(0); } - const serialized = serializeNotebookToString(data); - return new TextEncoder().encode(serialized); + return serializeNotebookToBytes(data); } } diff --git a/extensions/ipynb/src/notebookSerializerWorker.ts b/extensions/ipynb/src/notebookSerializerWorker.ts index 594af6da7916..6470132d1a7f 100644 --- a/extensions/ipynb/src/notebookSerializerWorker.ts +++ b/extensions/ipynb/src/notebookSerializerWorker.ts @@ -4,15 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { parentPort } from 'worker_threads'; -import { serializeNotebookToString } from './serializers'; +import { serializeNotebookToBytes } from './serializers'; import type { NotebookData } from 'vscode'; if (parentPort) { parentPort.on('message', ({ id, data }: { id: string; data: NotebookData }) => { if (parentPort) { - const json = serializeNotebookToString(data); - const bytes = new TextEncoder().encode(json); + const bytes = serializeNotebookToBytes(data); parentPort.postMessage({ id, data: bytes }); } }); diff --git a/extensions/ipynb/src/serializers.ts b/extensions/ipynb/src/serializers.ts index 33de79f41a64..cbff5d1b2d52 100644 --- a/extensions/ipynb/src/serializers.ts +++ b/extensions/ipynb/src/serializers.ts @@ -52,10 +52,13 @@ export function getCellMetadata(options: { cell: NotebookCell | NotebookCellData if ('cell' in options) { const cell = options.cell; const metadata = { + execution_count: null, // it contains the cell id, and the cell metadata, along with other nb cell metadata ...(cell.metadata ?? {}) - }; - + } satisfies CellMetadata; + if (cell.kind === NotebookCellKindMarkup) { + delete (metadata as any).execution_count; + } return metadata; } else { const cell = options; @@ -64,7 +67,7 @@ export function getCellMetadata(options: { cell: NotebookCell | NotebookCellData ...(cell.metadata ?? {}) }; - return metadata; + return metadata as CellMetadata; } } @@ -462,6 +465,95 @@ export function serializeNotebookToString(data: NotebookData): string { return serializeNotebookToJSON(notebookContent, indentAmount); } + +export function serializeNotebookToBytes(data: NotebookData): Uint8Array { + const notebookContent = getNotebookMetadata(data); + // use the preferred language from document metadata or the first cell language as the notebook preferred cell language + const preferredCellLanguage = notebookContent.metadata?.language_info?.name ?? data.cells.find(cell => cell.kind === 2)?.languageId; + + notebookContent.cells = data.cells + .map(cell => createJupyterCellFromNotebookCell(cell, preferredCellLanguage)) + .map(pruneCell); + + const indentAmount = data.metadata && 'indentAmount' in data.metadata && typeof data.metadata.indentAmount === 'string' ? + data.metadata.indentAmount : + ' '; + + return serializeNotebookToJSONBytes(notebookContent, indentAmount); +} + + +function serializeNotebookToJSONBytes(notebookContent: Partial, indentAmount: string): Uint8Array { + // const sorted = sortObjectPropertiesRecursively(notebookContent); + + // Using JSON.stringify can fail, if the entire size of the resulting JSON string exceeds the maximum size of a single string in JS. + // Hence build the JSON bytes in chunks. + return jsonToBytes(notebookContent, indentAmount); +} + +function jsonToBytes(jsonObject: any, indentAmount: string = ' '): Uint8Array { + const encoder = new TextEncoder(); + const arrays: Uint8Array[] = []; + + function encodeValue(value: any, indent: string): void { + if (typeof value === 'object' && value !== null) { + if (Array.isArray(value)) { + arrays.push(encoder.encode('[')); + if (value.length > 0) { + for (let i = 0; i < value.length; i++) { + if (i > 0) { + arrays.push(encoder.encode(',')); + } + arrays.push(encoder.encode('\n' + indent + indentAmount)); + encodeValue(value[i], indent + indentAmount); + } + arrays.push(encoder.encode('\n' + indent + ']')); + } else { + arrays.push(encoder.encode(']')); + } + } else { + arrays.push(encoder.encode('{')); + // // ipynb always sorts keys in alphabetical order. + const keys = Object.keys(value).sort(); + if (keys.length > 0) { + for (let i = 0; i < keys.length; i++) { + if (i > 0) { + arrays.push(encoder.encode(',')); + } + arrays.push(encoder.encode('\n' + indent + indentAmount + `"${keys[i]}": `)); + encodeValue(value[keys[i]], indent + indentAmount); + } + arrays.push(encoder.encode('\n' + indent + '}')); + } else { + arrays.push(encoder.encode('}')); + } + } + } else if (typeof value === 'string') { + arrays.push(encoder.encode(`"${value}"`)); + } else { + arrays.push(encoder.encode(String(value))); + } + } + + encodeValue(jsonObject, ''); + // ipynb always ends with a trailing new line (we add this so that SCMs do not show unnecessary changes, resulting from a missing trailing new line). + arrays.push(encoder.encode('\n')); + + return concatenateUint8Arrays(arrays); +} + +function concatenateUint8Arrays(arrays: Uint8Array[]): Uint8Array { + const totalLength = arrays.reduce((acc, arr) => acc + arr.length, 0); + const result = new Uint8Array(totalLength); + let offset = 0; + arrays.forEach(arr => { + result.set(arr, offset); + offset += arr.length; + }); + return result; +} + + function serializeNotebookToJSON(notebookContent: Partial, indentAmount: string): string { // ipynb always ends with a trailing new line (we add this so that SCMs do not show unnecessary changes, resulting from a missing trailing new line). const sorted = sortObjectPropertiesRecursively(notebookContent); diff --git a/extensions/ipynb/src/test/clearOutputs.test.ts b/extensions/ipynb/src/test/clearOutputs.test.ts new file mode 100644 index 000000000000..0437e9838bb5 --- /dev/null +++ b/extensions/ipynb/src/test/clearOutputs.test.ts @@ -0,0 +1,736 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as sinon from 'sinon'; +import type * as nbformat from '@jupyterlab/nbformat'; +import * as assert from 'assert'; +import * as vscode from 'vscode'; +import { jupyterNotebookModelToNotebookData } from '../deserializers'; +import { activate } from '../notebookModelStoreSync'; + + +suite(`ipynb Clear Outputs`, () => { + const disposables: vscode.Disposable[] = []; + const context = { subscriptions: disposables } as vscode.ExtensionContext; + setup(() => { + disposables.length = 0; + activate(context); + }); + teardown(async () => { + disposables.forEach(d => d.dispose()); + disposables.length = 0; + sinon.restore(); + await vscode.commands.executeCommand('workbench.action.closeAllEditors'); + }); + + test('Clear outputs after opening Notebook', async () => { + const cells: nbformat.ICell[] = [ + { + cell_type: 'code', + execution_count: 10, + outputs: [{ output_type: 'stream', name: 'stdout', text: ['Hello'] }], + source: 'print(1)', + metadata: {} + }, + { + cell_type: 'code', + outputs: [], + source: 'print(2)', + metadata: {} + }, + { + cell_type: 'markdown', + source: '# HEAD', + metadata: {} + } + ]; + const notebook = jupyterNotebookModelToNotebookData({ cells }, 'python'); + + const notebookDocument = await vscode.workspace.openNotebookDocument('jupyter-notebook', notebook); + await vscode.window.showNotebookDocument(notebookDocument); + + assert.strictEqual(notebookDocument.cellCount, 3); + assert.strictEqual(notebookDocument.cellAt(0).metadata.execution_count, 10); + assert.strictEqual(notebookDocument.cellAt(1).metadata.execution_count, null); + assert.strictEqual(notebookDocument.cellAt(2).metadata.execution_count, undefined); + + // Clear all outputs + await vscode.commands.executeCommand('notebook.clearAllCellsOutputs'); + + // Wait for all changes to be applied, could take a few ms. + const verifyMetadataChanges = () => { + assert.strictEqual(notebookDocument.cellAt(0).metadata.execution_count, null); + assert.strictEqual(notebookDocument.cellAt(1).metadata.execution_count, null); + assert.strictEqual(notebookDocument.cellAt(2).metadata.execution_count, undefined); + }; + + vscode.workspace.onDidChangeNotebookDocument(() => verifyMetadataChanges(), undefined, disposables); + + await new Promise((resolve, reject) => { + const interval = setInterval(() => { + try { + verifyMetadataChanges(); + clearInterval(interval); + resolve(); + } catch { + // Ignore + } + }, 50); + disposables.push({ dispose: () => clearInterval(interval) }); + const timeout = setTimeout(() => { + try { + verifyMetadataChanges(); + resolve(); + } catch (ex) { + reject(ex); + } + }, 1000); + disposables.push({ dispose: () => clearTimeout(timeout) }); + }); + }); + + + // test('Serialize', async () => { + // const markdownCell = new vscode.NotebookCellData(vscode.NotebookCellKind.Markup, '# header1', 'markdown'); + // markdownCell.metadata = { + // attachments: { + // 'image.png': { + // 'image/png': 'abc' + // } + // }, + // id: '123', + // metadata: { + // foo: 'bar' + // } + // }; + + // const cellMetadata = getCellMetadata({ cell: markdownCell }); + // assert.deepStrictEqual(cellMetadata, { + // id: '123', + // metadata: { + // foo: 'bar', + // }, + // attachments: { + // 'image.png': { + // 'image/png': 'abc' + // } + // } + // }); + + // const markdownCell2 = new vscode.NotebookCellData(vscode.NotebookCellKind.Markup, '# header1', 'markdown'); + // markdownCell2.metadata = { + // id: '123', + // metadata: { + // foo: 'bar' + // }, + // attachments: { + // 'image.png': { + // 'image/png': 'abc' + // } + // } + // }; + + // const nbMarkdownCell = createMarkdownCellFromNotebookCell(markdownCell); + // const nbMarkdownCell2 = createMarkdownCellFromNotebookCell(markdownCell2); + // assert.deepStrictEqual(nbMarkdownCell, nbMarkdownCell2); + + // assert.deepStrictEqual(nbMarkdownCell, { + // cell_type: 'markdown', + // source: ['# header1'], + // metadata: { + // foo: 'bar', + // }, + // attachments: { + // 'image.png': { + // 'image/png': 'abc' + // } + // }, + // id: '123' + // }); + // }); + + // suite('Outputs', () => { + // function validateCellOutputTranslation( + // outputs: nbformat.IOutput[], + // expectedOutputs: vscode.NotebookCellOutput[], + // propertiesToExcludeFromComparison: string[] = [] + // ) { + // const cells: nbformat.ICell[] = [ + // { + // cell_type: 'code', + // execution_count: 10, + // outputs, + // source: 'print(1)', + // metadata: {} + // } + // ]; + // const notebook = jupyterNotebookModelToNotebookData({ cells }, 'python'); + + // // OutputItems contain an `id` property generated by VSC. + // // Exclude that property when comparing. + // const propertiesToExclude = propertiesToExcludeFromComparison.concat(['id']); + // const actualOuts = notebook.cells[0].outputs; + // deepStripProperties(actualOuts, propertiesToExclude); + // deepStripProperties(expectedOutputs, propertiesToExclude); + // assert.deepStrictEqual(actualOuts, expectedOutputs); + // } + + // test('Empty output', () => { + // validateCellOutputTranslation([], []); + // }); + + // test('Stream output', () => { + // validateCellOutputTranslation( + // [ + // { + // output_type: 'stream', + // name: 'stderr', + // text: 'Error' + // }, + // { + // output_type: 'stream', + // name: 'stdout', + // text: 'NoError' + // } + // ], + // [ + // new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stderr('Error')], { + // outputType: 'stream' + // }), + // new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stdout('NoError')], { + // outputType: 'stream' + // }) + // ] + // ); + // }); + // test('Stream output and line endings', () => { + // validateCellOutputTranslation( + // [ + // { + // output_type: 'stream', + // name: 'stdout', + // text: [ + // 'Line1\n', + // '\n', + // 'Line3\n', + // 'Line4' + // ] + // } + // ], + // [ + // new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stdout('Line1\n\nLine3\nLine4')], { + // outputType: 'stream' + // }) + // ] + // ); + // validateCellOutputTranslation( + // [ + // { + // output_type: 'stream', + // name: 'stdout', + // text: [ + // 'Hello\n', + // 'Hello\n', + // 'Hello\n', + // 'Hello\n', + // 'Hello\n', + // 'Hello\n' + // ] + // } + // ], + // [ + // new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stdout('Hello\nHello\nHello\nHello\nHello\nHello\n')], { + // outputType: 'stream' + // }) + // ] + // ); + // }); + // test('Multi-line Stream output', () => { + // validateCellOutputTranslation( + // [ + // { + // name: 'stdout', + // output_type: 'stream', + // text: [ + // 'Epoch 1/5\n', + // '...\n', + // 'Epoch 2/5\n', + // '...\n', + // 'Epoch 3/5\n', + // '...\n', + // 'Epoch 4/5\n', + // '...\n', + // 'Epoch 5/5\n', + // '...\n' + // ] + // } + // ], + // [ + // new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stdout(['Epoch 1/5\n', + // '...\n', + // 'Epoch 2/5\n', + // '...\n', + // 'Epoch 3/5\n', + // '...\n', + // 'Epoch 4/5\n', + // '...\n', + // 'Epoch 5/5\n', + // '...\n'].join(''))], { + // outputType: 'stream' + // }) + // ] + // ); + // }); + + // test('Multi-line Stream output (last empty line should not be saved in ipynb)', () => { + // validateCellOutputTranslation( + // [ + // { + // name: 'stderr', + // output_type: 'stream', + // text: [ + // 'Epoch 1/5\n', + // '...\n', + // 'Epoch 2/5\n', + // '...\n', + // 'Epoch 3/5\n', + // '...\n', + // 'Epoch 4/5\n', + // '...\n', + // 'Epoch 5/5\n', + // '...\n' + // ] + // } + // ], + // [ + // new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stderr(['Epoch 1/5\n', + // '...\n', + // 'Epoch 2/5\n', + // '...\n', + // 'Epoch 3/5\n', + // '...\n', + // 'Epoch 4/5\n', + // '...\n', + // 'Epoch 5/5\n', + // '...\n', + // // This last empty line should not be saved in ipynb. + // '\n'].join(''))], { + // outputType: 'stream' + // }) + // ] + // ); + // }); + + // test('Streamed text with Ansi characters', async () => { + // validateCellOutputTranslation( + // [ + // { + // name: 'stderr', + // text: '\u001b[K\u001b[33m✅ \u001b[0m Loading\n', + // output_type: 'stream' + // } + // ], + // [ + // new vscode.NotebookCellOutput( + // [vscode.NotebookCellOutputItem.stderr('\u001b[K\u001b[33m✅ \u001b[0m Loading\n')], + // { + // outputType: 'stream' + // } + // ) + // ] + // ); + // }); + + // test('Streamed text with angle bracket characters', async () => { + // validateCellOutputTranslation( + // [ + // { + // name: 'stderr', + // text: '1 is < 2', + // output_type: 'stream' + // } + // ], + // [ + // new vscode.NotebookCellOutput([vscode.NotebookCellOutputItem.stderr('1 is < 2')], { + // outputType: 'stream' + // }) + // ] + // ); + // }); + + // test('Streamed text with angle bracket characters and ansi chars', async () => { + // validateCellOutputTranslation( + // [ + // { + // name: 'stderr', + // text: '1 is < 2\u001b[K\u001b[33m✅ \u001b[0m Loading\n', + // output_type: 'stream' + // } + // ], + // [ + // new vscode.NotebookCellOutput( + // [vscode.NotebookCellOutputItem.stderr('1 is < 2\u001b[K\u001b[33m✅ \u001b[0m Loading\n')], + // { + // outputType: 'stream' + // } + // ) + // ] + // ); + // }); + + // test('Error', async () => { + // validateCellOutputTranslation( + // [ + // { + // ename: 'Error Name', + // evalue: 'Error Value', + // traceback: ['stack1', 'stack2', 'stack3'], + // output_type: 'error' + // } + // ], + // [ + // new vscode.NotebookCellOutput( + // [ + // vscode.NotebookCellOutputItem.error({ + // name: 'Error Name', + // message: 'Error Value', + // stack: ['stack1', 'stack2', 'stack3'].join('\n') + // }) + // ], + // { + // outputType: 'error', + // originalError: { + // ename: 'Error Name', + // evalue: 'Error Value', + // traceback: ['stack1', 'stack2', 'stack3'], + // output_type: 'error' + // } + // } + // ) + // ] + // ); + // }); + + // ['display_data', 'execute_result'].forEach(output_type => { + // suite(`Rich output for output_type = ${output_type}`, () => { + // // Properties to exclude when comparing. + // let propertiesToExcludeFromComparison: string[] = []; + // setup(() => { + // if (output_type === 'display_data') { + // // With display_data the execution_count property will never exist in the output. + // // We can ignore that (as it will never exist). + // // But we leave it in the case of `output_type === 'execute_result'` + // propertiesToExcludeFromComparison = ['execution_count', 'executionCount']; + // } + // }); + + // test('Text mimeType output', async () => { + // validateCellOutputTranslation( + // [ + // { + // data: { + // 'text/plain': 'Hello World!' + // }, + // output_type, + // metadata: {}, + // execution_count: 1 + // } + // ], + // [ + // new vscode.NotebookCellOutput( + // [new vscode.NotebookCellOutputItem(Buffer.from('Hello World!', 'utf8'), 'text/plain')], + // { + // outputType: output_type, + // metadata: {}, // display_data & execute_result always have metadata. + // executionCount: 1 + // } + // ) + // ], + // propertiesToExcludeFromComparison + // ); + // }); + + // test('png,jpeg images', async () => { + // validateCellOutputTranslation( + // [ + // { + // execution_count: 1, + // data: { + // 'image/png': base64EncodedImage, + // 'image/jpeg': base64EncodedImage + // }, + // metadata: {}, + // output_type + // } + // ], + // [ + // new vscode.NotebookCellOutput( + // [ + // new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/png'), + // new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/jpeg') + // ], + // { + // executionCount: 1, + // outputType: output_type, + // metadata: {} // display_data & execute_result always have metadata. + // } + // ) + // ], + // propertiesToExcludeFromComparison + // ); + // }); + + // test('png image with a light background', async () => { + // validateCellOutputTranslation( + // [ + // { + // execution_count: 1, + // data: { + // 'image/png': base64EncodedImage + // }, + // metadata: { + // needs_background: 'light' + // }, + // output_type + // } + // ], + // [ + // new vscode.NotebookCellOutput( + // [new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/png')], + // { + // executionCount: 1, + // metadata: { + // needs_background: 'light' + // }, + // outputType: output_type + // } + // ) + // ], + // propertiesToExcludeFromComparison + // ); + // }); + + // test('png image with a dark background', async () => { + // validateCellOutputTranslation( + // [ + // { + // execution_count: 1, + // data: { + // 'image/png': base64EncodedImage + // }, + // metadata: { + // needs_background: 'dark' + // }, + // output_type + // } + // ], + // [ + // new vscode.NotebookCellOutput( + // [new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/png')], + // { + // executionCount: 1, + // metadata: { + // needs_background: 'dark' + // }, + // outputType: output_type + // } + // ) + // ], + // propertiesToExcludeFromComparison + // ); + // }); + + // test('png image with custom dimensions', async () => { + // validateCellOutputTranslation( + // [ + // { + // execution_count: 1, + // data: { + // 'image/png': base64EncodedImage + // }, + // metadata: { + // 'image/png': { height: '111px', width: '999px' } + // }, + // output_type + // } + // ], + // [ + // new vscode.NotebookCellOutput( + // [new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/png')], + // { + // executionCount: 1, + // metadata: { + // 'image/png': { height: '111px', width: '999px' } + // }, + // outputType: output_type + // } + // ) + // ], + // propertiesToExcludeFromComparison + // ); + // }); + + // test('png allowed to scroll', async () => { + // validateCellOutputTranslation( + // [ + // { + // execution_count: 1, + // data: { + // 'image/png': base64EncodedImage + // }, + // metadata: { + // unconfined: true, + // 'image/png': { width: '999px' } + // }, + // output_type + // } + // ], + // [ + // new vscode.NotebookCellOutput( + // [new vscode.NotebookCellOutputItem(Buffer.from(base64EncodedImage, 'base64'), 'image/png')], + // { + // executionCount: 1, + // metadata: { + // unconfined: true, + // 'image/png': { width: '999px' } + // }, + // outputType: output_type + // } + // ) + // ], + // propertiesToExcludeFromComparison + // ); + // }); + // }); + // }); + // }); + + // suite('Output Order', () => { + // test('Verify order of outputs', async () => { + // const dataAndExpectedOrder: { output: nbformat.IDisplayData; expectedMimeTypesOrder: string[] }[] = [ + // { + // output: { + // data: { + // 'application/vnd.vegalite.v4+json': 'some json', + // 'text/html': 'Hello' + // }, + // metadata: {}, + // output_type: 'display_data' + // }, + // expectedMimeTypesOrder: ['application/vnd.vegalite.v4+json', 'text/html'] + // }, + // { + // output: { + // data: { + // 'application/vnd.vegalite.v4+json': 'some json', + // 'application/javascript': 'some js', + // 'text/plain': 'some text', + // 'text/html': 'Hello' + // }, + // metadata: {}, + // output_type: 'display_data' + // }, + // expectedMimeTypesOrder: [ + // 'application/vnd.vegalite.v4+json', + // 'text/html', + // 'application/javascript', + // 'text/plain' + // ] + // }, + // { + // output: { + // data: { + // 'application/vnd.vegalite.v4+json': '', // Empty, should give preference to other mimetypes. + // 'application/javascript': 'some js', + // 'text/plain': 'some text', + // 'text/html': 'Hello' + // }, + // metadata: {}, + // output_type: 'display_data' + // }, + // expectedMimeTypesOrder: [ + // 'text/html', + // 'application/javascript', + // 'text/plain', + // 'application/vnd.vegalite.v4+json' + // ] + // }, + // { + // output: { + // data: { + // 'text/plain': 'some text', + // 'text/html': 'Hello' + // }, + // metadata: {}, + // output_type: 'display_data' + // }, + // expectedMimeTypesOrder: ['text/html', 'text/plain'] + // }, + // { + // output: { + // data: { + // 'application/javascript': 'some js', + // 'text/plain': 'some text' + // }, + // metadata: {}, + // output_type: 'display_data' + // }, + // expectedMimeTypesOrder: ['application/javascript', 'text/plain'] + // }, + // { + // output: { + // data: { + // 'image/svg+xml': 'some svg', + // 'text/plain': 'some text' + // }, + // metadata: {}, + // output_type: 'display_data' + // }, + // expectedMimeTypesOrder: ['image/svg+xml', 'text/plain'] + // }, + // { + // output: { + // data: { + // 'text/latex': 'some latex', + // 'text/plain': 'some text' + // }, + // metadata: {}, + // output_type: 'display_data' + // }, + // expectedMimeTypesOrder: ['text/latex', 'text/plain'] + // }, + // { + // output: { + // data: { + // 'application/vnd.jupyter.widget-view+json': 'some widget', + // 'text/plain': 'some text' + // }, + // metadata: {}, + // output_type: 'display_data' + // }, + // expectedMimeTypesOrder: ['application/vnd.jupyter.widget-view+json', 'text/plain'] + // }, + // { + // output: { + // data: { + // 'text/plain': 'some text', + // 'image/svg+xml': 'some svg', + // 'image/png': 'some png' + // }, + // metadata: {}, + // output_type: 'display_data' + // }, + // expectedMimeTypesOrder: ['image/png', 'image/svg+xml', 'text/plain'] + // } + // ]; + + // dataAndExpectedOrder.forEach(({ output, expectedMimeTypesOrder }) => { + // const sortedOutputs = jupyterCellOutputToCellOutput(output); + // const mimeTypes = sortedOutputs.items.map((item) => item.mime).join(','); + // assert.equal(mimeTypes, expectedMimeTypesOrder.join(',')); + // }); + // }); + // }); +}); diff --git a/extensions/ipynb/src/test/notebookModelStoreSync.test.ts b/extensions/ipynb/src/test/notebookModelStoreSync.test.ts index acb6ec546bb3..7174678ad61c 100644 --- a/extensions/ipynb/src/test/notebookModelStoreSync.test.ts +++ b/extensions/ipynb/src/test/notebookModelStoreSync.test.ts @@ -101,7 +101,8 @@ suite(`Notebook Model Store Sync`, () => { assert.strictEqual(editsApplied.length, 0); assert.strictEqual(cellMetadataUpdates.length, 0); }); - test('Adding cell will result in an update to the metadata', async () => { + test('Adding cell to nbformat 4.2 notebook will result in adding empty metadata', async () => { + sinon.stub(notebook, 'metadata').get(() => ({ nbformat: 4, nbformat_minor: 2 })); const cell: NotebookCell = { document: {} as any, executionSummary: {}, @@ -129,9 +130,9 @@ suite(`Notebook Model Store Sync`, () => { assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata; - assert.deepStrictEqual(newMetadata, { metadata: {} }); + assert.deepStrictEqual(newMetadata, { execution_count: null, metadata: {} }); }); - test('Add cell id if nbformat is 4.5', async () => { + test('Added cell will have a cell id if nbformat is 4.5', async () => { sinon.stub(notebook, 'metadata').get(() => ({ nbformat: 4, nbformat_minor: 5 })); const cell: NotebookCell = { document: {} as any, @@ -160,7 +161,8 @@ suite(`Notebook Model Store Sync`, () => { assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata || {}; - assert.strictEqual(Object.keys(newMetadata).length, 2); + assert.strictEqual(Object.keys(newMetadata).length, 3); + assert.deepStrictEqual(newMetadata.execution_count, null); assert.deepStrictEqual(newMetadata.metadata, {}); assert.ok(newMetadata.id); }); @@ -195,7 +197,8 @@ suite(`Notebook Model Store Sync`, () => { assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata || {}; - assert.strictEqual(Object.keys(newMetadata).length, 2); + assert.strictEqual(Object.keys(newMetadata).length, 3); + assert.deepStrictEqual(newMetadata.execution_count, null); assert.deepStrictEqual(newMetadata.metadata, {}); assert.strictEqual(newMetadata.id, '1234'); }); @@ -276,7 +279,8 @@ suite(`Notebook Model Store Sync`, () => { assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata || {}; - assert.strictEqual(Object.keys(newMetadata).length, 2); + assert.strictEqual(Object.keys(newMetadata).length, 3); + assert.deepStrictEqual(newMetadata.execution_count, null); assert.deepStrictEqual(newMetadata.metadata, { collapsed: true, scrolled: true, vscode: { languageId: 'javascript' } }); assert.strictEqual(newMetadata.id, '1234'); }); @@ -369,7 +373,8 @@ suite(`Notebook Model Store Sync`, () => { assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata || {}; - assert.strictEqual(Object.keys(newMetadata).length, 2); + assert.strictEqual(Object.keys(newMetadata).length, 3); + assert.deepStrictEqual(newMetadata.execution_count, null); assert.deepStrictEqual(newMetadata.metadata, { collapsed: true, scrolled: true }); assert.strictEqual(newMetadata.id, '1234'); }); @@ -419,7 +424,8 @@ suite(`Notebook Model Store Sync`, () => { assert.strictEqual(editsApplied.length, 1); assert.strictEqual(cellMetadataUpdates.length, 1); const newMetadata = cellMetadataUpdates[0].newCellMetadata || {}; - assert.strictEqual(Object.keys(newMetadata).length, 2); + assert.strictEqual(Object.keys(newMetadata).length, 3); + assert.deepStrictEqual(newMetadata.execution_count, null); assert.deepStrictEqual(newMetadata.metadata, { collapsed: true, scrolled: true, vscode: { languageId: 'powershell' } }); assert.strictEqual(newMetadata.id, '1234'); }); diff --git a/extensions/ipynb/src/test/serializers.test.ts b/extensions/ipynb/src/test/serializers.test.ts index cb461539c5d0..237b382ab33a 100644 --- a/extensions/ipynb/src/test/serializers.test.ts +++ b/extensions/ipynb/src/test/serializers.test.ts @@ -8,7 +8,7 @@ import type * as nbformat from '@jupyterlab/nbformat'; import * as assert from 'assert'; import * as vscode from 'vscode'; import { jupyterCellOutputToCellOutput, jupyterNotebookModelToNotebookData } from '../deserializers'; -import { createMarkdownCellFromNotebookCell, getCellMetadata } from '../serializers'; +import { createMarkdownCellFromNotebookCell, getCellMetadata, serializeNotebookToBytes, serializeNotebookToString } from '../serializers'; function deepStripProperties(obj: any, props: string[]) { for (const prop in obj) { @@ -41,6 +41,12 @@ suite(`ipynb serializer`, () => { source: 'print(1)', metadata: {} }, + { + cell_type: 'code', + outputs: [], + source: 'print(2)', + metadata: {} + }, { cell_type: 'markdown', source: '# HEAD', @@ -55,13 +61,18 @@ suite(`ipynb serializer`, () => { expectedCodeCell.metadata = { execution_count: 10, metadata: {} }; expectedCodeCell.executionSummary = { executionOrder: 10 }; + const expectedCodeCell2 = new vscode.NotebookCellData(vscode.NotebookCellKind.Code, 'print(2)', 'python'); + expectedCodeCell2.outputs = []; + expectedCodeCell2.metadata = { execution_count: null, metadata: {} }; + expectedCodeCell2.executionSummary = {}; + const expectedMarkdownCell = new vscode.NotebookCellData(vscode.NotebookCellKind.Markup, '# HEAD', 'markdown'); expectedMarkdownCell.outputs = []; expectedMarkdownCell.metadata = { metadata: {} }; - assert.deepStrictEqual(notebook.cells, [expectedCodeCell, expectedMarkdownCell]); + assert.deepStrictEqual(notebook.cells, [expectedCodeCell, expectedCodeCell2, expectedMarkdownCell]); }); @@ -124,6 +135,77 @@ suite(`ipynb serializer`, () => { }); }); + + test.only('JSON serialization (empty notebook)', () => { + const cells: nbformat.ICell[] = [ + ]; + const notebook = jupyterNotebookModelToNotebookData({ cells }, 'python'); + assert.ok(notebook); + + const jsonStr = serializeNotebookToString(notebook); + const bytes = serializeNotebookToBytes(notebook); + + assert.strictEqual(jsonStr, new TextDecoder().decode(bytes)); + }); + + test.only('JSON serialization (empty metadata with cells)', () => { + const cells: nbformat.ICell[] = [ + { + cell_type: 'code', + execution_count: 10, + outputs: [], + source: 'print(1)', + metadata: {} + }, + { + cell_type: 'markdown', + source: '# HEAD', + metadata: {} + } + ]; + const notebook = jupyterNotebookModelToNotebookData({ cells }, 'python'); + assert.ok(notebook); + + const jsonStr = serializeNotebookToString(notebook); + const bytes = serializeNotebookToBytes(notebook); + + assert.strictEqual(jsonStr, new TextDecoder().decode(bytes)); + }); + + test.only('JSON serialization (with metadata containing functions & cells)', () => { + const cells: nbformat.ICell[] = [ + { + cell_type: 'code', + execution_count: 10, + outputs: [], + source: 'print(1)', + metadata: { + jupyter: { + outputs_hidden: true, + scrolled: false + }, + custom: { + test: function () { + return 'Hello'; + } + } as any + } + }, + { + cell_type: 'markdown', + source: '# HEAD', + metadata: {} + } + ]; + const notebook = jupyterNotebookModelToNotebookData({ cells, metadata: { kernelspec: { display_name: 'foo', name: 'bar' }, language_info: { name: 'python', mimetype: 'xyz', file_extension: '.py' }, orig_nbformat: 4 } }, 'python'); + assert.ok(notebook); + + const jsonStr = serializeNotebookToString(notebook); + const bytes = serializeNotebookToBytes(notebook); + + assert.strictEqual(jsonStr, new TextDecoder().decode(bytes)); + }); + suite('Outputs', () => { function validateCellOutputTranslation( outputs: nbformat.IOutput[], @@ -706,4 +788,5 @@ suite(`ipynb serializer`, () => { }); }); }); + }); diff --git a/extensions/ipynb/tsconfig.json b/extensions/ipynb/tsconfig.json index 2a6cc47eeeb9..ee21f68d22a2 100644 --- a/extensions/ipynb/tsconfig.json +++ b/extensions/ipynb/tsconfig.json @@ -6,7 +6,6 @@ }, "include": [ "src/**/*", - "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.documentPaste.d.ts" + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/jake/package-lock.json b/extensions/jake/package-lock.json index ff50538c25e2..65202510fc94 100644 --- a/extensions/jake/package-lock.json +++ b/extensions/jake/package-lock.json @@ -9,26 +9,28 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "*" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/jake/package.json b/extensions/jake/package.json index 1d5d1250db0e..234bc2d3723c 100644 --- a/extensions/jake/package.json +++ b/extensions/jake/package.json @@ -18,7 +18,7 @@ }, "dependencies": {}, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "main": "./out/main", "activationEvents": [ diff --git a/extensions/java/language-configuration.json b/extensions/java/language-configuration.json index 610adc686b45..6ba09bbd15c0 100644 --- a/extensions/java/language-configuration.json +++ b/extensions/java/language-configuration.json @@ -1,28 +1,85 @@ { "comments": { "lineComment": "//", - "blockComment": [ "/*", "*/" ] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - { "open": "\"", "close": "\"", "notIn": ["string"] }, - { "open": "'", "close": "'", "notIn": ["string"] }, - { "open": "/**", "close": " */", "notIn": ["string"] } + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string" + ] + }, + { + "open": "/**", + "close": " */", + "notIn": [ + "string" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"], - ["<", ">"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "'", + "'" + ], + [ + "<", + ">" + ] ], "folding": { "markers": { @@ -97,6 +154,19 @@ "action": { "indent": "indent" } - } + }, + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, ] } diff --git a/extensions/javascript/javascript-language-configuration.json b/extensions/javascript/javascript-language-configuration.json index f7c332337cb8..20827982072c 100644 --- a/extensions/javascript/javascript-language-configuration.json +++ b/extensions/javascript/javascript-language-configuration.json @@ -228,5 +228,18 @@ "appendText": "\t", } }, + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/([dgimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/([dgimsuvy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.js" } }, - "end": "(/)([dgimsuy]*)", + "end": "(/)([dgimsuvy]*)", "endCaptures": { "1": { "name": "punctuation.definition.string.end.js" @@ -4912,13 +4921,13 @@ }, { "name": "string.regexp.js", - "begin": "((?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/([dgimsuy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", + "begin": "(?|&&|\\|\\||\\*\\/)\\s*(\\/)(?![\\/*])(?=(?:[^\\/\\\\\\[\\()]|\\\\.|\\[([^\\]\\\\]|\\\\.)+\\]|\\(([^\\)\\\\]|\\\\.)+\\))+\\/([dgimsuvy]+|(?![\\/\\*])|(?=\\/\\*))(?!\\s*[a-zA-Z0-9_$]))", "beginCaptures": { "1": { "name": "punctuation.definition.string.begin.js.jsx" } }, - "end": "(/)([dgimsuy]*)", + "end": "(/)([dgimsuvy]*)", "endCaptures": { "1": { "name": "punctuation.definition.string.end.js.jsx" @@ -4912,13 +4921,13 @@ }, { "name": "string.regexp.js.jsx", - "begin": "((? { + extensions.allAcrossExtensionHosts.forEach(extension => { const packageJSON = extension.packageJSON; if (packageJSON && packageJSON.contributes && packageJSON.contributes.jsonValidation) { const jsonValidation = packageJSON.contributes.jsonValidation; diff --git a/extensions/json-language-features/client/src/languageStatus.ts b/extensions/json-language-features/client/src/languageStatus.ts index 6f3d7468b2ef..1064a0b59561 100644 --- a/extensions/json-language-features/client/src/languageStatus.ts +++ b/extensions/json-language-features/client/src/languageStatus.ts @@ -185,13 +185,13 @@ export function createLanguageStatusItem(documentSelector: DocumentSelector, sta const schemas = (await statusRequest(document.uri.toString())).schemas; statusItem.detail = undefined; if (schemas.length === 0) { - statusItem.text = l10n.t('No Schema Validation'); + statusItem.text = l10n.t('No schema validation'); statusItem.detail = l10n.t('no JSON schema configured'); } else if (schemas.length === 1) { - statusItem.text = l10n.t('Schema Validated'); + statusItem.text = l10n.t('Schema validated'); statusItem.detail = l10n.t('JSON schema configured'); } else { - statusItem.text = l10n.t('Schema Validated'); + statusItem.text = l10n.t('Schema validated'); statusItem.detail = l10n.t('multiple JSON schemas configured'); } statusItem.command = { diff --git a/extensions/json-language-features/package-lock.json b/extensions/json-language-features/package-lock.json index e3ff4b95de67..8379766d5669 100644 --- a/extensions/json-language-features/package-lock.json +++ b/extensions/json-language-features/package-lock.json @@ -9,186 +9,196 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^1.2.0", "request-light": "^0.8.0", - "vscode-languageclient": "^10.0.0-next.13" + "vscode-languageclient": "^10.0.0-next.19" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.77.0" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.2.0.tgz", + "integrity": "sha512-En6dTwfy5NFzSMibvOpx/lKq2jtgWuR4++KJbi3SpQ2iT8gm+PHo9868/scocW122KDwTxl4ruxZ7i4rHmJJnQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" } }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c= sha512-9Y0g0Q8rmSt+H33DfKv7FOc3v+iRI+o1lbzt8jGcIosYW37IIW/2XVYq5NPdmaD5NQ59Nk26Kl/vZbwW9Fr8vg==" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -200,12 +210,10 @@ "integrity": "sha512-bH6E4PMmsEXYrLX6Kr1vu+xI3HproB1vECAwaPSJeroLE1kpWE3HR27uB4icx+6YORu1ajqBJXxuedv8ZQg5Lw==" }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -214,50 +222,50 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.6.tgz", - "integrity": "sha512-KCSvUNsFiVciG9iqjJKBZOd66CN3ZKohDlYRmoOi+pd8l15MFLZ8wRG4c+wuzePGba/8WcCG2TM+C/GVlvuaeA==", + "version": "9.0.0-next.11", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.11.tgz", + "integrity": "sha512-u6LElQNbSiE9OugEEmrUKwH6+8BpPz2S5MDHvQUqHL//I4Q8GPikKLOUf856UnbLkZdhxaPrExac1lA3XwpIPA==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageclient": { - "version": "10.0.0-next.13", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.13.tgz", - "integrity": "sha512-KLsOMJoYpkk36PIgcOjyZ4AekOfzp4kdWdRRbVKeVvSIrwrn/4RSZr0NlD6EvUBBJSsJW4WDrYY7Y3znkqa6+w==", + "version": "10.0.0-next.19", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.19.tgz", + "integrity": "sha512-sJtO8y0Dxs4ue/DK0QgO/ATBfZwQdee3TqvCsoqUej/GZrBA01DTf4pbfswRxHsTxN2yH0haImUnMafWHtE4CQ==", + "license": "MIT", "dependencies": { - "minimatch": "^9.0.3", - "semver": "^7.6.0", - "vscode-languageserver-protocol": "3.17.6-next.11" + "minimatch": "^10.0.3", + "semver": "^7.7.1", + "vscode-languageserver-protocol": "3.17.6-next.16" }, "engines": { "vscode": "^1.91.0" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.11.tgz", - "integrity": "sha512-GeJxEp1TiLsp79f8WG5n10wLViXfgFKb99hU9K8m7KDWM95/QFEqWkm79f9LVm54tUK74I91a9EeiQLCS/FABQ==", + "version": "3.17.6-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.16.tgz", + "integrity": "sha512-kQTjXEuyxMbdmmZ3U+Lib3oUl12xEKNc73RtWxPSDS3TFtjVwt98Q1CUzfDA9EUpsA24M46Bl6q3sLe9AUOKyw==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" + "vscode-jsonrpc": "9.0.0-next.11", + "vscode-languageserver-types": "3.17.6-next.6" } }, "node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.5.tgz", - "integrity": "sha512-QFmf3Yl1tCgUQfA77N9Me/LXldJXkIVypQbty2rJ1DNHQkC+iwvm4Z2tXg9czSwlhvv0pD4pbF5mT7WhAglolw==" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "3.17.6-next.6", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.6.tgz", + "integrity": "sha512-aiJY5/yW+xzw7KPNlwi3gQtddq/3EIn5z8X8nCgJfaiAij2R1APKePngv+MUdLdYJBVTLu+Qa0ODsT+pHgYguQ==", + "license": "MIT" } } } diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index cb6bc601e1d9..64e5a781fa30 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -139,6 +139,12 @@ "strings": true }, "editor.suggest.insertMode": "replace" + }, + "[snippets]": { + "editor.quickSuggestions": { + "strings": true + }, + "editor.suggest.insertMode": "replace" } }, "jsonValidation": [ @@ -161,12 +167,12 @@ ] }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^1.2.0", "request-light": "^0.8.0", - "vscode-languageclient": "^10.0.0-next.13" + "vscode-languageclient": "^10.0.0-next.19" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/json-language-features/server/package-lock.json b/extensions/json-language-features/server/package-lock.json index b4a35d28149c..2f4201db9e41 100644 --- a/extensions/json-language-features/server/package-lock.json +++ b/extensions/json-language-features/server/package-lock.json @@ -12,7 +12,7 @@ "@vscode/l10n": "^0.0.18", "jsonc-parser": "^3.3.1", "request-light": "^0.8.0", - "vscode-json-languageservice": "^5.4.1", + "vscode-json-languageservice": "^5.4.3", "vscode-languageserver": "^10.0.0-next.11", "vscode-uri": "^3.0.8" }, @@ -20,26 +20,28 @@ "vscode-json-languageserver": "bin/vscode-json-languageserver" }, "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x" + "@types/mocha": "^10.0.10", + "@types/node": "25.x" }, "engines": { "node": "*" } }, "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.12.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.5.tgz", - "integrity": "sha512-BD+BjQ9LS/D8ST9p5uqBxghlN+S42iuNxjsUGjeZobe/ciXzk2qb1B6IXc6AnRLS+yFJRpN2IPEHMzwspfDJNw==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@vscode/l10n": { @@ -58,55 +60,61 @@ "integrity": "sha512-bH6E4PMmsEXYrLX6Kr1vu+xI3HproB1vECAwaPSJeroLE1kpWE3HR27uB4icx+6YORu1ajqBJXxuedv8ZQg5Lw==" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-json-languageservice": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.4.1.tgz", - "integrity": "sha512-5czFGNyVPxz3ZJYl8R3a3SuIj5gjhmGF4Wv05MRPvD4DEnHK6b8km4VbNMJNHBlTCh7A0aHzUbPVzo+0C18mCA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.7.1.tgz", + "integrity": "sha512-sMK2F8p7St0lJCr/4IfbQRoEUDUZRR7Ud0IiSl8I/JtN+m9Gv+FJlNkSAYns2R7Ebm/PKxqUuWYOfBej/rAdBQ==", + "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", "jsonc-parser": "^3.3.1", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "^3.17.5", - "vscode-uri": "^3.0.8" + "vscode-uri": "^3.1.0" } }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.6.tgz", - "integrity": "sha512-KCSvUNsFiVciG9iqjJKBZOd66CN3ZKohDlYRmoOi+pd8l15MFLZ8wRG4c+wuzePGba/8WcCG2TM+C/GVlvuaeA==", + "version": "9.0.0-next.11", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.11.tgz", + "integrity": "sha512-u6LElQNbSiE9OugEEmrUKwH6+8BpPz2S5MDHvQUqHL//I4Q8GPikKLOUf856UnbLkZdhxaPrExac1lA3XwpIPA==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageserver": { - "version": "10.0.0-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.11.tgz", - "integrity": "sha512-cmobSrVDYhlh/t02vz/bV8nNpds8mus5HnILULae2iAvOjoaJPnTAp0jJWoYdUqTpIVzT9JV6JMKqLEvdqpeqg==", + "version": "10.0.0-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.16.tgz", + "integrity": "sha512-RbsYDOhddv1NtBCAR7+oVxxCmOpQUHhrtgUE0xz6J+BJGSCkfOqBCyLUIwSjKk2rK9llxUj/pR5aL8QCsXrxow==", + "license": "MIT", "dependencies": { - "vscode-languageserver-protocol": "3.17.6-next.11" + "vscode-languageserver-protocol": "3.17.6-next.16" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.11.tgz", - "integrity": "sha512-GeJxEp1TiLsp79f8WG5n10wLViXfgFKb99hU9K8m7KDWM95/QFEqWkm79f9LVm54tUK74I91a9EeiQLCS/FABQ==", + "version": "3.17.6-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.16.tgz", + "integrity": "sha512-kQTjXEuyxMbdmmZ3U+Lib3oUl12xEKNc73RtWxPSDS3TFtjVwt98Q1CUzfDA9EUpsA24M46Bl6q3sLe9AUOKyw==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" + "vscode-jsonrpc": "9.0.0-next.11", + "vscode-languageserver-types": "3.17.6-next.6" } }, "node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.5.tgz", - "integrity": "sha512-QFmf3Yl1tCgUQfA77N9Me/LXldJXkIVypQbty2rJ1DNHQkC+iwvm4Z2tXg9czSwlhvv0pD4pbF5mT7WhAglolw==" + "version": "3.17.6-next.6", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.6.tgz", + "integrity": "sha512-aiJY5/yW+xzw7KPNlwi3gQtddq/3EIn5z8X8nCgJfaiAij2R1APKePngv+MUdLdYJBVTLu+Qa0ODsT+pHgYguQ==", + "license": "MIT" }, "node_modules/vscode-languageserver-textdocument": { "version": "1.0.12", @@ -119,9 +127,10 @@ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" }, "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" } } } diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index 5560cf7c660b..cd0a660c53b9 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -15,13 +15,13 @@ "@vscode/l10n": "^0.0.18", "jsonc-parser": "^3.3.1", "request-light": "^0.8.0", - "vscode-json-languageservice": "^5.4.1", + "vscode-json-languageservice": "^5.4.3", "vscode-languageserver": "^10.0.0-next.11", "vscode-uri": "^3.0.8" }, "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x" + "@types/mocha": "^10.0.10", + "@types/node": "25.x" }, "scripts": { "prepublishOnly": "npm run clean && npm run compile", @@ -29,6 +29,7 @@ "watch": "npx gulp watch-extension:json-language-features-server", "clean": "../../../node_modules/.bin/rimraf out", "install-service-next": "npm install vscode-json-languageservice@next", + "install-service-latest": "npm install vscode-json-languageservice", "install-service-local": "npm link vscode-json-languageservice", "install-server-next": "npm install vscode-languageserver@next", "install-server-local": "npm link vscode-languageserver-server", diff --git a/extensions/json-language-features/server/src/node/jsonServerMain.ts b/extensions/json-language-features/server/src/node/jsonServerMain.ts index ad1ae439e599..71da2377c77f 100644 --- a/extensions/json-language-features/server/src/node/jsonServerMain.ts +++ b/extensions/json-language-features/server/src/node/jsonServerMain.ts @@ -9,7 +9,8 @@ import { RequestService, RuntimeEnvironment, startServer } from '../jsonServer'; import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light'; import { URI as Uri } from 'vscode-uri'; -import * as fs from 'fs'; +import { promises as fs } from 'fs'; +import * as l10n from '@vscode/l10n'; // Create a connection for the server. const connection: Connection = createConnection(); @@ -36,16 +37,18 @@ function getHTTPRequestService(): RequestService { function getFileRequestService(): RequestService { return { - getContent(location: string, encoding?: BufferEncoding) { - return new Promise((c, e) => { + async getContent(location: string, encoding?: BufferEncoding) { + try { const uri = Uri.parse(location); - fs.readFile(uri.fsPath, encoding, (err, buf) => { - if (err) { - return e(err); - } - c(buf.toString()); - }); - }); + return (await fs.readFile(uri.fsPath, encoding)).toString(); + } catch (e) { + if (e.code === 'ENOENT') { + throw new Error(l10n.t('Schema not found: {0}', location)); + } else if (e.code === 'EISDIR') { + throw new Error(l10n.t('{0} is a directory, not a file', location)); + } + throw e; + } } }; } diff --git a/extensions/json/build/update-grammars.js b/extensions/json/build/update-grammars.js index 2b7f76f8f909..13356a2c4c4e 100644 --- a/extensions/json/build/update-grammars.js +++ b/extensions/json/build/update-grammars.js @@ -9,7 +9,7 @@ var updateGrammar = require('vscode-grammar-updater'); function adaptJSON(grammar, name, replacementScope, replaceeScope = 'json') { grammar.name = name; grammar.scopeName = `source${replacementScope}`; - const regex = new RegExp(`\.${replaceeScope}`, 'g'); + const regex = new RegExp(`\\.${replaceeScope}`, 'g'); var fixScopeNames = function (rule) { if (typeof rule.name === 'string') { rule.name = rule.name.replace(regex, replacementScope); diff --git a/extensions/json/language-configuration.json b/extensions/json/language-configuration.json index f9ec3fec7810..d47efe2587ed 100644 --- a/extensions/json/language-configuration.json +++ b/extensions/json/language-configuration.json @@ -1,22 +1,84 @@ { "comments": { "lineComment": "//", - "blockComment": [ "/*", "*/" ] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"] + [ + "{", + "}" + ], + [ + "[", + "]" + ] ], "autoClosingPairs": [ - { "open": "{", "close": "}", "notIn": ["string"] }, - { "open": "[", "close": "]", "notIn": ["string"] }, - { "open": "(", "close": ")", "notIn": ["string"] }, - { "open": "'", "close": "'", "notIn": ["string"] }, - { "open": "\"", "close": "\"", "notIn": ["string", "comment"] }, - { "open": "`", "close": "`", "notIn": ["string", "comment"] } + { + "open": "{", + "close": "}", + "notIn": [ + "string" + ] + }, + { + "open": "[", + "close": "]", + "notIn": [ + "string" + ] + }, + { + "open": "(", + "close": ")", + "notIn": [ + "string" + ] + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string" + ] + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "`", + "close": "`", + "notIn": [ + "string", + "comment" + ] + } ], "indentationRules": { "increaseIndentPattern": "({+(?=((\\\\.|[^\"\\\\])*\"(\\\\.|[^\"\\\\])*\")*[^\"}]*)$)|(\\[+(?=((\\\\.|[^\"\\\\])*\"(\\\\.|[^\"\\\\])*\")*[^\"\\]]*)$)", "decreaseIndentPattern": "^\\s*[}\\]],?\\s*$" - } + }, + "onEnterRules": [ + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, + ] } diff --git a/extensions/json/package.json b/extensions/json/package.json index 8844345d6722..cd7cfb2fff9e 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -62,6 +62,7 @@ ], "filenames": [ "babel.config.json", + "bun.lock", ".babelrc.json", ".ember-cli", "typedoc.json" @@ -74,7 +75,8 @@ "JSON Lines" ], "extensions": [ - ".jsonl" + ".jsonl", + ".ndjson" ], "filenames": [], "configuration": "./language-configuration.json" diff --git a/extensions/julia/package.json b/extensions/julia/package.json index 31cdd4ce71a4..12d38ed31b24 100644 --- a/extensions/julia/package.json +++ b/extensions/julia/package.json @@ -50,6 +50,11 @@ "meta.embedded.inline.sql": "sql" } } - ] + ], + "configurationDefaults": { + "[julia]": { + "editor.defaultColorDecorators": "never" + } + } } } diff --git a/extensions/latex/cgmanifest.json b/extensions/latex/cgmanifest.json index 609d875ac2fa..d937ba4f4304 100644 --- a/extensions/latex/cgmanifest.json +++ b/extensions/latex/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "jlelong/vscode-latex-basics", "repositoryUrl": "https://github.com/jlelong/vscode-latex-basics", - "commitHash": "56e2dc967e6bafafc1acfeeb80af42b8328b021a" + "commitHash": "df6ef817c932d24da5cc72927344a547e463cc65" } }, "license": "MIT", - "version": "1.7.0", + "version": "1.9.0", "description": "The files in syntaxes/ were originally part of https://github.com/James-Yu/LaTeX-Workshop. They have been extracted in the hope that they can useful outside of the LaTeX-Workshop extension.", "licenseDetail": [ "Copyright (c) vscode-latex-basics authors", diff --git a/extensions/latex/syntaxes/LaTeX.tmLanguage.json b/extensions/latex/syntaxes/LaTeX.tmLanguage.json index e06d85538464..06c4c59c60e8 100644 --- a/extensions/latex/syntaxes/LaTeX.tmLanguage.json +++ b/extensions/latex/syntaxes/LaTeX.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/jlelong/vscode-latex-basics/commit/59971565a7065dbb617576c04add9d891b056319", + "version": "https://github.com/jlelong/vscode-latex-basics/commit/7b75bae583f3f9802c533e021f882428872c572c", "name": "LaTeX", "scopeName": "text.tex.latex", "patterns": [ @@ -761,6 +761,49 @@ } ] }, + { + "begin": "\\s*\\\\begin\\{(?:javacode|javaverbatim|javablock|javaconcode|javaconsole|javaconverbatim)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{|\\s*$)", + "end": "\\s*\\\\end\\{(?:javacode|javaverbatim|javablock|javaconcode|javaconsole|javaconverbatim)\\*?\\}", + "captures": { + "0": { + "patterns": [ + { + "include": "#begin-env-tokenizer" + } + ] + } + }, + "patterns": [ + { + "include": "#multiline-optional-arg-no-highlight" + }, + { + "begin": "(?:\\G|(?<=\\]))(\\{)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.arguments.begin.latex" + } + }, + "end": "(\\})", + "endCaptures": { + "1": { + "name": "punctuation.definition.arguments.end.latex" + } + }, + "contentName": "variable.parameter.function.latex" + }, + { + "begin": "^(?=\\s*)", + "end": "^\\s*(?=\\\\end\\{(?:javacode|javaverbatim|javablock|javaconcode|javaconsole|javaconverbatim)\\*?\\})", + "contentName": "source.java", + "patterns": [ + { + "include": "source.java" + } + ] + } + ] + }, { "begin": "\\s*\\\\begin\\{(?:jlcode|jlverbatim|jlblock|jlconcode|jlconsole|jlconverbatim)\\*?\\}(?:\\[[a-zA-Z0-9_-]*\\])?(?=\\[|\\{|\\s*$)", "end": "\\s*\\\\end\\{(?:jlcode|jlverbatim|jlblock|jlconcode|jlconsole|jlconverbatim)\\*?\\}", @@ -1106,7 +1149,7 @@ ] }, { - "begin": "\\s*\\\\begin\\{([a-zA-Z]*code|lstlisting|minted|pyglist)\\*?\\}(?:\\[.*\\])?(?:\\{.*\\})?", + "begin": "\\s*\\\\begin\\{((?:[a-zA-Z]*code|lstlisting|minted|pyglist)\\*?)\\}(?:\\[.*\\])?(?:\\{.*\\})?", "captures": { "0": { "patterns": [ @@ -2306,16 +2349,16 @@ ] } }, - "contentName": "meta.embedded.markdown_latex_combined", + "contentName": "meta.embedded.internal_only_markdown_latex_combined", "end": "(\\\\end\\{markdown\\})", "patterns": [ { - "include": "text.tex.markdown_latex_combined" + "include": "text.tex.internal_only_markdown_latex_combined" } ] }, { - "begin": "(\\s*\\\\begin\\{(\\w+\\*?)\\})", + "begin": "(\\s*\\\\begin\\{(\\p{Alphabetic}+\\*?)\\})", "captures": { "1": { "patterns": [ @@ -3149,7 +3192,7 @@ "name": "punctuation.definition.arguments.end.latex" } }, - "match": "\\s*((\\\\)(?:begin|end))(\\{)([a-zA-Z]*\\*?)(\\})(?:(\\[)([^\\]]*)(\\])){,2}(?:(\\{)([^{}]*)(\\}))?" + "match": "\\s*((\\\\)(?:begin|end))(\\{)(\\p{Alphabetic}+\\*?)(\\})(?:(\\[)([^\\]]*)(\\])){,2}(?:(\\{)([^{}]*)(\\}))?" }, "definition-label": { "begin": "((\\\\)z?label)((?:\\[[^\\[]*?\\])*)(\\{)", diff --git a/extensions/latex/syntaxes/cpp-grammar-bailout.tmLanguage.json b/extensions/latex/syntaxes/cpp-grammar-bailout.tmLanguage.json index 1f62f492b76f..4f70702d0bcf 100644 --- a/extensions/latex/syntaxes/cpp-grammar-bailout.tmLanguage.json +++ b/extensions/latex/syntaxes/cpp-grammar-bailout.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/jlelong/vscode-latex-basics/commit/f17f354528411340e22402230be1b72e5aa1126a", + "version": "https://github.com/jlelong/vscode-latex-basics/commit/dfa69a16a1154dbc820dc1111d72faa6954dd1e2", "name": "C++", "scopeName": "source.cpp.embedded.latex", "patterns": [ @@ -20,6 +20,9 @@ { "include": "#function_definition" }, + { + "include": "#simple_array_assignment" + }, { "include": "#operator_overload" }, @@ -98,7 +101,7 @@ ], "repository": { "access_control_keywords": { - "match": "((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(((?:(?:protected)|(?:private)|(?:public)))(?:\\s+)?(:))", + "match": "((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(((?:protected|private|public))(?:\\s+)?(:))", "captures": { "1": { "patterns": [ @@ -516,10 +519,16 @@ "name": "punctuation.definition.comment.end.cpp" } }, - "name": "comment.block.cpp" + "name": "comment.block.cpp", + "patterns": [ + { + "match": "[^\\*]*\\n" + } + ], + "applyEndPatternLast": 1 }, "builtin_storage_type_initilizer": { - "begin": "\\s*+(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:(?:(\\b[a-z0-9]+\\b)|(\\b[a-zA-Z0-9]+_[a-zA-Z0-9]*\\b))|(\\b[a-z]+[A-Z][a-zA-Z0-9]*\\b))|(\\b[A-Z][A-Z_0-9]*\\b))|((?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=\\\\end\\{(?:minted|cppcode)\\})", "beginCaptures": { "0": { @@ -1936,7 +2162,7 @@ ] }, "control_flow_keywords": { - "match": "((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(\\{)", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(\\{)", "end": "\\}|(?=\\\\end\\{(?:minted|cppcode)\\})", "beginCaptures": { "1": { - "name": "meta.qualified_type.cpp", + "name": "meta.qualified-type.cpp", "patterns": [ { "match": "::", "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, { - "match": "(?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=\\\\end\\{(?:minted|cppcode)\\})", "beginCaptures": { "0": { @@ -3442,6 +3668,15 @@ { "include": "#language_constants" }, + { + "include": "#constructor_bracket_call" + }, + { + "include": "#simple_constructor_call" + }, + { + "include": "#simple_array_assignment" + }, { "include": "#builtin_storage_type_initilizer" }, @@ -3477,6 +3712,9 @@ }, { "include": "#comma" + }, + { + "include": "#unknown_variable" } ] }, @@ -3503,9 +3741,6 @@ { "include": "#preprocessor_conditional_range" }, - { - "include": "#single_line_macro" - }, { "include": "#macro" }, @@ -3524,7 +3759,7 @@ ] }, "exception_keywords": { - "match": "((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?(\\()", - "end": "\\)|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "1": { - "patterns": [ - { - "include": "#scope_resolution_function_call_inner_generated" - } - ] - }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" - }, - "3": { - "patterns": [ - { - "include": "#template_call_range_helper" - } - ] - }, - "4": {}, - "5": { - "name": "entity.name.function.call.cpp" - }, - "6": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "7": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "8": { - "name": "comment.block.cpp" - }, - "9": { - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - "10": { - "name": "meta.template.call.cpp", + "patterns": [ + { + "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*\\s*+)([A-Z][A-Z_0-9]*)\\b(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?(\\()", + "end": "\\)|(?=\\\\end\\{(?:minted|cppcode)\\})", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#scope_resolution_function_call_inner_generated" + } + ] + }, + "2": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" + }, + "3": { + "patterns": [ + { + "include": "#template_call_range_helper" + } + ] + }, + "4": {}, + "5": { + "name": "entity.name.function.call.upper-case.cpp entity.name.function.call.cpp" + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "8": { + "name": "comment.block.cpp" + }, + "9": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "10": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range_helper" + } + ] + }, + "11": {}, + "12": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "13": { + "name": "comment.block.cpp" + }, + "14": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "15": { + "name": "punctuation.section.arguments.begin.bracket.round.function.call.cpp punctuation.section.arguments.begin.bracket.round.function.call.upper-case.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.function.call.cpp punctuation.section.arguments.begin.bracket.round.function.call.upper-case.cpp" + } + }, "patterns": [ { - "include": "#template_call_range_helper" + "include": "#evaluation_context" } ] }, - "11": {}, - "12": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "13": { - "name": "comment.block.cpp" - }, - "14": { - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - "15": { - "name": "punctuation.section.arguments.begin.bracket.round.function.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.arguments.end.bracket.round.function.call.cpp" - } - }, - "patterns": [ { - "include": "#evaluation_context" + "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?(\\()", + "end": "\\)|(?=\\\\end\\{(?:minted|cppcode)\\})", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#scope_resolution_function_call_inner_generated" + } + ] + }, + "2": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.function.call.cpp" + }, + "3": { + "patterns": [ + { + "include": "#template_call_range_helper" + } + ] + }, + "4": {}, + "5": { + "name": "entity.name.function.call.cpp" + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "8": { + "name": "comment.block.cpp" + }, + "9": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "10": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range_helper" + } + ] + }, + "11": {}, + "12": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "13": { + "name": "comment.block.cpp" + }, + "14": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "15": { + "name": "punctuation.section.arguments.begin.bracket.round.function.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.function.call.cpp" + } + }, + "patterns": [ + { + "include": "#evaluation_context" + } + ] } ] }, "function_definition": { - "begin": "(?:(?:^|\\G|(?<=;|\\}))|(?<=>|\\*\\/))\\s*+(?:((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?|\\*\\/))\\s*+(?:((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=\\\\end\\{(?:minted|cppcode)\\})", "beginCaptures": { "0": { @@ -3837,7 +4153,7 @@ "7": { "patterns": [ { - "match": "((?)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))", + "match": "(?<=^|\\))(?:\\s+)?(->)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))", "captures": { "1": { "name": "punctuation.definition.function.return-type.cpp" @@ -4209,14 +4525,14 @@ "name": "comment.block.cpp punctuation.definition.comment.end.cpp" }, "6": { - "name": "meta.qualified_type.cpp", + "name": "meta.qualified-type.cpp", "patterns": [ { "match": "::", "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, { - "match": "(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(\\()(\\*)(?:\\s+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:\\s+)?(?:(\\[)(\\w*)(\\])(?:\\s+)?)*(\\))(?:\\s+)?(\\()", - "end": "(\\))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?]|\\n)(?!\\()|(?=\\\\end\\{(?:minted|cppcode)\\})", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(\\()(\\*)(?:\\s+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:\\s+)?(?:(\\[)(\\w*)(\\])(?:\\s+)?)*(\\))(?:\\s+)?(\\()", + "end": "(\\))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?]|\\n)(?!\\()|(?=\\\\end\\{(?:minted|cppcode)\\})", "beginCaptures": { "1": { - "name": "meta.qualified_type.cpp", + "name": "meta.qualified-type.cpp", "patterns": [ { "match": "::", "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, { - "match": "(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(\\()(\\*)(?:\\s+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:\\s+)?(?:(\\[)(\\w*)(\\])(?:\\s+)?)*(\\))(?:\\s+)?(\\()", - "end": "(\\))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?]|\\n)(?!\\()|(?=\\\\end\\{(?:minted|cppcode)\\})", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(\\()(\\*)(?:\\s+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:\\s+)?(?:(\\[)(\\w*)(\\])(?:\\s+)?)*(\\))(?:\\s+)?(\\()", + "end": "(\\))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?]|\\n)(?!\\()|(?=\\\\end\\{(?:minted|cppcode)\\})", "beginCaptures": { "1": { - "name": "meta.qualified_type.cpp", + "name": "meta.qualified-type.cpp", "patterns": [ { "match": "::", "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, { - "match": "(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))", + "match": "(?<=protected|virtual|private|public|,|:)(?:\\s+)?(?!(?:(?:protected|private|public)|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))", "captures": { "1": { - "name": "meta.qualified_type.cpp", + "name": "meta.qualified-type.cpp", "patterns": [ { "match": "::", "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, { - "match": "(?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\s+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:\\s+)?)*)(?:\\s+)?(\\b(?!uint_least32_t[^\\w]|uint_least16_t[^\\w]|uint_least64_t[^\\w]|int_least32_t[^\\w]|int_least64_t[^\\w]|uint_fast32_t[^\\w]|uint_fast64_t[^\\w]|uint_least8_t[^\\w]|uint_fast16_t[^\\w]|int_least16_t[^\\w]|int_fast16_t[^\\w]|int_least8_t[^\\w]|uint_fast8_t[^\\w]|int_fast64_t[^\\w]|int_fast32_t[^\\w]|int_fast8_t[^\\w]|suseconds_t[^\\w]|useconds_t[^\\w]|in_addr_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|in_port_t[^\\w]|uintptr_t[^\\w]|blksize_t[^\\w]|uint32_t[^\\w]|uint64_t[^\\w]|u_quad_t[^\\w]|intmax_t[^\\w]|intmax_t[^\\w]|unsigned[^\\w]|blkcnt_t[^\\w]|uint16_t[^\\w]|intptr_t[^\\w]|swblk_t[^\\w]|wchar_t[^\\w]|u_short[^\\w]|qaddr_t[^\\w]|caddr_t[^\\w]|daddr_t[^\\w]|fixpt_t[^\\w]|nlink_t[^\\w]|segsz_t[^\\w]|clock_t[^\\w]|ssize_t[^\\w]|int16_t[^\\w]|int32_t[^\\w]|int64_t[^\\w]|uint8_t[^\\w]|int8_t[^\\w]|mode_t[^\\w]|quad_t[^\\w]|ushort[^\\w]|u_long[^\\w]|u_char[^\\w]|double[^\\w]|signed[^\\w]|time_t[^\\w]|size_t[^\\w]|key_t[^\\w]|div_t[^\\w]|ino_t[^\\w]|uid_t[^\\w]|gid_t[^\\w]|off_t[^\\w]|pid_t[^\\w]|float[^\\w]|dev_t[^\\w]|u_int[^\\w]|short[^\\w]|bool[^\\w]|id_t[^\\w]|uint[^\\w]|long[^\\w]|char[^\\w]|void[^\\w]|auto[^\\w]|id_t[^\\w]|int[^\\w])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", + "match": "(?:((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\s+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:\\s+)?)*)(?:\\s+)?(\\b(?!uint_least32_t[^\\w]|uint_least16_t[^\\w]|uint_least64_t[^\\w]|int_least32_t[^\\w]|int_least64_t[^\\w]|uint_fast32_t[^\\w]|uint_fast64_t[^\\w]|uint_least8_t[^\\w]|uint_fast16_t[^\\w]|int_least16_t[^\\w]|int_fast16_t[^\\w]|int_least8_t[^\\w]|uint_fast8_t[^\\w]|int_fast64_t[^\\w]|int_fast32_t[^\\w]|int_fast8_t[^\\w]|suseconds_t[^\\w]|useconds_t[^\\w]|in_addr_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|in_port_t[^\\w]|uintptr_t[^\\w]|blksize_t[^\\w]|uint32_t[^\\w]|uint64_t[^\\w]|u_quad_t[^\\w]|intmax_t[^\\w]|intmax_t[^\\w]|unsigned[^\\w]|blkcnt_t[^\\w]|uint16_t[^\\w]|intptr_t[^\\w]|swblk_t[^\\w]|wchar_t[^\\w]|u_short[^\\w]|qaddr_t[^\\w]|caddr_t[^\\w]|daddr_t[^\\w]|fixpt_t[^\\w]|nlink_t[^\\w]|segsz_t[^\\w]|clock_t[^\\w]|ssize_t[^\\w]|int16_t[^\\w]|int32_t[^\\w]|int64_t[^\\w]|uint8_t[^\\w]|int8_t[^\\w]|mode_t[^\\w]|quad_t[^\\w]|ushort[^\\w]|u_long[^\\w]|u_char[^\\w]|double[^\\w]|signed[^\\w]|time_t[^\\w]|size_t[^\\w]|key_t[^\\w]|div_t[^\\w]|ino_t[^\\w]|uid_t[^\\w]|gid_t[^\\w]|off_t[^\\w]|pid_t[^\\w]|float[^\\w]|dev_t[^\\w]|u_int[^\\w]|short[^\\w]|bool[^\\w]|id_t[^\\w]|uint[^\\w]|long[^\\w]|char[^\\w]|void[^\\w]|auto[^\\w]|id_t[^\\w]|int[^\\w])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", "captures": { "1": { "patterns": [ @@ -5881,18 +6197,30 @@ "name": "variable.language.this.cpp" }, "4": { - "name": "variable.other.object.access.cpp" + "name": "variable.lower-case.cpp variable.other.object.access.$4.cpp" }, "5": { - "name": "punctuation.separator.dot-access.cpp" + "name": "variable.snake-case.cpp variable.other.object.access.$5.cpp" }, "6": { - "name": "punctuation.separator.pointer-access.cpp" + "name": "variable.camel-case.cpp variable.other.object.access.$6.cpp" }, "7": { + "name": "variable.upper-case.cpp variable.other.object.access.$7.cpp" + }, + "8": { + "name": "variable.other.unknown.$8.cpp" + }, + "9": { + "name": "punctuation.separator.dot-access.cpp" + }, + "10": { + "name": "punctuation.separator.pointer-access.cpp" + }, + "11": { "patterns": [ { - "match": "(?<=(?:\\.\\*|\\.|->|->\\*))(?:\\s+)?(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?\\*|->)))", + "match": "(?<=(?:\\.\\*|\\.|->|->\\*))(?:\\s+)?(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -5914,18 +6242,30 @@ "name": "variable.language.this.cpp" }, "6": { - "name": "variable.other.object.property.cpp" + "name": "variable.lower-case.cpp variable.other.object.property.cpp" }, "7": { - "name": "punctuation.separator.dot-access.cpp" + "name": "variable.snake-case.cpp variable.other.object.property.cpp" }, "8": { + "name": "variable.camel-case.cpp variable.other.object.property.cpp" + }, + "9": { + "name": "variable.upper-case.cpp variable.other.object.property.cpp" + }, + "10": { + "name": "variable.other.unknown.$10.cpp" + }, + "11": { + "name": "punctuation.separator.dot-access.cpp" + }, + "12": { "name": "punctuation.separator.pointer-access.cpp" } } }, { - "match": "(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?\\*|->)))", + "match": "(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -5947,12 +6287,24 @@ "name": "variable.language.this.cpp" }, "6": { - "name": "variable.other.object.access.cpp" + "name": "variable.lower-case.cpp variable.other.object.access.$6.cpp" }, "7": { - "name": "punctuation.separator.dot-access.cpp" + "name": "variable.snake-case.cpp variable.other.object.access.$7.cpp" }, "8": { + "name": "variable.camel-case.cpp variable.other.object.access.$8.cpp" + }, + "9": { + "name": "variable.upper-case.cpp variable.other.object.access.$9.cpp" + }, + "10": { + "name": "variable.other.unknown.$10.cpp" + }, + "11": { + "name": "punctuation.separator.dot-access.cpp" + }, + "12": { "name": "punctuation.separator.pointer-access.cpp" } } @@ -5965,7 +6317,7 @@ } ] }, - "8": { + "12": { "name": "variable.other.property.cpp" } } @@ -6016,7 +6368,7 @@ } }, "method_access": { - "begin": "(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\s+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:\\s+)?)*)(?:\\s+)?(~?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)(?:\\s+)?(\\()", + "begin": "(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:\\s+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:\\s+)?)*)(?:\\s+)?(~?(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)(?:\\s+)?(\\()", "end": "\\)|(?=\\\\end\\{(?:minted|cppcode)\\})", "beginCaptures": { "1": { @@ -6039,18 +6391,30 @@ "name": "variable.language.this.cpp" }, "6": { - "name": "variable.other.object.access.cpp" + "name": "variable.lower-case.cpp variable.other.object.access.$6.cpp" }, "7": { - "name": "punctuation.separator.dot-access.cpp" + "name": "variable.snake-case.cpp variable.other.object.access.$7.cpp" }, "8": { - "name": "punctuation.separator.pointer-access.cpp" + "name": "variable.camel-case.cpp variable.other.object.access.$8.cpp" }, "9": { + "name": "variable.upper-case.cpp variable.other.object.access.$9.cpp" + }, + "10": { + "name": "variable.other.unknown.$10.cpp" + }, + "11": { + "name": "punctuation.separator.dot-access.cpp" + }, + "12": { + "name": "punctuation.separator.pointer-access.cpp" + }, + "13": { "patterns": [ { - "match": "(?<=(?:\\.\\*|\\.|->|->\\*))(?:\\s+)?(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?\\*|->)))", + "match": "(?<=(?:\\.\\*|\\.|->|->\\*))(?:\\s+)?(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6072,18 +6436,30 @@ "name": "variable.language.this.cpp" }, "6": { - "name": "variable.other.object.property.cpp" + "name": "variable.lower-case.cpp variable.other.object.property.cpp" }, "7": { - "name": "punctuation.separator.dot-access.cpp" + "name": "variable.snake-case.cpp variable.other.object.property.cpp" }, "8": { + "name": "variable.camel-case.cpp variable.other.object.property.cpp" + }, + "9": { + "name": "variable.upper-case.cpp variable.other.object.property.cpp" + }, + "10": { + "name": "variable.other.unknown.$10.cpp" + }, + "11": { + "name": "punctuation.separator.dot-access.cpp" + }, + "12": { "name": "punctuation.separator.pointer-access.cpp" } } }, { - "match": "(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?\\*|->)))", + "match": "(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?\\*|->)))", "captures": { "1": { "patterns": [ @@ -6105,12 +6481,24 @@ "name": "variable.language.this.cpp" }, "6": { - "name": "variable.other.object.access.cpp" + "name": "variable.lower-case.cpp variable.other.object.access.$6.cpp" }, "7": { - "name": "punctuation.separator.dot-access.cpp" + "name": "variable.snake-case.cpp variable.other.object.access.$7.cpp" }, "8": { + "name": "variable.camel-case.cpp variable.other.object.access.$8.cpp" + }, + "9": { + "name": "variable.upper-case.cpp variable.other.object.access.$9.cpp" + }, + "10": { + "name": "variable.other.unknown.$10.cpp" + }, + "11": { + "name": "punctuation.separator.dot-access.cpp" + }, + "12": { "name": "punctuation.separator.pointer-access.cpp" } } @@ -6123,10 +6511,10 @@ } ] }, - "10": { + "14": { "name": "entity.name.function.member.cpp" }, - "11": { + "15": { "name": "punctuation.section.arguments.begin.bracket.round.function.member.cpp" } }, @@ -6142,7 +6530,7 @@ ] }, "misc_keywords": { - "match": "((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(%=|\\+=|-=|\\*=|(?>=|\\|=)|(\\=))", + "begin": "^((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:((?:(?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:(?:(\\b[a-z0-9]+\\b)|(\\b[a-zA-Z0-9]+_[a-zA-Z0-9]*\\b))|(\\b[a-z]+[A-Z][a-zA-Z0-9]*\\b))|(\\b[A-Z][A-Z_0-9]*\\b))|((?>=|\\|=))|(\\=)))", "end": "(?=;)|(?=\\\\end\\{(?:minted|cppcode)\\})", "beginCaptures": { "1": { @@ -6446,46 +6834,49 @@ "name": "comment.block.cpp punctuation.definition.comment.end.cpp" }, "5": { + "name": "meta.assignment.cpp" + }, + "6": { "patterns": [ { "include": "#storage_specifiers" } ] }, - "6": { + "7": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "7": { + "8": { "name": "comment.block.cpp" }, - "8": { + "9": { "name": "comment.block.cpp punctuation.definition.comment.end.cpp" }, - "9": { + "10": { "patterns": [ { "include": "#inline_comment" } ] }, - "10": { + "11": { "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" }, - "11": { + "12": { "name": "comment.block.cpp" }, - "12": { + "13": { "name": "comment.block.cpp punctuation.definition.comment.end.cpp" }, - "13": { - "name": "meta.qualified_type.cpp", + "14": { + "name": "meta.qualified-type.cpp", "patterns": [ { "match": "::", "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, { - "match": "(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?=;|,)", + "begin": "^((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:((?:(?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:(?:(\\b[a-z0-9]+\\b)|(\\b[a-zA-Z0-9]+_[a-zA-Z0-9]*\\b))|(\\b[a-z]+[A-Z][a-zA-Z0-9]*\\b))|(\\b[A-Z][A-Z_0-9]*\\b))|((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)(operator)(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:<=>)|(?:<<=)|(?:new)|(?:>>=)|(?:\\->\\*)|(?:\\/=)|(?:%=)|(?:&=)|(?:>=)|(?:\\|=)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:<<)|(?:>>)|(?:\\-\\-)|(?:<=)|(?:\\^=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|,|\\+|\\-|!|~|\\*|&|\\*|\\/|%|\\+|\\-|<|>|&|\\^|\\||=))|((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)(operator)(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)(?:(?:((?:delete\\[\\]|delete|new\\[\\]|<=>|<<=|new|>>=|\\->\\*|\\/=|%=|&=|>=|\\|=|\\+\\+|\\-\\-|\\(\\)|\\[\\]|\\->|\\+\\+|<<|>>|\\-\\-|<=|\\^=|==|!=|&&|\\|\\||\\+=|\\-=|\\*=|,|\\+|\\-|!|~|\\*|&|\\*|\\/|%|\\+|\\-|<|>|&|\\^|\\||=))|((?|\\?\\?>)|(?=[;>\\[\\]=]))|(?=\\\\end\\{(?:minted|cppcode)\\})", "beginCaptures": { "0": { @@ -7305,14 +7725,14 @@ "name": "comment.block.cpp punctuation.definition.comment.end.cpp" }, "5": { - "name": "meta.qualified_type.cpp", + "name": "meta.qualified-type.cpp", "patterns": [ { "match": "::", "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, { - "match": "(?>", + "match": "(?:<<|>>)", "name": "keyword.operator.bitwise.shift.cpp" }, { - "match": "!=|<=|>=|==|<|>", + "match": "(?:!=|<=|>=|==|<|>)", "name": "keyword.operator.comparison.cpp" }, { - "match": "&&|!|\\|\\|", + "match": "(?:&&|!|\\|\\|)", "name": "keyword.operator.logical.cpp" }, { - "match": "&|\\||\\^|~", + "match": "(?:&|\\||\\^|~)", "name": "keyword.operator.bitwise.cpp" }, { - "match": "(?:(%=|\\+=|-=|\\*=|(?>=|\\|=)|(\\=))", + "match": "(?:((?:%=|\\+=|-=|\\*=|(?>=|\\|=))|(\\=))", "captures": { "1": { "name": "keyword.operator.assignment.compound.cpp" @@ -8141,7 +8561,7 @@ } }, { - "match": "%|\\*|\\/|-|\\+", + "match": "(?:%|\\*|\\/|-|\\+)", "name": "keyword.operator.arithmetic.cpp" }, { @@ -9189,7 +9609,7 @@ "include": "#vararg_ellipses" }, { - "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:\\s*+(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.])", + "match": "\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.])", "captures": { "0": { "patterns": [ @@ -11311,7 +11728,7 @@ "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, { - "match": "(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(:)(?!:)", + "match": "((?:((?:(?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(:)(?!:)", "captures": { "1": { "name": "meta.type.cpp" @@ -11530,14 +12023,14 @@ "name": "comment.block.cpp punctuation.definition.comment.end.cpp" }, "10": { - "name": "meta.qualified_type.cpp", + "name": "meta.qualified-type.cpp", "patterns": [ { "match": "::", "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, { - "match": "(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(\\[)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(,)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))*((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(\\])((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(:)(?!:)", + "match": "((?:((?:(?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(\\[)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(,)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))*((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(\\])((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(:)(?!:)", "captures": { "1": { "name": "meta.type.cpp" @@ -11794,14 +12287,14 @@ "name": "comment.block.cpp punctuation.definition.comment.end.cpp" }, "10": { - "name": "meta.qualified_type.cpp", + "name": "meta.qualified-type.cpp", "patterns": [ { "match": "::", "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, { - "match": "(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#scope_resolution_inner_generated" - } - ] - }, + "requires_keyword": { + "begin": "((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*\\s*+", - "captures": { - "0": { + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "4": { + "name": "comment.block.cpp" + }, + "5": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "6": { + "name": "punctuation.section.arguments.begin.bracket.round.requires.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.arguments.end.bracket.round.requires.cpp" + } + }, + "contentName": "meta.arguments.requires", + "patterns": [ + { + "include": "#evaluation_context" + } + ] + }, + "scope_resolution": { + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*\\s*+", + "captures": { + "0": { + "patterns": [ + { + "include": "#scope_resolution_inner_generated" + } + ] + }, + "1": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + }, + "2": { + "patterns": [ + { + "include": "#template_call_range_helper" + } + ] + } + } + }, + "scope_resolution_function_call": { + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*\\s*+", + "captures": { + "0": { "patterns": [ { "include": "#scope_resolution_function_call_inner_generated" @@ -12661,150 +13193,668 @@ } ] }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" - }, - "2": { + "1": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" + }, + "2": { + "patterns": [ + { + "include": "#template_call_range_helper" + } + ] + } + } + }, + "scope_resolution_template_call_inner_generated": { + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?(::)", + "captures": { + "1": { + "patterns": [ + { + "include": "#scope_resolution_template_call_inner_generated" + } + ] + }, + "2": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" + }, + "3": { + "patterns": [ + { + "include": "#template_call_range_helper" + } + ] + }, + "4": {}, + "5": { + "name": "entity.name.scope-resolution.template.call.cpp" + }, + "6": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range_helper" + } + ] + }, + "7": {}, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "11": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" + } + } + }, + "scope_resolution_template_definition": { + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*\\s*+", + "captures": { + "0": { + "patterns": [ + { + "include": "#scope_resolution_template_definition_inner_generated" + } + ] + }, + "1": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" + }, + "2": { + "patterns": [ + { + "include": "#template_call_range_helper" + } + ] + } + } + }, + "scope_resolution_template_definition_inner_generated": { + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?(::)", + "captures": { + "1": { + "patterns": [ + { + "include": "#scope_resolution_template_definition_inner_generated" + } + ] + }, + "2": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" + }, + "3": { + "patterns": [ + { + "include": "#template_call_range_helper" + } + ] + }, + "4": {}, + "5": { + "name": "entity.name.scope-resolution.template.definition.cpp" + }, + "6": { + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_range_helper" + } + ] + }, + "7": {}, + "8": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "9": { + "name": "comment.block.cpp" + }, + "10": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "11": { + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" + } + } + }, + "semicolon": { + "match": ";", + "name": "punctuation.terminator.statement.cpp" + }, + "simple_array_assignment": { + "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:(?:(\\b[a-z0-9]+\\b)|(\\b[a-zA-Z0-9]+_[a-zA-Z0-9]*\\b))|(\\b[a-z]+[A-Z][a-zA-Z0-9]*\\b))|(\\b[A-Z][A-Z_0-9]*\\b))|((?>=|\\|=))|(\\=))", + "captures": { + "1": { + "name": "meta.qualified-type.cpp", + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + }, + { + "match": "(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(?=((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?=(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?(::)", - "captures": { - "1": { + }, + "6": { "patterns": [ { - "include": "#scope_resolution_template_call_inner_generated" + "match": "\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + } + } } ] }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.call.cpp" - }, - "3": { + "7": { "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.type.cpp" + }, + { + "match": "(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*\\s*+", - "captures": { - "0": { "patterns": [ { - "include": "#scope_resolution_template_definition_inner_generated" + "match": "\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + } + } } ] }, - "1": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" - }, - "2": { + "12": {}, + "13": { "patterns": [ { - "include": "#template_call_range_helper" + "include": "#inline_comment" } ] - } - } - }, - "scope_resolution_template_definition_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?(::)", - "captures": { - "1": { + }, + "14": { "patterns": [ { - "include": "#scope_resolution_template_definition_inner_generated" + "match": "\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + } + } } ] }, - "2": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" - }, - "3": { + "15": { "patterns": [ { - "include": "#template_call_range_helper" + "include": "#inline_comment" } ] }, - "4": {}, - "5": { - "name": "entity.name.scope-resolution.template.definition.cpp" - }, - "6": { - "name": "meta.template.call.cpp", + "16": { "patterns": [ { - "include": "#template_call_range_helper" + "match": "\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+", + "captures": { + "1": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "2": { + "name": "comment.block.cpp" + }, + "3": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + } + } } ] - }, - "7": {}, - "8": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "9": { - "name": "comment.block.cpp" - }, - "10": { - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - }, - "11": { - "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.template.definition.cpp" } } }, - "semicolon": { - "match": ";", - "name": "punctuation.terminator.statement.cpp" - }, "simple_type": { - "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?", + "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?", "captures": { "1": { - "name": "meta.qualified_type.cpp", + "name": "meta.qualified-type.cpp", "patterns": [ { "match": "::", "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, { - "match": "(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))|(.*(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))|(.*(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(\\()(\\*)(?:\\s+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:\\s+)?(?:(\\[)(\\w*)(\\])(?:\\s+)?)*(\\))(?:\\s+)?(\\()", - "end": "(\\))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?]|\\n)(?!\\()|(?=\\\\end\\{(?:minted|cppcode)\\})", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:unsigned|signed|short|long)|(?:struct|class|union|enum))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(\\()(\\*)(?:\\s+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:\\s+)?(?:(\\[)(\\w*)(\\])(?:\\s+)?)*(\\))(?:\\s+)?(\\()", + "end": "(\\))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?]|\\n)(?!\\()|(?=\\\\end\\{(?:minted|cppcode)\\})", "beginCaptures": { "1": { - "name": "meta.qualified_type.cpp", + "name": "meta.qualified-type.cpp", "patterns": [ { "match": "::", "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, { - "match": "(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))", + "match": "(((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))", "captures": { "1": { "name": "storage.modifier.cpp" @@ -17605,14 +18627,14 @@ ] }, "6": { - "name": "meta.qualified_type.cpp", + "name": "meta.qualified-type.cpp", "patterns": [ { "match": "::", "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, { - "match": "(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(%=|\\+=|-=|\\*=|(?>=|\\|=)|(\\=))", + "match": "(?:((?:(?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:(?:(\\b[a-z0-9]+\\b)|(\\b[a-zA-Z0-9]+_[a-zA-Z0-9]*\\b))|(\\b[a-z]+[A-Z][a-zA-Z0-9]*\\b))|(\\b[A-Z][A-Z_0-9]*\\b))|((?>=|\\|=))|(\\=))", "captures": { "1": { "patterns": [ @@ -18346,14 +19388,14 @@ ] }, "5": { - "name": "meta.qualified_type.cpp", + "name": "meta.qualified-type.cpp", "patterns": [ { "match": "::", "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" }, { - "match": "(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?=;|,)", + "match": "(?:((?:(?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:transaction_safe_dynamic|__has_cpp_attribute|reinterpret_cast|transaction_safe|atomic_noexcept|atomic_commit|__has_include|atomic_cancel|synchronized|thread_local|dynamic_cast|static_cast|const_cast|constexpr|co_return|constinit|namespace|protected|consteval|constexpr|constexpr|co_return|consteval|co_await|continue|template|reflexpr|volatile|register|co_await|co_yield|restrict|noexcept|volatile|override|explicit|decltype|operator|noexcept|noexcept|typename|requires|co_yield|nullptr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|define|module|sizeof|switch|delete|pragma|and_eq|inline|xor_eq|typeid|import|extern|public|bitand|static|export|return|friend|ifndef|not_eq|false|final|break|const|catch|endif|ifdef|undef|error|audit|while|using|axiom|or_eq|compl|throw|bitor|const|line|case|else|this|true|goto|else|NULL|elif|new|asm|xor|and|try|not|for|do|if|or|if)\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:(?:(\\b[a-z0-9]+\\b)|(\\b[a-zA-Z0-9]+_[a-zA-Z0-9]*\\b))|(\\b[a-z]+[A-Z][a-zA-Z0-9]*\\b))|(\\b[A-Z][A-Z_0-9]*\\b))|((?= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", + "license": "MIT" }, "node_modules/@types/dompurify": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.2.tgz", - "integrity": "sha512-YBL4ziFebbbfQfH5mlC+QTJsvh0oJUrWbmxKMyEdL7emlHJqGR2Qb34TEFKj+VCayBvjKy3xczMFNhugThUsfQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.2.0.tgz", + "integrity": "sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==", + "deprecated": "This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed.", "dev": true, + "license": "MIT", "dependencies": { - "@types/trusted-types": "*" + "dompurify": "*" } }, "node_modules/@types/linkify-it": { @@ -174,22 +186,24 @@ "dev": true }, "node_modules/@types/lodash.throttle": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@types/lodash.throttle/-/lodash.throttle-4.1.3.tgz", - "integrity": "sha512-FUm7uMuYRX7dzqmgX02bxdBwC75owUxGA4dDKtFePDLJ6N1ofXxkRX3NhJV8wOrNs/wCjaY6sDVJrD1lbyERoQ==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/lodash.throttle/-/lodash.throttle-4.1.9.tgz", + "integrity": "sha512-PCPVfpfueguWZQB7pJQK890F2scYKoDUL3iM522AptHWn7d5NQmeS/LTEHIcLr5PaTzl3dK2Z0xSUHHTHwaL5g==", "dev": true, + "license": "MIT", "dependencies": { "@types/lodash": "*" } }, "node_modules/@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", "dev": true, + "license": "MIT", "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" + "@types/linkify-it": "^5", + "@types/mdurl": "^2" } }, "node_modules/@types/mdurl": { @@ -199,37 +213,42 @@ "dev": true }, "node_modules/@types/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g==", - "dev": true + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/trusted-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", - "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==", - "dev": true + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true }, "node_modules/@types/vscode-notebook-renderer": { - "version": "1.60.0", - "resolved": "https://registry.npmjs.org/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.60.0.tgz", - "integrity": "sha512-u7TD2uuEZTVuitx0iijOJdKI0JLiQP6PsSBSRy2XmHXUOXcp5p1S56NrjOEDoF+PIHd3NL3eO6KTRSf5nukDqQ==", - "dev": true + "version": "1.72.4", + "resolved": "https://registry.npmjs.org/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.72.4.tgz", + "integrity": "sha512-bdKO41c6Dc24pH/O/eM/jqfCwGH4zc76o/g/6Gt1y/eg/bvvqP2/VpbV+Sa5Te2sZekFRcbYnSSFTKo3wcVGUg==", + "dev": true, + "license": "MIT" }, "node_modules/@types/vscode-webview": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/@types/vscode-webview/-/vscode-webview-1.57.0.tgz", - "integrity": "sha512-x3Cb/SMa1IwRHfSvKaZDZOTh4cNoG505c3NjTqGlMC082m++x/ETUmtYniDsw6SSmYzZXO8KBNhYxR0+VqymqA==", - "dev": true + "version": "1.57.5", + "resolved": "https://registry.npmjs.org/@types/vscode-webview/-/vscode-webview-1.57.5.tgz", + "integrity": "sha512-iBAUYNYkz+uk1kdsq05fEcoh8gJmwT3lqqFPN7MGyjQ3HVloViMdo7ZJ8DFIP8WOK74PjOEilosqAyxV2iUFUw==", + "dev": true, + "license": "MIT" }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.2.0.tgz", + "integrity": "sha512-En6dTwfy5NFzSMibvOpx/lKq2jtgWuR4++KJbi3SpQ2iT8gm+PHo9868/scocW122KDwTxl4ruxZ7i4rHmJJnQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" @@ -241,10 +260,11 @@ "integrity": "sha512-ukOMWnCg1tCvT7WnDfsUKQOFDQGsyR5tNgRpwmqi+5/vzU3ghdDXzvIM4IOPdSb3OeSsBNvmSL8nxIVOqi2WXA==" }, "node_modules/@vscode/markdown-it-katex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@vscode/markdown-it-katex/-/markdown-it-katex-1.0.2.tgz", - "integrity": "sha512-QY/OnOHPTqc8tQoCoAjVblILX4yE6xGZHKODtiTKqA328OXra+lSpeJO5Ouo9AAvrs9AwcCLz6xvW3zwcsPBQg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vscode/markdown-it-katex/-/markdown-it-katex-1.1.2.tgz", + "integrity": "sha512-+4IIv5PgrmhKvW/3LpkpkGg257OViEhXkOOgCyj5KMsjsOfnRXkni8XAuuF9Ui5p3B8WnUovlDXAQNb8RJ/RaQ==", "dev": true, + "license": "MIT", "dependencies": { "katex": "^0.16.4" } @@ -257,7 +277,8 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" }, "node_modules/boolbase": { "version": "1.0.0", @@ -265,12 +286,12 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/commander": { @@ -278,15 +299,11 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "dev": true, + "license": "MIT", "engines": { "node": ">= 12" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, "node_modules/css-select": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", @@ -352,9 +369,13 @@ } }, "node_modules/dompurify": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.1.3.tgz", - "integrity": "sha512-5sOWYSNPaxz6o2MUPvtyxTTqR4D3L77pr5rUQoWgD5ROQtVIZQgJkXbo1DLlK3vj11YGw5+LnF4SYti4gZmwng==" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", + "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } }, "node_modules/domutils": { "version": "3.1.0", @@ -389,22 +410,24 @@ } }, "node_modules/highlight.js": { - "version": "11.8.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.8.0.tgz", - "integrity": "sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==", + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", "engines": { "node": ">=12.0.0" } }, "node_modules/katex": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.10.tgz", - "integrity": "sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==", + "version": "0.16.21", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.21.tgz", + "integrity": "sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==", "dev": true, "funding": [ "https://opencollective.com/katex", "https://github.com/sponsors/katex" ], + "license": "MIT", "dependencies": { "commander": "^8.3.0" }, @@ -413,11 +436,12 @@ } }, "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", "dependencies": { - "uc.micro": "^1.0.1" + "uc.micro": "^2.0.0" } }, "node_modules/lodash.throttle": { @@ -438,18 +462,20 @@ } }, "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", "dependencies": { "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, "bin": { - "markdown-it": "bin/markdown-it.js" + "markdown-it": "bin/markdown-it.mjs" } }, "node_modules/markdown-it-front-matter": { @@ -457,34 +483,29 @@ "resolved": "https://registry.npmjs.org/markdown-it-front-matter/-/markdown-it-front-matter-0.2.4.tgz", "integrity": "sha512-25GUs0yjS2hLl8zAemVndeEzThB1p42yxuDEKbd4JlL3jiz+jsm6e56Ya8B0VREOkNxLYB4TTwaoPJ3ElMmW+w==" }, - "node_modules/markdown-it/node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=10" } }, "node_modules/morphdom": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/morphdom/-/morphdom-2.6.1.tgz", - "integrity": "sha512-Y8YRbAEP3eKykroIBWrjcfMw7mmwJfjhqdpSvoqinu8Y702nAwikpXcNFDiIkyvfCLxLM9Wu95RZqo4a9jFBaA==" + "version": "2.7.8", + "resolved": "https://registry.npmjs.org/morphdom/-/morphdom-2.7.8.tgz", + "integrity": "sha512-D/fR4xgGUyVRbdMGU6Nejea1RFzYxYtyurG4Fbv2Fi/daKlWKuXGLOdXtl+3eIwL110cI2hz1ZojGICjjFLgTg==", + "license": "MIT" }, "node_modules/node-html-parser": { "version": "6.1.13", @@ -507,11 +528,12 @@ } }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -525,6 +547,15 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -540,29 +571,32 @@ } }, "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" }, "node_modules/vscode-jsonrpc": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz", - "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageclient": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.0.2.tgz", - "integrity": "sha512-lHlthJtphG9gibGb/y72CKqQUxwPsMXijJVpHEC2bvbFqxmkj9LwQ3aGU9dwjBLqsX1S4KjShYppLvg1UJDF/Q==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz", + "integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==", + "license": "MIT", "dependencies": { - "minimatch": "^3.0.4", - "semver": "^7.3.5", - "vscode-languageserver-protocol": "3.17.2" + "minimatch": "^5.1.0", + "semver": "^7.3.7", + "vscode-languageserver-protocol": "3.17.5" }, "engines": { - "vscode": "^1.67.0" + "vscode": "^1.82.0" } }, "node_modules/vscode-languageserver": { @@ -577,23 +611,26 @@ } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2.tgz", - "integrity": "sha512-8kYisQ3z/SQ2kyjlNeQxbkkTNmVFoQCqkmGrzLH6A9ecPlgTbp3wDTnUNqaUxYr4vlAcloxx8zwy7G5WdguYNg==", + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "8.0.2", - "vscode-languageserver-types": "3.17.2" + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" } }, "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz", - "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==" + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "license": "MIT" }, "node_modules/vscode-languageserver-types": { - "version": "3.17.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz", - "integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==" + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" }, "node_modules/vscode-languageserver/node_modules/vscode-jsonrpc": { "version": "8.1.0", @@ -618,30 +655,39 @@ "integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==" }, "node_modules/vscode-markdown-languageserver": { - "version": "0.5.0-alpha.8", - "resolved": "https://registry.npmjs.org/vscode-markdown-languageserver/-/vscode-markdown-languageserver-0.5.0-alpha.8.tgz", - "integrity": "sha512-Bp6YXHy4EMQ8JpsmXpQHa78byLvv83wVnLmhVfTaJsZTBwF8IOJ56NBUasepg9L6QVffUA9T2H/PfBqEOGpeOQ==", + "version": "0.5.0-alpha.12", + "resolved": "https://registry.npmjs.org/vscode-markdown-languageserver/-/vscode-markdown-languageserver-0.5.0-alpha.12.tgz", + "integrity": "sha512-cDRJKwWPZBHrrwufTHrhuZqGgBEGJcYo29Iwhvgh2BgTnIB+fp6Vs62LlqNUu25qsDjmZLLkIEQxntcX4kbfUQ==", + "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.11", "vscode-languageserver": "^8.1.0", "vscode-languageserver-textdocument": "^1.0.8", "vscode-languageserver-types": "^3.17.3", - "vscode-markdown-languageservice": "^0.5.0-alpha.7", + "vscode-markdown-languageservice": "^0.5.0-alpha.11", "vscode-uri": "^3.0.7" }, "engines": { "node": "*" } }, - "node_modules/vscode-markdown-languageserver/node_modules/vscode-languageserver-types": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + "node_modules/vscode-markdown-languageserver/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, "node_modules/vscode-markdown-languageserver/node_modules/vscode-markdown-languageservice": { - "version": "0.5.0-alpha.7", - "resolved": "https://registry.npmjs.org/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.5.0-alpha.7.tgz", - "integrity": "sha512-Iq9S5YGHm3D/UG9Usm8a/O5tYCo9FwaMF7nJsDQCxKgVZu5OzwOj3ixDkhoM+c8GKXiwt23DxhhWRuvI4odkTg==", + "version": "0.5.0-alpha.11", + "resolved": "https://registry.npmjs.org/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.5.0-alpha.11.tgz", + "integrity": "sha512-P1uBMAD5iylgpcweWCU1kQwk8SZngktnljXsZk1vFPorXv1mrEI7BkBpOUU0fhVssKgvFlCNLkI7KmwZLC7pdA==", + "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.10", "node-html-parser": "^6.1.5", @@ -657,19 +703,22 @@ "node_modules/vscode-markdown-languageserver/node_modules/vscode-markdown-languageservice/node_modules/@vscode/l10n": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.10.tgz", - "integrity": "sha512-E1OCmDcDWa0Ya7vtSjp/XfHFGqYJfh+YPC1RkATU71fTac+j1JjCcB3qwSzmlKAighx2WxhLlfhS0RwAN++PFQ==" + "integrity": "sha512-E1OCmDcDWa0Ya7vtSjp/XfHFGqYJfh+YPC1RkATU71fTac+j1JjCcB3qwSzmlKAighx2WxhLlfhS0RwAN++PFQ==", + "license": "MIT" }, "node_modules/vscode-markdown-languageservice": { - "version": "0.3.0-alpha.3", - "resolved": "https://registry.npmjs.org/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.3.0-alpha.3.tgz", - "integrity": "sha512-KPjIuCkSqabkzci7TnlLKep5FYIC45tS7UC5H8zoOii7aoKJru5mZBDXJt86bM3XTgnfpW7rUYqhNnvXbbCBbw==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.4.0.tgz", + "integrity": "sha512-3C8pZlC0ofHEYmWwHgenxL6//XrpkrgyytrqNpMlft46q9uBxSUfcXtEGt7wIDNLWsvmgqPqHBwEnBFtLwrWFA==", "dev": true, + "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.10", + "node-html-parser": "^6.1.5", "picomatch": "^2.3.1", - "vscode-languageserver-textdocument": "^1.0.5", - "vscode-languageserver-types": "^3.17.1", - "vscode-uri": "^3.0.3" + "vscode-languageserver-textdocument": "^1.0.8", + "vscode-languageserver-types": "^3.17.3", + "vscode-uri": "^3.0.7" }, "engines": { "node": "*" @@ -681,10 +730,24 @@ "integrity": "sha512-E1OCmDcDWa0Ya7vtSjp/XfHFGqYJfh+YPC1RkATU71fTac+j1JjCcB3qwSzmlKAighx2WxhLlfhS0RwAN++PFQ==", "dev": true }, + "node_modules/vscode-markdown-languageservice/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" }, "node_modules/yallist": { "version": "4.0.0", diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index a4555a9e588c..09d6ee785b77 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -15,9 +15,6 @@ "categories": [ "Programming Languages" ], - "enabledApiProposals": [ - "documentPaste" - ], "activationEvents": [ "onLanguage:markdown", "onCommand:markdown.api.render", @@ -125,6 +122,11 @@ "title": "%markdown.copyImage.title%", "category": "Markdown" }, + { + "command": "_markdown.openImage", + "title": "%markdown.openImage.title%", + "category": "Markdown" + }, { "command": "markdown.showPreview", "title": "%markdown.preview.title%", @@ -189,7 +191,11 @@ "webview/context": [ { "command": "_markdown.copyImage", - "when": "webviewId == 'markdown.preview' && webviewSection == 'image'" + "when": "webviewId == 'markdown.preview' && (webviewSection == 'image' || webviewSection == 'localImage')" + }, + { + "command": "_markdown.openImage", + "when": "webviewId == 'markdown.preview' && webviewSection == 'localImage'" } ], "editor/title": [ @@ -244,6 +250,10 @@ } ], "commandPalette": [ + { + "command": "_markdown.openImage", + "when": "false" + }, { "command": "_markdown.copyImage", "when": "false" @@ -432,16 +442,6 @@ "%configuration.markdown.suggest.paths.includeWorkspaceHeaderCompletions.onSingleOrDoubleHash%" ] }, - "markdown.trace.extension": { - "type": "string", - "enum": [ - "off", - "verbose" - ], - "default": "off", - "description": "%markdown.trace.extension.desc%", - "scope": "window" - }, "markdown.trace.server": { "type": "string", "scope": "window", @@ -763,30 +763,30 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", - "dompurify": "^3.1.3", - "highlight.js": "^11.8.0", - "markdown-it": "^12.3.2", + "@vscode/extension-telemetry": "^1.2.0", + "dompurify": "^3.2.4", + "highlight.js": "^11.11.1", + "markdown-it": "^14.1.0", "markdown-it-front-matter": "^0.2.4", - "morphdom": "^2.6.1", - "picomatch": "^2.3.1", + "morphdom": "^2.7.8", + "picomatch": "^4.0.3", "punycode": "^2.3.1", - "vscode-languageclient": "^8.0.2", - "vscode-languageserver-textdocument": "^1.0.11", - "vscode-markdown-languageserver": "^0.5.0-alpha.8", - "vscode-uri": "^3.0.3" + "vscode-languageclient": "^9.0.1", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-markdown-languageserver": "^0.5.0-alpha.12", + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/dompurify": "^3.0.2", - "@types/lodash.throttle": "^4.1.3", - "@types/markdown-it": "12.2.3", - "@types/picomatch": "^2.3.0", - "@types/vscode-notebook-renderer": "^1.60.0", - "@types/vscode-webview": "^1.57.0", - "@vscode/markdown-it-katex": "^1.0.2", + "@types/dompurify": "^3.2.0", + "@types/lodash.throttle": "^4.1.9", + "@types/markdown-it": "14.1.2", + "@types/picomatch": "^4.0.2", + "@types/vscode-notebook-renderer": "^1.72.4", + "@types/vscode-webview": "^1.57.5", + "@vscode/markdown-it-katex": "^1.1.2", "lodash.throttle": "^4.1.1", - "vscode-languageserver-types": "^3.17.2", - "vscode-markdown-languageservice": "^0.3.0-alpha.3" + "vscode-languageserver-types": "^3.17.5", + "vscode-markdown-languageservice": "^0.4.0" }, "repository": { "type": "git", diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index da567b46665b..fe98103daff2 100644 --- a/extensions/markdown-language-features/package.nls.json +++ b/extensions/markdown-language-features/package.nls.json @@ -2,6 +2,7 @@ "displayName": "Markdown Language Features", "description": "Provides rich language support for Markdown.", "markdown.copyImage.title": "Copy Image", + "markdown.openImage.title": "Open Image", "markdown.preview.breaks.desc": "Sets how line-breaks are rendered in the Markdown preview. Setting it to `true` creates a `
` for newlines inside paragraphs.", "markdown.preview.linkify": "Convert URL-like text to links in the Markdown preview.", "markdown.preview.typographer": "Enable some language-neutral replacement and quotes beautification in the Markdown preview.", @@ -71,7 +72,7 @@ "configuration.markdown.updateLinksOnFileMove.enableForDirectories": "Enable updating links when a directory is moved or renamed in the workspace.", "configuration.markdown.occurrencesHighlight.enabled": "Enable highlighting link occurrences in the current document.", "configuration.markdown.copyFiles.destination": { - "message": "Configures the path and file name of files created by copy/paste or drag and drop. This is a map of globs that match against a Markdown document path to the destination path where the new file should be created.\n\nThe destination path may use the following variables:\n\n- `${documentDirName}` — Absolute parent directory path of the Markdown document, e.g. `/Users/me/myProject/docs`.\n- `${documentRelativeDirName}` — Relative parent directory path of the Markdown document, e.g. `docs`. This is the same as `${documentDirName}` if the file is not part of a workspace.\n- `${documentFileName}` — The full filename of the Markdown document, e.g. `README.md`.\n- `${documentBaseName}` — The basename of the Markdown document, e.g. `README`.\n- `${documentExtName}` — The extension of the Markdown document, e.g. `md`.\n- `${documentFilePath}` — Absolute path of the Markdown document, e.g. `/Users/me/myProject/docs/README.md`.\n- `${documentRelativeFilePath}` — Relative path of the Markdown document, e.g. `docs/README.md`. This is the same as `${documentFilePath}` if the file is not part of a workspace.\n- `${documentWorkspaceFolder}` — The workspace folder for the Markdown document, e.g. `/Users/me/myProject`. This is the same as `${documentDirName}` if the file is not part of a workspace.\n- `${fileName}` — The file name of the dropped file, e.g. `image.png`.\n- `${fileExtName}` — The extension of the dropped file, e.g. `png`.", + "message": "Configures the path and file name of files created by copy/paste or drag and drop. This is a map of globs that match against a Markdown document path to the destination path where the new file should be created.\n\nThe destination path may use the following variables:\n\n- `${documentDirName}` — Absolute parent directory path of the Markdown document, e.g. `/Users/me/myProject/docs`.\n- `${documentRelativeDirName}` — Relative parent directory path of the Markdown document, e.g. `docs`. This is the same as `${documentDirName}` if the file is not part of a workspace.\n- `${documentFileName}` — The full filename of the Markdown document, e.g. `README.md`.\n- `${documentBaseName}` — The basename of the Markdown document, e.g. `README`.\n- `${documentExtName}` — The extension of the Markdown document, e.g. `md`.\n- `${documentFilePath}` — Absolute path of the Markdown document, e.g. `/Users/me/myProject/docs/README.md`.\n- `${documentRelativeFilePath}` — Relative path of the Markdown document, e.g. `docs/README.md`. This is the same as `${documentFilePath}` if the file is not part of a workspace.\n- `${documentWorkspaceFolder}` — The workspace folder for the Markdown document, e.g. `/Users/me/myProject`. This is the same as `${documentDirName}` if the file is not part of a workspace.\n- `${fileName}` — The file name of the dropped file, e.g. `image.png`.\n- `${fileExtName}` — The extension of the dropped file, e.g. `png`.\n- `${unixTime}` — The current Unix timestamp in milliseconds.", "comment": [ "This setting is use the user drops or pastes image data into the editor. In this case, VS Code automatically creates a new image file in the workspace containing the dropped/pasted image.", "It's easier to explain this setting with an example. For example, let's say the setting value was:", diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index 33c84e0a3847..8153953b606b 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -7,10 +7,12 @@ import { ActiveLineMarker } from './activeLineMarker'; import { onceDocumentLoaded } from './events'; import { createPosterForVsCode } from './messaging'; import { getEditorLineNumberForPageOffset, scrollToRevealSourceLine, getLineElementForFragment } from './scroll-sync'; -import { SettingsManager, getData } from './settings'; +import { SettingsManager, getData, getRawData } from './settings'; import throttle = require('lodash.throttle'); import morphdom from 'morphdom'; +import DOMPurify from 'dompurify'; import type { ToWebviewMessage } from '../types/previewMessaging'; +import { isOfScheme, Schemes } from '../src/util/schemes'; let scrollDisabledCount = 0; @@ -61,8 +63,16 @@ function doAfterImagesLoaded(cb: () => void) { } onceDocumentLoaded(() => { + // Load initial html + const htmlParser = new DOMParser(); + const markDownHtml = htmlParser.parseFromString( + getRawData('data-initial-md-content'), + 'text/html' + ); + document.body.append(...markDownHtml.body.children); + + // Restore const scrollProgress = state.scrollProgress; - addImageContexts(); if (typeof scrollProgress === 'number' && !settings.settings.fragment) { doAfterImagesLoaded(() => { @@ -132,7 +142,10 @@ function addImageContexts() { for (const img of images) { img.id = 'image-' + idNumber; idNumber += 1; - img.setAttribute('data-vscode-context', JSON.stringify({ webviewSection: 'image', id: img.id, 'preventDefaultContextMenuItems': true, resource: documentResource })); + const imageSource = img.getAttribute('data-src'); + const isLocalFile = imageSource && !(isOfScheme(Schemes.http, imageSource) || isOfScheme(Schemes.https, imageSource)); + const webviewSection = isLocalFile ? 'localImage' : 'image'; + img.setAttribute('data-vscode-context', JSON.stringify({ webviewSection, id: img.id, 'preventDefaultContextMenuItems': true, resource: documentResource, imageSource })); } } @@ -194,7 +207,8 @@ window.addEventListener('message', async event => { const root = document.querySelector('.markdown-body')!; const parser = new DOMParser(); - const newContent = parser.parseFromString(data.content, 'text/html'); // CodeQL [SM03712] This renderers content from the workspace into the Markdown preview. Webviews (and the markdown preview) have many other security measures in place to make this safe + const sanitizedContent = DOMPurify.sanitize(data.content); + const newContent = parser.parseFromString(sanitizedContent, 'text/html'); // CodeQL [SM03712] This renderers content from the workspace into the Markdown preview. Webviews (and the markdown preview) have many other security measures in place to make this safe // Strip out meta http-equiv tags for (const metaElement of Array.from(newContent.querySelectorAll('meta'))) { diff --git a/extensions/markdown-language-features/preview-src/settings.ts b/extensions/markdown-language-features/preview-src/settings.ts index 1bbe3477f254..0fb5d0c2686c 100644 --- a/extensions/markdown-language-features/preview-src/settings.ts +++ b/extensions/markdown-language-features/preview-src/settings.ts @@ -16,18 +16,22 @@ export interface PreviewSettings { readonly webviewResourceRoot: string; } -export function getData(key: string): T { +export function getRawData(key: string): string { const element = document.getElementById('vscode-markdown-preview-data'); if (element) { const data = element.getAttribute(key); if (data) { - return JSON.parse(data); + return data; } } throw new Error(`Could not load data for ${key}`); } +export function getData(key: string): T { + return JSON.parse(getRawData(key)); +} + export class SettingsManager { private _settings: PreviewSettings = getData('data-settings'); diff --git a/extensions/markdown-language-features/src/client/fileWatchingManager.ts b/extensions/markdown-language-features/src/client/fileWatchingManager.ts index dabb8f1fc880..e2010edda8a4 100644 --- a/extensions/markdown-language-features/src/client/fileWatchingManager.ts +++ b/extensions/markdown-language-features/src/client/fileWatchingManager.ts @@ -11,7 +11,7 @@ import { Schemes } from '../util/schemes'; type DirWatcherEntry = { readonly uri: vscode.Uri; - readonly listeners: IDisposable[]; + readonly disposables: readonly IDisposable[]; }; @@ -28,6 +28,11 @@ export class FileWatcherManager { }>(); create(id: number, uri: vscode.Uri, watchParentDirs: boolean, listeners: { create?: () => void; change?: () => void; delete?: () => void }): void { + // Non-writable file systems do not support file watching + if (!vscode.workspace.fs.isWritableFileSystem(uri.scheme)) { + return; + } + const watcher = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(uri, '*'), !listeners.create, !listeners.change, !listeners.delete); const parentDirWatchers: DirWatcherEntry[] = []; this._fileWatchers.set(id, { watcher, dirWatchers: parentDirWatchers }); @@ -39,7 +44,7 @@ export class FileWatcherManager { if (watchParentDirs && uri.scheme !== Schemes.untitled) { // We need to watch the parent directories too for when these are deleted / created for (let dirUri = Utils.dirname(uri); dirUri.path.length > 1; dirUri = Utils.dirname(dirUri)) { - const dirWatcher: DirWatcherEntry = { uri: dirUri, listeners: [] }; + const disposables: IDisposable[] = []; let parentDirWatcher = this._dirWatchers.get(dirUri); if (!parentDirWatcher) { @@ -51,7 +56,7 @@ export class FileWatcherManager { parentDirWatcher.refCount++; if (listeners.create) { - dirWatcher.listeners.push(parentDirWatcher.watcher.onDidCreate(async () => { + disposables.push(parentDirWatcher.watcher.onDidCreate(async () => { // Just because the parent dir was created doesn't mean our file was created try { const stat = await vscode.workspace.fs.stat(uri); @@ -67,10 +72,10 @@ export class FileWatcherManager { if (listeners.delete) { // When the parent dir is deleted, consider our file deleted too // TODO: this fires if the file previously did not exist and then the parent is deleted - dirWatcher.listeners.push(parentDirWatcher.watcher.onDidDelete(listeners.delete)); + disposables.push(parentDirWatcher.watcher.onDidDelete(listeners.delete)); } - parentDirWatchers.push(dirWatcher); + parentDirWatchers.push({ uri: dirUri, disposables }); } } } @@ -79,7 +84,7 @@ export class FileWatcherManager { const entry = this._fileWatchers.get(id); if (entry) { for (const dirWatcher of entry.dirWatchers) { - disposeAll(dirWatcher.listeners); + disposeAll(dirWatcher.disposables); const dirWatcherEntry = this._dirWatchers.get(dirWatcher.uri); if (dirWatcherEntry) { diff --git a/extensions/markdown-language-features/src/commands/index.ts b/extensions/markdown-language-features/src/commands/index.ts index e1a4f2b41ffe..382b2a8b6d51 100644 --- a/extensions/markdown-language-features/src/commands/index.ts +++ b/extensions/markdown-language-features/src/commands/index.ts @@ -18,6 +18,7 @@ import { CopyImageCommand } from './copyImage'; import { ShowPreviewSecuritySelectorCommand } from './showPreviewSecuritySelector'; import { ShowSourceCommand } from './showSource'; import { ToggleLockCommand } from './toggleLock'; +import { OpenImageCommand } from './openImage'; export function registerMarkdownCommands( commandManager: CommandManager, @@ -28,6 +29,7 @@ export function registerMarkdownCommands( ): vscode.Disposable { const previewSecuritySelector = new PreviewSecuritySelector(cspArbiter, previewManager); + commandManager.register(new OpenImageCommand(previewManager)); commandManager.register(new CopyImageCommand(previewManager)); commandManager.register(new ShowPreviewCommand(previewManager, telemetryReporter)); commandManager.register(new ShowPreviewToSideCommand(previewManager, telemetryReporter)); diff --git a/extensions/markdown-language-features/src/commands/insertResource.ts b/extensions/markdown-language-features/src/commands/insertResource.ts index 9369141465ee..e6ede4367426 100644 --- a/extensions/markdown-language-features/src/commands/insertResource.ts +++ b/extensions/markdown-language-features/src/commands/insertResource.ts @@ -6,7 +6,8 @@ import * as vscode from 'vscode'; import { Utils } from 'vscode-uri'; import { Command } from '../commandManager'; -import { createUriListSnippet, mediaFileExtensions } from '../languageFeatures/copyFiles/shared'; +import { createUriListSnippet, linkEditKind } from '../languageFeatures/copyFiles/shared'; +import { mediaFileExtensions } from '../util/mimes'; import { coalesce } from '../util/arrays'; import { getParentDocumentUri } from '../util/document'; import { Schemes } from '../util/schemes'; @@ -84,7 +85,7 @@ function createInsertLinkEdit(activeEditor: vscode.TextEditor, selectedFiles: re const snippetEdits = coalesce(activeEditor.selections.map((selection, i): vscode.SnippetTextEdit | undefined => { const selectionText = activeEditor.document.getText(selection); const snippet = createUriListSnippet(activeEditor.document.uri, selectedFiles.map(uri => ({ uri })), { - insertAsMedia: insertAsMedia, + linkKindHint: insertAsMedia ? 'media' : linkEditKind, placeholderText: selectionText, placeholderStartIndex: (i + 1) * selectedFiles.length, separator: insertAsMedia ? '\n' : ' ', diff --git a/extensions/markdown-language-features/src/commands/openImage.ts b/extensions/markdown-language-features/src/commands/openImage.ts new file mode 100644 index 000000000000..64b1831df0d9 --- /dev/null +++ b/extensions/markdown-language-features/src/commands/openImage.ts @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { Command } from '../commandManager'; +import { MarkdownPreviewManager } from '../preview/previewManager'; + +export class OpenImageCommand implements Command { + public readonly id = '_markdown.openImage'; + + public constructor( + private readonly _webviewManager: MarkdownPreviewManager, + ) { } + + public execute(args: { resource: string; imageSource: string }) { + const source = vscode.Uri.parse(args.resource); + this._webviewManager.openDocumentLink(args.imageSource, source); + } +} diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyFiles.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyFiles.ts index dd41b5fa9248..0c2461548ad4 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyFiles.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/copyFiles.ts @@ -96,6 +96,7 @@ function resolveCopyDestinationSetting(documentUri: vscode.Uri, fileName: string // File ['fileName', fileName], // The file name of the dropped file, e.g. `image.png`. ['fileExtName', path.extname(fileName).replace('.', '')], // The extension of the dropped file, e.g. `png`. + ['unixTime', Date.now().toString()], // The current Unix timestamp in milliseconds. ]); return outDest.replaceAll(/(?\\\$)|(?\w+)(?:\/(?(?:\\\/|[^\}\/])+)\/(?(?:\\\/|[^\}\/])*)\/)?\}/g, (match, _escape, name, pattern, replacement, _offset, _str, groups) => { diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/dropOrPasteResource.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/dropOrPasteResource.ts index 9fd3e4f388d9..7791d6b19e42 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/dropOrPasteResource.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/dropOrPasteResource.ts @@ -7,12 +7,12 @@ import * as vscode from 'vscode'; import { IMdParser } from '../../markdownEngine'; import { coalesce } from '../../util/arrays'; import { getParentDocumentUri } from '../../util/document'; -import { Mime, mediaMimes } from '../../util/mimes'; +import { getMediaKindForMime, MediaKind, Mime, rootMediaMimesTypes } from '../../util/mimes'; import { Schemes } from '../../util/schemes'; +import { UriList } from '../../util/uriList'; import { NewFilePathGenerator } from './newFilePathGenerator'; -import { DropOrPasteEdit, createInsertUriListEdit, createUriListSnippet, getSnippetLabel } from './shared'; +import { audioEditKind, baseLinkEditKind, createInsertUriListEdit, createUriListSnippet, DropOrPasteEdit, getSnippetLabelAndKind, imageEditKind, linkEditKind, videoEditKind } from './shared'; import { InsertMarkdownLink, shouldInsertMarkdownLinkByDefault } from './smartDropOrPaste'; -import { UriList } from '../../util/uriList'; enum CopyFilesSettings { Never = 'never', @@ -30,17 +30,15 @@ enum CopyFilesSettings { */ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, vscode.DocumentDropEditProvider { - public static readonly kind = vscode.DocumentDropOrPasteEditKind.Empty.append('markdown', 'link'); - public static readonly mimeTypes = [ Mime.textUriList, 'files', - ...mediaMimes, + ...Object.values(rootMediaMimesTypes).map(type => `${type}/*`), ]; private readonly _yieldTo = [ - vscode.DocumentDropOrPasteEditKind.Empty.append('text'), - vscode.DocumentDropOrPasteEditKind.Empty.append('markdown', 'image', 'attachment'), + vscode.DocumentDropOrPasteEditKind.Text, + vscode.DocumentDropOrPasteEditKind.Empty.append('markdown', 'link', 'image', 'attachment'), // Prefer notebook attachments ]; constructor( @@ -64,7 +62,7 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v const dropEdit = new vscode.DocumentDropEdit(edit.snippet); dropEdit.title = edit.label; - dropEdit.kind = ResourcePasteOrDropProvider.kind; + dropEdit.kind = edit.kind; dropEdit.additionalEdit = edit.additionalEdits; dropEdit.yieldTo = [...this._yieldTo, ...edit.yieldTo]; return dropEdit; @@ -86,7 +84,7 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v return; } - const pasteEdit = new vscode.DocumentPasteEdit(edit.snippet, edit.label, ResourcePasteOrDropProvider.kind); + const pasteEdit = new vscode.DocumentPasteEdit(edit.snippet, edit.label, edit.kind); pasteEdit.additionalEdit = edit.additionalEdits; pasteEdit.yieldTo = [...this._yieldTo, ...edit.yieldTo]; return [pasteEdit]; @@ -108,10 +106,10 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v document: vscode.TextDocument, ranges: readonly vscode.Range[], dataTransfer: vscode.DataTransfer, - settings: { + settings: Readonly<{ insert: InsertMarkdownLink; copyIntoWorkspace: CopyFilesSettings; - }, + }>, context: vscode.DocumentPasteEditContext | undefined, token: vscode.CancellationToken, ): Promise { @@ -162,7 +160,7 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v if ( uriList.entries.length === 1 && (uriList.entries[0].uri.scheme === Schemes.http || uriList.entries[0].uri.scheme === Schemes.https) - && !context?.only?.contains(ResourcePasteOrDropProvider.kind) + && !context?.only?.contains(baseLinkEditKind) ) { const text = await dataTransfer.get(Mime.textPlain)?.asString(); if (token.isCancellationRequested) { @@ -174,7 +172,7 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v } } - const edit = createInsertUriListEdit(document, ranges, uriList); + const edit = createInsertUriListEdit(document, ranges, uriList, { linkKindHint: context?.only }); if (!edit) { return; } @@ -184,6 +182,7 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v return { label: edit.label, + kind: edit.kind, snippet: new vscode.SnippetString(''), additionalEdits, yieldTo: [] @@ -207,12 +206,14 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v interface FileEntry { readonly uri: vscode.Uri; + readonly kind: MediaKind; readonly newFile?: { readonly contents: vscode.DataTransferFile; readonly overwrite: boolean }; } const pathGenerator = new NewFilePathGenerator(); const fileEntries = coalesce(await Promise.all(Array.from(dataTransfer, async ([mime, item]): Promise => { - if (!mediaMimes.has(mime)) { + const mediaKind = getMediaKindForMime(mime); + if (!mediaKind) { return; } @@ -225,7 +226,7 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v // If the file is already in a workspace, we don't want to create a copy of it const workspaceFolder = vscode.workspace.getWorkspaceFolder(file.uri); if (workspaceFolder) { - return { uri: file.uri }; + return { uri: file.uri, kind: mediaKind }; } } @@ -233,7 +234,7 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v if (!newFile) { return; } - return { uri: newFile.uri, newFile: { contents: file, overwrite: newFile.overwrite } }; + return { uri: newFile.uri, kind: mediaKind, newFile: { contents: file, overwrite: newFile.overwrite } }; }))); if (!fileEntries.length) { return; @@ -254,9 +255,11 @@ class ResourcePasteOrDropProvider implements vscode.DocumentPasteEditProvider, v } } + const { label, kind } = getSnippetLabelAndKind(snippet); return { snippet: snippet.snippet, - label: getSnippetLabel(snippet), + label, + kind, additionalEdits, yieldTo: [], }; @@ -277,13 +280,21 @@ function textMatchesUriList(text: string, uriList: UriList): boolean { } export function registerResourceDropOrPasteSupport(selector: vscode.DocumentSelector, parser: IMdParser): vscode.Disposable { + const providedEditKinds = [ + baseLinkEditKind, + linkEditKind, + imageEditKind, + audioEditKind, + videoEditKind, + ]; + return vscode.Disposable.from( vscode.languages.registerDocumentPasteEditProvider(selector, new ResourcePasteOrDropProvider(parser), { - providedPasteEditKinds: [ResourcePasteOrDropProvider.kind], + providedPasteEditKinds: providedEditKinds, pasteMimeTypes: ResourcePasteOrDropProvider.mimeTypes, }), vscode.languages.registerDocumentDropEditProvider(selector, new ResourcePasteOrDropProvider(parser), { - providedDropEditKinds: [ResourcePasteOrDropProvider.kind], + providedDropEditKinds: providedEditKinds, dropMimeTypes: ResourcePasteOrDropProvider.mimeTypes, }), ); diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts index 661b0bfd05f3..a947216fe327 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/pasteUrlProvider.ts @@ -6,9 +6,9 @@ import * as vscode from 'vscode'; import { IMdParser } from '../../markdownEngine'; import { Mime } from '../../util/mimes'; -import { createInsertUriListEdit } from './shared'; -import { InsertMarkdownLink, findValidUriInText, shouldInsertMarkdownLinkByDefault } from './smartDropOrPaste'; import { UriList } from '../../util/uriList'; +import { createInsertUriListEdit, linkEditKind } from './shared'; +import { InsertMarkdownLink, findValidUriInText, shouldInsertMarkdownLinkByDefault } from './smartDropOrPaste'; /** * Adds support for pasting text uris to create markdown links. @@ -17,7 +17,7 @@ import { UriList } from '../../util/uriList'; */ class PasteUrlEditProvider implements vscode.DocumentPasteEditProvider { - public static readonly kind = vscode.DocumentDropOrPasteEditKind.Empty.append('markdown', 'link'); + public static readonly kind = linkEditKind; public static readonly pasteMimeTypes = [Mime.textPlain]; @@ -29,7 +29,7 @@ class PasteUrlEditProvider implements vscode.DocumentPasteEditProvider { document: vscode.TextDocument, ranges: readonly vscode.Range[], dataTransfer: vscode.DataTransfer, - _context: vscode.DocumentPasteEditContext, + context: vscode.DocumentPasteEditContext, token: vscode.CancellationToken, ): Promise { const pasteUrlSetting = vscode.workspace.getConfiguration('markdown', document) @@ -44,12 +44,17 @@ class PasteUrlEditProvider implements vscode.DocumentPasteEditProvider { return; } + // TODO: If the user has explicitly requested to paste as a markdown link, + // try to paste even if we don't have a valid uri const uriText = findValidUriInText(text); if (!uriText) { return; } - const edit = createInsertUriListEdit(document, ranges, UriList.from(uriText), { preserveAbsoluteUris: true }); + const edit = createInsertUriListEdit(document, ranges, UriList.from(uriText), { + linkKindHint: context.only, + preserveAbsoluteUris: true + }); if (!edit) { return; } @@ -61,7 +66,7 @@ class PasteUrlEditProvider implements vscode.DocumentPasteEditProvider { if (!(await shouldInsertMarkdownLinkByDefault(this._parser, document, pasteUrlSetting, ranges, token))) { pasteEdit.yieldTo = [ - vscode.DocumentDropOrPasteEditKind.Empty.append('text'), + vscode.DocumentDropOrPasteEditKind.Text, vscode.DocumentDropOrPasteEditKind.Empty.append('uri') ]; } diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts index 4ab245c192bf..17f7ad1b8050 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts @@ -11,57 +11,83 @@ import { getDocumentDir } from '../../util/document'; import { Schemes } from '../../util/schemes'; import { UriList } from '../../util/uriList'; import { resolveSnippet } from './snippets'; +import { mediaFileExtensions, MediaKind } from '../../util/mimes'; -enum MediaKind { - Image, - Video, - Audio, -} +/** Base kind for any sort of markdown link, including both path and media links */ +export const baseLinkEditKind = vscode.DocumentDropOrPasteEditKind.Empty.append('markdown', 'link'); + +/** Kind for normal markdown links, i.e. `[text](path/to/file.md)` */ +export const linkEditKind = baseLinkEditKind.append('uri'); + +export const imageEditKind = baseLinkEditKind.append('image'); +export const audioEditKind = baseLinkEditKind.append('audio'); +export const videoEditKind = baseLinkEditKind.append('video'); -export const mediaFileExtensions = new Map([ - // Images - ['avif', MediaKind.Image], - ['bmp', MediaKind.Image], - ['gif', MediaKind.Image], - ['ico', MediaKind.Image], - ['jpe', MediaKind.Image], - ['jpeg', MediaKind.Image], - ['jpg', MediaKind.Image], - ['png', MediaKind.Image], - ['psd', MediaKind.Image], - ['svg', MediaKind.Image], - ['tga', MediaKind.Image], - ['tif', MediaKind.Image], - ['tiff', MediaKind.Image], - ['webp', MediaKind.Image], - - // Videos - ['ogg', MediaKind.Video], - ['mp4', MediaKind.Video], - - // Audio Files - ['mp3', MediaKind.Audio], - ['aac', MediaKind.Audio], - ['wav', MediaKind.Audio], -]); - -export function getSnippetLabel(counter: { insertedAudioVideoCount: number; insertedImageCount: number; insertedLinkCount: number }) { - if (counter.insertedAudioVideoCount > 0) { +export function getSnippetLabelAndKind(counter: { readonly insertedAudioCount: number; readonly insertedVideoCount: number; readonly insertedImageCount: number; readonly insertedLinkCount: number }): { + label: string; + kind: vscode.DocumentDropOrPasteEditKind; +} { + if (counter.insertedVideoCount > 0 || counter.insertedAudioCount > 0) { + // Any media plus links if (counter.insertedLinkCount > 0) { - return vscode.l10n.t('Insert Markdown Media and Links'); - } else { - return vscode.l10n.t('Insert Markdown Media'); + return { + label: vscode.l10n.t('Insert Markdown Media and Links'), + kind: baseLinkEditKind, + }; + } + + // Any media plus images + if (counter.insertedImageCount > 0) { + return { + label: vscode.l10n.t('Insert Markdown Media and Images'), + kind: baseLinkEditKind, + }; + } + + // Audio only + if (counter.insertedAudioCount > 0 && !counter.insertedVideoCount) { + return { + label: vscode.l10n.t('Insert Markdown Audio'), + kind: audioEditKind, + }; } - } else if (counter.insertedImageCount > 0 && counter.insertedLinkCount > 0) { - return vscode.l10n.t('Insert Markdown Images and Links'); + + // Video only + if (counter.insertedVideoCount > 0 && !counter.insertedAudioCount) { + return { + label: vscode.l10n.t('Insert Markdown Video'), + kind: videoEditKind, + }; + } + + // Mix of audio and video + return { + label: vscode.l10n.t('Insert Markdown Media'), + kind: baseLinkEditKind, + }; } else if (counter.insertedImageCount > 0) { - return counter.insertedImageCount > 1 - ? vscode.l10n.t('Insert Markdown Images') - : vscode.l10n.t('Insert Markdown Image'); + // Mix of images and links + if (counter.insertedLinkCount > 0) { + return { + label: vscode.l10n.t('Insert Markdown Images and Links'), + kind: baseLinkEditKind, + }; + } + + // Just images + return { + label: counter.insertedImageCount > 1 + ? vscode.l10n.t('Insert Markdown Images') + : vscode.l10n.t('Insert Markdown Image'), + kind: imageEditKind, + }; } else { - return counter.insertedLinkCount > 1 - ? vscode.l10n.t('Insert Markdown Links') - : vscode.l10n.t('Insert Markdown Link'); + return { + label: counter.insertedLinkCount > 1 + ? vscode.l10n.t('Insert Markdown Links') + : vscode.l10n.t('Insert Markdown Link'), + kind: linkEditKind, + }; } } @@ -70,7 +96,7 @@ export function createInsertUriListEdit( ranges: readonly vscode.Range[], urlList: UriList, options?: UriListSnippetOptions, -): { edits: vscode.SnippetTextEdit[]; label: string } | undefined { +): { edits: vscode.SnippetTextEdit[]; label: string; kind: vscode.DocumentDropOrPasteEditKind } | undefined { if (!ranges.length || !urlList.entries.length) { return; } @@ -79,7 +105,8 @@ export function createInsertUriListEdit( let insertedLinkCount = 0; let insertedImageCount = 0; - let insertedAudioVideoCount = 0; + let insertedAudioCount = 0; + let insertedVideoCount = 0; // Use 1 for all empty ranges but give non-empty range unique indices starting after 1 let placeHolderStartIndex = 1 + urlList.entries.length; @@ -100,15 +127,16 @@ export function createInsertUriListEdit( insertedLinkCount += snippet.insertedLinkCount; insertedImageCount += snippet.insertedImageCount; - insertedAudioVideoCount += snippet.insertedAudioVideoCount; + insertedAudioCount += snippet.insertedAudioCount; + insertedVideoCount += snippet.insertedVideoCount; placeHolderStartIndex += urlList.entries.length; edits.push(new vscode.SnippetTextEdit(range, snippet.snippet)); } - const label = getSnippetLabel({ insertedAudioVideoCount, insertedImageCount, insertedLinkCount }); - return { edits, label }; + const { label, kind } = getSnippetLabelAndKind({ insertedAudioCount, insertedVideoCount, insertedImageCount, insertedLinkCount }); + return { edits, label, kind }; } interface UriListSnippetOptions { @@ -117,11 +145,11 @@ interface UriListSnippetOptions { readonly placeholderStartIndex?: number; /** - * Controls if a media link (`![](...)`) is inserted instead of a normal markdown link. + * Hints how links should be inserted, e.g. as normal markdown link or as an image. * - * By default tries to infer this from the uri. + * By default this is inferred from the uri. If you use `media`, we will insert the resource as an image, video, or audio. */ - readonly insertAsMedia?: boolean; + readonly linkKindHint?: vscode.DocumentDropOrPasteEditKind | 'media'; readonly separator?: string; @@ -134,11 +162,12 @@ interface UriListSnippetOptions { } -interface UriSnippet { - snippet: vscode.SnippetString; - insertedLinkCount: number; - insertedImageCount: number; - insertedAudioVideoCount: number; +export interface UriSnippet { + readonly snippet: vscode.SnippetString; + readonly insertedLinkCount: number; + readonly insertedImageCount: number; + readonly insertedVideoCount: number; + readonly insertedAudioCount: number; } export function createUriListSnippet( @@ -146,6 +175,7 @@ export function createUriListSnippet( uris: ReadonlyArray<{ readonly uri: vscode.Uri; readonly str?: string; + readonly kind?: MediaKind; }>, options?: UriListSnippetOptions, ): UriSnippet | undefined { @@ -159,7 +189,8 @@ export function createUriListSnippet( let insertedLinkCount = 0; let insertedImageCount = 0; - let insertedAudioVideoCount = 0; + let insertedAudioCount = 0; + let insertedVideoCount = 0; const snippet = new vscode.SnippetString(); let placeholderIndex = options?.placeholderStartIndex ?? 1; @@ -167,14 +198,22 @@ export function createUriListSnippet( uris.forEach((uri, i) => { const mdPath = (!options?.preserveAbsoluteUris ? getRelativeMdPath(documentDir, uri.uri) : undefined) ?? uri.str ?? uri.uri.toString(); - const ext = URI.Utils.extname(uri.uri).toLowerCase().replace('.', ''); - const insertAsMedia = options?.insertAsMedia || (typeof options?.insertAsMedia === 'undefined' && mediaFileExtensions.has(ext)); + const desiredKind = getDesiredLinkKind(uri.uri, uri.kind, options); - if (insertAsMedia) { - const insertAsVideo = mediaFileExtensions.get(ext) === MediaKind.Video; - const insertAsAudio = mediaFileExtensions.get(ext) === MediaKind.Audio; + if (desiredKind === DesiredLinkKind.Link) { + insertedLinkCount++; + snippet.appendText('['); + snippet.appendPlaceholder(escapeBrackets(options?.placeholderText ?? 'text'), placeholderIndex); + snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`); + } else { + const insertAsVideo = desiredKind === DesiredLinkKind.Video; + const insertAsAudio = desiredKind === DesiredLinkKind.Audio; if (insertAsVideo || insertAsAudio) { - insertedAudioVideoCount++; + if (insertAsVideo) { + insertedVideoCount++; + } else { + insertedAudioCount++; + } const mediaSnippet = insertAsVideo ? config.get('editor.filePaste.videoSnippet', '') : config.get('editor.filePaste.audioSnippet', ''); @@ -189,11 +228,6 @@ export function createUriListSnippet( snippet.appendPlaceholder(placeholderText, placeholderIndex); snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`); } - } else { - insertedLinkCount++; - snippet.appendText('['); - snippet.appendPlaceholder(escapeBrackets(options?.placeholderText ?? 'text'), placeholderIndex); - snippet.appendText(`](${escapeMarkdownLinkPath(mdPath)})`); } if (i < uris.length - 1 && uris.length > 1) { @@ -201,9 +235,48 @@ export function createUriListSnippet( } }); - return { snippet, insertedAudioVideoCount, insertedImageCount, insertedLinkCount }; + return { snippet, insertedAudioCount, insertedVideoCount, insertedImageCount, insertedLinkCount }; +} + +enum DesiredLinkKind { + Link, + Image, + Video, + Audio, } +function getDesiredLinkKind(uri: vscode.Uri, uriFileKind: MediaKind | undefined, options: UriListSnippetOptions | undefined): DesiredLinkKind { + if (options?.linkKindHint instanceof vscode.DocumentDropOrPasteEditKind) { + if (linkEditKind.contains(options.linkKindHint)) { + return DesiredLinkKind.Link; + } else if (imageEditKind.contains(options.linkKindHint)) { + return DesiredLinkKind.Image; + } else if (audioEditKind.contains(options.linkKindHint)) { + return DesiredLinkKind.Audio; + } else if (videoEditKind.contains(options.linkKindHint)) { + return DesiredLinkKind.Video; + } + } + + if (typeof uriFileKind !== 'undefined') { + switch (uriFileKind) { + case MediaKind.Video: return DesiredLinkKind.Video; + case MediaKind.Audio: return DesiredLinkKind.Audio; + case MediaKind.Image: return DesiredLinkKind.Image; + } + } + + const normalizedExt = URI.Utils.extname(uri).toLowerCase().replace('.', ''); + if (options?.linkKindHint === 'media' || mediaFileExtensions.has(normalizedExt)) { + switch (mediaFileExtensions.get(normalizedExt)) { + case MediaKind.Video: return DesiredLinkKind.Video; + case MediaKind.Audio: return DesiredLinkKind.Audio; + default: return DesiredLinkKind.Image; + } + } + + return DesiredLinkKind.Link; +} function getRelativeMdPath(dir: vscode.Uri | undefined, file: vscode.Uri): string | undefined { if (dir && dir.scheme === file.scheme && dir.authority === file.authority) { @@ -229,7 +302,7 @@ function escapeMarkdownLinkPath(mdPath: string): string { } function escapeBrackets(value: string): string { - value = value.replace(/[\[\]]/g, '\\$&'); // CodeQL [SM02383] The Markdown is fully sanitized after being rendered. + value = value.replace(/\\/g, '\\\\').replace(/[\[\]]/g, '\\$&'); // CodeQL [SM02383] The Markdown is fully sanitized after being rendered. return value; } @@ -264,6 +337,7 @@ function needsBracketLink(mdPath: string): boolean { export interface DropOrPasteEdit { readonly snippet: vscode.SnippetString; + readonly kind: vscode.DocumentDropOrPasteEditKind; readonly label: string; readonly additionalEdits: vscode.WorkspaceEdit; readonly yieldTo: vscode.DocumentDropOrPasteEditKind[]; diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/snippets.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/snippets.ts index 854cf2175d0c..2430d21c342d 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/snippets.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/snippets.ts @@ -7,7 +7,7 @@ * Resolves variables in a VS Code snippet style string */ export function resolveSnippet(snippetString: string, vars: ReadonlyMap): string { - return snippetString.replaceAll(/(?\\\$)|(?\w+)(?:\/(?(?:\\\/|[^\}])+?)\/(?(?:\\\/|[^\}])+?)\/)?\}/g, (match, _escape, name, pattern, replacement, _offset, _str, groups) => { + return snippetString.replaceAll(/(?\\\$)|(?\w+)(?:\/(?(?:\\\/|[^\/}])+?)\/(?(?:\\\/|[^\/}])+?)\/)?\}/g, (match, _escape, name, pattern, replacement, _offset, _str, groups) => { if (groups?.['escape']) { return '$'; } diff --git a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index f54c136ad1a4..48e579da7fce 100644 --- a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts @@ -72,10 +72,61 @@ class AddToIgnoreLinksQuickFixProvider implements vscode.CodeActionProvider { } } +function registerMarkdownStatusItem(selector: vscode.DocumentSelector, commandManager: CommandManager): vscode.Disposable { + const statusItem = vscode.languages.createLanguageStatusItem('markdownStatus', selector); + + const enabledSettingId = 'validate.enabled'; + const commandId = '_markdown.toggleValidation'; + + const commandSub = commandManager.register({ + id: commandId, + execute: (enabled: boolean) => { + vscode.workspace.getConfiguration('markdown').update(enabledSettingId, enabled); + } + }); + + const update = () => { + const activeDoc = vscode.window.activeTextEditor?.document; + const markdownDoc = activeDoc?.languageId === 'markdown' ? activeDoc : undefined; + + const enabled = vscode.workspace.getConfiguration('markdown', markdownDoc).get(enabledSettingId); + if (enabled) { + statusItem.text = vscode.l10n.t('Markdown link validation enabled'); + statusItem.command = { + command: commandId, + arguments: [false], + title: vscode.l10n.t('Disable'), + tooltip: vscode.l10n.t('Disable validation of Markdown links'), + }; + } else { + statusItem.text = vscode.l10n.t('Markdown link validation disabled'); + statusItem.command = { + command: commandId, + arguments: [true], + title: vscode.l10n.t('Enable'), + tooltip: vscode.l10n.t('Enable validation of Markdown links'), + }; + } + }; + update(); + + return vscode.Disposable.from( + statusItem, + commandSub, + vscode.workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('markdown.' + enabledSettingId)) { + update(); + } + }), + ); +} export function registerDiagnosticSupport( selector: vscode.DocumentSelector, commandManager: CommandManager, ): vscode.Disposable { - return AddToIgnoreLinksQuickFixProvider.register(selector, commandManager); + return vscode.Disposable.from( + AddToIgnoreLinksQuickFixProvider.register(selector, commandManager), + registerMarkdownStatusItem(selector, commandManager), + ); } diff --git a/extensions/markdown-language-features/src/languageFeatures/updateLinksOnPaste.ts b/extensions/markdown-language-features/src/languageFeatures/updateLinksOnPaste.ts index c8ad4c722fde..f8a7128eb05f 100644 --- a/extensions/markdown-language-features/src/languageFeatures/updateLinksOnPaste.ts +++ b/extensions/markdown-language-features/src/languageFeatures/updateLinksOnPaste.ts @@ -9,9 +9,9 @@ import { Mime } from '../util/mimes'; class UpdatePastedLinksEditProvider implements vscode.DocumentPasteEditProvider { - public static readonly kind = vscode.DocumentDropOrPasteEditKind.Empty.append('markdown', 'updateLinks'); + public static readonly kind = vscode.DocumentDropOrPasteEditKind.Text.append('updateLinks', 'markdown'); - public static readonly metadataMime = 'vnd.vscode.markdown.updateLinksMetadata'; + public static readonly metadataMime = 'application/vnd.vscode.markdown.updatelinks.metadata'; constructor( private readonly _client: MdLanguageClient, @@ -67,7 +67,7 @@ class UpdatePastedLinksEditProvider implements vscode.DocumentPasteEditProvider pasteEdit.additionalEdit = workspaceEdit; if (!context.only || !UpdatePastedLinksEditProvider.kind.contains(context.only)) { - pasteEdit.yieldTo = [vscode.DocumentDropOrPasteEditKind.Empty.append('text')]; + pasteEdit.yieldTo = [vscode.DocumentDropOrPasteEditKind.Text]; } return [pasteEdit]; diff --git a/extensions/markdown-language-features/src/logging.ts b/extensions/markdown-language-features/src/logging.ts index 6c114ae40e57..b5ea76f36081 100644 --- a/extensions/markdown-language-features/src/logging.ts +++ b/extensions/markdown-language-features/src/logging.ts @@ -6,87 +6,24 @@ import * as vscode from 'vscode'; import { Disposable } from './util/dispose'; -enum Trace { - Off, - Verbose -} - -namespace Trace { - export function fromString(value: string): Trace { - value = value.toLowerCase(); - switch (value) { - case 'off': - return Trace.Off; - case 'verbose': - return Trace.Verbose; - default: - return Trace.Off; - } - } -} export interface ILogger { - verbose(title: string, message: string, data?: any): void; + trace(title: string, message: string, data?: any): void; } export class VsCodeOutputLogger extends Disposable implements ILogger { - private _trace?: Trace; - - private _outputChannelValue?: vscode.OutputChannel; + private _outputChannelValue?: vscode.LogOutputChannel; private get _outputChannel() { - this._outputChannelValue ??= this._register(vscode.window.createOutputChannel('Markdown')); + this._outputChannelValue ??= this._register(vscode.window.createOutputChannel('Markdown', { log: true })); return this._outputChannelValue; } constructor() { super(); - - this._register(vscode.workspace.onDidChangeConfiguration(() => { - this._updateConfiguration(); - })); - - this._updateConfiguration(); - } - - public verbose(title: string, message: string, data?: any): void { - if (this._trace === Trace.Verbose) { - this._appendLine(`[Verbose ${this._now()}] ${title}: ${message}`); - if (data) { - this._appendLine(VsCodeOutputLogger._data2String(data)); - } - } - } - - private _now(): string { - const now = new Date(); - return String(now.getUTCHours()).padStart(2, '0') - + ':' + String(now.getMinutes()).padStart(2, '0') - + ':' + String(now.getUTCSeconds()).padStart(2, '0') + '.' + String(now.getMilliseconds()).padStart(3, '0'); - } - - private _updateConfiguration(): void { - this._trace = this._readTrace(); - } - - private _appendLine(value: string): void { - this._outputChannel.appendLine(value); - } - - private _readTrace(): Trace { - return Trace.fromString(vscode.workspace.getConfiguration().get('markdown.trace.extension', 'off')); } - private static _data2String(data: any): string { - if (data instanceof Error) { - if (typeof data.stack === 'string') { - return data.stack; - } - return data.message; - } - if (typeof data === 'string') { - return data; - } - return JSON.stringify(data, undefined, 2); + public trace(title: string, message: string, data?: any): void { + this._outputChannel.trace(`${title}: ${message}`, ...(data ? [JSON.stringify(data, null, 4)] : [])); } } diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 83925eab1324..89363a77a86d 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -186,7 +186,7 @@ export class MarkdownItEngine implements IMdParser { return cached; } - this._logger.verbose('MarkdownItEngine', `tokenizeDocument - ${document.uri}`); + this._logger.trace('MarkdownItEngine', `tokenizeDocument - ${document.uri}`); const tokens = this._tokenizeString(document.getText(), engine); this._tokenCache.update(document, config, tokens); return tokens; diff --git a/extensions/markdown-language-features/src/preview/documentRenderer.ts b/extensions/markdown-language-features/src/preview/documentRenderer.ts index 331fb5566a0c..8a39c9cdf4d3 100644 --- a/extensions/markdown-language-features/src/preview/documentRenderer.ts +++ b/extensions/markdown-language-features/src/preview/documentRenderer.ts @@ -79,7 +79,7 @@ export class MdDocumentRenderer { webviewResourceRoot: resourceProvider.asWebviewUri(markdownDocument.uri).toString(), }; - this._logger.verbose('DocumentRenderer', `provideTextDocumentContent - ${markdownDocument.uri}`, initialData); + this._logger.trace('DocumentRenderer', `provideTextDocumentContent - ${markdownDocument.uri}`, initialData); // Content Security Policy const nonce = getNonce(); @@ -98,13 +98,13 @@ export class MdDocumentRenderer { + data-state="${escapeAttribute(JSON.stringify(state || {}))}" + data-initial-md-content="${escapeAttribute(body.html)}"> ${this._getStyles(resourceProvider, sourceUri, config, imageInfo)} - ${body.html} ${this._getScripts(resourceProvider, nonce)} `; diff --git a/extensions/markdown-language-features/src/preview/preview.ts b/extensions/markdown-language-features/src/preview/preview.ts index 7ccbc625b473..2d4186df9ae3 100644 --- a/extensions/markdown-language-features/src/preview/preview.ts +++ b/extensions/markdown-language-features/src/preview/preview.ts @@ -221,7 +221,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { return; } - this._logger.verbose('MarkdownPreview', 'updateForView', { markdownFile: this._resource }); + this._logger.trace('MarkdownPreview', 'updateForView', { markdownFile: this._resource }); this._line = topLine; this.postMessage({ type: 'updateView', diff --git a/extensions/markdown-language-features/src/preview/previewManager.ts b/extensions/markdown-language-features/src/preview/previewManager.ts index 3b46c2a43220..3aa126da063d 100644 --- a/extensions/markdown-language-features/src/preview/previewManager.ts +++ b/extensions/markdown-language-features/src/preview/previewManager.ts @@ -170,6 +170,11 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview } } + public openDocumentLink(linkText: string, fromResource: vscode.Uri) { + const viewColumn = this.findPreview(fromResource)?.resourceColumn; + return this._opener.openDocumentLink(linkText, fromResource, viewColumn); + } + public async deserializeWebviewPanel( webview: vscode.WebviewPanel, state: any diff --git a/extensions/markdown-language-features/src/test/nulLogging.ts b/extensions/markdown-language-features/src/test/nulLogging.ts index a786ab83b4f2..035e7d74c6dc 100644 --- a/extensions/markdown-language-features/src/test/nulLogging.ts +++ b/extensions/markdown-language-features/src/test/nulLogging.ts @@ -6,7 +6,7 @@ import { ILogger } from '../logging'; export const nulLogger = new class implements ILogger { - verbose(): void { + trace(): void { // noop } }; diff --git a/extensions/markdown-language-features/src/test/pasteUrl.test.ts b/extensions/markdown-language-features/src/test/pasteUrl.test.ts index fdb6e6c2f717..45737c354dad 100644 --- a/extensions/markdown-language-features/src/test/pasteUrl.test.ts +++ b/extensions/markdown-language-features/src/test/pasteUrl.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; import { InMemoryDocument } from '../client/inMemoryDocument'; -import { createInsertUriListEdit } from '../languageFeatures/copyFiles/shared'; +import { createInsertUriListEdit, imageEditKind, linkEditKind } from '../languageFeatures/copyFiles/shared'; import { InsertMarkdownLink, findValidUriInText, shouldInsertMarkdownLinkByDefault } from '../languageFeatures/copyFiles/smartDropOrPaste'; import { noopToken } from '../util/cancellation'; import { UriList } from '../util/uriList'; @@ -20,8 +20,6 @@ function makeTestDoc(contents: string) { suite('createEditAddingLinksForUriList', () => { test('Markdown Link Pasting should occur for a valid link (end to end)', async () => { - // createEditAddingLinksForUriList -> checkSmartPaste -> tryGetUriListSnippet -> createUriListSnippet -> createLinkSnippet - const result = createInsertUriListEdit( new InMemoryDocument(vscode.Uri.file('test.md'), 'hello world!'), [new vscode.Range(0, 0, 0, 12)], UriList.from('https://www.microsoft.com/')); // need to check the actual result -> snippet value @@ -110,7 +108,6 @@ suite('createEditAddingLinksForUriList', () => { }); suite('createInsertUriListEdit', () => { - test('Should create snippet with < > when pasted link has an mismatched parentheses', () => { const edit = createInsertUriListEdit(makeTestDoc(''), [new vscode.Range(0, 0, 0, 0)], UriList.from('https://www.mic(rosoft.com')); assert.strictEqual(edit?.edits?.[0].snippet.value, '[${1:text}]()'); @@ -135,6 +132,25 @@ suite('createEditAddingLinksForUriList', () => { const edit = createInsertUriListEdit(makeTestDoc(''), [new vscode.Range(0, 0, 0, 0)], UriList.from('https://www.example.com/path?query=value&another=value#fragment')); assert.strictEqual(edit?.edits?.[0].snippet.value, '[${1:text}](https://www.example.com/path?query=value&another=value#fragment)'); }); + + test('Should add image for image file by default', () => { + const edit = createInsertUriListEdit(makeTestDoc(''), [new vscode.Range(0, 0, 0, 0)], UriList.from('https://www.example.com/cat.png')); + assert.strictEqual(edit?.edits?.[0].snippet.value, '![${1:alt text}](https://www.example.com/cat.png)'); + }); + + test('Should be able to override insert style to use link', () => { + const edit = createInsertUriListEdit(makeTestDoc(''), [new vscode.Range(0, 0, 0, 0)], UriList.from('https://www.example.com/cat.png'), { + linkKindHint: linkEditKind, + }); + assert.strictEqual(edit?.edits?.[0].snippet.value, '[${1:text}](https://www.example.com/cat.png)'); + }); + + test('Should be able to override insert style to use images', () => { + const edit = createInsertUriListEdit(makeTestDoc(''), [new vscode.Range(0, 0, 0, 0)], UriList.from('https://www.example.com/'), { + linkKindHint: imageEditKind, + }); + assert.strictEqual(edit?.edits?.[0].snippet.value, '![${1:alt text}](https://www.example.com/)'); + }); }); diff --git a/extensions/markdown-language-features/src/util/dom.ts b/extensions/markdown-language-features/src/util/dom.ts index 0f6c00da9daf..8bbce79c3037 100644 --- a/extensions/markdown-language-features/src/util/dom.ts +++ b/extensions/markdown-language-features/src/util/dom.ts @@ -5,7 +5,10 @@ import * as vscode from 'vscode'; export function escapeAttribute(value: string | vscode.Uri): string { - return value.toString().replace(/"/g, '"'); + return value.toString() + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, '''); } export function getNonce() { diff --git a/extensions/markdown-language-features/src/util/mimes.ts b/extensions/markdown-language-features/src/util/mimes.ts index f33b807b83e3..32dcc021815b 100644 --- a/extensions/markdown-language-features/src/util/mimes.ts +++ b/extensions/markdown-language-features/src/util/mimes.ts @@ -8,16 +8,52 @@ export const Mime = { textPlain: 'text/plain', } as const; -export const mediaMimes = new Set([ - 'image/avif', - 'image/bmp', - 'image/gif', - 'image/jpeg', - 'image/png', - 'image/webp', - 'video/mp4', - 'video/ogg', - 'audio/mpeg', - 'audio/aac', - 'audio/x-wav', +export const rootMediaMimesTypes = Object.freeze({ + image: 'image', + audio: 'audio', + video: 'video', +}); + +export enum MediaKind { + Image = 1, + Video, + Audio +} + +export function getMediaKindForMime(mime: string): MediaKind | undefined { + const root = mime.toLowerCase().split('/').at(0); + switch (root) { + case 'image': return MediaKind.Image; + case 'video': return MediaKind.Video; + case 'audio': return MediaKind.Audio; + default: return undefined; + } +} + +export const mediaFileExtensions = new Map([ + // Images + ['avif', MediaKind.Image], + ['bmp', MediaKind.Image], + ['gif', MediaKind.Image], + ['ico', MediaKind.Image], + ['jpe', MediaKind.Image], + ['jpeg', MediaKind.Image], + ['jpg', MediaKind.Image], + ['png', MediaKind.Image], + ['psd', MediaKind.Image], + ['svg', MediaKind.Image], + ['tga', MediaKind.Image], + ['tif', MediaKind.Image], + ['tiff', MediaKind.Image], + ['webp', MediaKind.Image], + + // Videos + ['ogg', MediaKind.Video], + ['mp4', MediaKind.Video], + ['mov', MediaKind.Video], + + // Audio Files + ['mp3', MediaKind.Audio], + ['aac', MediaKind.Audio], + ['wav', MediaKind.Audio], ]); diff --git a/extensions/markdown-language-features/tsconfig.json b/extensions/markdown-language-features/tsconfig.json index 75edc8fdacfa..fcd79775de5f 100644 --- a/extensions/markdown-language-features/tsconfig.json +++ b/extensions/markdown-language-features/tsconfig.json @@ -5,7 +5,6 @@ }, "include": [ "src/**/*", - "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.documentPaste.d.ts" + "../../src/vscode-dts/vscode.d.ts" ] } diff --git a/extensions/markdown-language-features/types/previewMessaging.d.ts b/extensions/markdown-language-features/types/previewMessaging.d.ts index 05d10af65972..686b21bf1a91 100644 --- a/extensions/markdown-language-features/types/previewMessaging.d.ts +++ b/extensions/markdown-language-features/types/previewMessaging.d.ts @@ -71,10 +71,17 @@ export namespace ToWebviewMessage { readonly id: string; } + export interface OpenImageContent extends BaseMessage { + readonly type: 'openImage'; + readonly source: string; + readonly imageSource: string; + } + export type Type = | OnDidChangeTextEditorSelection | UpdateView | UpdateContent | CopyImageContent + | OpenImageContent ; } diff --git a/extensions/markdown-math/package-lock.json b/extensions/markdown-math/package-lock.json index 53a0866e61f4..869798dc0689 100644 --- a/extensions/markdown-math/package-lock.json +++ b/extensions/markdown-math/package-lock.json @@ -9,32 +9,53 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/markdown-it-katex": "^1.1.0" + "@vscode/markdown-it-katex": "^1.1.2" }, "devDependencies": { - "@types/markdown-it": "^0.0.0", - "@types/vscode-notebook-renderer": "^1.60.0" + "@types/markdown-it": "^14.1.2", + "@types/vscode-notebook-renderer": "^1.72.4" }, "engines": { "vscode": "^1.54.0" } }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/markdown-it": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-0.0.0.tgz", - "integrity": "sha512-rLEOTm6Wi9M8GFnIK7VczXSEThIN/eVoevpTYVk+FD/DPX3N15Sj9b3vkjjDY63U0Zw1yawf13CI92CCHpC5kw==", - "dev": true + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" }, "node_modules/@types/vscode-notebook-renderer": { - "version": "1.72.0", - "resolved": "https://registry.npmjs.org/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.72.0.tgz", - "integrity": "sha512-5iTjb39DpLn03ULUwrDR3L2Dy59RV4blSUHy0oLdQuIY11PhgWO4mXIcoFS0VxY1GZQ4IcjSf3ooT2Jrrcahnw==", - "dev": true + "version": "1.72.4", + "resolved": "https://registry.npmjs.org/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.72.4.tgz", + "integrity": "sha512-bdKO41c6Dc24pH/O/eM/jqfCwGH4zc76o/g/6Gt1y/eg/bvvqP2/VpbV+Sa5Te2sZekFRcbYnSSFTKo3wcVGUg==", + "dev": true, + "license": "MIT" }, "node_modules/@vscode/markdown-it-katex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@vscode/markdown-it-katex/-/markdown-it-katex-1.1.0.tgz", - "integrity": "sha512-9cF2eJpsJOEs2V1cCAoJW/boKz9GQQLvZhNvI030K90z6ZE9lRGc9hDVvKut8zdFO2ObjwylPXXXVYvTdP2O2Q==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vscode/markdown-it-katex/-/markdown-it-katex-1.1.2.tgz", + "integrity": "sha512-+4IIv5PgrmhKvW/3LpkpkGg257OViEhXkOOgCyj5KMsjsOfnRXkni8XAuuF9Ui5p3B8WnUovlDXAQNb8RJ/RaQ==", + "license": "MIT", "dependencies": { "katex": "^0.16.4" } @@ -43,18 +64,20 @@ "version": "8.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "license": "MIT", "engines": { "node": ">= 12" } }, "node_modules/katex": { - "version": "0.16.10", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.10.tgz", - "integrity": "sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==", + "version": "0.16.21", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.21.tgz", + "integrity": "sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==", "funding": [ "https://opencollective.com/katex", "https://github.com/sponsors/katex" ], + "license": "MIT", "dependencies": { "commander": "^8.3.0" }, diff --git a/extensions/markdown-math/package.json b/extensions/markdown-math/package.json index 9669efc24351..f397c9ca66be 100644 --- a/extensions/markdown-math/package.json +++ b/extensions/markdown-math/package.json @@ -94,7 +94,9 @@ }, "markdown.math.macros": { "type": "object", - "additionalProperties": { "type": "string" }, + "additionalProperties": { + "type": "string" + }, "default": {}, "description": "%config.markdown.math.macros%", "scope": "resource" @@ -108,15 +110,15 @@ "watch": "npm run build-notebook", "build-notebook": "node ./esbuild" }, - "dependencies": { - "@vscode/markdown-it-katex": "^1.1.0" - }, "devDependencies": { - "@types/markdown-it": "^0.0.0", - "@types/vscode-notebook-renderer": "^1.60.0" + "@types/markdown-it": "^14.1.2", + "@types/vscode-notebook-renderer": "^1.72.4" }, "repository": { "type": "git", "url": "https://github.com/microsoft/vscode.git" + }, + "dependencies": { + "@vscode/markdown-it-katex": "^1.1.2" } } diff --git a/extensions/media-preview/media/imagePreview.js b/extensions/media-preview/media/imagePreview.js index ab8ad542a2d9..282e2a82cb94 100644 --- a/extensions/media-preview/media/imagePreview.js +++ b/extensions/media-preview/media/imagePreview.js @@ -311,7 +311,45 @@ document.body.classList.remove('loading'); }); - image.src = settings.src; + /** + * Validate and ensure only safe image sources can be used. + * @param {string} src + * @return {boolean} + */ + function isSafeImageSrc(src) { + try { + if (typeof src !== 'string' || src.length > 2048) { + return false; + } + // Allow http, https, file, and data URIs for images only + const allowedSchemes = ['http:', 'https:', 'file:']; + const urlMatch = src.match(/^([a-zA-Z0-9+.-]+):/); + if (!urlMatch) { + return false; + } + const scheme = urlMatch[1].toLowerCase() + ':'; + if (allowedSchemes.includes(scheme)) { + return true; + } + if (scheme === 'data:') { + // Allow only image media types in data URLs + // Disallow SVG images for data URIs to mitigate XSS + return /^data:image\/(png|jpe?g|gif|bmp|webp);base64,/.test(src); + } + return false; + } catch { + return false; + } + } + + if (isSafeImageSrc(settings.src)) { + image.src = settings.src; + } else { + console.error('Unsafe image src detected:', settings.src); + image.src = ''; + document.body.classList.add('error'); + document.body.classList.remove('loading'); + } document.querySelector('.open-file-link')?.addEventListener('click', (e) => { e.preventDefault(); diff --git a/extensions/media-preview/media/videoPreview.js b/extensions/media-preview/media/videoPreview.js index eeed26972a31..8ff6ddcb8d52 100644 --- a/extensions/media-preview/media/videoPreview.js +++ b/extensions/media-preview/media/videoPreview.js @@ -5,6 +5,18 @@ // @ts-check "use strict"; +// Returns true if src is a safe URL for video.src +function isSafeVideoSrc(src) { + try { + const allowedProtocols = ['http:', 'https:', 'blob:', 'filesystem:', '']; + // If relative URL, use document.baseURI as base + const url = new URL(src, document.baseURI); + return allowedProtocols.includes(url.protocol); + } catch { + return false; + } +} + (function () { // @ts-ignore const vscode = acquireVsCodeApi(); @@ -28,7 +40,7 @@ // Elements const video = document.createElement('video'); - if (settings.src !== null) { + if (settings.src !== null && isSafeVideoSrc(settings.src)) { video.src = settings.src; } video.playsInline = true; diff --git a/extensions/media-preview/package-lock.json b/extensions/media-preview/package-lock.json index 68391b8c4be9..329322a5a689 100644 --- a/extensions/media-preview/package-lock.json +++ b/extensions/media-preview/package-lock.json @@ -9,144 +9,156 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", - "vscode-uri": "^3.0.6" + "@vscode/extension-telemetry": "^0.9.8", + "vscode-uri": "^3.1.0" }, "engines": { "vscode": "^1.70.0" } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", + "license": "MIT" }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.2.0.tgz", + "integrity": "sha512-En6dTwfy5NFzSMibvOpx/lKq2jtgWuR4++KJbi3SpQ2iT8gm+PHo9868/scocW122KDwTxl4ruxZ7i4rHmJJnQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" } }, "node_modules/vscode-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.6.tgz", - "integrity": "sha512-fmL7V1eiDBFRRnu+gfRWTzyPpNIHJTc4mWnFkwBUmO9U3KPgJAmTx7oxi2bl/Rh6HLdU7+4C9wlj0k2E4AdKFQ==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" } } } diff --git a/extensions/media-preview/package.json b/extensions/media-preview/package.json index b42256a22601..6676dbcad798 100644 --- a/extensions/media-preview/package.json +++ b/extensions/media-preview/package.json @@ -50,7 +50,7 @@ "priority": "builtin", "selector": [ { - "filenamePattern": "*.{jpg,jpe,jpeg,png,bmp,gif,ico,webp,avif}" + "filenamePattern": "*.{jpg,jpe,jpeg,png,bmp,gif,ico,webp,avif,svg}" } ] }, @@ -126,8 +126,8 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", - "vscode-uri": "^3.0.6" + "@vscode/extension-telemetry": "^0.9.8", + "vscode-uri": "^3.1.0" }, "repository": { "type": "git", diff --git a/extensions/merge-conflict/package-lock.json b/extensions/merge-conflict/package-lock.json index a57272606cd0..c4cbf3546d0b 100644 --- a/extensions/merge-conflict/package-lock.json +++ b/extensions/merge-conflict/package-lock.json @@ -9,156 +9,169 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0" + "@vscode/extension-telemetry": "^1.2.0" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.5.0" } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.2.0.tgz", + "integrity": "sha512-En6dTwfy5NFzSMibvOpx/lKq2jtgWuR4++KJbi3SpQ2iT8gm+PHo9868/scocW122KDwTxl4ruxZ7i4rHmJJnQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/merge-conflict/package.json b/extensions/merge-conflict/package.json index cdda46fab322..c4273cd805b6 100644 --- a/extensions/merge-conflict/package.json +++ b/extensions/merge-conflict/package.json @@ -166,10 +166,10 @@ } }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.0" + "@vscode/extension-telemetry": "^1.2.0" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/microsoft-authentication/.vscodeignore b/extensions/microsoft-authentication/.vscodeignore index 98b90d34d825..e7feddb5da86 100644 --- a/extensions/microsoft-authentication/.vscodeignore +++ b/extensions/microsoft-authentication/.vscodeignore @@ -12,3 +12,4 @@ vsc-extension-quickstart.md **/tslint.json **/*.map **/*.ts +packageMocks/ diff --git a/extensions/microsoft-authentication/extension.webpack.config.js b/extensions/microsoft-authentication/extension.webpack.config.js index 45600607fc5b..395c011b1dba 100644 --- a/extensions/microsoft-authentication/extension.webpack.config.js +++ b/extensions/microsoft-authentication/extension.webpack.config.js @@ -8,10 +8,39 @@ 'use strict'; const withDefaults = require('../shared.webpack.config'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const path = require('path'); + +const isWindows = process.platform === 'win32'; module.exports = withDefaults({ context: __dirname, entry: { extension: './src/extension.ts' - } + }, + externals: { + // The @azure/msal-node-runtime package requires this native node module (.node). + // It is currently only included on Windows, but the package handles unsupported platforms + // gracefully. + './msal-node-runtime': 'commonjs ./msal-node-runtime' + }, + resolve: { + alias: { + 'keytar': path.resolve(__dirname, 'packageMocks', 'keytar', 'index.js') + } + }, + plugins: [ + ...withDefaults.nodePlugins(__dirname), + new CopyWebpackPlugin({ + patterns: [ + { + // The native files we need to ship with the extension + from: '**/dist/msal*.(node|dll)', + to: '[name][ext]', + // These will only be present on Windows for now + noErrorOnMissing: !isWindows + } + ] + }) + ] }); diff --git a/extensions/microsoft-authentication/package-lock.json b/extensions/microsoft-authentication/package-lock.json index 8f05b14f02ae..c9a60f744349 100644 --- a/extensions/microsoft-authentication/package-lock.json +++ b/extensions/microsoft-authentication/package-lock.json @@ -10,16 +10,18 @@ "license": "MIT", "dependencies": { "@azure/ms-rest-azure-env": "^2.0.0", - "@azure/msal-node": "^2.13.1", - "@vscode/extension-telemetry": "^0.9.0", + "@azure/msal-node": "^5.0.2", + "@azure/msal-node-extensions": "^5.0.2", + "@vscode/extension-telemetry": "^1.2.0", + "keytar": "file:./packageMocks/keytar", "vscode-tas-client": "^0.1.84" }, "devDependencies": { - "@types/node": "20.x", - "@types/node-fetch": "^2.5.7", - "@types/randombytes": "^2.0.0", - "@types/sha.js": "^2.4.0", - "@types/uuid": "8.0.0" + "@types/node": "25.x", + "@types/node-fetch": "^2.6.13", + "@types/randombytes": "^2.0.3", + "@types/sha.js": "^2.4.4", + "@types/uuid": "11.0.0" }, "engines": { "vscode": "^1.42.0" @@ -31,191 +33,235 @@ "integrity": "sha512-dG76W7ElfLi+fbTjnZVGj+M9e0BIEJmRxU6fHaUQ12bZBe8EJKYb2GV50YWNaP2uJiVQ5+7nXEVj1VN1UQtaEw==" }, "node_modules/@azure/msal-common": { - "version": "14.14.2", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.14.2.tgz", - "integrity": "sha512-XV0P5kSNwDwCA/SjIxTe9mEAsKB0NqGNSuaVrkCCE2lAyBr/D6YtD80Vkdp4tjWnPFwjzkwldjr1xU/facOJog==", + "version": "16.0.2", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.0.2.tgz", + "integrity": "sha512-ZJ/UR7lyqIntURrIJCyvScwJFanM9QhJYcJCheB21jZofGKpP9QxWgvADANo7UkresHKzV+6YwoeZYP7P7HvUg==", + "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-node": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.13.1.tgz", - "integrity": "sha512-sijfzPNorKt6+9g1/miHwhj6Iapff4mPQx1azmmZExgzUROqWTM1o3ACyxDja0g47VpowFy/sxTM/WsuCyXTiw==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.0.2.tgz", + "integrity": "sha512-3tHeJghckgpTX98TowJoXOjKGuds0L+FKfeHJtoZFl2xvwE6RF65shZJzMQ5EQZWXzh3sE1i9gE+m3aRMachjA==", + "license": "MIT", "dependencies": { - "@azure/msal-common": "14.14.2", + "@azure/msal-common": "16.0.2", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" }, "engines": { - "node": ">=16" + "node": ">=20" } }, + "node_modules/@azure/msal-node-extensions": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node-extensions/-/msal-node-extensions-5.0.2.tgz", + "integrity": "sha512-695boK9IFmHrYPviFgj5DaeJ65iUGjzHiSIAgzyIRfwTO8k3J4KrF+xbFy2uC9JZ2U8HHEobi7UGloc768HhUQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "16.0.2", + "@azure/msal-node-runtime": "^0.20.0", + "keytar": "^7.8.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@azure/msal-node-runtime": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@azure/msal-node-runtime/-/msal-node-runtime-0.20.1.tgz", + "integrity": "sha512-WVbMedbJHjt9M+qeZMH/6U1UmjXsKaMB6fN8OZUtGY7UVNYofrowZNx4nVvWN/ajPKBQCEW4Rr/MwcRuA8HGcQ==", + "hasInstallScript": true, + "license": "MIT" + }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@types/node-fetch": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz", - "integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==", + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", - "form-data": "^3.0.0" + "form-data": "^4.0.4" } }, "node_modules/@types/randombytes": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/randombytes/-/randombytes-2.0.0.tgz", - "integrity": "sha512-bz8PhAVlwN72vqefzxa14DKNT8jK/mV66CSjwdVQM/k3Th3EPKfUtdMniwZgMedQTFuywAsfjnZsg+pEnltaMA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/randombytes/-/randombytes-2.0.3.tgz", + "integrity": "sha512-+NRgihTfuURllWCiIAhm1wsJqzsocnqXM77V/CalsdJIYSRGEHMnritxh+6EsBklshC+clo1KgnN14qgSGeQdw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/sha.js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@types/sha.js/-/sha.js-2.4.0.tgz", - "integrity": "sha512-amxKgPy6WJTKuw8mpUwjX2BSxuBtBmZfRwIUDIuPJKNwGN8CWDli8JTg5ONTWOtcTkHIstvT7oAhhYXqEjStHQ==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/sha.js/-/sha.js-2.4.4.tgz", + "integrity": "sha512-Qukd+D6S2Hm0wLVt2Vh+/eWBIoUt+wF8jWjBsG4F8EFQRwKtYvtXCPcNl2OEUQ1R+eTr3xuSaBYUyM3WD1x/Qw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw==", - "dev": true + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-11.0.0.tgz", + "integrity": "sha512-HVyk8nj2m+jcFRNazzqyVKiZezyhDKrGUA3jlEcg/nZ6Ms+qHwocba1Y/AaVaznJTAM9xpdFSh+ptbNrhOGvZA==", + "deprecated": "This is a stub types definition. uuid provides its own type definitions, so you do not need this installed.", + "dev": true, + "license": "MIT", + "dependencies": { + "uuid": "*" + } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.2.0.tgz", + "integrity": "sha512-En6dTwfy5NFzSMibvOpx/lKq2jtgWuR4++KJbi3SpQ2iT8gm+PHo9868/scocW122KDwTxl4ruxZ7i4rHmJJnQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" @@ -224,19 +270,36 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k= sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -247,34 +310,207 @@ "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk= sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/form-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", - "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { "node": ">= 6" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -297,24 +533,30 @@ } }, "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", "dependencies": { - "buffer-equal-constant-time": "1.0.1", + "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", + "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", + "license": "MIT", "dependencies": { - "jwa": "^1.4.1", + "jwa": "^1.4.2", "safe-buffer": "^5.0.1" } }, + "node_modules/keytar": { + "resolved": "packageMocks/keytar", + "link": true + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -350,22 +592,34 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, + "license": "MIT", "dependencies": { - "mime-db": "1.44.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -376,6 +630,9 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, + "node_modules/packageMocks/keytar": { + "extraneous": true + }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -393,7 +650,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/semver": { "version": "7.6.2", @@ -412,10 +670,11 @@ "integrity": "sha512-V+uqV66BOQnWxvI6HjDnE4VkInmYZUQ4dgB7gzaDyFyFSK1i1nF/j7DpS9UbQAgV9NaF1XpcyuavnM1qOeiEIg==" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/uuid": { "version": "8.3.2", @@ -435,6 +694,9 @@ "engines": { "vscode": "^1.85.0" } + }, + "packageMocks/keytar": { + "version": "7.9.0" } } } diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json index 15acb5db286d..2e64ed511fb6 100644 --- a/extensions/microsoft-authentication/package.json +++ b/extensions/microsoft-authentication/package.json @@ -14,7 +14,8 @@ ], "activationEvents": [], "enabledApiProposals": [ - "idToken" + "idToken", + "nativeWindowHandle" ], "capabilities": { "virtualWorkspaces": true, @@ -99,9 +100,21 @@ { "title": "Microsoft", "properties": { - "microsoft.useMsal": { - "type": "boolean", - "description": "%useMsal.description%" + "microsoft-authentication.implementation": { + "type": "string", + "default": "msal", + "enum": [ + "msal", + "classic" + ], + "enumDescriptions": [ + "%microsoft-authentication.implementation.enumDescriptions.msal%", + "%microsoft-authentication.implementation.enumDescriptions.classic%" + ], + "markdownDescription": "%microsoft-authentication.implementation.description%", + "tags": [ + "onExP" + ] } } } @@ -118,16 +131,18 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "devDependencies": { - "@types/node": "20.x", - "@types/node-fetch": "^2.5.7", - "@types/randombytes": "^2.0.0", - "@types/sha.js": "^2.4.0", - "@types/uuid": "8.0.0" + "@types/node": "25.x", + "@types/node-fetch": "^2.6.13", + "@types/randombytes": "^2.0.3", + "@types/sha.js": "^2.4.4", + "@types/uuid": "11.0.0" }, "dependencies": { "@azure/ms-rest-azure-env": "^2.0.0", - "@azure/msal-node": "^2.13.1", - "@vscode/extension-telemetry": "^0.9.0", + "@azure/msal-node": "^5.0.2", + "@azure/msal-node-extensions": "^5.0.2", + "@vscode/extension-telemetry": "^1.2.0", + "keytar": "file:./packageMocks/keytar", "vscode-tas-client": "^0.1.84" }, "repository": { diff --git a/extensions/microsoft-authentication/package.nls.json b/extensions/microsoft-authentication/package.nls.json index 80cbb32d4ab1..c8e0189c08f9 100644 --- a/extensions/microsoft-authentication/package.nls.json +++ b/extensions/microsoft-authentication/package.nls.json @@ -3,7 +3,15 @@ "description": "Microsoft authentication provider", "signIn": "Sign In", "signOut": "Sign Out", - "useMsal.description": "Use the Microsoft Authentication Library (MSAL) to sign in with a Microsoft account.", + "microsoft-authentication.implementation.description": { + "message": "The authentication implementation to use for signing in with a Microsoft account.\n\n*NOTE: The `classic` implementation is deprecated and will be removed, along with this setting, in a future release. If only the `classic` implementation works for you, please [open an issue](command:workbench.action.openIssueReporter) and explain what you are trying to log in to.*", + "comment": [ + "{Locked='[(command:workbench.action.openIssueReporter)]'}", + "The `command:` syntax will turn into a link. Do not translate it." + ] + }, + "microsoft-authentication.implementation.enumDescriptions.msal": "Use the Microsoft Authentication Library (MSAL) to sign in with a Microsoft account.", + "microsoft-authentication.implementation.enumDescriptions.classic": "(deprecated) Use the classic authentication flow to sign in with a Microsoft account.", "microsoft-sovereign-cloud.environment.description": { "message": "The Sovereign Cloud to use for authentication. If you select `custom`, you must also set the `#microsoft-sovereign-cloud.customEnvironment#` setting.", "comment": [ diff --git a/src/vs/editor/contrib/inlineEdit/browser/inlineEditSideBySideWidget.css b/extensions/microsoft-authentication/packageMocks/dpapi/dpapi.js similarity index 59% rename from src/vs/editor/contrib/inlineEdit/browser/inlineEditSideBySideWidget.css rename to extensions/microsoft-authentication/packageMocks/dpapi/dpapi.js index bc7e553e4d85..636112a188ff 100644 --- a/src/vs/editor/contrib/inlineEdit/browser/inlineEditSideBySideWidget.css +++ b/extensions/microsoft-authentication/packageMocks/dpapi/dpapi.js @@ -3,10 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-editor .inlineEditSideBySide { - z-index: 39; - color: var(--vscode-editorHoverWidget-foreground); - background-color: var(--vscode-editorHoverWidget-background); - border: 1px solid var(--vscode-editorHoverWidget-border); - white-space: pre; +class defaultDpapi { + protectData() { + throw new Error('Dpapi bindings unavailable'); + } + unprotectData() { + throw new Error('Dpapi bindings unavailable'); + } } +const Dpapi = new defaultDpapi(); +export { Dpapi }; diff --git a/extensions/microsoft-authentication/packageMocks/keytar/index.js b/extensions/microsoft-authentication/packageMocks/keytar/index.js new file mode 100644 index 000000000000..418d592ffddb --- /dev/null +++ b/extensions/microsoft-authentication/packageMocks/keytar/index.js @@ -0,0 +1,8 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +exports.setPassword = () => Promise.resolve(); +exports.getPassword = () => Promise.resolve(); +exports.deletePassword = () => Promise.resolve(); diff --git a/extensions/microsoft-authentication/packageMocks/keytar/package.json b/extensions/microsoft-authentication/packageMocks/keytar/package.json new file mode 100644 index 000000000000..0014152ac935 --- /dev/null +++ b/extensions/microsoft-authentication/packageMocks/keytar/package.json @@ -0,0 +1,7 @@ +{ + "name": "keytar", + "version": "7.9.0", + "description": "OVERRIDE Keytar since we don't need the feature", + "homepage": "https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/node", + "main": "index.js" +} diff --git a/extensions/microsoft-authentication/src/common/accountAccess.ts b/extensions/microsoft-authentication/src/common/accountAccess.ts new file mode 100644 index 000000000000..27d56b0bc1b7 --- /dev/null +++ b/extensions/microsoft-authentication/src/common/accountAccess.ts @@ -0,0 +1,172 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, Event, EventEmitter, LogOutputChannel, SecretStorage } from 'vscode'; +import { AccountInfo } from '@azure/msal-node'; + +export interface IAccountAccess { + onDidAccountAccessChange: Event; + isAllowedAccess(account: AccountInfo): boolean; + setAllowedAccess(account: AccountInfo, allowed: boolean): Promise; +} + +export class ScopedAccountAccess implements IAccountAccess, Disposable { + private readonly _onDidAccountAccessChangeEmitter = new EventEmitter(); + readonly onDidAccountAccessChange = this._onDidAccountAccessChangeEmitter.event; + + private value = new Array(); + + private readonly _disposable: Disposable; + + private constructor( + private readonly _accountAccessSecretStorage: IAccountAccessSecretStorage, + disposables: Disposable[] = [] + ) { + this._disposable = Disposable.from( + ...disposables, + this._onDidAccountAccessChangeEmitter, + this._accountAccessSecretStorage.onDidChange(() => this.update()) + ); + } + + static async create( + secretStorage: SecretStorage, + cloudName: string, + logger: LogOutputChannel, + migrations: { clientId: string; authority: string }[] | undefined, + ): Promise { + const storage = await AccountAccessSecretStorage.create(secretStorage, cloudName, logger, migrations); + const access = new ScopedAccountAccess(storage, [storage]); + await access.initialize(); + return access; + } + + dispose() { + this._disposable.dispose(); + } + + private async initialize(): Promise { + await this.update(); + } + + isAllowedAccess(account: AccountInfo): boolean { + return this.value.includes(account.homeAccountId); + } + + async setAllowedAccess(account: AccountInfo, allowed: boolean): Promise { + if (allowed) { + if (this.value.includes(account.homeAccountId)) { + return; + } + await this._accountAccessSecretStorage.store([...this.value, account.homeAccountId]); + return; + } + await this._accountAccessSecretStorage.store(this.value.filter(id => id !== account.homeAccountId)); + } + + private async update() { + const current = new Set(this.value); + const value = await this._accountAccessSecretStorage.get(); + + this.value = value ?? []; + if (current.size !== this.value.length || !this.value.every(id => current.has(id))) { + this._onDidAccountAccessChangeEmitter.fire(); + } + } +} + +interface IAccountAccessSecretStorage { + get(): Promise; + store(value: string[]): Thenable; + delete(): Thenable; + onDidChange: Event; +} + +class AccountAccessSecretStorage implements IAccountAccessSecretStorage, Disposable { + private _disposable: Disposable; + + private readonly _onDidChangeEmitter = new EventEmitter(); + readonly onDidChange: Event = this._onDidChangeEmitter.event; + + private readonly _key = `accounts-${this._cloudName}`; + + private constructor( + private readonly _secretStorage: SecretStorage, + private readonly _cloudName: string, + private readonly _logger: LogOutputChannel, + private readonly _migrations?: { clientId: string; authority: string }[], + ) { + this._disposable = Disposable.from( + this._onDidChangeEmitter, + this._secretStorage.onDidChange(e => { + if (e.key === this._key) { + this._onDidChangeEmitter.fire(); + } + }) + ); + } + + static async create( + secretStorage: SecretStorage, + cloudName: string, + logger: LogOutputChannel, + migrations?: { clientId: string; authority: string }[], + ): Promise { + const storage = new AccountAccessSecretStorage(secretStorage, cloudName, logger, migrations); + await storage.initialize(); + return storage; + } + + /** + * TODO: Remove this method after a release with the migration + */ + private async initialize(): Promise { + if (!this._migrations) { + return; + } + const current = await this.get(); + // If the secret storage already has the new key, we have already run the migration + if (current) { + return; + } + try { + const allValues = new Set(); + for (const { clientId, authority } of this._migrations) { + const oldKey = `accounts-${this._cloudName}-${clientId}-${authority}`; + const value = await this._secretStorage.get(oldKey); + if (value) { + const parsed = JSON.parse(value) as string[]; + parsed.forEach(v => allValues.add(v)); + } + } + if (allValues.size > 0) { + await this.store(Array.from(allValues)); + } + } catch (e) { + // Migration is best effort + this._logger.error(`Failed to migrate account access secret storage: ${e}`); + } + } + + async get(): Promise { + const value = await this._secretStorage.get(this._key); + if (!value) { + return undefined; + } + return JSON.parse(value); + } + + store(value: string[]): Thenable { + return this._secretStorage.store(this._key, JSON.stringify(value)); + } + + delete(): Thenable { + return this._secretStorage.delete(this._key); + } + + dispose() { + this._disposable.dispose(); + } +} diff --git a/extensions/microsoft-authentication/src/common/async.ts b/extensions/microsoft-authentication/src/common/async.ts index 094861518fc6..5f02cc5976d6 100644 --- a/extensions/microsoft-authentication/src/common/async.ts +++ b/extensions/microsoft-authentication/src/common/async.ts @@ -3,12 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationError, CancellationToken, Disposable, Event, EventEmitter } from 'vscode'; - -/** - * Can be passed into the Delayed to defer using a microtask - */ -export const MicrotaskDelay = Symbol('MicrotaskDelay'); +import { CancellationError, CancellationToken, Disposable, Event } from 'vscode'; export class SequencerByKey { @@ -57,7 +52,7 @@ export class IntervalTimer extends Disposable { * Returns a promise that rejects with an {@CancellationError} as soon as the passed token is cancelled. * @see {@link raceCancellation} */ -export function raceCancellationError(promise: Promise, token: CancellationToken): Promise { +function raceCancellationError(promise: Promise, token: CancellationToken): Promise { return new Promise((resolve, reject) => { const ref = token.onCancellationRequested(() => { ref.dispose(); @@ -67,13 +62,7 @@ export function raceCancellationError(promise: Promise, token: Cancellatio }); } -export class TimeoutError extends Error { - constructor() { - super('Timed out'); - } -} - -export function raceTimeoutError(promise: Promise, timeout: number): Promise { +function raceTimeoutError(promise: Promise, timeout: number): Promise { return new Promise((resolve, reject) => { const ref = setTimeout(() => { reject(new CancellationError()); @@ -86,383 +75,12 @@ export function raceCancellationAndTimeoutError(promise: Promise, token: C return raceCancellationError(raceTimeoutError(promise, timeout), token); } -interface ILimitedTaskFactory { - factory: () => Promise; - c: (value: T | Promise) => void; - e: (error?: unknown) => void; -} - -export interface ILimiter { - - readonly size: number; - - queue(factory: () => Promise): Promise; - - clear(): void; -} - -/** - * A helper to queue N promises and run them all with a max degree of parallelism. The helper - * ensures that at any time no more than M promises are running at the same time. - */ -export class Limiter implements ILimiter { - - private _size = 0; - private _isDisposed = false; - private runningPromises: number; - private readonly maxDegreeOfParalellism: number; - private readonly outstandingPromises: ILimitedTaskFactory[]; - private readonly _onDrained: EventEmitter; - - constructor(maxDegreeOfParalellism: number) { - this.maxDegreeOfParalellism = maxDegreeOfParalellism; - this.outstandingPromises = []; - this.runningPromises = 0; - this._onDrained = new EventEmitter(); - } - - /** - * - * @returns A promise that resolved when all work is done (onDrained) or when - * there is nothing to do - */ - whenIdle(): Promise { - return this.size > 0 - ? toPromise(this.onDrained) - : Promise.resolve(); - } - - get onDrained(): Event { - return this._onDrained.event; - } - - get size(): number { - return this._size; - } - - queue(factory: () => Promise): Promise { - if (this._isDisposed) { - throw new Error('Object has been disposed'); - } - this._size++; - - return new Promise((c, e) => { - this.outstandingPromises.push({ factory, c, e }); - this.consume(); - }); - } - - private consume(): void { - while (this.outstandingPromises.length && this.runningPromises < this.maxDegreeOfParalellism) { - const iLimitedTask = this.outstandingPromises.shift()!; - this.runningPromises++; - - const promise = iLimitedTask.factory(); - promise.then(iLimitedTask.c, iLimitedTask.e); - promise.then(() => this.consumed(), () => this.consumed()); - } - } - - private consumed(): void { - if (this._isDisposed) { - return; - } - this.runningPromises--; - if (--this._size === 0) { - this._onDrained.fire(); - } - - if (this.outstandingPromises.length > 0) { - this.consume(); - } - } - - clear(): void { - if (this._isDisposed) { - throw new Error('Object has been disposed'); - } - this.outstandingPromises.length = 0; - this._size = this.runningPromises; - } - - dispose(): void { - this._isDisposed = true; - this.outstandingPromises.length = 0; // stop further processing - this._size = 0; - this._onDrained.dispose(); - } -} - - -interface IScheduledLater extends Disposable { - isTriggered(): boolean; -} - -const timeoutDeferred = (timeout: number, fn: () => void): IScheduledLater => { - let scheduled = true; - const handle = setTimeout(() => { - scheduled = false; - fn(); - }, timeout); - return { - isTriggered: () => scheduled, - dispose: () => { - clearTimeout(handle); - scheduled = false; - }, - }; -}; - -const microtaskDeferred = (fn: () => void): IScheduledLater => { - let scheduled = true; - queueMicrotask(() => { - if (scheduled) { - scheduled = false; - fn(); - } - }); - - return { - isTriggered: () => scheduled, - dispose: () => { scheduled = false; }, - }; -}; - -/** - * A helper to delay (debounce) execution of a task that is being requested often. - * - * Following the throttler, now imagine the mail man wants to optimize the number of - * trips proactively. The trip itself can be long, so he decides not to make the trip - * as soon as a letter is submitted. Instead he waits a while, in case more - * letters are submitted. After said waiting period, if no letters were submitted, he - * decides to make the trip. Imagine that N more letters were submitted after the first - * one, all within a short period of time between each other. Even though N+1 - * submissions occurred, only 1 delivery was made. - * - * The delayer offers this behavior via the trigger() method, into which both the task - * to be executed and the waiting period (delay) must be passed in as arguments. Following - * the example: - * - * const delayer = new Delayer(WAITING_PERIOD); - * const letters = []; - * - * function letterReceived(l) { - * letters.push(l); - * delayer.trigger(() => { return makeTheTrip(); }); - * } - */ -export class Delayer implements Disposable { - - private deferred: IScheduledLater | null; - private completionPromise: Promise | null; - private doResolve: ((value?: any | Promise) => void) | null; - private doReject: ((err: any) => void) | null; - private task: (() => T | Promise) | null; - - constructor(public defaultDelay: number | typeof MicrotaskDelay) { - this.deferred = null; - this.completionPromise = null; - this.doResolve = null; - this.doReject = null; - this.task = null; - } - - trigger(task: () => T | Promise, delay = this.defaultDelay): Promise { - this.task = task; - this.cancelTimeout(); - - if (!this.completionPromise) { - this.completionPromise = new Promise((resolve, reject) => { - this.doResolve = resolve; - this.doReject = reject; - }).then(() => { - this.completionPromise = null; - this.doResolve = null; - if (this.task) { - const task = this.task; - this.task = null; - return task(); - } - return undefined; - }); - } - - const fn = () => { - this.deferred = null; - this.doResolve?.(null); - }; - - this.deferred = delay === MicrotaskDelay ? microtaskDeferred(fn) : timeoutDeferred(delay, fn); - - return this.completionPromise; - } - - isTriggered(): boolean { - return !!this.deferred?.isTriggered(); - } - - cancel(): void { - this.cancelTimeout(); - - if (this.completionPromise) { - this.doReject?.(new CancellationError()); - this.completionPromise = null; - } - } - - private cancelTimeout(): void { - this.deferred?.dispose(); - this.deferred = null; - } - - dispose(): void { - this.cancel(); - } -} - -/** - * A helper to prevent accumulation of sequential async tasks. - * - * Imagine a mail man with the sole task of delivering letters. As soon as - * a letter submitted for delivery, he drives to the destination, delivers it - * and returns to his base. Imagine that during the trip, N more letters were submitted. - * When the mail man returns, he picks those N letters and delivers them all in a - * single trip. Even though N+1 submissions occurred, only 2 deliveries were made. - * - * The throttler implements this via the queue() method, by providing it a task - * factory. Following the example: - * - * const throttler = new Throttler(); - * const letters = []; - * - * function deliver() { - * const lettersToDeliver = letters; - * letters = []; - * return makeTheTrip(lettersToDeliver); - * } - * - * function onLetterReceived(l) { - * letters.push(l); - * throttler.queue(deliver); - * } - */ -export class Throttler implements Disposable { - - private activePromise: Promise | null; - private queuedPromise: Promise | null; - private queuedPromiseFactory: (() => Promise) | null; - - private isDisposed = false; - - constructor() { - this.activePromise = null; - this.queuedPromise = null; - this.queuedPromiseFactory = null; - } - - queue(promiseFactory: () => Promise): Promise { - if (this.isDisposed) { - return Promise.reject(new Error('Throttler is disposed')); - } - - if (this.activePromise) { - this.queuedPromiseFactory = promiseFactory; - - if (!this.queuedPromise) { - const onComplete = () => { - this.queuedPromise = null; - - if (this.isDisposed) { - return; - } - - const result = this.queue(this.queuedPromiseFactory!); - this.queuedPromiseFactory = null; - - return result; - }; - - this.queuedPromise = new Promise(resolve => { - this.activePromise!.then(onComplete, onComplete).then(resolve); - }); - } - - return new Promise((resolve, reject) => { - this.queuedPromise!.then(resolve, reject); - }); - } - - this.activePromise = promiseFactory(); - - return new Promise((resolve, reject) => { - this.activePromise!.then((result: T) => { - this.activePromise = null; - resolve(result); - }, (err: unknown) => { - this.activePromise = null; - reject(err); - }); - }); - } - - dispose(): void { - this.isDisposed = true; - } -} - -/** - * A helper to delay execution of a task that is being requested often, while - * preventing accumulation of consecutive executions, while the task runs. - * - * The mail man is clever and waits for a certain amount of time, before going - * out to deliver letters. While the mail man is going out, more letters arrive - * and can only be delivered once he is back. Once he is back the mail man will - * do one more trip to deliver the letters that have accumulated while he was out. - */ -export class ThrottledDelayer { - - private delayer: Delayer>; - private throttler: Throttler; - - constructor(defaultDelay: number) { - this.delayer = new Delayer(defaultDelay); - this.throttler = new Throttler(); - } - - trigger(promiseFactory: () => Promise, delay?: number): Promise { - return this.delayer.trigger(() => this.throttler.queue(promiseFactory), delay) as unknown as Promise; - } - - isTriggered(): boolean { - return this.delayer.isTriggered(); - } - - cancel(): void { - this.delayer.cancel(); - } - - dispose(): void { - this.delayer.dispose(); - this.throttler.dispose(); - } -} - -/** - * A queue is handles one promise at a time and guarantees that at any time only one promise is executing. - */ -export class Queue extends Limiter { - - constructor() { - super(1); - } -} - /** * Given an event, returns another event which only fires once. * * @param event The event source for the new event. */ -export function once(event: Event): Event { +function once(event: Event): Event { return (listener, thisArgs = null, disposables?) => { // we need this, in case the event fires during the listener call let didFire = false; @@ -494,6 +112,8 @@ export function toPromise(event: Event): Promise { return new Promise(resolve => once(event)(resolve)); } +//#region DeferredPromise + export type ValueCallback = (value: T | Promise) => void; const enum DeferredOutcome { @@ -555,3 +175,5 @@ export class DeferredPromise { return this.error(new CancellationError()); } } + +//#endregion diff --git a/extensions/microsoft-authentication/src/common/cachePlugin.ts b/extensions/microsoft-authentication/src/common/cachePlugin.ts index 91b4f0ee6a8e..b87fdb78d9b5 100644 --- a/extensions/microsoft-authentication/src/common/cachePlugin.ts +++ b/extensions/microsoft-authentication/src/common/cachePlugin.ts @@ -6,7 +6,7 @@ import { ICachePlugin, TokenCacheContext } from '@azure/msal-node'; import { Disposable, EventEmitter, SecretStorage } from 'vscode'; -export class SecretStorageCachePlugin implements ICachePlugin { +export class SecretStorageCachePlugin implements ICachePlugin, Disposable { private readonly _onDidChange: EventEmitter = new EventEmitter(); readonly onDidChange = this._onDidChange.event; diff --git a/extensions/microsoft-authentication/src/common/env.ts b/extensions/microsoft-authentication/src/common/env.ts new file mode 100644 index 000000000000..5d19183e70cb --- /dev/null +++ b/extensions/microsoft-authentication/src/common/env.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { Uri } from 'vscode'; + +const VALID_DESKTOP_CALLBACK_SCHEMES = [ + 'vscode', + 'vscode-insiders', + // On Windows, some browsers don't seem to redirect back to OSS properly. + // As a result, you get stuck in the auth flow. We exclude this from the + // list until we can figure out a way to fix this behavior in browsers. + // 'code-oss', + 'vscode-wsl', + 'vscode-exploration' +]; + +export function isSupportedClient(uri: Uri): boolean { + return ( + VALID_DESKTOP_CALLBACK_SCHEMES.includes(uri.scheme) || + // vscode.dev & insiders.vscode.dev + /(?:^|\.)vscode\.dev$/.test(uri.authority) || + // github.dev & codespaces + /(?:^|\.)github\.dev$/.test(uri.authority) || + // localhost + /^localhost:\d+$/.test(uri.authority) || + // 127.0.0.1 + /^127\.0\.0\.1:\d+$/.test(uri.authority) + ); +} diff --git a/extensions/microsoft-authentication/src/common/loggerOptions.ts b/extensions/microsoft-authentication/src/common/loggerOptions.ts index 86443c0281f0..d572f655f926 100644 --- a/extensions/microsoft-authentication/src/common/loggerOptions.ts +++ b/extensions/microsoft-authentication/src/common/loggerOptions.ts @@ -17,9 +17,13 @@ export class MsalLoggerOptions { loggerCallback(level: MsalLogLevel, message: string, containsPii: boolean): void { if (containsPii) { + // TODO: Should we still log the message if it contains PII? It's just going to + // an output channel that doesn't leave the machine. + this._output.debug('Skipped logging message because it may contain PII'); return; } + // Log to output channel one level lower than the MSAL log level switch (level) { case MsalLogLevel.Error: this._output.error(message); @@ -28,16 +32,16 @@ export class MsalLoggerOptions { this._output.warn(message); return; case MsalLogLevel.Info: - this._output.info(message); + this._output.debug(message); return; case MsalLogLevel.Verbose: - this._output.debug(message); + this._output.trace(message); return; case MsalLogLevel.Trace: - this._output.trace(message); + // Do not log trace messages return; default: - this._output.info(message); + this._output.debug(message); return; } } diff --git a/extensions/microsoft-authentication/src/common/loopbackClientAndOpener.ts b/extensions/microsoft-authentication/src/common/loopbackClientAndOpener.ts index 3fbb03400379..e68663efe43a 100644 --- a/extensions/microsoft-authentication/src/common/loopbackClientAndOpener.ts +++ b/extensions/microsoft-authentication/src/common/loopbackClientAndOpener.ts @@ -5,14 +5,17 @@ import type { ILoopbackClient, ServerAuthorizationCodeResponse } from '@azure/msal-node'; import type { UriEventHandler } from '../UriEventHandler'; -import { env, LogOutputChannel, Uri } from 'vscode'; -import { toPromise } from './async'; +import { Disposable, env, l10n, LogOutputChannel, Uri, window } from 'vscode'; +import { DeferredPromise, toPromise } from './async'; +import { isSupportedClient } from './env'; export interface ILoopbackClientAndOpener extends ILoopbackClient { openBrowser(url: string): Promise; } export class UriHandlerLoopbackClient implements ILoopbackClientAndOpener { + private _responseDeferred: DeferredPromise | undefined; + constructor( private readonly _uriHandler: UriEventHandler, private readonly _redirectUri: string, @@ -20,17 +23,14 @@ export class UriHandlerLoopbackClient implements ILoopbackClientAndOpener { ) { } async listenForAuthCode(): Promise { - const url = await toPromise(this._uriHandler.event); - this._logger.debug(`Received URL event. Authority: ${url.authority}`); - const result = new URL(url.toString(true)); - - return { - code: result.searchParams.get('code') ?? undefined, - state: result.searchParams.get('state') ?? undefined, - error: result.searchParams.get('error') ?? undefined, - error_description: result.searchParams.get('error_description') ?? undefined, - error_uri: result.searchParams.get('error_uri') ?? undefined, - }; + await this._responseDeferred?.cancel(); + this._responseDeferred = new DeferredPromise(); + const result = await this._responseDeferred.p; + this._responseDeferred = undefined; + if (result) { + return result; + } + throw new Error('No valid response received for authorization code.'); } getRedirectUri(): string { @@ -46,7 +46,93 @@ export class UriHandlerLoopbackClient implements ILoopbackClientAndOpener { async openBrowser(url: string): Promise { const callbackUri = await env.asExternalUri(Uri.parse(`${env.uriScheme}://vscode.microsoft-authentication`)); + if (isSupportedClient(callbackUri)) { + void this._getCodeResponseFromUriHandler(); + } else { + // Unsupported clients will be shown the code in the browser, but it will not redirect back since this + // isn't a supported client. Instead, they will copy that code in the browser and paste it in an input box + // that will be shown to them by the extension. + void this._getCodeResponseFromQuickPick(); + } + const uri = Uri.parse(url + `&state=${encodeURI(callbackUri.toString(true))}`); await env.openExternal(uri); } + + private async _getCodeResponseFromUriHandler(): Promise { + if (!this._responseDeferred) { + throw new Error('No listener for auth code'); + } + const url = await toPromise(this._uriHandler.event); + this._logger.debug(`Received URL event. Authority: ${url.authority}`); + const result = new URL(url.toString(true)); + + this._responseDeferred?.complete({ + code: result.searchParams.get('code') ?? undefined, + state: result.searchParams.get('state') ?? undefined, + error: result.searchParams.get('error') ?? undefined, + error_description: result.searchParams.get('error_description') ?? undefined, + error_uri: result.searchParams.get('error_uri') ?? undefined, + }); + } + + private async _getCodeResponseFromQuickPick(): Promise { + if (!this._responseDeferred) { + throw new Error('No listener for auth code'); + } + const inputBox = window.createInputBox(); + inputBox.ignoreFocusOut = true; + inputBox.title = l10n.t('Microsoft Authentication'); + inputBox.prompt = l10n.t('Provide the authorization code to complete the sign in flow.'); + inputBox.placeholder = l10n.t('Paste authorization code here...'); + inputBox.show(); + const code = await new Promise((resolve) => { + let resolvedValue: string | undefined = undefined; + const disposable = Disposable.from( + inputBox, + inputBox.onDidAccept(async () => { + if (!inputBox.value) { + inputBox.validationMessage = l10n.t('Authorization code is required.'); + return; + } + const code = inputBox.value; + resolvedValue = code; + resolve(code); + inputBox.hide(); + }), + inputBox.onDidChangeValue(() => { + inputBox.validationMessage = undefined; + }), + inputBox.onDidHide(() => { + disposable.dispose(); + if (!resolvedValue) { + resolve(undefined); + } + }) + ); + Promise.allSettled([this._responseDeferred?.p]).then(() => disposable.dispose()); + }); + // Something canceled the original deferred promise, so just return. + if (this._responseDeferred.isSettled) { + return; + } + if (code) { + this._logger.debug('Received auth code from quick pick'); + this._responseDeferred.complete({ + code, + state: undefined, + error: undefined, + error_description: undefined, + error_uri: undefined + }); + return; + } + this._responseDeferred.complete({ + code: undefined, + state: undefined, + error: 'User cancelled', + error_description: 'User cancelled', + error_uri: undefined + }); + } } diff --git a/extensions/microsoft-authentication/src/common/publicClientCache.ts b/extensions/microsoft-authentication/src/common/publicClientCache.ts index 925a4d1a88c3..acc8ba0d3077 100644 --- a/extensions/microsoft-authentication/src/common/publicClientCache.ts +++ b/extensions/microsoft-authentication/src/common/publicClientCache.ts @@ -2,23 +2,22 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { AccountInfo, AuthenticationResult, InteractiveRequest, SilentFlowRequest } from '@azure/msal-node'; +import type { AccountInfo, AuthenticationResult, InteractiveRequest, RefreshTokenRequest, SilentFlowRequest } from '@azure/msal-node'; import type { Disposable, Event } from 'vscode'; -export interface ICachedPublicClientApplication extends Disposable { - initialize(): Promise; +export interface ICachedPublicClientApplication { onDidAccountsChange: Event<{ added: AccountInfo[]; changed: AccountInfo[]; deleted: AccountInfo[] }>; onDidRemoveLastAccount: Event; acquireTokenSilent(request: SilentFlowRequest): Promise; acquireTokenInteractive(request: InteractiveRequest): Promise; + acquireTokenByRefreshToken(request: RefreshTokenRequest): Promise; removeAccount(account: AccountInfo): Promise; accounts: AccountInfo[]; clientId: string; - authority: string; } export interface ICachedPublicClientApplicationManager { onDidAccountsChange: Event<{ added: AccountInfo[]; changed: AccountInfo[]; deleted: AccountInfo[] }>; - getOrCreate(clientId: string, authority: string): Promise; + getOrCreate(clientId: string, refreshTokensToMigrate?: string[]): Promise; getAll(): ICachedPublicClientApplication[]; } diff --git a/extensions/microsoft-authentication/src/common/scopeData.ts b/extensions/microsoft-authentication/src/common/scopeData.ts index 4432abfed435..88a0aad68cc1 100644 --- a/extensions/microsoft-authentication/src/common/scopeData.ts +++ b/extensions/microsoft-authentication/src/common/scopeData.ts @@ -10,7 +10,6 @@ const OIDC_SCOPES = ['openid', 'email', 'profile', 'offline_access']; const GRAPH_TACK_ON_SCOPE = 'User.Read'; export class ScopeData { - /** * The full list of scopes including: * * the original scopes passed to the constructor @@ -35,10 +34,15 @@ export class ScopeData { readonly clientId: string; /** - * The tenant ID to use for the token request. This is the value of the `VSCODE_TENANT:...` scope if present, otherwise the default tenant ID. + * The tenant ID or `organizations`, `common`, `consumers` to use for the token request. This is the value of the `VSCODE_TENANT:...` scope if present, otherwise it's the default. */ readonly tenant: string; + /** + * The tenant ID to use for the token request. This will only ever be a GUID if one was specified via the `VSCODE_TENANT:...` scope, otherwise undefined. + */ + readonly tenantId: string | undefined; + constructor(readonly originalScopes: readonly string[] = []) { const modifiedScopes = [...originalScopes]; modifiedScopes.sort(); @@ -46,10 +50,11 @@ export class ScopeData { this.scopeStr = modifiedScopes.join(' '); this.scopesToSend = this.getScopesToSend(modifiedScopes); this.clientId = this.getClientId(this.allScopes); - this.tenant = this.getTenantId(this.allScopes); + this.tenant = this.getTenant(this.allScopes); + this.tenantId = this.getTenantId(this.tenant); } - private getClientId(scopes: string[]) { + private getClientId(scopes: string[]): string { return scopes.reduce((prev, current) => { if (current.startsWith('VSCODE_CLIENT_ID:')) { return current.split('VSCODE_CLIENT_ID:')[1]; @@ -58,7 +63,7 @@ export class ScopeData { }, undefined) ?? DEFAULT_CLIENT_ID; } - private getTenantId(scopes: string[]) { + private getTenant(scopes: string[]): string { return scopes.reduce((prev, current) => { if (current.startsWith('VSCODE_TENANT:')) { return current.split('VSCODE_TENANT:')[1]; @@ -67,7 +72,19 @@ export class ScopeData { }, undefined) ?? DEFAULT_TENANT; } - private getScopesToSend(scopes: string[]) { + private getTenantId(tenant: string): string | undefined { + switch (tenant) { + case 'organizations': + case 'common': + case 'consumers': + // These are not valid tenant IDs, so we return undefined + return undefined; + default: + return this.tenant; + } + } + + private getScopesToSend(scopes: string[]): string[] { const scopesToSend = scopes.filter(s => !s.startsWith('VSCODE_')); const set = new Set(scopesToSend); diff --git a/extensions/microsoft-authentication/src/common/test/scopeData.test.ts b/extensions/microsoft-authentication/src/common/test/scopeData.test.ts index 9250d7cecbda..4c70e4fd07c8 100644 --- a/extensions/microsoft-authentication/src/common/test/scopeData.test.ts +++ b/extensions/microsoft-authentication/src/common/test/scopeData.test.ts @@ -56,4 +56,21 @@ suite('ScopeData', () => { const scopeData = new ScopeData(['custom_scope', 'VSCODE_TENANT:some_tenant']); assert.strictEqual(scopeData.tenant, 'some_tenant'); }); + + test('should have tenantId be undefined if no VSCODE_TENANT scope is present', () => { + const scopeData = new ScopeData(['custom_scope']); + assert.strictEqual(scopeData.tenantId, undefined); + }); + + test('should have tenantId be undefined if typical tenant values are present', () => { + for (const element of ['common', 'organizations', 'consumers']) { + const scopeData = new ScopeData(['custom_scope', `VSCODE_TENANT:${element}`]); + assert.strictEqual(scopeData.tenantId, undefined); + } + }); + + test('should have tenantId be the value of VSCODE_TENANT scope if set to a specific value', () => { + const scopeData = new ScopeData(['custom_scope', 'VSCODE_TENANT:some_guid']); + assert.strictEqual(scopeData.tenantId, 'some_guid'); + }); }); diff --git a/extensions/microsoft-authentication/src/extension.ts b/extensions/microsoft-authentication/src/extension.ts index 3f9b5d3a4d1c..bd32a82290a6 100644 --- a/extensions/microsoft-authentication/src/extension.ts +++ b/extensions/microsoft-authentication/src/extension.ts @@ -13,33 +13,33 @@ import Logger from './logger'; function shouldUseMsal(expService: IExperimentationService): boolean { // First check if there is a setting value to allow user to override the default - const inspect = workspace.getConfiguration('microsoft').inspect('useMsal'); + const inspect = workspace.getConfiguration('microsoft-authentication').inspect<'msal' | 'classic'>('implementation'); if (inspect?.workspaceFolderValue !== undefined) { - Logger.debug(`Acquired MSAL enablement value from 'workspaceFolderValue'. Value: ${inspect.workspaceFolderValue}`); - return inspect.workspaceFolderValue; + Logger.info(`Acquired MSAL enablement value from 'workspaceFolderValue'. Value: ${inspect.workspaceFolderValue}`); + return inspect.workspaceFolderValue === 'msal'; } if (inspect?.workspaceValue !== undefined) { - Logger.debug(`Acquired MSAL enablement value from 'workspaceValue'. Value: ${inspect.workspaceValue}`); - return inspect.workspaceValue; + Logger.info(`Acquired MSAL enablement value from 'workspaceValue'. Value: ${inspect.workspaceValue}`); + return inspect.workspaceValue === 'msal'; } if (inspect?.globalValue !== undefined) { - Logger.debug(`Acquired MSAL enablement value from 'globalValue'. Value: ${inspect.globalValue}`); - return inspect.globalValue; + Logger.info(`Acquired MSAL enablement value from 'globalValue'. Value: ${inspect.globalValue}`); + return inspect.globalValue === 'msal'; } // Then check if the experiment value const expValue = expService.getTreatmentVariable('vscode', 'microsoft.useMsal'); if (expValue !== undefined) { - Logger.debug(`Acquired MSAL enablement value from 'exp'. Value: ${expValue}`); + Logger.info(`Acquired MSAL enablement value from 'exp'. Value: ${expValue}`); return expValue; } - Logger.debug('Acquired MSAL enablement value from default. Value: false'); - // If no setting or experiment value is found, default to false - return false; + Logger.info('Acquired MSAL enablement value from default. Value: false'); + // If no setting or experiment value is found, default to true + return true; } -let useMsal: boolean | undefined; +let useMsal: boolean | undefined; export async function activate(context: ExtensionContext) { const mainTelemetryReporter = new MicrosoftAuthenticationTelemetryReporter(context.extension.packageJSON.aiKey); const expService = await createExperimentationService( @@ -48,9 +48,14 @@ export async function activate(context: ExtensionContext) { env.uriScheme !== 'vscode', // isPreRelease ); useMsal = shouldUseMsal(expService); + const clientIdVersion = workspace.getConfiguration('microsoft-authentication').get<'v1' | 'v2'>('clientIdVersion', 'v1'); context.subscriptions.push(workspace.onDidChangeConfiguration(async e => { - if (!e.affectsConfiguration('microsoft.useMsal') || useMsal === shouldUseMsal(expService)) { + if (!e.affectsConfiguration('microsoft-authentication')) { + return; + } + + if (useMsal === shouldUseMsal(expService) && clientIdVersion === workspace.getConfiguration('microsoft-authentication').get<'v1' | 'v2'>('clientIdVersion', 'v1')) { return; } diff --git a/extensions/microsoft-authentication/src/extensionV1.ts b/extensions/microsoft-authentication/src/extensionV1.ts index f785adad85cc..370ea0c76b11 100644 --- a/extensions/microsoft-authentication/src/extensionV1.ts +++ b/extensions/microsoft-authentication/src/extensionV1.ts @@ -105,6 +105,10 @@ async function initMicrosoftSovereignCloudAuthProvider(context: vscode.Extension } export async function activate(context: vscode.ExtensionContext, telemetryReporter: TelemetryReporter) { + // If we ever activate the old flow, then mark that we will need to migrate when the user upgrades to v2. + // TODO: MSAL Migration. Remove this when we remove the old flow. + context.globalState.update('msalMigration', false); + const uriHandler = new UriEventHandler(); context.subscriptions.push(uriHandler); const betterSecretStorage = new BetterTokenStorage('microsoft.login.keylist', context); diff --git a/extensions/microsoft-authentication/src/extensionV2.ts b/extensions/microsoft-authentication/src/extensionV2.ts index 9610af37977c..978603ad1322 100644 --- a/extensions/microsoft-authentication/src/extensionV2.ts +++ b/extensions/microsoft-authentication/src/extensionV2.ts @@ -49,14 +49,13 @@ async function initMicrosoftSovereignCloudAuthProvider( return undefined; } - const authProvider = new MsalAuthProvider( + const authProvider = await MsalAuthProvider.create( context, new MicrosoftSovereignCloudAuthenticationTelemetryReporter(context.extension.packageJSON.aiKey), window.createOutputChannel(l10n.t('Microsoft Sovereign Cloud Authentication'), { log: true }), uriHandler, env ); - await authProvider.initialize(); const disposable = authentication.registerAuthenticationProvider( 'microsoft-sovereign-cloud', authProviderName, @@ -70,13 +69,12 @@ async function initMicrosoftSovereignCloudAuthProvider( export async function activate(context: ExtensionContext, mainTelemetryReporter: MicrosoftAuthenticationTelemetryReporter) { const uriHandler = new UriEventHandler(); context.subscriptions.push(uriHandler); - const authProvider = new MsalAuthProvider( + const authProvider = await MsalAuthProvider.create( context, mainTelemetryReporter, Logger, uriHandler ); - await authProvider.initialize(); context.subscriptions.push(authentication.registerAuthenticationProvider( 'microsoft', 'Microsoft', diff --git a/extensions/microsoft-authentication/src/node/authProvider.ts b/extensions/microsoft-authentication/src/node/authProvider.ts index 0c285e0cb2ba..a26008fb7802 100644 --- a/extensions/microsoft-authentication/src/node/authProvider.ts +++ b/extensions/microsoft-authentication/src/node/authProvider.ts @@ -2,17 +2,18 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { AccountInfo, AuthenticationResult, ServerError } from '@azure/msal-node'; -import { AuthenticationGetSessionOptions, AuthenticationProvider, AuthenticationProviderAuthenticationSessionsChangeEvent, AuthenticationProviderSessionOptions, AuthenticationSession, AuthenticationSessionAccountInformation, CancellationError, env, EventEmitter, ExtensionContext, l10n, LogOutputChannel, Memento, SecretStorage, Uri, window } from 'vscode'; +import { AccountInfo, AuthenticationResult, ClientAuthError, ClientAuthErrorCodes, ServerError } from '@azure/msal-node'; +import { AuthenticationGetSessionOptions, AuthenticationProvider, AuthenticationProviderAuthenticationSessionsChangeEvent, AuthenticationProviderSessionOptions, AuthenticationSession, AuthenticationSessionAccountInformation, CancellationError, EventEmitter, ExtensionContext, ExtensionKind, l10n, LogOutputChannel, window } from 'vscode'; import { Environment } from '@azure/ms-rest-azure-env'; import { CachedPublicClientApplicationManager } from './publicClientCache'; -import { UriHandlerLoopbackClient } from '../common/loopbackClientAndOpener'; import { UriEventHandler } from '../UriEventHandler'; -import { ICachedPublicClientApplication } from '../common/publicClientCache'; +import { ICachedPublicClientApplication, ICachedPublicClientApplicationManager } from '../common/publicClientCache'; import { MicrosoftAccountType, MicrosoftAuthenticationTelemetryReporter } from '../common/telemetryReporter'; -import { loopbackTemplate } from './loopbackTemplate'; import { ScopeData } from '../common/scopeData'; import { EventBufferer } from '../common/event'; +import { BetterTokenStorage } from '../betterSecretStorage'; +import { IStoredSession } from '../AADHelper'; +import { ExtensionHost, getMsalFlows } from './flows'; const redirectUri = 'https://vscode.dev/redirect'; const MSA_TID = '9188040d-6c67-4c5b-b112-36a304b66dad'; @@ -21,7 +22,6 @@ const MSA_PASSTHRU_TID = 'f8cdef31-a31e-4b4a-93e4-5f571e91255a'; export class MsalAuthProvider implements AuthenticationProvider { private readonly _disposables: { dispose(): void }[]; - private readonly _publicClientManager: CachedPublicClientApplicationManager; private readonly _eventBufferer = new EventBufferer(); /** @@ -42,20 +42,15 @@ export class MsalAuthProvider implements AuthenticationProvider { */ onDidChangeSessions = this._onDidChangeSessionsEmitter.event; - constructor( - context: ExtensionContext, + private constructor( + private readonly _context: ExtensionContext, private readonly _telemetryReporter: MicrosoftAuthenticationTelemetryReporter, private readonly _logger: LogOutputChannel, private readonly _uriHandler: UriEventHandler, + private readonly _publicClientManager: ICachedPublicClientApplicationManager, private readonly _env: Environment = Environment.AzureCloud ) { - this._disposables = context.subscriptions; - this._publicClientManager = new CachedPublicClientApplicationManager( - context.globalState, - context.secrets, - this._logger, - this._env.name - ); + this._disposables = _context.subscriptions; const accountChangeEvent = this._eventBufferer.wrapEvent( this._publicClientManager.onDidAccountsChange, (last, newEvent) => { @@ -80,13 +75,56 @@ export class MsalAuthProvider implements AuthenticationProvider { )(e => this._handleAccountChange(e)); this._disposables.push( this._onDidChangeSessionsEmitter, - this._publicClientManager, accountChangeEvent ); } - async initialize(): Promise { - await this._eventBufferer.bufferEventsAsync(() => this._publicClientManager.initialize()); + static async create( + context: ExtensionContext, + telemetryReporter: MicrosoftAuthenticationTelemetryReporter, + logger: LogOutputChannel, + uriHandler: UriEventHandler, + env: Environment = Environment.AzureCloud + ): Promise { + const publicClientManager = await CachedPublicClientApplicationManager.create(context.secrets, logger, env.name); + context.subscriptions.push(publicClientManager); + const authProvider = new MsalAuthProvider(context, telemetryReporter, logger, uriHandler, publicClientManager, env); + await authProvider.initialize(); + return authProvider; + } + + /** + * Migrate sessions from the old secret storage to MSAL. + * TODO: MSAL Migration. Remove this when we remove the old flow. + */ + private async _migrateSessions() { + const betterSecretStorage = new BetterTokenStorage('microsoft.login.keylist', this._context); + const sessions = await betterSecretStorage.getAll(item => { + item.endpoint ||= Environment.AzureCloud.activeDirectoryEndpointUrl; + return item.endpoint === this._env.activeDirectoryEndpointUrl; + }); + this._context.globalState.update('msalMigration', true); + + const clientTenantMap = new Map(); + + for (const session of sessions) { + const scopeData = new ScopeData(session.scope.split(' ')); + const key = `${scopeData.clientId}:${scopeData.tenant}`; + if (!clientTenantMap.has(key)) { + clientTenantMap.set(key, { clientId: scopeData.clientId, tenant: scopeData.tenant, refreshTokens: [] }); + } + clientTenantMap.get(key)!.refreshTokens.push(session.refreshToken); + } + + for (const { clientId, refreshTokens } of clientTenantMap.values()) { + await this._publicClientManager.getOrCreate(clientId, refreshTokens); + } + } + + private async initialize(): Promise { + if (!this._context.globalState.get('msalMigration', false)) { + await this._migrateSessions(); + } // Send telemetry for existing accounts for (const cachedPca of this._publicClientManager.getAll()) { @@ -140,8 +178,8 @@ export class MsalAuthProvider implements AuthenticationProvider { return allSessions; } - const cachedPca = await this.getOrCreatePublicClientApplication(scopeData.clientId, scopeData.tenant); - const sessions = await this.getAllSessionsForPca(cachedPca, scopeData.originalScopes, scopeData.scopesToSend, options?.account); + const cachedPca = await this._publicClientManager.getOrCreate(scopeData.clientId); + const sessions = await this.getAllSessionsForPca(cachedPca, scopeData, options?.account); this._logger.info(`[getSessions] [${scopeData.scopeStr}] returned ${sessions.length} session(s)`); return sessions; @@ -152,77 +190,72 @@ export class MsalAuthProvider implements AuthenticationProvider { // Do NOT use `scopes` beyond this place in the code. Use `scopeData` instead. this._logger.info('[createSession]', `[${scopeData.scopeStr}]`, 'starting'); - const cachedPca = await this.getOrCreatePublicClientApplication(scopeData.clientId, scopeData.tenant); - let result: AuthenticationResult | undefined; - - try { - result = await cachedPca.acquireTokenInteractive({ - openBrowser: async (url: string) => { await env.openExternal(Uri.parse(url)); }, - scopes: scopeData.scopesToSend, - // The logic for rendering one or the other of these templates is in the - // template itself, so we pass the same one for both. - successTemplate: loopbackTemplate, - errorTemplate: loopbackTemplate, - // Pass the label of the account to the login hint so that we prefer signing in to that account - loginHint: options.account?.label, - // If we aren't logging in to a specific account, then we can use the prompt to make sure they get - // the option to choose a different account. - prompt: options.account?.label ? undefined : 'select_account' - }); - } catch (e) { - if (e instanceof CancellationError) { - const yes = l10n.t('Yes'); - const result = await window.showErrorMessage( - l10n.t('Having trouble logging in?'), - { - modal: true, - detail: l10n.t('Would you like to try a different way to sign in to your Microsoft account? ({0})', 'protocol handler') - }, - yes - ); - if (!result) { - this._telemetryReporter.sendLoginFailedEvent(); - throw e; - } + const cachedPca = await this._publicClientManager.getOrCreate(scopeData.clientId); + + // Used for showing a friendlier message to the user when the explicitly cancel a flow. + let userCancelled: boolean | undefined; + const yes = l10n.t('Yes'); + const no = l10n.t('No'); + const promptToContinue = async (mode: string) => { + if (userCancelled === undefined) { + // We haven't had a failure yet so wait to prompt + return; } - // This error comes from the backend and is likely not due to the user's machine - // failing to open a port or something local that would require us to try the - // URL handler loopback client. - if (e instanceof ServerError) { - this._telemetryReporter.sendLoginFailedEvent(); - throw e; + const message = userCancelled + ? l10n.t('Having trouble logging in? Would you like to try a different way? ({0})', mode) + : l10n.t('You have not yet finished authorizing this extension to use your Microsoft Account. Would you like to try a different way? ({0})', mode); + const result = await window.showWarningMessage(message, yes, no); + if (result !== yes) { + throw new CancellationError(); } + }; - // The user wants to try the loopback client or we got an error likely due to spinning up the server - const loopbackClient = new UriHandlerLoopbackClient(this._uriHandler, redirectUri, this._logger); + const flows = getMsalFlows({ + extensionHost: typeof navigator === 'undefined' + ? this._context.extension.extensionKind === ExtensionKind.UI ? ExtensionHost.Local : ExtensionHost.Remote + : ExtensionHost.WebWorker, + }); + + const authority = new URL(scopeData.tenant, this._env.activeDirectoryEndpointUrl).toString(); + let lastError: Error | undefined; + for (const flow of flows) { + if (flow !== flows[0]) { + try { + await promptToContinue(flow.label); + } finally { + this._telemetryReporter.sendLoginFailedEvent(); + } + } try { - result = await cachedPca.acquireTokenInteractive({ - openBrowser: (url: string) => loopbackClient.openBrowser(url), + const result = await flow.trigger({ + cachedPca, + authority, scopes: scopeData.scopesToSend, - loopbackClient, loginHint: options.account?.label, - prompt: options.account?.label ? undefined : 'select_account' + windowHandle: window.nativeHandle ? Buffer.from(window.nativeHandle) : undefined, + logger: this._logger, + uriHandler: this._uriHandler }); + + const session = this.sessionFromAuthenticationResult(result, scopeData.originalScopes); + this._telemetryReporter.sendLoginEvent(session.scopes); + this._logger.info('[createSession]', `[${scopeData.scopeStr}]`, 'returned session'); + return session; } catch (e) { - this._telemetryReporter.sendLoginFailedEvent(); - throw e; + lastError = e; + if (e instanceof ServerError || (e as ClientAuthError)?.errorCode === ClientAuthErrorCodes.userCanceled) { + this._telemetryReporter.sendLoginFailedEvent(); + throw e; + } + // Continue to next flow + if (e instanceof CancellationError) { + userCancelled = true; + } } } - if (!result) { - this._telemetryReporter.sendLoginFailedEvent(); - throw new Error('No result returned from MSAL'); - } - - const session = this.sessionFromAuthenticationResult(result, scopeData.originalScopes); - this._telemetryReporter.sendLoginEvent(session.scopes); - this._logger.info('[createSession]', `[${scopeData.scopeStr}]`, 'returned session'); - // This is the only scenario in which we need to fire the _onDidChangeSessionsEmitter out of band... - // the badge flow (when the client passes no options in to getSession) will only remove a badge if a session - // was created that _matches the scopes_ that that badge requests. See `onDidChangeSessions` for more info. - // TODO: This should really be fixed in Core. - this._onDidChangeSessionsEmitter.fire({ added: [session], changed: [], removed: [] }); - return session; + this._telemetryReporter.sendLoginFailedEvent(); + throw lastError ?? new Error('No auth flow succeeded'); } async removeSession(sessionId: string): Promise { @@ -234,7 +267,7 @@ export class MsalAuthProvider implements AuthenticationProvider { if (account.homeAccountId === sessionId) { this._telemetryReporter.sendLogoutEvent(); promises.push(cachedPca.removeAccount(account)); - this._logger.info(`[removeSession] [${sessionId}] [${cachedPca.clientId}] [${cachedPca.authority}] removing session...`); + this._logger.info(`[removeSession] [${sessionId}] [${cachedPca.clientId}] removing session...`); } } } @@ -255,26 +288,69 @@ export class MsalAuthProvider implements AuthenticationProvider { //#endregion - private async getOrCreatePublicClientApplication(clientId: string, tenant: string): Promise { - const authority = new URL(tenant, this._env.activeDirectoryEndpointUrl).toString(); - return await this._publicClientManager.getOrCreate(clientId, authority); - } - private async getAllSessionsForPca( cachedPca: ICachedPublicClientApplication, - originalScopes: readonly string[], - scopesToSend: string[], + scopeData: ScopeData, accountFilter?: AuthenticationSessionAccountInformation ): Promise { - const accounts = accountFilter + let filteredAccounts = accountFilter ? cachedPca.accounts.filter(a => a.homeAccountId === accountFilter.id) : cachedPca.accounts; + + // Group accounts by homeAccountId + const accountGroups = new Map(); + for (const account of filteredAccounts) { + const existing = accountGroups.get(account.homeAccountId) || []; + existing.push(account); + accountGroups.set(account.homeAccountId, existing); + } + + // Filter to one account per homeAccountId + filteredAccounts = Array.from(accountGroups.values()).map(accounts => { + if (accounts.length === 1) { + return accounts[0]; + } + + // If we have a specific tenant to target, prefer that one + if (scopeData.tenantId) { + const matchingTenant = accounts.find(a => a.tenantId === scopeData.tenantId); + if (matchingTenant) { + return matchingTenant; + } + } + + // Otherwise prefer the home tenant + return accounts.find(a => a.tenantId === a.idTokenClaims?.tid) || accounts[0]; + }); + + const authority = new URL(scopeData.tenant, this._env.activeDirectoryEndpointUrl).toString(); const sessions: AuthenticationSession[] = []; return this._eventBufferer.bufferEventsAsync(async () => { - for (const account of accounts) { + for (const account of filteredAccounts) { try { - const result = await cachedPca.acquireTokenSilent({ account, scopes: scopesToSend, redirectUri }); - sessions.push(this.sessionFromAuthenticationResult(result, originalScopes)); + let forceRefresh: true | undefined; + if (scopeData.tenantId) { + // If the tenants do not match, then we need to skip the cache + // to get a new token for the new tenant + if (account.tenantId !== scopeData.tenantId) { + forceRefresh = true; + } + } else { + // If we are requesting the home tenant and we don't yet have + // a token for the home tenant, we need to skip the cache + // to get a new token for the home tenant + if (account.tenantId !== account.idTokenClaims?.tid) { + forceRefresh = true; + } + } + const result = await cachedPca.acquireTokenSilent({ + account, + authority, + scopes: scopeData.scopesToSend, + redirectUri, + forceRefresh + }); + sessions.push(this.sessionFromAuthenticationResult(result, scopeData.originalScopes)); } catch (e) { // If we can't get a token silently, the account is probably in a bad state so we should skip it // MSAL will log this already, so we don't need to log it again diff --git a/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts b/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts index c679610466a2..f0f4eb7b9bcd 100644 --- a/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts +++ b/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts @@ -3,48 +3,31 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { PublicClientApplication, AccountInfo, Configuration, SilentFlowRequest, AuthenticationResult, InteractiveRequest, LogLevel } from '@azure/msal-node'; -import { Disposable, Memento, SecretStorage, LogOutputChannel, window, ProgressLocation, l10n, EventEmitter } from 'vscode'; -import { Delayer, raceCancellationAndTimeoutError } from '../common/async'; +import { PublicClientApplication, AccountInfo, SilentFlowRequest, AuthenticationResult, InteractiveRequest, LogLevel, RefreshTokenRequest } from '@azure/msal-node'; +import { NativeBrokerPlugin } from '@azure/msal-node-extensions'; +import { Disposable, SecretStorage, LogOutputChannel, window, ProgressLocation, l10n, EventEmitter } from 'vscode'; +import { raceCancellationAndTimeoutError } from '../common/async'; import { SecretStorageCachePlugin } from '../common/cachePlugin'; import { MsalLoggerOptions } from '../common/loggerOptions'; import { ICachedPublicClientApplication } from '../common/publicClientCache'; +import { IAccountAccess } from '../common/accountAccess'; export class CachedPublicClientApplication implements ICachedPublicClientApplication { + // Core properties private _pca: PublicClientApplication; - private _sequencer = new Sequencer(); - private readonly _refreshDelayer = new DelayerByKey(); - private _accounts: AccountInfo[] = []; + private _sequencer = new Sequencer(); private readonly _disposable: Disposable; - private readonly _loggerOptions = new MsalLoggerOptions(this._logger); + // Cache properties private readonly _secretStorageCachePlugin = new SecretStorageCachePlugin( this._secretStorage, // Include the prefix as a differentiator to other secrets - `pca:${JSON.stringify({ clientId: this._clientId, authority: this._authority })}` + `pca:${this._clientId}` ); - private readonly _config: Configuration = { - auth: { clientId: this._clientId, authority: this._authority }, - system: { - loggerOptions: { - correlationId: `${this._clientId}] [${this._authority}`, - loggerCallback: (level, message, containsPii) => this._loggerOptions.loggerCallback(level, message, containsPii), - logLevel: LogLevel.Trace - } - }, - cache: { - cachePlugin: this._secretStorageCachePlugin - } - }; - /** - * We keep track of the last time an account was removed so we can recreate the PCA if we detect that an account was removed. - * This is due to MSAL-node not providing a way to detect when an account is removed from the cache. An internal issue has been - * filed to track this. If MSAL-node ever provides a way to detect this or handle this better in the Persistant Cache Plugin, - * we can remove this logic. - */ - private _lastCreated: Date; + // Broker properties + private readonly _isBrokerAvailable: boolean; //#region Events @@ -56,28 +39,51 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica //#endregion - constructor( + private constructor( private readonly _clientId: string, - private readonly _authority: string, - private readonly _globalMemento: Memento, private readonly _secretStorage: SecretStorage, - private readonly _logger: LogOutputChannel + private readonly _accountAccess: IAccountAccess, + private readonly _logger: LogOutputChannel, ) { - this._pca = new PublicClientApplication(this._config); - this._lastCreated = new Date(); + const loggerOptions = new MsalLoggerOptions(_logger); + const nativeBrokerPlugin = new NativeBrokerPlugin(); + this._isBrokerAvailable = nativeBrokerPlugin.isBrokerAvailable; + this._pca = new PublicClientApplication({ + auth: { clientId: _clientId }, + system: { + loggerOptions: { + correlationId: _clientId, + loggerCallback: (level, message, containsPii) => loggerOptions.loggerCallback(level, message, containsPii), + logLevel: LogLevel.Trace + } + }, + broker: { nativeBrokerPlugin }, + cache: { cachePlugin: this._secretStorageCachePlugin } + }); this._disposable = Disposable.from( this._registerOnSecretStorageChanged(), this._onDidAccountsChangeEmitter, - this._onDidRemoveLastAccountEmitter + this._onDidRemoveLastAccountEmitter, + this._secretStorageCachePlugin ); } get accounts(): AccountInfo[] { return this._accounts; } get clientId(): string { return this._clientId; } - get authority(): string { return this._authority; } - initialize(): Promise { - return this._update(); + static async create( + clientId: string, + secretStorage: SecretStorage, + accountAccess: IAccountAccess, + logger: LogOutputChannel + ): Promise { + const app = new CachedPublicClientApplication(clientId, secretStorage, accountAccess, logger); + await app.initialize(); + return app; + } + + private async initialize(): Promise { + await this._sequencer.queue(() => this._update()); } dispose(): void { @@ -85,58 +91,173 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica } async acquireTokenSilent(request: SilentFlowRequest): Promise { - this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] starting...`); - const result = await this._sequencer.queue(() => this._pca.acquireTokenSilent(request)); - this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] got result`); - if (result.account && !result.fromCache) { - this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] firing event due to change`); - this._setupRefresh(result); + this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] starting...`); + let result = await this._sequencer.queue(() => this._pca.acquireTokenSilent(request)); + this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] got result`); + // Check expiration of id token and if it's 5min before expiration, force a refresh. + // this is what MSAL does for access tokens already so we're just adding it for id tokens since we care about those. + // NOTE: Once we stop depending on id tokens for some things we can remove all of this. + const idTokenExpirationInSecs = (result.idTokenClaims as { exp?: number }).exp; + if (idTokenExpirationInSecs) { + const fiveMinutesBefore = new Date( + (idTokenExpirationInSecs - 5 * 60) // subtract 5 minutes + * 1000 // convert to milliseconds + ); + if (fiveMinutesBefore < new Date()) { + this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] id token is expired or about to expire. Forcing refresh...`); + const newRequest = this._isBrokerAvailable + // HACK: Broker doesn't support forceRefresh so we need to pass in claims which will force a refresh + ? { ...request, claims: '{ "id_token": {}}' } + : { ...request, forceRefresh: true }; + result = await this._sequencer.queue(() => this._pca.acquireTokenSilent(newRequest)); + this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] got forced result`); + } + const newIdTokenExpirationInSecs = (result.idTokenClaims as { exp?: number }).exp; + if (newIdTokenExpirationInSecs) { + const fiveMinutesBefore = new Date( + (newIdTokenExpirationInSecs - 5 * 60) // subtract 5 minutes + * 1000 // convert to milliseconds + ); + if (fiveMinutesBefore < new Date()) { + this._logger.error(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] id token is still expired.`); + + // HACK: Only for the Broker we try one more time with different claims to force a refresh. Why? We've seen the Broker caching tokens by the claims requested, thus + // there has been a situation where both tokens are expired. + if (this._isBrokerAvailable) { + this._logger.error(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] forcing refresh with different claims...`); + const newRequest = { ...request, claims: '{ "access_token": {}}' }; + result = await this._sequencer.queue(() => this._pca.acquireTokenSilent(newRequest)); + this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] got forced result with different claims`); + const newIdTokenExpirationInSecs = (result.idTokenClaims as { exp?: number }).exp; + if (newIdTokenExpirationInSecs) { + const fiveMinutesBefore = new Date( + (newIdTokenExpirationInSecs - 5 * 60) // subtract 5 minutes + * 1000 // convert to milliseconds + ); + if (fiveMinutesBefore < new Date()) { + this._logger.error(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] id token is still expired.`); + } + } + } + } + } + } + + if (!result.account) { + this._logger.error(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] no account found in result`); + } else if (!result.fromCache && this._verifyIfUsingBroker(result)) { + this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] firing event due to change`); this._onDidAccountsChangeEmitter.fire({ added: [], changed: [result.account], deleted: [] }); } return result; } async acquireTokenInteractive(request: InteractiveRequest): Promise { - this._logger.debug(`[acquireTokenInteractive] [${this._clientId}] [${this._authority}] [${request.scopes?.join(' ')}] loopbackClientOverride: ${request.loopbackClient ? 'true' : 'false'}`); - const result = await window.withProgress( + this._logger.debug(`[acquireTokenInteractive] [${this._clientId}] [${request.authority}] [${request.scopes?.join(' ')}] loopbackClientOverride: ${request.loopbackClient ? 'true' : 'false'}`); + return await window.withProgress( { location: ProgressLocation.Notification, cancellable: true, title: l10n.t('Signing in to Microsoft...') }, - (_process, token) => raceCancellationAndTimeoutError( - this._pca.acquireTokenInteractive(request), - token, - 1000 * 60 * 5 - ) + (_process, token) => this._sequencer.queue(async () => { + const result = await raceCancellationAndTimeoutError( + this._pca.acquireTokenInteractive(request), + token, + 1000 * 60 * 5 + ); + if (this._isBrokerAvailable) { + await this._accountAccess.setAllowedAccess(result.account!, true); + } + // Force an update so that the account cache is updated. + // TODO:@TylerLeonhardt The problem is, we use the sequencer for + // change events but we _don't_ use it for the accounts cache. + // We should probably use it for the accounts cache as well. + await this._update(); + return result; + }) ); - this._setupRefresh(result); + } + + /** + * Allows for passing in a refresh token to get a new access token. This is the migration scenario. + * TODO: MSAL Migration. Remove this when we remove the old flow. + * @param request a {@link RefreshTokenRequest} object that contains the refresh token and other parameters. + * @returns an {@link AuthenticationResult} object that contains the result of the token acquisition operation. + */ + async acquireTokenByRefreshToken(request: RefreshTokenRequest): Promise { + this._logger.debug(`[acquireTokenByRefreshToken] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}]`); + const result = await this._sequencer.queue(async () => { + const result = await this._pca.acquireTokenByRefreshToken(request); + // Force an update so that the account cache is updated. + // TODO:@TylerLeonhardt The problem is, we use the sequencer for + // change events but we _don't_ use it for the accounts cache. + // We should probably use it for the accounts cache as well. + await this._update(); + return result; + }); + if (result) { + // this._setupRefresh(result); + if (this._isBrokerAvailable && result.account) { + await this._accountAccess.setAllowedAccess(result.account, true); + } + } return result; } removeAccount(account: AccountInfo): Promise { - this._globalMemento.update(`lastRemoval:${this._clientId}:${this._authority}`, new Date()); - return this._pca.getTokenCache().removeAccount(account); + if (this._isBrokerAvailable) { + return this._accountAccess.setAllowedAccess(account, false); + } + return this._sequencer.queue(() => this._pca.getTokenCache().removeAccount(account)); } private _registerOnSecretStorageChanged() { - return this._secretStorageCachePlugin.onDidChange(() => this._update()); + if (this._isBrokerAvailable) { + return this._accountAccess.onDidAccountAccessChange(() => this._sequencer.queue(() => this._update())); + } + return this._secretStorageCachePlugin.onDidChange(() => this._sequencer.queue(() => this._update())); + } + + private _lastSeen = new Map(); + private _verifyIfUsingBroker(result: AuthenticationResult): boolean { + // If we're not brokering, we don't need to verify the date + // the cache check will be sufficient + if (!result.fromNativeBroker) { + return true; + } + // The nativeAccountId is what the broker uses to differenciate all + // types of accounts. Even if the "account" is a duplicate of another because + // it's actaully a guest account in another tenant. + let key = result.account!.nativeAccountId; + if (!key) { + this._logger.error(`[verifyIfUsingBroker] [${this._clientId}] [${result.account!.username}] no nativeAccountId found. Using homeAccountId instead.`); + key = result.account!.homeAccountId; + } + const lastSeen = this._lastSeen.get(key); + const lastTimeAuthed = result.account!.idTokenClaims!.iat!; + if (!lastSeen) { + this._lastSeen.set(key, lastTimeAuthed); + return true; + } + if (lastSeen === lastTimeAuthed) { + return false; + } + this._lastSeen.set(key, lastTimeAuthed); + return true; } private async _update() { const before = this._accounts; - this._logger.debug(`[update] [${this._clientId}] [${this._authority}] CachedPublicClientApplication update before: ${before.length}`); - // Dates are stored as strings in the memento - const lastRemovalDate = this._globalMemento.get(`lastRemoval:${this._clientId}:${this._authority}`); - if (lastRemovalDate && this._lastCreated && Date.parse(lastRemovalDate) > this._lastCreated.getTime()) { - this._logger.debug(`[update] [${this._clientId}] [${this._authority}] CachedPublicClientApplication removal detected... recreating PCA...`); - this._pca = new PublicClientApplication(this._config); - this._lastCreated = new Date(); + this._logger.debug(`[update] [${this._clientId}] CachedPublicClientApplication update before: ${before.length}`); + // Clear in-memory cache so we know we're getting account data from the SecretStorage + this._pca.clearCache(); + let after = await this._pca.getAllAccounts(); + if (this._isBrokerAvailable) { + after = after.filter(a => this._accountAccess.isAllowedAccess(a)); } - - const after = await this._pca.getAllAccounts(); this._accounts = after; - this._logger.debug(`[update] [${this._clientId}] [${this._authority}] CachedPublicClientApplication update after: ${after.length}`); + this._logger.debug(`[update] [${this._clientId}] CachedPublicClientApplication update after: ${after.length}`); const beforeSet = new Set(before.map(b => b.homeAccountId)); const afterSet = new Set(after.map(a => a.homeAccountId)); @@ -145,32 +266,13 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica const deleted = before.filter(b => !afterSet.has(b.homeAccountId)); if (added.length > 0 || deleted.length > 0) { this._onDidAccountsChangeEmitter.fire({ added, changed: [], deleted }); - this._logger.debug(`[update] [${this._clientId}] [${this._authority}] CachedPublicClientApplication accounts changed. added: ${added.length}, deleted: ${deleted.length}`); + this._logger.debug(`[update] [${this._clientId}] CachedPublicClientApplication accounts changed. added: ${added.length}, deleted: ${deleted.length}`); if (!after.length) { - this._logger.debug(`[update] [${this._clientId}] [${this._authority}] CachedPublicClientApplication final account deleted. Firing event.`); + this._logger.debug(`[update] [${this._clientId}] CachedPublicClientApplication final account deleted. Firing event.`); this._onDidRemoveLastAccountEmitter.fire(); } } - this._logger.debug(`[update] [${this._clientId}] [${this._authority}] CachedPublicClientApplication update complete`); - } - - private _setupRefresh(result: AuthenticationResult) { - const on = result.refreshOn || result.expiresOn; - if (!result.account || !on) { - return; - } - - const account = result.account; - const scopes = result.scopes; - const timeToRefresh = on.getTime() - Date.now() - 5 * 60 * 1000; // 5 minutes before expiry - const key = JSON.stringify({ accountId: account.homeAccountId, scopes }); - this._logger.debug(`[_setupRefresh] [${this._clientId}] [${this._authority}] [${scopes.join(' ')}] [${account.username}] timeToRefresh: ${timeToRefresh}`); - this._refreshDelayer.trigger( - key, - // This may need the redirectUri when we switch to the broker - () => this.acquireTokenSilent({ account, scopes, redirectUri: undefined, forceRefresh: true }), - timeToRefresh > 0 ? timeToRefresh : 0 - ); + this._logger.debug(`[update] [${this._clientId}] CachedPublicClientApplication update complete`); } } @@ -182,17 +284,3 @@ export class Sequencer { return this.current = this.current.then(() => promiseTask(), () => promiseTask()); } } - -class DelayerByKey { - private _delayers = new Map>(); - - trigger(key: string, fn: () => Promise, delay: number): Promise { - let delayer = this._delayers.get(key); - if (!delayer) { - delayer = new Delayer(delay); - this._delayers.set(key, delayer); - } - - return delayer.trigger(fn, delay); - } -} diff --git a/extensions/microsoft-authentication/src/node/flows.ts b/extensions/microsoft-authentication/src/node/flows.ts new file mode 100644 index 000000000000..ac678d8313dc --- /dev/null +++ b/extensions/microsoft-authentication/src/node/flows.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { AuthenticationResult } from '@azure/msal-node'; +import { Uri, LogOutputChannel, env } from 'vscode'; +import { ICachedPublicClientApplication } from '../common/publicClientCache'; +import { UriHandlerLoopbackClient } from '../common/loopbackClientAndOpener'; +import { UriEventHandler } from '../UriEventHandler'; +import { loopbackTemplate } from './loopbackTemplate'; + +const redirectUri = 'https://vscode.dev/redirect'; + +export const enum ExtensionHost { + WebWorker, + Remote, + Local +} + +interface IMsalFlowOptions { + supportsRemoteExtensionHost: boolean; + supportsWebWorkerExtensionHost: boolean; +} + +interface IMsalFlowTriggerOptions { + cachedPca: ICachedPublicClientApplication; + authority: string; + scopes: string[]; + loginHint?: string; + windowHandle?: Buffer; + logger: LogOutputChannel; + uriHandler: UriEventHandler; +} + +interface IMsalFlow { + readonly label: string; + readonly options: IMsalFlowOptions; + trigger(options: IMsalFlowTriggerOptions): Promise; +} + +class DefaultLoopbackFlow implements IMsalFlow { + label = 'default'; + options: IMsalFlowOptions = { + supportsRemoteExtensionHost: false, + supportsWebWorkerExtensionHost: false + }; + + async trigger({ cachedPca, authority, scopes, loginHint, windowHandle, logger }: IMsalFlowTriggerOptions): Promise { + logger.info('Trying default msal flow...'); + return await cachedPca.acquireTokenInteractive({ + openBrowser: async (url: string) => { await env.openExternal(Uri.parse(url)); }, + scopes, + authority, + successTemplate: loopbackTemplate, + errorTemplate: loopbackTemplate, + loginHint, + prompt: loginHint ? undefined : 'select_account', + windowHandle + }); + } +} + +class UrlHandlerFlow implements IMsalFlow { + label = 'protocol handler'; + options: IMsalFlowOptions = { + supportsRemoteExtensionHost: true, + supportsWebWorkerExtensionHost: false + }; + + async trigger({ cachedPca, authority, scopes, loginHint, windowHandle, logger, uriHandler }: IMsalFlowTriggerOptions): Promise { + logger.info('Trying protocol handler flow...'); + const loopbackClient = new UriHandlerLoopbackClient(uriHandler, redirectUri, logger); + return await cachedPca.acquireTokenInteractive({ + openBrowser: (url: string) => loopbackClient.openBrowser(url), + scopes, + authority, + loopbackClient, + loginHint, + prompt: loginHint ? undefined : 'select_account', + windowHandle + }); + } +} + +const allFlows: IMsalFlow[] = [ + new DefaultLoopbackFlow(), + new UrlHandlerFlow() +]; + +export interface IMsalFlowQuery { + extensionHost: ExtensionHost; +} + +export function getMsalFlows(query: IMsalFlowQuery): IMsalFlow[] { + return allFlows.filter(flow => { + let useFlow: boolean = true; + switch (query.extensionHost) { + case ExtensionHost.Remote: + useFlow &&= flow.options.supportsRemoteExtensionHost; + break; + case ExtensionHost.WebWorker: + useFlow &&= flow.options.supportsWebWorkerExtensionHost; + break; + } + return useFlow; + }); +} diff --git a/extensions/microsoft-authentication/src/node/publicClientCache.ts b/extensions/microsoft-authentication/src/node/publicClientCache.ts index fc6ce38e9757..16ccb80321fd 100644 --- a/extensions/microsoft-authentication/src/node/publicClientCache.ts +++ b/extensions/microsoft-authentication/src/node/publicClientCache.ts @@ -7,6 +7,7 @@ import { AccountInfo } from '@azure/msal-node'; import { SecretStorage, LogOutputChannel, Disposable, EventEmitter, Memento, Event } from 'vscode'; import { ICachedPublicClientApplication, ICachedPublicClientApplicationManager } from '../common/publicClientCache'; import { CachedPublicClientApplication } from './cachedPublicClientApplication'; +import { IAccountAccess, ScopedAccountAccess } from '../common/accountAccess'; export interface IPublicClientApplicationInfo { clientId: string; @@ -14,56 +15,68 @@ export interface IPublicClientApplicationInfo { } export class CachedPublicClientApplicationManager implements ICachedPublicClientApplicationManager { - // The key is the clientId and authority JSON stringified - private readonly _pcas = new Map(); + // The key is the clientId + private readonly _pcas = new Map(); private readonly _pcaDisposables = new Map(); private _disposable: Disposable; - private _pcasSecretStorage: PublicClientApplicationsSecretStorage; private readonly _onDidAccountsChangeEmitter = new EventEmitter<{ added: AccountInfo[]; changed: AccountInfo[]; deleted: AccountInfo[] }>(); readonly onDidAccountsChange = this._onDidAccountsChangeEmitter.event; - constructor( - private readonly _globalMemento: Memento, + private constructor( + private readonly _pcasSecretStorage: IPublicClientApplicationSecretStorage, + private readonly _accountAccess: IAccountAccess, private readonly _secretStorage: SecretStorage, private readonly _logger: LogOutputChannel, - cloudName: string + disposables: Disposable[] ) { - this._pcasSecretStorage = new PublicClientApplicationsSecretStorage(_secretStorage, cloudName); this._disposable = Disposable.from( - this._pcasSecretStorage, + ...disposables, this._registerSecretStorageHandler(), this._onDidAccountsChangeEmitter ); } + static async create( + secretStorage: SecretStorage, + logger: LogOutputChannel, + cloudName: string + ): Promise { + const pcasSecretStorage = await PublicClientApplicationsSecretStorage.create(secretStorage, cloudName); + // TODO: Remove the migrations in a version + const migrations = await pcasSecretStorage.getOldValue(); + const accountAccess = await ScopedAccountAccess.create(secretStorage, cloudName, logger, migrations); + const manager = new CachedPublicClientApplicationManager(pcasSecretStorage, accountAccess, secretStorage, logger, [pcasSecretStorage, accountAccess]); + await manager.initialize(); + return manager; + } + private _registerSecretStorageHandler() { return this._pcasSecretStorage.onDidChange(() => this._handleSecretStorageChange()); } - async initialize() { + private async initialize() { this._logger.debug('[initialize] Initializing PublicClientApplicationManager'); - let keys: string[] | undefined; + let clientIds: string[] | undefined; try { - keys = await this._pcasSecretStorage.get(); + clientIds = await this._pcasSecretStorage.get(); } catch (e) { // data is corrupted this._logger.error('[initialize] Error initializing PublicClientApplicationManager:', e); await this._pcasSecretStorage.delete(); } - if (!keys) { + if (!clientIds) { return; } const promises = new Array>(); - for (const key of keys) { + for (const clientId of clientIds) { try { - const { clientId, authority } = JSON.parse(key) as IPublicClientApplicationInfo; // Load the PCA in memory - promises.push(this._doCreatePublicClientApplication(clientId, authority, key)); + promises.push(this._doCreatePublicClientApplication(clientId)); } catch (e) { - this._logger.error('[initialize] Error intitializing PCA:', key); + this._logger.error('[initialize] Error intitializing PCA:', clientId); } } @@ -75,11 +88,11 @@ export class CachedPublicClientApplicationManager implements ICachedPublicClient } else { if (!result.value.accounts.length) { pcasChanged = true; - const pcaKey = JSON.stringify({ clientId: result.value.clientId, authority: result.value.authority }); - this._pcaDisposables.get(pcaKey)?.dispose(); - this._pcaDisposables.delete(pcaKey); - this._pcas.delete(pcaKey); - this._logger.debug(`[initialize] [${result.value.clientId}] [${result.value.authority}] PCA disposed because it's empty.`); + const clientId = result.value.clientId; + this._pcaDisposables.get(clientId)?.dispose(); + this._pcaDisposables.delete(clientId); + this._pcas.delete(clientId); + this._logger.debug(`[initialize] [${clientId}] PCA disposed because it's empty.`); } } } @@ -94,25 +107,39 @@ export class CachedPublicClientApplicationManager implements ICachedPublicClient Disposable.from(...this._pcaDisposables.values()).dispose(); } - async getOrCreate(clientId: string, authority: string): Promise { - // Use the clientId and authority as the key - const pcasKey = JSON.stringify({ clientId, authority }); - let pca = this._pcas.get(pcasKey); + async getOrCreate(clientId: string, refreshTokensToMigrate?: string[]): Promise { + let pca = this._pcas.get(clientId); if (pca) { - this._logger.debug(`[getOrCreate] [${clientId}] [${authority}] PublicClientApplicationManager cache hit`); - return pca; + this._logger.debug(`[getOrCreate] [${clientId}] PublicClientApplicationManager cache hit`); + } else { + this._logger.debug(`[getOrCreate] [${clientId}] PublicClientApplicationManager cache miss, creating new PCA...`); + pca = await this._doCreatePublicClientApplication(clientId); + await this._storePublicClientApplications(); + this._logger.debug(`[getOrCreate] [${clientId}] PCA created.`); } - this._logger.debug(`[getOrCreate] [${clientId}] [${authority}] PublicClientApplicationManager cache miss, creating new PCA...`); - pca = await this._doCreatePublicClientApplication(clientId, authority, pcasKey); - await this._storePublicClientApplications(); - this._logger.debug(`[getOrCreate] [${clientId}] [${authority}] PCA created.`); + // TODO: MSAL Migration. Remove this when we remove the old flow. + if (refreshTokensToMigrate?.length) { + this._logger.debug(`[getOrCreate] [${clientId}] Migrating refresh tokens to PCA...`); + for (const refreshToken of refreshTokensToMigrate) { + try { + // Use the refresh token to acquire a result. This will cache the refresh token for future operations. + // The scopes don't matter here since we can create any token from the refresh token. + const result = await pca.acquireTokenByRefreshToken({ refreshToken, forceCache: true, scopes: [] }); + if (result?.account) { + this._logger.debug(`[getOrCreate] [${clientId}] Refresh token migrated to PCA.`); + } + } catch (e) { + this._logger.error(`[getOrCreate] [${clientId}] Error migrating refresh token:`, e); + } + } + } return pca; } - private async _doCreatePublicClientApplication(clientId: string, authority: string, pcasKey: string) { - const pca = new CachedPublicClientApplication(clientId, authority, this._globalMemento, this._secretStorage, this._logger); - this._pcas.set(pcasKey, pca); + private async _doCreatePublicClientApplication(clientId: string): Promise { + const pca = await CachedPublicClientApplication.create(clientId, this._secretStorage, this._accountAccess, this._logger); + this._pcas.set(clientId, pca); const disposable = Disposable.from( pca, pca.onDidAccountsChange(e => this._onDidAccountsChangeEmitter.fire(e)), @@ -120,15 +147,17 @@ export class CachedPublicClientApplicationManager implements ICachedPublicClient // The PCA has no more accounts, so we can dispose it so we're not keeping it // around forever. disposable.dispose(); - this._pcaDisposables.delete(pcasKey); - this._pcas.delete(pcasKey); - this._logger.debug(`[_doCreatePublicClientApplication] [${clientId}] [${authority}] PCA disposed. Firing off storing of PCAs...`); + this._pcaDisposables.delete(clientId); + this._pcas.delete(clientId); + this._logger.debug(`[_doCreatePublicClientApplication] [${clientId}] PCA disposed. Firing off storing of PCAs...`); void this._storePublicClientApplications(); }) ); - this._pcaDisposables.set(pcasKey, disposable); - // Intialize the PCA after the `onDidAccountsChange` is set so we get initial state. - await pca.initialize(); + this._pcaDisposables.set(clientId, disposable); + // Fire for the initial state and only if accounts exist + if (pca.accounts.length > 0) { + this._onDidAccountsChangeEmitter.fire({ added: pca.accounts, changed: [], deleted: [] }); + } return pca; } @@ -160,24 +189,19 @@ export class CachedPublicClientApplicationManager implements ICachedPublicClient // Handle the deleted ones for (const pcaKey of this._pcas.keys()) { if (!pcaKeysFromStorage.delete(pcaKey)) { - // This PCA has been removed in another window - this._pcaDisposables.get(pcaKey)?.dispose(); - this._pcaDisposables.delete(pcaKey); - this._pcas.delete(pcaKey); - this._logger.debug(`[_handleSecretStorageChange] Disposed PCA that was deleted in another window: ${pcaKey}`); + this._logger.debug(`[_handleSecretStorageChange] PCA was deleted in another window: ${pcaKey}`); } } // Handle the new ones - for (const newPca of pcaKeysFromStorage) { + for (const clientId of pcaKeysFromStorage) { try { - const { clientId, authority } = JSON.parse(newPca); - this._logger.debug(`[_handleSecretStorageChange] [${clientId}] [${authority}] Creating new PCA that was created in another window...`); - await this._doCreatePublicClientApplication(clientId, authority, newPca); - this._logger.debug(`[_handleSecretStorageChange] [${clientId}] [${authority}] PCA created.`); + this._logger.debug(`[_handleSecretStorageChange] [${clientId}] Creating new PCA that was created in another window...`); + await this._doCreatePublicClientApplication(clientId); + this._logger.debug(`[_handleSecretStorageChange] [${clientId}] PCA created.`); } catch (_e) { // This really shouldn't happen, but should we do something about this? - this._logger.error(`Failed to parse new PublicClientApplication: ${newPca}`); + this._logger.error(`Failed to create new PublicClientApplication: ${clientId}`); continue; } } @@ -190,15 +214,24 @@ export class CachedPublicClientApplicationManager implements ICachedPublicClient } } -class PublicClientApplicationsSecretStorage { +interface IPublicClientApplicationSecretStorage { + get(): Promise; + getOldValue(): Promise<{ clientId: string; authority: string }[] | undefined>; + store(value: string[]): Thenable; + delete(): Thenable; + onDidChange: Event; +} + +class PublicClientApplicationsSecretStorage implements IPublicClientApplicationSecretStorage, Disposable { private _disposable: Disposable; private readonly _onDidChangeEmitter = new EventEmitter; readonly onDidChange: Event = this._onDidChangeEmitter.event; - private readonly _key = `publicClientApplications-${this._cloudName}`; + private readonly _oldKey = `publicClientApplications-${this._cloudName}`; + private readonly _key = `publicClients-${this._cloudName}`; - constructor(private readonly _secretStorage: SecretStorage, private readonly _cloudName: string) { + private constructor(private readonly _secretStorage: SecretStorage, private readonly _cloudName: string) { this._disposable = Disposable.from( this._onDidChangeEmitter, this._secretStorage.onDidChange(e => { @@ -209,6 +242,30 @@ class PublicClientApplicationsSecretStorage { ); } + static async create(secretStorage: SecretStorage, cloudName: string): Promise { + const storage = new PublicClientApplicationsSecretStorage(secretStorage, cloudName); + await storage.initialize(); + return storage; + } + + /** + * Runs the migration. + * TODO: Remove this after a version. + */ + private async initialize() { + const oldValue = await this.getOldValue(); + if (!oldValue) { + return; + } + const newValue = await this.get() ?? []; + for (const { clientId } of oldValue) { + if (!newValue.includes(clientId)) { + newValue.push(clientId); + } + } + await this.store(newValue); + } + async get(): Promise { const value = await this._secretStorage.get(this._key); if (!value) { @@ -217,6 +274,25 @@ class PublicClientApplicationsSecretStorage { return JSON.parse(value); } + /** + * Old representation of data that included the authority. This should be removed in a version or 2. + * @returns An array of objects with clientId and authority + */ + async getOldValue(): Promise<{ clientId: string; authority: string }[] | undefined> { + const value = await this._secretStorage.get(this._oldKey); + if (!value) { + return undefined; + } + const result: { clientId: string; authority: string }[] = []; + for (const stringifiedObj of JSON.parse(value)) { + const obj = JSON.parse(stringifiedObj); + if (obj.clientId && obj.authority) { + result.push(obj); + } + } + return result; + } + store(value: string[]): Thenable { return this._secretStorage.store(this._key, JSON.stringify(value)); } diff --git a/extensions/microsoft-authentication/tsconfig.json b/extensions/microsoft-authentication/tsconfig.json index 4b9d06d1847e..b40c2eb8716d 100644 --- a/extensions/microsoft-authentication/tsconfig.json +++ b/extensions/microsoft-authentication/tsconfig.json @@ -22,6 +22,7 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", - "../../src/vscode-dts/vscode.proposed.idToken.d.ts" + "../../src/vscode-dts/vscode.proposed.idToken.d.ts", + "../../src/vscode-dts/vscode.proposed.nativeWindowHandle.d.ts" ] } diff --git a/extensions/notebook-renderers/package-lock.json b/extensions/notebook-renderers/package-lock.json index 8dbc5f5ad4c2..ad0178475cd6 100644 --- a/extensions/notebook-renderers/package-lock.json +++ b/extensions/notebook-renderers/package-lock.json @@ -9,28 +9,215 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@types/jsdom": "^21.1.0", - "@types/vscode-notebook-renderer": "^1.60.0", - "jsdom": "^21.1.1" + "@types/jsdom": "^27.0.0", + "@types/vscode-notebook-renderer": "^1.72.4", + "jsdom": "^27.4.0" }, "engines": { "vscode": "^1.57.0" } }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "node_modules/@acemir/cssom": { + "version": "0.9.30", + "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.30.tgz", + "integrity": "sha512-9CnlMCI0LmCIq0olalQqdWrJHPzm0/tw3gzOA9zJSgvFX7Xau3D24mAGa4BtwxwY69nsuJW6kQqqCzf/mEcQgg==", "dev": true, + "license": "MIT" + }, + "node_modules/@asamuzakjp/css-color": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.1.tgz", + "integrity": "sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.4", + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "lru-cache": "^11.2.4" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.7.6", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.6.tgz", + "integrity": "sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.4" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.22.tgz", + "integrity": "sha512-qBcx6zYlhleiFfdtzkRgwNC7VVoAwfK76Vmsw5t+PbvtdknO9StgRk7ROvq9so1iqbdW4uLIDAsXRsTfUrIoOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.6.0.tgz", + "integrity": "sha512-y32mI9627q5LR/L8fLc4YyDRJQOi+jK0D9okzLilAdiU3F9we3zC7Y7CFrR/8vAvUyv7FgBAYcNHtvbmhKCFcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@exodus/crypto": "^1.0.0-rc.4" + }, + "peerDependenciesMeta": { + "@exodus/crypto": { + "optional": true + } } }, "node_modules/@types/jsdom": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.0.tgz", - "integrity": "sha512-leWreJOdnuIxq9Y70tBVm/bvTuh31DSlF/r4l7Cfi4uhVQqLHD0Q4v301GMisEMwwbMgF7ZKxuZ+Jbd4NcdmRw==", + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-27.0.0.tgz", + "integrity": "sha512-NZyFl/PViwKzdEkQg96gtnB8wm+1ljhdDay9ahn4hgb+SfVtPCbm3TlmDUFXTA+MGN3CijicnMhG18SI5H3rFw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "@types/tough-cookie": "*", @@ -50,112 +237,83 @@ "dev": true }, "node_modules/@types/vscode-notebook-renderer": { - "version": "1.60.0", - "resolved": "https://registry.npmjs.org/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.60.0.tgz", - "integrity": "sha512-u7TD2uuEZTVuitx0iijOJdKI0JLiQP6PsSBSRy2XmHXUOXcp5p1S56NrjOEDoF+PIHd3NL3eO6KTRSf5nukDqQ==", - "dev": true - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "deprecated": "Use your platform's native atob() and btoa() methods instead", - "dev": true - }, - "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "1.72.4", + "resolved": "https://registry.npmjs.org/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.72.4.tgz", + "integrity": "sha512-bdKO41c6Dc24pH/O/eM/jqfCwGH4zc76o/g/6Gt1y/eg/bvvqP2/VpbV+Sa5Te2sZekFRcbYnSSFTKo3wcVGUg==", "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", - "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", - "dev": true, - "dependencies": { - "acorn": "^8.1.0", - "acorn-walk": "^8.0.2" - } + "license": "MIT" }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.4.0" + "node": ">= 14" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" + "require-from-string": "^2.0.2" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", "dev": true, + "license": "MIT", "dependencies": { - "delayed-stream": "~1.0.0" + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" }, "engines": { - "node": ">= 0.8" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, "node_modules/cssstyle": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", - "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==", + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.5.tgz", + "integrity": "sha512-GlsEptulso7Jg0VaOZ8BXQi3AkYM5BOJKEO/rjMidSCq70FkIC5y0eawrCXeYzxgt3OCf4Ls+eoxN+/05vN0Ag==", "dev": true, + "license": "MIT", "dependencies": { - "rrweb-cssom": "^0.6.0" + "@asamuzakjp/css-color": "^4.1.1", + "@csstools/css-syntax-patches-for-csstree": "^1.0.21", + "css-tree": "^3.1.0" }, "engines": { - "node": ">=14" + "node": ">=20" } }, "node_modules/data-urls": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz", - "integrity": "sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-6.0.0.tgz", + "integrity": "sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==", "dev": true, + "license": "MIT", "dependencies": { - "abab": "^2.0.6", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^12.0.0" + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.0.0" }, "engines": { - "node": ">=14" + "node": ">=20" } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -167,38 +325,11 @@ } }, "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "deprecated": "Use your platform's native DOMException instead", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", "dev": true, - "dependencies": { - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } + "license": "MIT" }, "node_modules/entities": { "version": "4.4.0", @@ -212,128 +343,45 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", "dev": true, + "license": "MIT", "dependencies": { - "whatwg-encoding": "^2.0.0" + "@exodus/bytes": "^1.6.0" }, "engines": { - "node": ">=12" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, + "license": "MIT", "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, + "license": "MIT", "dependencies": { - "agent-base": "6", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" + "node": ">= 14" } }, "node_modules/is-potential-custom-element-name": { @@ -343,43 +391,38 @@ "dev": true }, "node_modules/jsdom": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-21.1.1.tgz", - "integrity": "sha512-Jjgdmw48RKcdAIQyUD1UdBh2ecH7VqwaXPN3ehoZN6MqgVbMn+lRm1aAT1AsdJRAJpwfa4IpwgzySn61h2qu3w==", + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.4.0.tgz", + "integrity": "sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==", "dev": true, + "license": "MIT", "dependencies": { - "abab": "^2.0.6", - "acorn": "^8.8.2", - "acorn-globals": "^7.0.0", - "cssstyle": "^3.0.0", - "data-urls": "^4.0.0", - "decimal.js": "^10.4.3", - "domexception": "^4.0.0", - "escodegen": "^2.0.0", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", + "@acemir/cssom": "^0.9.28", + "@asamuzakjp/dom-selector": "^6.7.6", + "@exodus/bytes": "^1.6.0", + "cssstyle": "^5.3.4", + "data-urls": "^6.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.2", - "parse5": "^7.1.2", - "rrweb-cssom": "^0.6.0", + "parse5": "^8.0.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.2", - "w3c-xmlserializer": "^4.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^2.0.0", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^12.0.1", - "ws": "^8.13.0", - "xml-name-validator": "^4.0.0" + "tough-cookie": "^6.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.0", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.1.0", + "ws": "^8.18.3", + "xml-name-validator": "^5.0.0" }, "engines": { - "node": ">=14" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "canvas": "^2.5.0" + "canvas": "^3.0.0" }, "peerDependenciesMeta": { "canvas": { @@ -387,68 +430,55 @@ } } }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "node_modules/jsdom/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">= 0.8.0" + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/jsdom/node_modules/parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", "dev": true, - "engines": { - "node": ">= 0.6" + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "dependencies": { - "mime-db": "1.52.0" - }, + "license": "BlueOak-1.0.0", "engines": { - "node": ">= 0.6" + "node": "20 || >=22" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/nwsapi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", - "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==", - "dev": true + "node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true, + "license": "CC0-1.0" }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } + "license": "MIT" }, "node_modules/parse5": { "version": "7.1.2", @@ -462,54 +492,26 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.8.0" + "node": ">=6" } }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, - "node_modules/rrweb-cssom": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", - "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", - "dev": true - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", @@ -522,12 +524,12 @@ "node": ">=v12.22.7" } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, - "optional": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -538,133 +540,105 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, - "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "node_modules/tldts": { + "version": "7.0.19", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.19.tgz", + "integrity": "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==", "dev": true, + "license": "MIT", "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" + "tldts-core": "^7.0.19" }, - "engines": { - "node": ">=6" + "bin": { + "tldts": "bin/cli.js" } }, - "node_modules/tr46": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", - "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "node_modules/tldts-core": { + "version": "7.0.19", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.19.tgz", + "integrity": "sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==", "dev": true, - "dependencies": { - "punycode": "^2.3.0" - }, - "engines": { - "node": ">=14" - } + "license": "MIT" }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "node_modules/tough-cookie": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", + "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "prelude-ls": "~1.1.2" + "tldts": "^7.0.5" }, "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" + "node": ">=16" } }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", "dev": true, + "license": "MIT", "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" } }, "node_modules/w3c-xmlserializer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", - "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "dev": true, + "license": "MIT", "dependencies": { - "xml-name-validator": "^4.0.0" + "xml-name-validator": "^5.0.0" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.0.tgz", + "integrity": "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==", "dev": true, - "dependencies": { - "iconv-lite": "0.6.3" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">=12" + "node": ">=20" } }, "node_modules/whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/whatwg-url": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz", - "integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==", + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz", + "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==", "dev": true, + "license": "MIT", "dependencies": { - "tr46": "^4.1.1", - "webidl-conversions": "^7.0.0" + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.0" }, "engines": { - "node": ">=14" - } - }, - "node_modules/word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node": ">=20" } }, "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -682,12 +656,13 @@ } }, "node_modules/xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/xmlchars": { diff --git a/extensions/notebook-renderers/package.json b/extensions/notebook-renderers/package.json index d6ece35af112..6420afcea5c5 100644 --- a/extensions/notebook-renderers/package.json +++ b/extensions/notebook-renderers/package.json @@ -48,9 +48,9 @@ }, "dependencies": {}, "devDependencies": { - "@types/jsdom": "^21.1.0", - "@types/vscode-notebook-renderer": "^1.60.0", - "jsdom": "^21.1.1" + "@types/jsdom": "^27.0.0", + "@types/vscode-notebook-renderer": "^1.72.4", + "jsdom": "^27.4.0" }, "repository": { "type": "git", diff --git a/extensions/notebook-renderers/src/ansi.ts b/extensions/notebook-renderers/src/ansi.ts index ee26d37d1f6d..90abdb73b5f1 100644 --- a/extensions/notebook-renderers/src/ansi.ts +++ b/extensions/notebook-renderers/src/ansi.ts @@ -59,7 +59,7 @@ export function handleANSIOutput(text: string, linkOptions: LinkOptions): HTMLSp * Certain ranges that are matched here do not contain real graphics rendition sequences. For * the sake of having a simpler expression, they have been included anyway. */ - if (ansiSequence.match(/^(?:[34][0-8]|9[0-7]|10[0-7]|[0-9]|2[1-5,7-9]|[34]9|5[8,9]|1[0-9])(?:;[349][0-7]|10[0-7]|[013]|[245]|[34]9)?(?:;[012]?[0-9]?[0-9])*;?m$/)) { + if (ansiSequence.match(/^(?:[34][0-8]|9[0-7]|10[0-7]|[0-9]|2[1-5,7-9]|[34]9|5[8,9]|1[0-9])(?:;[349][0-7]|10[0-7]|[013]|[245]|[34]9)?(?:;[0-9]{1,3})*;?m$/)) { const styleCodes: number[] = ansiSequence.slice(0, -1) // Remove final 'm' character. .split(';') // Separate style codes. diff --git a/extensions/notebook-renderers/src/index.ts b/extensions/notebook-renderers/src/index.ts index 8f5fa908cb93..1c81a249d82c 100644 --- a/extensions/notebook-renderers/src/index.ts +++ b/extensions/notebook-renderers/src/index.ts @@ -404,9 +404,9 @@ function renderText(outputInfo: OutputItem, outputElement: HTMLElement, ctx: IRi const outputOptions = { linesLimit: ctx.settings.lineLimit, scrollable: outputScrolling, trustHtml: false, linkifyFilePaths: ctx.settings.linkifyFilePaths }; const content = createOutputContent(outputInfo.id, text, outputOptions); content.classList.add('output-plaintext'); - outputElement.classList.toggle('word-wrap', ctx.settings.outputWordWrap); + content.classList.toggle('word-wrap', ctx.settings.outputWordWrap); disposableStore.push(ctx.onDidChangeSettings(e => { - outputElement.classList.toggle('word-wrap', e.outputWordWrap); + content.classList.toggle('word-wrap', e.outputWordWrap); })); content.classList.toggle('scrollable', outputScrolling); diff --git a/extensions/notebook-renderers/src/test/notebookRenderer.test.ts b/extensions/notebook-renderers/src/test/notebookRenderer.test.ts index dfc7e2b15f87..9dc8f6c845e2 100644 --- a/extensions/notebook-renderers/src/test/notebookRenderer.test.ts +++ b/extensions/notebook-renderers/src/test/notebookRenderer.test.ts @@ -152,8 +152,12 @@ suite('Notebook builtin output renderer', () => { const inserted = outputElement.firstChild as HTMLElement; assert.ok(inserted, `nothing appended to output element: ${outputElement.innerHTML}`); assert.ok(outputElement.classList.contains('remove-padding'), `Padding should be removed for scrollable outputs ${outputElement.classList}`); - assert.ok(outputElement.classList.contains('word-wrap') && inserted.classList.contains('scrollable'), - `output content classList should contain word-wrap and scrollable ${inserted.classList}`); + if (mimeType === 'text/plain') { + assert.ok(inserted.classList.contains('word-wrap'), `Word wrap should be enabled for text/plain ${outputElement.classList}`); + } else { + assert.ok(outputElement.classList.contains('word-wrap') && inserted.classList.contains('scrollable'), + `output content classList should contain word-wrap and scrollable ${inserted.classList}`); + } assert.ok(inserted.innerHTML.indexOf('>content -1, `Content was not added to output element: ${outputElement.innerHTML}`); }); diff --git a/extensions/npm/.vscode/launch.json b/extensions/npm/.vscode/launch.json index 017c87624153..b5cc96144bc8 100644 --- a/extensions/npm/.vscode/launch.json +++ b/extensions/npm/.vscode/launch.json @@ -9,10 +9,7 @@ "args": [ "--extensionDevelopmentPath=${workspaceFolder}" ], - "stopOnEntry": false, "sourceMaps": true, - "outFiles": ["${workspaceFolder}/client/out/**/*.js"], - "preLaunchTask": "npm" } ] -} \ No newline at end of file +} diff --git a/extensions/npm/README.md b/extensions/npm/README.md index 215ca927ff43..d5538706019b 100644 --- a/extensions/npm/README.md +++ b/extensions/npm/README.md @@ -34,7 +34,8 @@ The extension fetches data from and =22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "dev": true + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-6.0.0.tgz", + "integrity": "sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==", + "deprecated": "This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed.", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "*" + } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@types/which": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.0.tgz", - "integrity": "sha512-ASCxdbsrwNfSMXALlC3Decif9rwDMu+80KGp5zI2RLRotfMsTv7fHL8W8VDp24wymzDyIFudhUeSCugrgRFfHQ==", - "dev": true + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.4.tgz", + "integrity": "sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w==", + "dev": true, + "license": "MIT" }, "node_modules/argparse": { "version": "1.0.9", @@ -56,19 +84,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c= sha512-9Y0g0Q8rmSt+H33DfKv7FOc3v+iRI+o1lbzt8jGcIosYW37IIW/2XVYq5NPdmaD5NQ59Nk26Kl/vZbwW9Fr8vg==" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -104,15 +119,16 @@ } }, "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-8.0.0.tgz", + "integrity": "sha512-JGG8pvDi2C+JxidYdIwQDyS/CgcrIdh18cvgxcBge3wSHRQOrooMD3GlFBcmMJAN9M42SAZjDp5zv1dglJjwww==", + "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "locate-path": "^8.0.0", + "unicorn-magic": "^0.3.0" }, "engines": { - "node": ">=10" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -148,9 +164,10 @@ } }, "node_modules/js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -160,9 +177,10 @@ } }, "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" }, "node_modules/load-yaml-file": { "version": "0.2.0", @@ -179,14 +197,15 @@ } }, "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-8.0.0.tgz", + "integrity": "sha512-XT9ewWAC43tiAV7xDAPflMkG0qOPn2QjHqlgX8FOqmWa/rxnyYDulF9T0F7tRy1u+TVTmK/M//6VIOye+2zDXg==", + "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "p-locate": "^6.0.0" }, "engines": { - "node": ">=10" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -206,52 +225,50 @@ } }, "node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=10" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "yocto-queue": "^1.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "p-limit": "^4.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -273,9 +290,10 @@ } }, "node_modules/request-light": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.7.0.tgz", - "integrity": "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==" + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.8.0.tgz", + "integrity": "sha512-bH6E4PMmsEXYrLX6Kr1vu+xI3HproB1vECAwaPSJeroLE1kpWE3HR27uB4icx+6YORu1ajqBJXxuedv8ZQg5Lw==", + "license": "MIT" }, "node_modules/sprintf-js": { "version": "1.0.3", @@ -302,20 +320,35 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" }, "node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", + "license": "ISC", "dependencies": { "isexe": "^3.1.1" }, @@ -323,27 +356,28 @@ "node-which": "bin/which.js" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/which-pm": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/which-pm/-/which-pm-2.1.1.tgz", - "integrity": "sha512-xzzxNw2wMaoCWXiGE8IJ9wuPMU+EYhFksjHxrRT8kMT5SnocBPRg69YAMtyV4D12fP582RA+k3P8H9J5EMdIxQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which-pm/-/which-pm-3.0.1.tgz", + "integrity": "sha512-v2JrMq0waAI4ju1xU5x3blsxBBMgdgZve580iYMN5frDaLGjbA24fok7wKCsya8KLVO19Ju4XDc5+zTZCJkQfg==", + "license": "MIT", "dependencies": { - "load-yaml-file": "^0.2.0", - "path-exists": "^4.0.0" + "load-yaml-file": "^0.2.0" }, "engines": { - "node": ">=8.15" + "node": ">=18.12" } }, "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" diff --git a/extensions/npm/package.json b/extensions/npm/package.json index 545ce102ab57..e4d1b7aa7a7b 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -21,19 +21,19 @@ "watch": "npx gulp watch-extension:npm" }, "dependencies": { - "find-up": "^5.0.0", + "find-up": "^8.0.0", "find-yarn-workspace-root": "^2.0.0", - "jsonc-parser": "^3.2.0", - "minimatch": "^5.1.6", - "request-light": "^0.7.0", - "which": "^4.0.0", - "which-pm": "^2.1.1", - "vscode-uri": "^3.0.8" + "jsonc-parser": "^3.3.1", + "minimatch": "^10.1.1", + "request-light": "^0.8.0", + "which": "^6.0.0", + "which-pm": "^3.0.1", + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/minimatch": "^5.1.2", - "@types/node": "20.x", - "@types/which": "^3.0.0" + "@types/minimatch": "^6.0.0", + "@types/node": "25.x", + "@types/which": "^3.0.4" }, "main": "./out/npmMain", "browser": "./dist/browser/npmBrowserMain", @@ -251,6 +251,28 @@ "default": "auto", "description": "%config.npm.packageManager%" }, + "npm.scriptRunner": { + "scope": "resource", + "type": "string", + "enum": [ + "auto", + "npm", + "yarn", + "pnpm", + "bun", + "node" + ], + "enumDescriptions": [ + "%config.npm.scriptRunner.auto%", + "%config.npm.scriptRunner.npm%", + "%config.npm.scriptRunner.yarn%", + "%config.npm.scriptRunner.pnpm%", + "%config.npm.scriptRunner.bun%", + "%config.npm.scriptRunner.node%" + ], + "default": "auto", + "description": "%config.npm.scriptRunner%" + }, "npm.exclude": { "type": [ "string", @@ -341,18 +363,18 @@ } ], "terminalQuickFixes": [ - { - "id": "ms-vscode.npm-command", - "commandLineMatcher": "npm", - "commandExitResult": "error", - "outputMatcher": { - "anchor": "bottom", - "length": 8, - "lineMatcher": "Did you mean (?:this|one of these)\\?((?:\\n.+?npm .+ #.+)+)", - "offset": 2 - } + { + "id": "ms-vscode.npm-command", + "commandLineMatcher": "npm", + "commandExitResult": "error", + "outputMatcher": { + "anchor": "bottom", + "length": 8, + "lineMatcher": "Did you mean (?:this|one of these)\\?((?:\\n.+?npm .+ #.+)+)", + "offset": 2 } - ] + } + ] }, "repository": { "type": "git", diff --git a/extensions/npm/package.nls.json b/extensions/npm/package.nls.json index ff8581bde179..56a77ff4f822 100644 --- a/extensions/npm/package.nls.json +++ b/extensions/npm/package.nls.json @@ -5,12 +5,19 @@ "virtualWorkspaces": "Functionality that requires running the 'npm' command is not available in virtual workspaces.", "config.npm.autoDetect": "Controls whether npm scripts should be automatically detected.", "config.npm.runSilent": "Run npm commands with the `--silent` option.", - "config.npm.packageManager": "The package manager used to run scripts.", - "config.npm.packageManager.npm": "Use npm as the package manager for running scripts.", - "config.npm.packageManager.yarn": "Use yarn as the package manager for running scripts.", - "config.npm.packageManager.pnpm": "Use pnpm as the package manager for running scripts.", - "config.npm.packageManager.bun": "Use bun as the package manager for running scripts.", - "config.npm.packageManager.auto": "Auto-detect which package manager to use for running scripts based on lock files and installed package managers.", + "config.npm.packageManager": "The package manager used to install dependencies.", + "config.npm.packageManager.npm": "Use npm as the package manager.", + "config.npm.packageManager.yarn": "Use yarn as the package manager.", + "config.npm.packageManager.pnpm": "Use pnpm as the package manager.", + "config.npm.packageManager.bun": "Use bun as the package manager.", + "config.npm.packageManager.auto": "Auto-detect which package manager to use based on lock files and installed package managers.", + "config.npm.scriptRunner": "The script runner used to run scripts.", + "config.npm.scriptRunner.npm": "Use npm as the script runner.", + "config.npm.scriptRunner.yarn": "Use yarn as the script runner.", + "config.npm.scriptRunner.pnpm": "Use pnpm as the script runner.", + "config.npm.scriptRunner.bun": "Use bun as the script runner.", + "config.npm.scriptRunner.node": "Use Node.js as the script runner.", + "config.npm.scriptRunner.auto": "Auto-detect which script runner to use based on lock files and installed package managers.", "config.npm.exclude": "Configure glob patterns for folders that should be excluded from automatic script detection.", "config.npm.enableScriptExplorer": "Enable an explorer view for npm scripts when there is no top-level 'package.json' file.", "config.npm.scriptExplorerAction": "The default click action used in the NPM Scripts Explorer: `open` or `run`, the default is `open`.", diff --git a/extensions/npm/src/npmMain.ts b/extensions/npm/src/npmMain.ts index 60758c8cb642..c37056fc4a65 100644 --- a/extensions/npm/src/npmMain.ts +++ b/extensions/npm/src/npmMain.ts @@ -8,7 +8,7 @@ import * as vscode from 'vscode'; import { addJSONProviders } from './features/jsonContributions'; import { runSelectedScript, selectAndRunScriptFromFolder } from './commands'; import { NpmScriptsTreeDataProvider } from './npmView'; -import { getPackageManager, invalidateTasksCache, NpmTaskProvider, hasPackageJson } from './tasks'; +import { getScriptRunner, getPackageManager, invalidateTasksCache, NpmTaskProvider, hasPackageJson } from './tasks'; import { invalidateHoverScriptsCache, NpmScriptHoverProvider } from './scriptHover'; import { NpmScriptLensProvider } from './npmScriptLens'; import which from 'which'; @@ -38,7 +38,7 @@ export async function activate(context: vscode.ExtensionContext): Promise treeDataProvider = registerExplorer(context); context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => { - if (e.affectsConfiguration('npm.exclude') || e.affectsConfiguration('npm.autoDetect') || e.affectsConfiguration('npm.scriptExplorerExclude')) { + if (e.affectsConfiguration('npm.exclude') || e.affectsConfiguration('npm.autoDetect') || e.affectsConfiguration('npm.scriptExplorerExclude') || e.affectsConfiguration('npm.runSilent') || e.affectsConfiguration('npm.packageManager') || e.affectsConfiguration('npm.scriptRunner')) { invalidateTasksCache(); if (treeDataProvider) { treeDataProvider.refresh(); @@ -63,9 +63,15 @@ export async function activate(context: vscode.ExtensionContext): Promise context.subscriptions.push(vscode.commands.registerCommand('npm.refresh', () => { invalidateScriptCaches(); })); + context.subscriptions.push(vscode.commands.registerCommand('npm.scriptRunner', (args) => { + if (args instanceof vscode.Uri) { + return getScriptRunner(args, context, true); + } + return ''; + })); context.subscriptions.push(vscode.commands.registerCommand('npm.packageManager', (args) => { if (args instanceof vscode.Uri) { - return getPackageManager(context, args); + return getPackageManager(args, context, true); } return ''; })); diff --git a/extensions/npm/src/npmScriptLens.ts b/extensions/npm/src/npmScriptLens.ts index c8e506904f88..84dfeca86aa0 100644 --- a/extensions/npm/src/npmScriptLens.ts +++ b/extensions/npm/src/npmScriptLens.ts @@ -15,8 +15,8 @@ import { workspace, l10n } from 'vscode'; -import { findPreferredPM } from './preferred-pm'; import { readScripts } from './readScripts'; +import { getRunScriptCommand } from './tasks'; const enum Constants { @@ -87,18 +87,20 @@ export class NpmScriptLensProvider implements CodeLensProvider, Disposable { } if (this.lensLocation === 'all') { - const packageManager = await findPreferredPM(Uri.joinPath(document.uri, '..').fsPath); - return tokens.scripts.map( - ({ name, nameRange }) => - new CodeLens( + const folder = Uri.joinPath(document.uri, '..'); + return Promise.all(tokens.scripts.map( + async ({ name, nameRange }) => { + const runScriptCommand = await getRunScriptCommand(name, folder); + return new CodeLens( nameRange, { title, command: 'extension.js-debug.createDebuggerTerminal', - arguments: [`${packageManager.name} run ${name}`, workspace.getWorkspaceFolder(document.uri), { cwd }], + arguments: [runScriptCommand.join(' '), workspace.getWorkspaceFolder(document.uri), { cwd }], }, - ), - ); + ); + }, + )); } return []; diff --git a/extensions/npm/src/npmView.ts b/extensions/npm/src/npmView.ts index e041b43f0919..027d7f60e548 100644 --- a/extensions/npm/src/npmView.ts +++ b/extensions/npm/src/npmView.ts @@ -13,9 +13,10 @@ import { } from 'vscode'; import { readScripts } from './readScripts'; import { - createTask, getPackageManager, getTaskName, isAutoDetectionEnabled, isWorkspaceFolder, INpmTaskDefinition, + createInstallationTask, getTaskName, isAutoDetectionEnabled, isWorkspaceFolder, INpmTaskDefinition, NpmTaskProvider, startDebugging, + detectPackageManager, ITaskWithLocation, INSTALL_SCRIPT } from './tasks'; @@ -150,8 +151,8 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { } private async runScript(script: NpmScript) { - // Call getPackageManager to trigger the multiple lock files warning. - await getPackageManager(this.context, script.getFolder().uri); + // Call detectPackageManager to trigger the multiple lock files warning. + await detectPackageManager(script.getFolder().uri, this.context, true); tasks.executeTask(script.task); } @@ -181,7 +182,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider { if (!uri) { return; } - const task = await createTask(await getPackageManager(this.context, selection.folder.workspaceFolder.uri, true), 'install', ['install'], selection.folder.workspaceFolder, uri, undefined, []); + const task = await createInstallationTask(this.context, selection.folder.workspaceFolder, uri); tasks.executeTask(task); } diff --git a/extensions/npm/src/preferred-pm.ts b/extensions/npm/src/preferred-pm.ts index c85b65b6ea35..452319671eaf 100644 --- a/extensions/npm/src/preferred-pm.ts +++ b/extensions/npm/src/preferred-pm.ts @@ -28,6 +28,10 @@ async function isBunPreferred(pkgPath: string): Promise { return { isPreferred: true, hasLockfile: true }; } + if (await pathExists(path.join(pkgPath, 'bun.lock'))) { + return { isPreferred: true, hasLockfile: true }; + } + return { isPreferred: false, hasLockfile: false }; } diff --git a/extensions/npm/src/scriptHover.ts b/extensions/npm/src/scriptHover.ts index b3a87e051982..33f2346e8158 100644 --- a/extensions/npm/src/scriptHover.ts +++ b/extensions/npm/src/scriptHover.ts @@ -12,8 +12,8 @@ import { } from 'vscode'; import { INpmScriptInfo, readScripts } from './readScripts'; import { - createTask, - getPackageManager, startDebugging + createScriptRunnerTask, + startDebugging } from './tasks'; @@ -114,7 +114,7 @@ export class NpmScriptHoverProvider implements HoverProvider { const documentUri = args.documentUri; const folder = workspace.getWorkspaceFolder(documentUri); if (folder) { - const task = await createTask(await getPackageManager(this.context, folder.uri), script, ['run', script], folder, documentUri); + const task = await createScriptRunnerTask(this.context, script, folder, documentUri); await tasks.executeTask(task); } } diff --git a/extensions/npm/src/tasks.ts b/extensions/npm/src/tasks.ts index 6f1a1fbe649c..19a45488c077 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -71,11 +71,16 @@ export class NpmTaskProvider implements TaskProvider { } else { packageJsonUri = _task.scope.uri.with({ path: _task.scope.uri.path + '/package.json' }); } - const cmd = [kind.script]; - if (kind.script !== INSTALL_SCRIPT) { - cmd.unshift('run'); + let task: Task; + if (kind.script === INSTALL_SCRIPT) { + task = await createInstallationTask(this.context, _task.scope, packageJsonUri); + } else { + task = await createScriptRunnerTask(this.context, kind.script, _task.scope, packageJsonUri); } - return createTask(await getPackageManager(this.context, _task.scope.uri), kind, cmd, _task.scope, packageJsonUri); + // VSCode requires that task.definition must not change between resolutions + // We need to restore task.definition to its original value + task.definition = kind; + return task; } return undefined; } @@ -104,49 +109,60 @@ function isTestTask(name: string): boolean { } return false; } +const preScripts: Set = new Set([ + 'install', 'pack', 'pack', 'publish', 'restart', 'shrinkwrap', + 'stop', 'test', 'uninstall', 'version' +]); + +const postScripts: Set = new Set([ + 'install', 'pack', 'pack', 'publish', 'publishOnly', 'restart', 'shrinkwrap', + 'stop', 'test', 'uninstall', 'version' +]); + +function canHavePrePostScript(name: string): boolean { + return preScripts.has(name) || postScripts.has(name); +} -function isPrePostScript(name: string): boolean { - const prePostScripts: Set = new Set([ - 'preuninstall', 'postuninstall', 'prepack', 'postpack', 'preinstall', 'postinstall', - 'prepack', 'postpack', 'prepublish', 'postpublish', 'preversion', 'postversion', - 'prestop', 'poststop', 'prerestart', 'postrestart', 'preshrinkwrap', 'postshrinkwrap', - 'pretest', 'postest', 'prepublishOnly' - ]); +export function isWorkspaceFolder(value: any): value is WorkspaceFolder { + return value && typeof value !== 'number'; +} - const prepost = ['pre' + name, 'post' + name]; - for (const knownScript of prePostScripts) { - if (knownScript === prepost[0] || knownScript === prepost[1]) { - return true; - } +export async function getScriptRunner(folder: Uri, context?: ExtensionContext, showWarning?: boolean): Promise { + let scriptRunner = workspace.getConfiguration('npm', folder).get('scriptRunner', 'npm'); + + if (scriptRunner === 'auto') { + scriptRunner = await detectPackageManager(folder, context, showWarning); } - return false; + + return scriptRunner; } -export function isWorkspaceFolder(value: any): value is WorkspaceFolder { - return value && typeof value !== 'number'; +export async function getPackageManager(folder: Uri, context?: ExtensionContext, showWarning?: boolean): Promise { + let packageManager = workspace.getConfiguration('npm', folder).get('packageManager', 'npm'); + + if (packageManager === 'auto') { + packageManager = await detectPackageManager(folder, context, showWarning); + } + + return packageManager; } -export async function getPackageManager(extensionContext: ExtensionContext, folder: Uri, showWarning: boolean = true): Promise { - let packageManagerName = workspace.getConfiguration('npm', folder).get('packageManager', 'npm'); - - if (packageManagerName === 'auto') { - const { name, multipleLockFilesDetected: multiplePMDetected } = await findPreferredPM(folder.fsPath); - packageManagerName = name; - const neverShowWarning = 'npm.multiplePMWarning.neverShow'; - if (showWarning && multiplePMDetected && !extensionContext.globalState.get(neverShowWarning)) { - const multiplePMWarning = l10n.t('Using {0} as the preferred package manager. Found multiple lockfiles for {1}. To resolve this issue, delete the lockfiles that don\'t match your preferred package manager or change the setting "npm.packageManager" to a value other than "auto".', packageManagerName, folder.fsPath); - const neverShowAgain = l10n.t("Do not show again"); - const learnMore = l10n.t("Learn more"); - window.showInformationMessage(multiplePMWarning, learnMore, neverShowAgain).then(result => { - switch (result) { - case neverShowAgain: extensionContext.globalState.update(neverShowWarning, true); break; - case learnMore: env.openExternal(Uri.parse('https://docs.npmjs.com/cli/v9/configuring-npm/package-lock-json')); - } - }); - } +export async function detectPackageManager(folder: Uri, extensionContext?: ExtensionContext, showWarning: boolean = false): Promise { + const { name, multipleLockFilesDetected: multiplePMDetected } = await findPreferredPM(folder.fsPath); + const neverShowWarning = 'npm.multiplePMWarning.neverShow'; + if (showWarning && multiplePMDetected && extensionContext && !extensionContext.globalState.get(neverShowWarning)) { + const multiplePMWarning = l10n.t('Using {0} as the preferred package manager. Found multiple lockfiles for {1}. To resolve this issue, delete the lockfiles that don\'t match your preferred package manager or change the setting "npm.packageManager" to a value other than "auto".', name, folder.fsPath); + const neverShowAgain = l10n.t("Do not show again"); + const learnMore = l10n.t("Learn more"); + window.showInformationMessage(multiplePMWarning, learnMore, neverShowAgain).then(result => { + switch (result) { + case neverShowAgain: extensionContext.globalState.update(neverShowWarning, true); break; + case learnMore: env.openExternal(Uri.parse('https://docs.npmjs.com/cli/v9/configuring-npm/package-lock-json')); + } + }); } - return packageManagerName; + return name; } export async function hasNpmScripts(): Promise { @@ -154,49 +170,37 @@ export async function hasNpmScripts(): Promise { if (!folders) { return false; } - try { - for (const folder of folders) { - if (isAutoDetectionEnabled(folder) && !excludeRegex.test(Utils.basename(folder.uri))) { - const relativePattern = new RelativePattern(folder, '**/package.json'); - const paths = await workspace.findFiles(relativePattern, '**/node_modules/**'); - if (paths.length > 0) { - return true; - } + for (const folder of folders) { + if (isAutoDetectionEnabled(folder) && !excludeRegex.test(Utils.basename(folder.uri))) { + const relativePattern = new RelativePattern(folder, '**/package.json'); + const paths = await workspace.findFiles(relativePattern, '**/node_modules/**'); + if (paths.length > 0) { + return true; } } - return false; - } catch (error) { - return Promise.reject(error); } + return false; } -async function detectNpmScripts(context: ExtensionContext, showWarning: boolean): Promise { +async function* findNpmPackages(): AsyncGenerator { - const emptyTasks: ITaskWithLocation[] = []; - const allTasks: ITaskWithLocation[] = []; const visitedPackageJsonFiles: Set = new Set(); const folders = workspace.workspaceFolders; if (!folders) { - return emptyTasks; + return; } - try { - for (const folder of folders) { - if (isAutoDetectionEnabled(folder) && !excludeRegex.test(Utils.basename(folder.uri))) { - const relativePattern = new RelativePattern(folder, '**/package.json'); - const paths = await workspace.findFiles(relativePattern, '**/{node_modules,.vscode-test}/**'); - for (const path of paths) { - if (!isExcluded(folder, path) && !visitedPackageJsonFiles.has(path.fsPath)) { - const tasks = await provideNpmScriptsForFolder(context, path, showWarning); - visitedPackageJsonFiles.add(path.fsPath); - allTasks.push(...tasks); - } + for (const folder of folders) { + if (isAutoDetectionEnabled(folder) && !excludeRegex.test(Utils.basename(folder.uri))) { + const relativePattern = new RelativePattern(folder, '**/package.json'); + const paths = await workspace.findFiles(relativePattern, '**/{node_modules,.vscode-test}/**'); + for (const path of paths) { + if (!isExcluded(folder, path) && !visitedPackageJsonFiles.has(path.fsPath)) { + yield path; + visitedPackageJsonFiles.add(path.fsPath); } } } - return allTasks; - } catch (error) { - return Promise.reject(error); } } @@ -205,30 +209,31 @@ export async function detectNpmScriptsForFolder(context: ExtensionContext, folde const folderTasks: IFolderTaskItem[] = []; - try { - if (excludeRegex.test(Utils.basename(folder))) { - return folderTasks; - } - const relativePattern = new RelativePattern(folder.fsPath, '**/package.json'); - const paths = await workspace.findFiles(relativePattern, '**/node_modules/**'); - - const visitedPackageJsonFiles: Set = new Set(); - for (const path of paths) { - if (!visitedPackageJsonFiles.has(path.fsPath)) { - const tasks = await provideNpmScriptsForFolder(context, path, true); - visitedPackageJsonFiles.add(path.fsPath); - folderTasks.push(...tasks.map(t => ({ label: t.task.name, task: t.task }))); - } - } + if (excludeRegex.test(Utils.basename(folder))) { return folderTasks; - } catch (error) { - return Promise.reject(error); } + const relativePattern = new RelativePattern(folder.fsPath, '**/package.json'); + const paths = await workspace.findFiles(relativePattern, '**/node_modules/**'); + + const visitedPackageJsonFiles: Set = new Set(); + for (const path of paths) { + if (!visitedPackageJsonFiles.has(path.fsPath)) { + const tasks = await provideNpmScriptsForFolder(context, path, true); + visitedPackageJsonFiles.add(path.fsPath); + folderTasks.push(...tasks.map(t => ({ label: t.task.name, task: t.task }))); + } + } + return folderTasks; } export async function provideNpmScripts(context: ExtensionContext, showWarning: boolean): Promise { if (!cachedTasks) { - cachedTasks = await detectNpmScripts(context, showWarning); + const allTasks: ITaskWithLocation[] = []; + for await (const path of findNpmPackages()) { + const tasks = await provideNpmScriptsForFolder(context, path, showWarning); + allTasks.push(...tasks); + } + cachedTasks = allTasks; } return cachedTasks; } @@ -278,15 +283,13 @@ async function provideNpmScriptsForFolder(context: ExtensionContext, packageJson const result: ITaskWithLocation[] = []; - const packageManager = await getPackageManager(context, folder.uri, showWarning); - for (const { name, value, nameRange } of scripts.scripts) { - const task = await createTask(packageManager, name, ['run', name], folder!, packageJsonUri, value, undefined); + const task = await createScriptRunnerTask(context, name, folder!, packageJsonUri, value, showWarning); result.push({ task, location: new Location(packageJsonUri, nameRange) }); } if (!workspace.getConfiguration('npm', folder).get('scriptExplorerExclude', []).find(e => e.includes(INSTALL_SCRIPT))) { - result.push({ task: await createTask(packageManager, INSTALL_SCRIPT, [INSTALL_SCRIPT], folder, packageJsonUri, 'install dependencies from package', []) }); + result.push({ task: await createInstallationTask(context, folder, packageJsonUri, 'install dependencies from package', showWarning) }); } return result; } @@ -298,50 +301,56 @@ export function getTaskName(script: string, relativePath: string | undefined) { return script; } -export async function createTask(packageManager: string, script: INpmTaskDefinition | string, cmd: string[], folder: WorkspaceFolder, packageJsonUri: Uri, scriptValue?: string, matcher?: any): Promise { - let kind: INpmTaskDefinition; - if (typeof script === 'string') { - kind = { type: 'npm', script: script }; - } else { - kind = script; - } - - function getCommandLine(cmd: string[]): (string | ShellQuotedString)[] { - const result: (string | ShellQuotedString)[] = new Array(cmd.length); - for (let i = 0; i < cmd.length; i++) { - if (/\s/.test(cmd[i])) { - result[i] = { value: cmd[i], quoting: cmd[i].includes('--') ? ShellQuoting.Weak : ShellQuoting.Strong }; - } else { - result[i] = cmd[i]; - } +function escapeCommandLine(cmd: string[]): (string | ShellQuotedString)[] { + return cmd.map(arg => { + if (/\s/.test(arg)) { + return { value: arg, quoting: arg.includes('--') ? ShellQuoting.Weak : ShellQuoting.Strong }; + } else { + return arg; } - if (workspace.getConfiguration('npm', folder.uri).get('runSilent')) { - result.unshift('--silent'); + }); +} + +function getRelativePath(rootUri: Uri, packageJsonUri: Uri): string { + const absolutePath = packageJsonUri.path.substring(0, packageJsonUri.path.length - 'package.json'.length); + return absolutePath.substring(rootUri.path.length + 1); +} + +export async function getRunScriptCommand(script: string, folder: Uri, context?: ExtensionContext, showWarning = true): Promise { + const scriptRunner = await getScriptRunner(folder, context, showWarning); + + if (scriptRunner === 'node') { + return ['node', '--run', script]; + } else { + const result = [scriptRunner, 'run']; + if (workspace.getConfiguration('npm', folder).get('runSilent')) { + result.push('--silent'); } + result.push(script); return result; } +} - function getRelativePath(packageJsonUri: Uri): string { - const rootUri = folder.uri; - const absolutePath = packageJsonUri.path.substring(0, packageJsonUri.path.length - 'package.json'.length); - return absolutePath.substring(rootUri.path.length + 1); - } +export async function createScriptRunnerTask(context: ExtensionContext, script: string, folder: WorkspaceFolder, packageJsonUri: Uri, scriptValue?: string, showWarning?: boolean): Promise { + const kind: INpmTaskDefinition = { type: 'npm', script }; - const relativePackageJson = getRelativePath(packageJsonUri); + const relativePackageJson = getRelativePath(folder.uri, packageJsonUri); if (relativePackageJson.length && !kind.path) { kind.path = relativePackageJson.substring(0, relativePackageJson.length - 1); } - const taskName = getTaskName(kind.script, relativePackageJson); + const taskName = getTaskName(script, relativePackageJson); const cwd = path.dirname(packageJsonUri.fsPath); - const task = new Task(kind, folder, taskName, 'npm', new ShellExecution(packageManager, getCommandLine(cmd), { cwd: cwd }), matcher); + const args = await getRunScriptCommand(script, folder.uri, context, showWarning); + const scriptRunner = args.shift()!; + const task = new Task(kind, folder, taskName, 'npm', new ShellExecution(scriptRunner, escapeCommandLine(args), { cwd: cwd })); task.detail = scriptValue; - const lowerCaseTaskName = kind.script.toLowerCase(); + const lowerCaseTaskName = script.toLowerCase(); if (isBuildTask(lowerCaseTaskName)) { task.group = TaskGroup.Build; } else if (isTestTask(lowerCaseTaskName)) { task.group = TaskGroup.Test; - } else if (isPrePostScript(lowerCaseTaskName)) { + } else if (canHavePrePostScript(lowerCaseTaskName)) { task.group = TaskGroup.Clean; // hack: use Clean group to tag pre/post scripts } else if (scriptValue && isDebugScript(scriptValue)) { // todo@connor4312: all scripts are now debuggable, what is a 'debug script'? @@ -350,6 +359,33 @@ export async function createTask(packageManager: string, script: INpmTaskDefinit return task; } +async function getInstallDependenciesCommand(folder: Uri, context?: ExtensionContext, showWarning = true): Promise { + const packageManager = await getPackageManager(folder, context, showWarning); + const result = [packageManager, INSTALL_SCRIPT]; + if (workspace.getConfiguration('npm', folder).get('runSilent')) { + result.push('--silent'); + } + return result; +} + +export async function createInstallationTask(context: ExtensionContext, folder: WorkspaceFolder, packageJsonUri: Uri, scriptValue?: string, showWarning?: boolean): Promise { + const kind: INpmTaskDefinition = { type: 'npm', script: INSTALL_SCRIPT }; + + const relativePackageJson = getRelativePath(folder.uri, packageJsonUri); + if (relativePackageJson.length && !kind.path) { + kind.path = relativePackageJson.substring(0, relativePackageJson.length - 1); + } + const taskName = getTaskName(INSTALL_SCRIPT, relativePackageJson); + const cwd = path.dirname(packageJsonUri.fsPath); + const args = await getInstallDependenciesCommand(folder.uri, context, showWarning); + const packageManager = args.shift()!; + const task = new Task(kind, folder, taskName, 'npm', new ShellExecution(packageManager, escapeCommandLine(args), { cwd: cwd })); + task.detail = scriptValue; + task.group = TaskGroup.Clean; + + return task; +} + export function getPackageJsonUriFromTask(task: Task): Uri | null { if (isWorkspaceFolder(task.scope)) { @@ -403,15 +439,17 @@ export async function runScript(context: ExtensionContext, script: string, docum const uri = document.uri; const folder = workspace.getWorkspaceFolder(uri); if (folder) { - const task = await createTask(await getPackageManager(context, folder.uri), script, ['run', script], folder, uri); + const task = await createScriptRunnerTask(context, script, folder, uri); tasks.executeTask(task); } } export async function startDebugging(context: ExtensionContext, scriptName: string, cwd: string, folder: WorkspaceFolder) { + const runScriptCommand = await getRunScriptCommand(scriptName, folder.uri, context, true); + commands.executeCommand( 'extension.js-debug.createDebuggerTerminal', - `${await getPackageManager(context, folder.uri)} run ${scriptName}`, + runScriptCommand.join(' '), folder, { cwd }, ); diff --git a/extensions/objective-c/language-configuration.json b/extensions/objective-c/language-configuration.json index a81a8864a512..39e5fd4092c0 100644 --- a/extensions/objective-c/language-configuration.json +++ b/extensions/objective-c/language-configuration.json @@ -1,25 +1,88 @@ { "comments": { "lineComment": "//", - "blockComment": [ "/*", "*/" ] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - { "open": "\"", "close": "\"", "notIn": ["string"] }, - { "open": "'", "close": "'", "notIn": ["string"] } + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "'", + "'" + ] + ], + "onEnterRules": [ + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, ] } diff --git a/extensions/package-lock.json b/extensions/package-lock.json index aa07b93e52e8..f2978a2e5631 100644 --- a/extensions/package-lock.json +++ b/extensions/package-lock.json @@ -10,22 +10,23 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "typescript": "5.6.3" + "typescript": "^5.9.3" }, "devDependencies": { - "@parcel/watcher": "2.1.0", - "esbuild": "0.23.0", + "@parcel/watcher": "2.5.6", + "esbuild": "0.27.2", "vscode-grammar-updater": "^1.1.0" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", - "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" @@ -35,13 +36,14 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", - "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -51,13 +53,14 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", - "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -67,13 +70,14 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", - "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -83,13 +87,14 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", - "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -99,13 +104,14 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", - "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -115,13 +121,14 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", - "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -131,13 +138,14 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", - "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -147,13 +155,14 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", - "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -163,13 +172,14 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", - "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -179,13 +189,14 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", - "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -195,13 +206,14 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", - "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -211,13 +223,14 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", - "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -227,13 +240,14 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", - "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -243,13 +257,14 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", - "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -259,13 +274,14 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", - "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -275,13 +291,14 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", - "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -290,14 +307,32 @@ "node": ">=18" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", - "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -307,13 +342,14 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", - "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -323,13 +359,14 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", - "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -338,14 +375,32 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", - "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" @@ -355,13 +410,14 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", - "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -371,13 +427,14 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", - "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -387,13 +444,14 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", - "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -403,17 +461,54 @@ } }, "node_modules/@parcel/watcher": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.1.0.tgz", - "integrity": "sha512-8s8yYjd19pDSsBpbkOHnT6Z2+UJSuLQx61pCFM0s5wSRvKCEMDjd/cHY3/GI1szHIWbpXpsJdg3V6ISGGx9xDw==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { + "detect-libc": "^2.0.3", "is-glob": "^4.0.3", - "micromatch": "^4.0.5", - "node-addon-api": "^3.2.1", - "node-gyp-build": "^4.3.0" + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], "engines": { "node": ">= 10.0.0" }, @@ -422,16 +517,256 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "fill-range": "^7.1.1" + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=8" + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/coffeescript": { @@ -459,12 +794,23 @@ "node": ">=10.13" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/esbuild": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", - "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -472,30 +818,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.0", - "@esbuild/android-arm": "0.23.0", - "@esbuild/android-arm64": "0.23.0", - "@esbuild/android-x64": "0.23.0", - "@esbuild/darwin-arm64": "0.23.0", - "@esbuild/darwin-x64": "0.23.0", - "@esbuild/freebsd-arm64": "0.23.0", - "@esbuild/freebsd-x64": "0.23.0", - "@esbuild/linux-arm": "0.23.0", - "@esbuild/linux-arm64": "0.23.0", - "@esbuild/linux-ia32": "0.23.0", - "@esbuild/linux-loong64": "0.23.0", - "@esbuild/linux-mips64el": "0.23.0", - "@esbuild/linux-ppc64": "0.23.0", - "@esbuild/linux-riscv64": "0.23.0", - "@esbuild/linux-s390x": "0.23.0", - "@esbuild/linux-x64": "0.23.0", - "@esbuild/netbsd-x64": "0.23.0", - "@esbuild/openbsd-arm64": "0.23.0", - "@esbuild/openbsd-x64": "0.23.0", - "@esbuild/sunos-x64": "0.23.0", - "@esbuild/win32-arm64": "0.23.0", - "@esbuild/win32-ia32": "0.23.0", - "@esbuild/win32-x64": "0.23.0" + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" } }, "node_modules/fast-plist": { @@ -504,18 +852,6 @@ "integrity": "sha1-pFr/NFGWAG1AbKbNzQX2kFHvNbg= sha512-2HxzrqJhmMoxVzARjYFvkzkL2dCBB8sogU5sD8gqcZWv5UCivK9/cXM9KIPDRwU+eD3mbRDN/GhW8bO/4dtMfg==", "dev": true }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -537,29 +873,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/node-addon-api": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz", @@ -570,45 +883,24 @@ "node": "^16 || ^18 || >= 20" } }, - "node_modules/node-gyp-build": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.1.tgz", - "integrity": "sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw==", - "dev": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/typescript": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", - "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/extensions/package.json b/extensions/package.json index a1a224947891..1a70ed9731dc 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -4,20 +4,17 @@ "license": "MIT", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "5.6.3" + "typescript": "^5.9.3" }, "scripts": { "postinstall": "node ./postinstall.mjs" }, "devDependencies": { - "@parcel/watcher": "2.1.0", - "esbuild": "0.23.0", + "@parcel/watcher": "2.5.6", + "esbuild": "0.27.2", "vscode-grammar-updater": "^1.1.0" }, "overrides": { - "node-gyp-build": "4.8.1", - "@parcel/watcher@2.1.0": { - "node-addon-api": "7.1.0" - } + "node-gyp-build": "4.8.1" } } diff --git a/extensions/perl/cgmanifest.json b/extensions/perl/cgmanifest.json index b7c850dd1aab..cd175abe37de 100644 --- a/extensions/perl/cgmanifest.json +++ b/extensions/perl/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "textmate/perl.tmbundle", "repositoryUrl": "https://github.com/textmate/perl.tmbundle", - "commitHash": "d9841a0878239fa43f88c640f8d458590f97e8f5" + "commitHash": "a85927a902d6e5d7805f56a653f324d34dfad53a" } }, "licenseDetail": [ diff --git a/extensions/php-language-features/package-lock.json b/extensions/php-language-features/package-lock.json index 21fdb6b8e9f6..b4db78806c17 100644 --- a/extensions/php-language-features/package-lock.json +++ b/extensions/php-language-features/package-lock.json @@ -9,54 +9,62 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "which": "^2.0.2" + "which": "^6.0.0" }, "devDependencies": { - "@types/node": "20.x", - "@types/which": "^2.0.0" + "@types/node": "25.x", + "@types/which": "^3.0.4" }, "engines": { "vscode": "0.10.x" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@types/which": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.0.tgz", - "integrity": "sha512-JHTNOEpZnACQdsTojWggn+SQ8IucfqEhtz7g8Z0G67WdSj4x3F0X5I2c/CVcl8z/QukGrIHeQ/N49v1au74XFQ==", - "dev": true + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.4.tgz", + "integrity": "sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w==", + "dev": true, + "license": "MIT" }, "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "license": "ISC", + "engines": { + "node": ">=16" + } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", + "license": "ISC", "dependencies": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" }, "bin": { - "node-which": "bin/node-which" + "node-which": "bin/which.js" }, "engines": { - "node": ">= 8" + "node": "^20.17.0 || >=22.9.0" } } } diff --git a/extensions/php-language-features/package.json b/extensions/php-language-features/package.json index 989213b6c0c0..0194aeda2a9c 100644 --- a/extensions/php-language-features/package.json +++ b/extensions/php-language-features/package.json @@ -74,11 +74,11 @@ "watch": "npx gulp watch-extension:php-language-features" }, "dependencies": { - "which": "^2.0.2" + "which": "^6.0.0" }, "devDependencies": { - "@types/node": "20.x", - "@types/which": "^2.0.0" + "@types/node": "25.x", + "@types/which": "^3.0.4" }, "repository": { "type": "git", diff --git a/extensions/php-language-features/src/features/phpGlobalFunctions.ts b/extensions/php-language-features/src/features/phpGlobalFunctions.ts index caf9eb11b711..ab1c5487ae86 100644 --- a/extensions/php-language-features/src/features/phpGlobalFunctions.ts +++ b/extensions/php-language-features/src/features/phpGlobalFunctions.ts @@ -1360,7 +1360,7 @@ export const globalfunctions: IEntries = { }, date_add: { description: 'Adds an amount of days, months, years, hours, minutes and seconds to a DateTime object', - signature: '( DateInterval $interval , DateTime $object ): DateTime' + signature: '( DateTime $object , DateInterval $interval ): DateTime' }, date_create: { description: 'Returns new DateTime object', @@ -1376,31 +1376,31 @@ export const globalfunctions: IEntries = { }, date_modify: { description: 'Alters the timestamp', - signature: '( string $modify , DateTime $object ): DateTime' + signature: '( DateTime $object , string $modify ): DateTime' }, date_date_set: { description: 'Sets the date', - signature: '( int $year , int $month , int $day , DateTime $object ): DateTime' + signature: '( DateTime $object , int $year , int $month , int $day ): DateTime' }, date_isodate_set: { description: 'Sets the ISO date', - signature: '( int $year , int $week [, int $day = 1 , DateTime $object ]): DateTime' + signature: '( DateTime $object , int $year , int $week [, int $day = 1 ]): DateTime' }, date_time_set: { description: 'Sets the time', - signature: '( int $hour , int $minute [, int $second = 0 [, int $microseconds = 0 , DateTime $object ]]): DateTime' + signature: '( DateTime $object , int $hour , int $minute [, int $second = 0 [, int $microseconds = 0 ]]): DateTime' }, date_timestamp_set: { description: 'Sets the date and time based on an Unix timestamp', - signature: '( int $unixtimestamp , DateTime $object ): DateTime' + signature: '( DateTime $object , int $unixtimestamp ): DateTime' }, date_timezone_set: { description: 'Sets the time zone for the DateTime object', - signature: '( DateTimeZone $timezone , DateTime $object ): object' + signature: '( DateTime $object , DateTimeZone $timezone ): object' }, date_sub: { description: 'Subtracts an amount of days, months, years, hours, minutes and seconds from a DateTime object', - signature: '( DateInterval $interval , DateTime $object ): DateTime' + signature: '( DateTime $object , DateInterval $interval ): DateTime' }, date_create_immutable: { description: 'Returns new DateTimeImmutable object', diff --git a/extensions/php/language-configuration.json b/extensions/php/language-configuration.json index f44d7a25cb61..d696ffa29503 100644 --- a/extensions/php/language-configuration.json +++ b/extensions/php/language-configuration.json @@ -1,28 +1,96 @@ { "comments": { "lineComment": "//", // "#" - "blockComment": [ "/*", "*/" ] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - { "open": "{", "close": "}", "notIn": ["string"] }, - { "open": "[", "close": "]", "notIn": ["string"] }, - { "open": "(", "close": ")", "notIn": ["string"] }, - { "open": "'", "close": "'", "notIn": ["string", "comment"] }, - { "open": "\"", "close": "\"", "notIn": ["string", "comment"] }, - { "open": "/**", "close": " */", "notIn": ["string"] } + { + "open": "{", + "close": "}", + "notIn": [ + "string" + ] + }, + { + "open": "[", + "close": "]", + "notIn": [ + "string" + ] + }, + { + "open": "(", + "close": ")", + "notIn": [ + "string" + ] + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "/**", + "close": " */", + "notIn": [ + "string" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["'", "'"], - ["\"", "\""], - ["`", "`"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "'", + "'" + ], + [ + "\"", + "\"" + ], + [ + "`", + "`" + ] ], "indentationRules": { "increaseIndentPattern": "({(?!.*}).*|\\(|\\[|((else(\\s)?)?if|else|for(each)?|while|switch|case).*:)\\s*((/[/*].*|)?$|\\?>)", @@ -85,6 +153,19 @@ "action": { "indent": "outdent" } - } + }, + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, ] } diff --git a/extensions/python/package.json b/extensions/python/package.json index 543268de7157..9ffffb90e067 100644 --- a/extensions/python/package.json +++ b/extensions/python/package.json @@ -49,7 +49,8 @@ ], "configurationDefaults": { "[python]": { - "diffEditor.ignoreTrimWhitespace": false + "diffEditor.ignoreTrimWhitespace": false, + "editor.defaultColorDecorators": "never" } } }, diff --git a/extensions/razor/cgmanifest.json b/extensions/razor/cgmanifest.json index b8b0e5dae4f7..188b960c4ece 100644 --- a/extensions/razor/cgmanifest.json +++ b/extensions/razor/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "dotnet/razor", "repositoryUrl": "https://github.com/dotnet/razor", - "commitHash": "39159764277f3c80a786d8872eba7730da3d7ef0" + "commitHash": "9b1e979b6c3fe7cfbe30f595b9b0994d20bd482c" } }, "license": "MIT", diff --git a/extensions/razor/syntaxes/cshtml.tmLanguage.json b/extensions/razor/syntaxes/cshtml.tmLanguage.json index 71055e66e102..27f953ea4620 100644 --- a/extensions/razor/syntaxes/cshtml.tmLanguage.json +++ b/extensions/razor/syntaxes/cshtml.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/dotnet/razor/commit/39159764277f3c80a786d8872eba7730da3d7ef0", + "version": "https://github.com/dotnet/razor/commit/9b1e979b6c3fe7cfbe30f595b9b0994d20bd482c", "name": "ASP.NET Razor", "scopeName": "text.html.cshtml", "injections": { @@ -1636,6 +1636,9 @@ { "include": "source.cs#switch-label" }, + { + "include": "#csharp-code-block" + }, { "include": "#razor-codeblock-body" } diff --git a/extensions/references-view/package-lock.json b/extensions/references-view/package-lock.json index bb5c5e10e1fe..448fb756e80b 100644 --- a/extensions/references-view/package-lock.json +++ b/extensions/references-view/package-lock.json @@ -9,26 +9,28 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.67.0" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/references-view/package.json b/extensions/references-view/package.json index 9566a965c760..97b98648c001 100644 --- a/extensions/references-view/package.json +++ b/extensions/references-view/package.json @@ -399,6 +399,6 @@ "watch": "npx gulp watch-extension:references-view" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" } } diff --git a/extensions/ruby/cgmanifest.json b/extensions/ruby/cgmanifest.json index 2cd9595e4e53..f54ddba7ce79 100644 --- a/extensions/ruby/cgmanifest.json +++ b/extensions/ruby/cgmanifest.json @@ -4,27 +4,56 @@ "component": { "type": "git", "git": { - "name": "textmate/ruby.tmbundle", - "repositoryUrl": "https://github.com/textmate/ruby.tmbundle", - "commitHash": "efcb8941c701343f1b2e9fb105c678152fea6892" + "name": "Shopify/ruby-lsp", + "repositoryUrl": "https://github.com/Shopify/ruby-lsp", + "commitHash": "958bb1aa0c7aa4b6119c947b69afa7f12b19dceb" } }, "licenseDetail": [ - "Copyright (c) textmate-ruby.tmbundle project authors", + "The MIT License (MIT)", "", - "If not otherwise specified (see below), files in this folder fall under the following license: ", + "Copyright (c) 2022-present, Shopify Inc.", "", - "Permission to copy, use, modify, sell and distribute this", - "software is granted. This software is provided \"as is\" without", - "express or implied warranty, and with no claim as to its", - "suitability for any purpose.", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", "", - "An exception is made for files in readable text which contain their own license information, ", - "or files where an accompanying file exists (in the same directory) with a \"-license\" suffix added ", - "to the base-name name of the original file, and an extension of txt, html, or similar. For example ", - "\"tidy\" is accompanied by \"tidy-license.txt\"." + "The above copyright notice and this permission notice shall be included in", + "all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN", + "THE SOFTWARE.", + "", + "================================================================================", + "The following files and related configuration in package.json are based on a", + "sequence of adaptions: grammars/ruby.cson.json, grammars/erb.cson.json,", + "languages/erb.json.", + "", + "Copyright (c) 2016 Peng Lv", + "Copyright (c) 2017-2019 Stafford Brunk", + "https://github.com/rubyide/vscode-ruby", + "", + " Released under the MIT license", + " https://github.com/rubyide/vscode-ruby/blob/main/LICENSE.txt", + "", + "Copyright (c) 2014 GitHub Inc.", + "https://github.com/atom/language-ruby", + "", + " Released under the MIT license", + " https://github.com/atom/language-ruby/blob/master/LICENSE.md", + "", + "https://github.com/textmate/ruby.tmbundle", + " https://github.com/textmate/ruby.tmbundle#license" ], - "license": "TextMate Bundle License", + "license": "MIT License", "version": "0.0.0" } ], diff --git a/extensions/ruby/package.json b/extensions/ruby/package.json index 70dd99f26b54..355e0cd58a9c 100644 --- a/extensions/ruby/package.json +++ b/extensions/ruby/package.json @@ -9,7 +9,7 @@ "vscode": "*" }, "scripts": { - "update-grammar": "node ../node_modules/vscode-grammar-updater/bin textmate/ruby.tmbundle Syntaxes/Ruby.plist ./syntaxes/ruby.tmLanguage.json" + "update-grammar": "node ../node_modules/vscode-grammar-updater/bin Shopify/ruby-lsp vscode/grammars/ruby.cson.json ./syntaxes/ruby.tmLanguage.json" }, "categories": ["Programming Languages"], "contributes": { @@ -66,7 +66,12 @@ "scopeName": "source.ruby", "path": "./syntaxes/ruby.tmLanguage.json" } - ] + ], + "configurationDefaults": { + "[ruby]": { + "editor.defaultColorDecorators": "never" + } + } }, "repository": { "type": "git", diff --git a/extensions/ruby/syntaxes/ruby.tmLanguage.json b/extensions/ruby/syntaxes/ruby.tmLanguage.json index 84d619cc51a4..785b0e4a9287 100644 --- a/extensions/ruby/syntaxes/ruby.tmLanguage.json +++ b/extensions/ruby/syntaxes/ruby.tmLanguage.json @@ -1,13 +1,12 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/textmate/ruby.tmbundle/blob/master/Syntaxes/Ruby.plist", + "This file has been converted from https://github.com/Shopify/ruby-lsp/blob/master/vscode/grammars/ruby.cson.json", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/textmate/ruby.tmbundle/commit/efcb8941c701343f1b2e9fb105c678152fea6892", + "version": "https://github.com/Shopify/ruby-lsp/commit/958bb1aa0c7aa4b6119c947b69afa7f12b19dceb", "name": "Ruby", "scopeName": "source.ruby", - "comment": "\n\tTODO: unresolved issues\n\n\ttext:\n\t\"p <>)=)", + "comment": "A local variable operation assignment (+=, -=, *=, /=)" + }, + { + "captures": { + "1": { + "name": "keyword.control.ruby" }, - "7": { - "name": "entity.other.inherited-class.module.third.ruby" + "3": { + "name": "variable.ruby" }, - "8": { - "name": "punctuation.separator.inheritance.ruby" + "5": { + "name": "keyword.operator.assignment.augmented.ruby" } }, - "match": "^\\s*(module)\\s+(([A-Z]\\w*(::))?([A-Z]\\w*(::))?([A-Z]\\w*(::))*[A-Z]\\w*)", - "name": "meta.module.ruby" + "match": "(?>)=)", + "comment": "A local variable operation assignment in a condition" }, { - "comment": "else if is a common mistake carried over from other languages. it works if you put in a second end, but it’s never what you want.", - "match": "(?])", + "comment": "A local variable assignment" }, { "captures": { "1": { - "name": "punctuation.definition.constant.ruby" + "name": "keyword.control.ruby" + }, + "3": { + "name": "variable.ruby" + } + }, + "match": "(?]", + "comment": "A local variable assignment in a condition" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.constant.hashkey.ruby" } }, "comment": "symbols as hash key (1.9 syntax)", "match": "(?>[a-zA-Z_]\\w*(?>[?!])?)(:)(?!:)", - "name": "constant.other.symbol.hashkey.ruby" + "name": "constant.language.symbol.hashkey.ruby" }, { "captures": { @@ -86,7 +158,7 @@ }, "comment": "symbols as hash key (1.8 syntax)", "match": "(?[a-zA-Z_]\\w*(?>[?!])?)(?=\\s*=>)", - "name": "constant.other.symbol.hashkey.ruby" + "name": "constant.language.symbol.hashkey.ruby" }, { "comment": "everything being a reserved word, not a value and needing a 'end' is a..", @@ -100,61 +172,42 @@ }, { "comment": "contextual smart pair support", - "match": "(?<=\\{)(\\s+)", + "match": "(?<={)(\\s+)", "name": "meta.syntax.ruby.start-block" }, { - "match": "(?|_|\\*|\\$|\\?|:|\"|-[0adFiIlpvw])", + "match": "(\\$)(!|@|&|`|'|\\+|\\d+|~|=|/|\\\\|,|;|\\.|<|>|_|\\*|\\$|\\?|:|\"|-[0adFiIlpv])", "name": "variable.other.readwrite.global.pre-defined.ruby" }, { @@ -205,7 +258,7 @@ "name": "variable.other.constant.ruby" } }, - "end": "\\]", + "end": "]", "name": "meta.environment-variable.ruby", "patterns": [ { @@ -218,15 +271,39 @@ "name": "support.class.ruby" }, { - "match": "\\b(abort|at_exit|autoload[?]?|binding|callcc|caller|caller_locations|chomp|chop|eval|exec|exit|exit!|fork|format|gets|global_variables|gsub|lambda|load|local_variables|open|p|print|printf|proc|putc|puts|rand|readline|readlines|select|set_trace_func|sleep|spawn|sprintf|srand|sub|syscall|system|test|trace_var|trap|untrace_var|warn)(\\b|(?<=[?!]))(?![?!])", + "match": "\\b((abort|at_exit|autoload|binding|callcc|caller|caller_locations|chomp|chop|eval|exec|exit|fork|format|gets|global_variables|gsub|lambda|load|local_variables|open|p|print|printf|proc|putc|puts|rand|readline|readlines|select|set_trace_func|sleep|spawn|sprintf|srand|sub|syscall|system|test|trace_var|trap|untrace_var|warn)\\b(?![?!])|autoload\\?|exit!)", "name": "support.function.kernel.ruby" }, { - "match": "\\b[A-Z]\\w*\\b", + "match": "\\b[_A-Z]\\w*\\b", "name": "variable.other.constant.ruby" }, { - "begin": "(?x)\n\t\t\t (?=def\\b) # an optimization to help Oniguruma fail fast\n\t\t\t (?<=^|\\s)(def)\\s+ # the def keyword\n\t\t\t ( (?>[a-zA-Z_]\\w*(?>\\.|::))? # a method name prefix\n\t\t\t (?>[a-zA-Z_]\\w*(?>[?!]|=(?!>))? # the method name\n\t\t\t |===?|!=|!~|>[>=]?|<=>|<[<=]?|[%&`/\\|^]|\\*\\*?|=?~|[-+]@?|\\[\\]=?) ) # …or an operator method\n\t\t\t \\s*(\\() # the openning parenthesis for arguments\n\t\t\t ", + "begin": "(->)\\(", + "beginCaptures": { + "1": { + "name": "support.function.kernel.ruby" + } + }, + "comment": "Lambda parameters.", + "end": "\\)", + "patterns": [ + { + "begin": "(?=[&*_a-zA-Z])", + "end": "(?=[,)])", + "patterns": [ + { + "include": "#method_parameters" + } + ] + }, + { + "include": "#method_parameters" + } + ] + }, + { + "begin": "(?x)\n(?=def\\b) # optimization to help Oniguruma fail fast\n(?<=^|\\s)(def)\\s+\n(\n (?>[a-zA-Z_]\\w*(?>\\.|::))? # method prefix\n (?> # method name\n [a-zA-Z_]\\w*(?>[?!]|=(?!>))?\n |\n ===?|!=|>[>=]?|<=>|<[<=]?|[%&`/\\|]|\\*\\*?|=?~|[-+]@?|\\[]=?\n )\n)\n\\s*(\\()", "beginCaptures": { "1": { "name": "keyword.control.def.ruby" @@ -238,7 +315,7 @@ "name": "punctuation.definition.parameters.ruby" } }, - "comment": "the method pattern comes from the symbol pattern, see there for a explaination", + "comment": "The method pattern comes from the symbol pattern. See there for an explanation.", "end": "\\)", "endCaptures": { "0": { @@ -252,89 +329,17 @@ "end": "(?=[,)])", "patterns": [ { - "captures": { - "1": { - "name": "storage.type.variable.ruby" - }, - "2": { - "name": "constant.other.symbol.hashkey.parameter.function.ruby" - }, - "3": { - "name": "punctuation.definition.constant.ruby" - }, - "4": { - "name": "variable.parameter.function.ruby" - } - }, - "match": "\\G([&*]?)(?:([_a-zA-Z]\\w*(:))|([_a-zA-Z]\\w*))" - }, - { - "include": "#parens" - }, - { - "include": "#braces" - }, - { - "include": "$self" - } - ] - } - ], - "repository": { - "braces": { - "begin": "\\{", - "beginCaptures": { - "0": { - "name": "punctuation.section.function.begin.ruby" - } - }, - "end": "\\}", - "endCaptures": { - "0": { - "name": "punctuation.section.function.end.ruby" - } - }, - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#braces" - }, - { - "include": "$self" + "include": "#method_parameters" } ] }, - "parens": { - "begin": "\\(", - "beginCaptures": { - "0": { - "name": "punctuation.section.function.begin.ruby" - } - }, - "end": "\\)", - "endCaptures": { - "0": { - "name": "punctuation.section.function.end.ruby" - } - }, - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#braces" - }, - { - "include": "$self" - } - ] + { + "include": "#method_parameters" } - } + ] }, { - "begin": "(?x)\n\t\t\t (?=def\\b) # an optimization to help Oniguruma fail fast\n\t\t\t (?<=^|\\s)(def)\\s+ # the def keyword\n\t\t\t ( (?>[a-zA-Z_]\\w*(?>\\.|::))? # a method name prefix\n\t\t\t (?>[a-zA-Z_]\\w*(?>[?!]|=(?!>))? # the method name\n\t\t\t |===?|!=|!~|>[>=]?|<=>|<[<=]?|[%&`/\\|^]|\\*\\*?|=?~|[-+]@?|\\[\\]=?) ) # …or an operator method\n\t\t\t [ \\t] # the space separating the arguments\n\t\t\t (?=[ \\t]*[^\\s#;]) # make sure arguments and not a comment follow\n\t\t\t ", + "begin": "(?x)\n(?=def\\b) # optimization to help Oniguruma fail fast\n(?<=^|\\s)(def)\\s+\n(\n (?>[a-zA-Z_]\\w*(?>\\.|::))? # method prefix\n (?> # method name\n [a-zA-Z_]\\w*(?>[?!]|=(?!>))?\n |\n ===?|!=|>[>=]?|<=>|<[<=]?|[%&`/\\|]|\\*\\*?|=?~|[-+]@?|\\[]=?\n )\n)\n[ \\t]\n(?=[ \\t]*[^\\s#;]) # make sure the following is not comment", "beginCaptures": { "1": { "name": "keyword.control.def.ruby" @@ -344,35 +349,20 @@ } }, "comment": "same as the previous rule, but without parentheses around the arguments", - "end": "$", + "end": "(?=;)|(?<=[\\w\\])}`'\"!?])(?=\\s*#|\\s*$)", "name": "meta.function.method.with-arguments.ruby", "patterns": [ { - "begin": "(?![\\s,])", - "end": "(?=,|$)", + "begin": "(?=[&*_a-zA-Z])", + "end": "(?=,|;|\\s*#|\\s*$)", "patterns": [ { - "captures": { - "1": { - "name": "storage.type.variable.ruby" - }, - "2": { - "name": "constant.other.symbol.hashkey.parameter.function.ruby" - }, - "3": { - "name": "punctuation.definition.constant.ruby" - }, - "4": { - "name": "variable.parameter.function.ruby" - } - }, - "match": "\\G([&*]?)(?:([_a-zA-Z]\\w*(:))|([_a-zA-Z]\\w*))", - "name": "variable.parameter.function.ruby" - }, - { - "include": "$self" + "include": "#method_parameters" } ] + }, + { + "include": "#method_parameters" } ] }, @@ -386,38 +376,28 @@ } }, "comment": " the optional name is just to catch the def also without a method-name", - "match": "(?x)\n\t\t\t (?=def\\b) # an optimization to help Oniguruma fail fast\n\t\t\t (?<=^|\\s)(def)\\b # the def keyword\n\t\t\t ( \\s+ # an optional group of whitespace followed by…\n\t\t\t ( (?>[a-zA-Z_]\\w*(?>\\.|::))? # a method name prefix\n\t\t\t (?>[a-zA-Z_]\\w*(?>[?!]|=(?!>))? # the method name\n\t\t\t |===?|!=|!~|>[>=]?|<=>|<[<=]?|[%&`/\\|^]|\\*\\*?|=?~|[-+]@?|\\[\\]=?) ) )? # …or an operator method\n\t\t\t ", + "match": "(?x)\n(?=def\\b) # optimization to help Oniguruma fail fast\n(?<=^|\\s)(def)\\b\n(\n \\s+\n (\n (?>[a-zA-Z_]\\w*(?>\\.|::))? # method prefix\n (?> # method name\n [a-zA-Z_]\\w*(?>[?!]|=(?!>))?\n |\n ===?|!=|>[>=]?|<=>|<[<=]?|[%&`/\\|]|\\*\\*?|=?~|[-+]@?|\\[]=?\n )\n )\n)?", "name": "meta.function.method.without-arguments.ruby" }, { - "match": "\\b\\d(?>_?\\d)*(?=\\.\\d|[eE])(\\.\\d(?>_?\\d)*)?([eE][-+]?\\d(?>_?\\d)*)?r?i?\\b", - "name": "constant.numeric.float.ruby" - }, - { - "match": "\\b(0|(0[dD]\\d|[1-9])(?>_?\\d)*)r?i?\\b", - "name": "constant.numeric.integer.ruby" - }, - { - "match": "\\b0[xX]\\h(?>_?\\h)*r?i?\\b", - "name": "constant.numeric.hex.ruby" - }, - { - "match": "\\b0[bB][01](?>_?[01])*r?i?\\b", - "name": "constant.numeric.binary.ruby" - }, - { - "match": "\\b0([oO]?[0-7](?>_?[0-7])*)?r?i?\\b", - "name": "constant.numeric.octal.ruby" + "match": "(?x)\n\\b\n(\n [\\d](?>_?\\d)* # 100_000\n (\\.(?![^[:space:][:digit:]])(?>_?\\d)*)? # fractional part\n ([eE][-+]?\\d(?>_?\\d)*)? # 1.23e-4\n |\n 0\n (?:\n [xX]\\h(?>_?\\h)*|\n [oO]?[0-7](?>_?[0-7])*|\n [bB][01](?>_?[01])*|\n [dD]\\d(?>_?\\d)*\n ) # A base indicator can only be used with an integer\n)\\b", + "name": "constant.numeric.ruby" }, { "begin": ":'", - "captures": { + "beginCaptures": { "0": { - "name": "punctuation.definition.constant.ruby" + "name": "punctuation.definition.symbol.begin.ruby" } }, + "comment": "symbol literal with '' delimiter", "end": "'", - "name": "constant.other.symbol.single-quoted.ruby", + "endCaptures": { + "0": { + "name": "punctuation.definition.symbol.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", "patterns": [ { "match": "\\\\['\\\\]", @@ -427,13 +407,19 @@ }, { "begin": ":\"", - "captures": { + "beginCaptures": { "0": { - "name": "punctuation.definition.constant.ruby" + "name": "punctuation.section.symbol.begin.ruby" } }, + "comment": "symbol literal with \"\" delimiter", "end": "\"", - "name": "constant.other.symbol.double-quoted.ruby", + "endCaptures": { + "0": { + "name": "punctuation.section.symbol.end.ruby" + } + }, + "name": "constant.language.symbol.interpolated.ruby", "patterns": [ { "include": "#interpolated_ruby" @@ -455,7 +441,7 @@ "name": "punctuation.definition.string.begin.ruby" } }, - "comment": "single quoted string (does not allow interpolation)", + "comment": "string literal with '' delimiter", "end": "'", "endCaptures": { "0": { @@ -477,14 +463,14 @@ "name": "punctuation.definition.string.begin.ruby" } }, - "comment": "double quoted string (allows for interpolation)", + "comment": "string literal with interpolation and \"\" delimiter", "end": "\"", "endCaptures": { "0": { "name": "punctuation.definition.string.end.ruby" } }, - "name": "string.quoted.double.ruby", + "name": "string.quoted.double.interpolated.ruby", "patterns": [ { "include": "#interpolated_ruby" @@ -495,7 +481,7 @@ ] }, { - "begin": "`", + "begin": "(?~(?:\\[,|&;]\n\t\t\t | [\\s;]if\\s\t\t\t# keywords\n\t\t\t | [\\s;]elsif\\s\n\t\t\t | [\\s;]while\\s\n\t\t\t | [\\s;]unless\\s\n\t\t\t | [\\s;]when\\s\n\t\t\t | [\\s;]assert_match\\s\n\t\t\t | [\\s;]or\\s\t\t\t# boolean opperators\n\t\t\t | [\\s;]and\\s\n\t\t\t | [\\s;]not\\s\n\t\t\t | [\\s.]index\\s\t\t\t# methods\n\t\t\t | [\\s.]scan\\s\n\t\t\t | [\\s.]sub\\s\n\t\t\t | [\\s.]sub!\\s\n\t\t\t | [\\s.]gsub\\s\n\t\t\t | [\\s.]gsub!\\s\n\t\t\t | [\\s.]match\\s\n\t\t\t )\n\t\t\t | (?<= # or a look-behind with line anchor:\n\t\t\t ^when\\s # duplication necessary due to limits of regex\n\t\t\t | ^if\\s\n\t\t\t | ^elsif\\s\n\t\t\t | ^while\\s\n\t\t\t | ^unless\\s\n\t\t\t )\n\t\t\t )\n\t\t\t \\s*((/))(?![*+{}?])\n\t\t\t", + "begin": "(?x)\n(?|=>|==|=~|!~|!=|;|$|\n if|else|elsif|then|do|end|unless|while|until|or|and\n )\n |\n $\n)", "captures": { "1": { - "name": "string.regexp.classic.ruby" + "name": "string.regexp.interpolated.ruby" }, "2": { - "name": "punctuation.definition.string.ruby" + "name": "punctuation.section.regexp.ruby" } }, - "comment": "regular expressions (normal)\n\t\t\twe only start a regexp if the character before it (excluding whitespace)\n\t\t\tis what we think is before a regexp\n\t\t\t", - "contentName": "string.regexp.classic.ruby", + "comment": "regular expression literal with interpolation", + "contentName": "string.regexp.interpolated.ruby", "end": "((/[eimnosux]*))", "patterns": [ { @@ -541,2145 +524,2269 @@ ] }, { - "captures": { - "1": { - "name": "punctuation.definition.constant.ruby" + "begin": "%r{", + "beginCaptures": { + "0": { + "name": "punctuation.section.regexp.begin.ruby" } }, - "comment": "symbols", - "match": "(?[a-zA-Z_]\\w*(?>[?!]|=(?![>=]))?|===?|>[>=]?|<=>|<[<=]?|[%&`/\\|]|\\*\\*?|=?~|[-+]@?|\\[\\]=?|(@@?|\\$)[a-zA-Z_]\\w*)", - "name": "constant.other.symbol.ruby" - }, - { - "begin": "^=begin", - "captures": { + "end": "}[eimnosux]*", + "endCaptures": { "0": { - "name": "punctuation.definition.comment.ruby" + "name": "punctuation.section.regexp.end.ruby" } }, - "comment": "multiline comments", - "end": "^=end", - "name": "comment.block.documentation.ruby" - }, - { - "begin": "(^[ \\t]+)?(?=#)", + "name": "string.regexp.interpolated.ruby", + "patterns": [ + { + "include": "#regex_sub" + }, + { + "include": "#nest_curly_r" + } + ] + }, + { + "begin": "%r\\[", "beginCaptures": { - "1": { - "name": "punctuation.whitespace.comment.leading.ruby" + "0": { + "name": "punctuation.section.regexp.begin.ruby" } }, - "end": "(?!\\G)", + "end": "][eimnosux]*", + "endCaptures": { + "0": { + "name": "punctuation.section.regexp.end.ruby" + } + }, + "name": "string.regexp.interpolated.ruby", "patterns": [ { - "begin": "#", - "beginCaptures": { - "0": { - "name": "punctuation.definition.comment.ruby" - } - }, - "end": "\\n", - "name": "comment.line.number-sign.ruby" + "include": "#regex_sub" + }, + { + "include": "#nest_brackets_r" } ] }, { - "comment": "\n\t\t\tmatches questionmark-letters.\n\n\t\t\texamples (1st alternation = hex):\n\t\t\t?\\x1 ?\\x61\n\n\t\t\texamples (2nd alternation = octal):\n\t\t\t?\\0 ?\\07 ?\\017\n\n\t\t\texamples (3rd alternation = escaped):\n\t\t\t?\\n ?\\b\n\n\t\t\texamples (4th alternation = meta-ctrl):\n\t\t\t?\\C-a ?\\M-a ?\\C-\\M-\\C-\\M-a\n\n\t\t\texamples (4th alternation = normal):\n\t\t\t?a ?A ?0 \n\t\t\t?* ?\" ?( \n\t\t\t?. ?#\n\t\t\t\n\t\t\t\n\t\t\tthe negative lookbehind prevents against matching\n\t\t\tp(42.tainted?)\n\t\t\t", - "match": "(?<<[-~](\"?)((?:[_\\w]+_|)HTML)\\b\\1))", - "comment": "Heredoc with embedded html", - "end": "(?!\\G)", - "name": "meta.embedded.block.html", + "begin": "%r<", + "beginCaptures": { + "0": { + "name": "punctuation.section.regexp.begin.ruby" + } + }, + "end": ">[eimnosux]*", + "endCaptures": { + "0": { + "name": "punctuation.section.regexp.end.ruby" + } + }, + "name": "string.regexp.interpolated.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)HTML)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "text.html", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "text.html.basic" - }, - { - "include": "#escaped_char" - } - ] + "include": "#regex_sub" + }, + { + "include": "#nest_ltgt_r" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)XML)\\b\\1))", - "comment": "Heredoc with embedded xml", - "end": "(?!\\G)", - "name": "meta.embedded.block.xml", + "begin": "%r([^\\w])", + "beginCaptures": { + "0": { + "name": "punctuation.section.regexp.begin.ruby" + } + }, + "end": "\\1[eimnosux]*", + "endCaptures": { + "0": { + "name": "punctuation.section.regexp.end.ruby" + } + }, + "name": "string.regexp.interpolated.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)XML)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "text.xml", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "text.xml" - }, - { - "include": "#escaped_char" - } - ] + "include": "#regex_sub" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)SQL)\\b\\1))", - "comment": "Heredoc with embedded sql", - "end": "(?!\\G)", - "name": "meta.embedded.block.sql", + "begin": "%I\\[", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "]", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "constant.language.symbol.interpolated.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)SQL)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.sql", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.sql" - }, - { - "include": "#escaped_char" - } - ] + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_brackets_i" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)CSS)\\b\\1))", - "comment": "Heredoc with embedded css", - "end": "(?!\\G)", - "name": "meta.embedded.block.css", + "begin": "%I\\(", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "constant.language.symbol.interpolated.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)CSS)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.css", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.css" - }, - { - "include": "#escaped_char" - } - ] + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_parens_i" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)CPP)\\b\\1))", - "comment": "Heredoc with embedded c++", - "end": "(?!\\G)", - "name": "meta.embedded.block.c++", + "begin": "%I<", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "constant.language.symbol.interpolated.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)CPP)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.c++", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.c++" - }, - { - "include": "#escaped_char" - } - ] + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_ltgt_i" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)C)\\b\\1))", - "comment": "Heredoc with embedded c", - "end": "(?!\\G)", - "name": "meta.embedded.block.c", + "begin": "%I{", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "constant.language.symbol.interpolated.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)C)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.c", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.c" - }, - { - "include": "#escaped_char" - } - ] - } - ] - }, - { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)(?:JS|JAVASCRIPT))\\b\\1))", - "comment": "Heredoc with embedded javascript", - "end": "(?!\\G)", - "name": "meta.embedded.block.js", - "patterns": [ + "include": "#interpolated_ruby" + }, { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)(?:JS|JAVASCRIPT))\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.js", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.js" - }, - { - "include": "#escaped_char" - } - ] + "include": "#escaped_char" + }, + { + "include": "#nest_curly_i" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)JQUERY)\\b\\1))", - "comment": "Heredoc with embedded jQuery javascript", - "end": "(?!\\G)", - "name": "meta.embedded.block.js.jquery", + "begin": "%I([^\\w])", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "\\1", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "constant.language.symbol.interpolated.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)JQUERY)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.js.jquery", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.js.jquery" - }, - { - "include": "#escaped_char" - } - ] + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)(?:SH|SHELL))\\b\\1))", - "comment": "Heredoc with embedded shell", - "end": "(?!\\G)", - "name": "meta.embedded.block.shell", + "begin": "%i\\[", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "]", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)(?:SH|SHELL))\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.shell", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.shell" - }, - { - "include": "#escaped_char" - } - ] + "match": "\\\\]|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_brackets" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)LUA)\\b\\1))", - "comment": "Heredoc with embedded lua", - "end": "(?!\\G)", - "name": "meta.embedded.block.lua", + "begin": "%i\\(", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)LUA)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.lua", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.lua" - }, - { - "include": "#escaped_char" - } - ] + "match": "\\\\\\)|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_parens" } ] }, { - "begin": "(?=(?><<[-~](\"?)((?:[_\\w]+_|)RUBY)\\b\\1))", - "comment": "Heredoc with embedded ruby", - "end": "(?!\\G)", - "name": "meta.embedded.block.ruby", + "begin": "%i<", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", "patterns": [ { - "begin": "(?><<[-~](\"?)((?:[_\\w]+_|)RUBY)\\b\\1)", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "contentName": "source.ruby", - "end": "\\s*\\2$\\n?", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.unquoted.heredoc.ruby", - "patterns": [ - { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "source.ruby" - }, - { - "include": "#escaped_char" - } - ] + "match": "\\\\>|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_ltgt" } ] }, { - "begin": "(?>=\\s*<<(\\w+))", + "begin": "%i{", "beginCaptures": { "0": { - "name": "punctuation.definition.string.begin.ruby" + "name": "punctuation.section.array.begin.ruby" } }, - "end": "^\\1$", + "end": "}", "endCaptures": { "0": { - "name": "punctuation.definition.string.end.ruby" + "name": "punctuation.section.array.end.ruby" } }, - "name": "string.unquoted.heredoc.ruby", + "name": "constant.language.symbol.ruby", "patterns": [ { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" + "match": "\\\\}|\\\\\\\\", + "name": "constant.character.escape.ruby" }, { - "include": "#escaped_char" + "include": "#nest_curly" } ] }, { - "begin": "(?><<[-~](\\w+))", + "begin": "%i([^\\w])", "beginCaptures": { "0": { - "name": "punctuation.definition.string.begin.ruby" + "name": "punctuation.section.array.begin.ruby" } }, - "comment": "heredoc with indented terminator", - "end": "\\s*\\1$", + "end": "\\1", "endCaptures": { "0": { - "name": "punctuation.definition.string.end.ruby" + "name": "punctuation.section.array.end.ruby" } }, - "name": "string.unquoted.heredoc.ruby", + "name": "constant.language.symbol.ruby", "patterns": [ { - "include": "#heredoc" - }, - { - "include": "#interpolated_ruby" - }, - { - "include": "#escaped_char" + "comment": "Cant be named because its not necessarily an escape.", + "match": "\\\\." } ] }, { - "begin": "(?<=\\{|do|\\{\\s|do\\s)(\\|)", - "captures": { - "1": { - "name": "punctuation.separator.arguments.ruby" + "begin": "%W\\[", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" } }, - "end": "(?", - "name": "punctuation.separator.key-value" - }, - { - "match": "->", - "name": "support.function.kernel.lambda.ruby" - }, - { - "match": "<<=|%=|&{1,2}=|\\*=|\\*\\*=|\\+=|-=|\\^=|\\|{1,2}=|<<", - "name": "keyword.operator.assignment.augmented.ruby" - }, - { - "match": "<=>|<(?!<|=)|>(?!<|=|>)|<=|>=|===|==|=~|!=|!~|(?<=[ \\t])\\?", - "name": "keyword.operator.comparison.ruby" - }, - { - "match": "(?>", - "name": "keyword.operator.other.ruby" - }, - { - "match": ";", - "name": "punctuation.separator.statement.ruby" - }, - { - "match": ",", - "name": "punctuation.separator.object.ruby" - }, - { - "captures": { - "1": { - "name": "punctuation.separator.namespace.ruby" + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" } }, - "comment": "Mark as namespace separator if double colons followed by capital letter", - "match": "(::)\\s*(?=[A-Z])" + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_parens_i" + } + ] }, { - "captures": { - "1": { - "name": "punctuation.separator.method.ruby" + "begin": "%W<", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" } }, - "comment": "Mark as method separator if double colons not followed by capital letter", - "match": "(\\.|::)\\s*(?![A-Z])" - }, - { - "comment": "Must come after method and constant separators to prefer double colons", - "match": ":", - "name": "punctuation.separator.other.ruby" - }, - { - "match": "\\{", - "name": "punctuation.section.scope.begin.ruby" + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_ltgt_i" + } + ] }, { - "match": "\\}", - "name": "punctuation.section.scope.end.ruby" + "begin": "%W{", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_curly_i" + } + ] }, { - "match": "\\[", - "name": "punctuation.section.array.begin.ruby" + "begin": "%W([^\\w])", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "\\1", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + } + ] }, { - "match": "\\]", - "name": "punctuation.section.array.end.ruby" + "begin": "%w\\[", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "]", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "string.quoted.other.ruby", + "patterns": [ + { + "match": "\\\\]|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_brackets" + } + ] }, { - "match": "\\(|\\)", - "name": "punctuation.section.function.ruby" - } - ], - "repository": { - "escaped_char": { - "match": "\\\\(?:[0-7]{1,3}|x[\\da-fA-F]{1,2}|.)", - "name": "constant.character.escape.ruby" - }, - "heredoc": { - "begin": "^<<[-~]?\\w+", - "end": "$", + "begin": "%w\\(", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "string.quoted.other.ruby", "patterns": [ { - "include": "$self" + "match": "\\\\\\)|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_parens" } ] }, - "interpolated_ruby": { + { + "begin": "%w<", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "string.quoted.other.ruby", "patterns": [ { - "begin": "#\\{", - "beginCaptures": { - "0": { - "name": "punctuation.section.embedded.begin.ruby" - } - }, - "contentName": "source.ruby", - "end": "(\\})", - "endCaptures": { - "0": { - "name": "punctuation.section.embedded.end.ruby" - }, - "1": { - "name": "source.ruby" - } - }, - "name": "meta.embedded.line.ruby", - "patterns": [ - { - "include": "#nest_curly_and_self" - }, - { - "include": "$self" - } - ], - "repository": { - "nest_curly_and_self": { - "patterns": [ - { - "begin": "\\{", - "captures": { - "0": { - "name": "punctuation.section.scope.ruby" - } - }, - "end": "\\}", - "patterns": [ - { - "include": "#nest_curly_and_self" - } - ] - }, - { - "include": "$self" - } - ] - } - } + "match": "\\\\>|\\\\\\\\", + "name": "constant.character.escape.ruby" }, { - "captures": { - "1": { - "name": "punctuation.definition.variable.ruby" - } - }, - "match": "(#@)[a-zA-Z_]\\w*", - "name": "variable.other.readwrite.instance.ruby" - }, + "include": "#nest_ltgt" + } + ] + }, + { + "begin": "%w{", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "string.quoted.other.ruby", + "patterns": [ { - "captures": { - "1": { - "name": "punctuation.definition.variable.ruby" - } - }, - "match": "(#@@)[a-zA-Z_]\\w*", - "name": "variable.other.readwrite.class.ruby" + "match": "\\\\}|\\\\\\\\", + "name": "constant.character.escape.ruby" }, { - "captures": { - "1": { - "name": "punctuation.definition.variable.ruby" - } - }, - "match": "(#\\$)[a-zA-Z_]\\w*", - "name": "variable.other.readwrite.global.ruby" + "include": "#nest_curly" } ] }, - "percent_literals": { + { + "begin": "%w([^\\w])", + "beginCaptures": { + "0": { + "name": "punctuation.section.array.begin.ruby" + } + }, + "end": "\\1", + "endCaptures": { + "0": { + "name": "punctuation.section.array.end.ruby" + } + }, + "name": "string.quoted.other.ruby", "patterns": [ { - "begin": "%i(?:([(\\[{<])|([^\\w\\s]|_))", - "beginCaptures": { - "0": { - "name": "punctuation.section.array.begin.ruby" - } - }, - "end": "[)\\]}>]\\2|\\1\\2", + "comment": "Cant be named because its not necessarily an escape.", + "match": "\\\\." + } + ] + }, + { + "begin": "%[Qx]?\\(", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_parens_i" + } + ] + }, + { + "begin": "%[Qx]?\\[", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "]", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_brackets_i" + } + ] + }, + { + "begin": "%[Qx]?{", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_curly_i" + } + ] + }, + { + "begin": "%[Qx]?<", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_ltgt_i" + } + ] + }, + { + "begin": "%[Qx]([^\\w])", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "\\1", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + } + ] + }, + { + "begin": "%([^\\w\\s=])", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "\\1", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.interpolated.ruby", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + } + ] + }, + { + "begin": "%q\\(", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.ruby", + "patterns": [ + { + "match": "\\\\\\)|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_parens" + } + ] + }, + { + "begin": "%q<", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.ruby", + "patterns": [ + { + "match": "\\\\>|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_ltgt" + } + ] + }, + { + "begin": "%q\\[", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "]", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.ruby", + "patterns": [ + { + "match": "\\\\]|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_brackets" + } + ] + }, + { + "begin": "%q{", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.ruby", + "patterns": [ + { + "match": "\\\\}|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_curly" + } + ] + }, + { + "begin": "%q([^\\w])", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "\\1", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.quoted.other.ruby", + "patterns": [ + { + "comment": "Cant be named because its not necessarily an escape.", + "match": "\\\\." + } + ] + }, + { + "begin": "%s\\(", + "beginCaptures": { + "0": { + "name": "punctuation.definition.symbol.begin.ruby" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.symbol.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", + "patterns": [ + { + "match": "\\\\\\)|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_parens" + } + ] + }, + { + "begin": "%s<", + "beginCaptures": { + "0": { + "name": "punctuation.definition.symbol.begin.ruby" + } + }, + "end": ">", + "endCaptures": { + "0": { + "name": "punctuation.definition.symbol.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", + "patterns": [ + { + "match": "\\\\>|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_ltgt" + } + ] + }, + { + "begin": "%s\\[", + "beginCaptures": { + "0": { + "name": "punctuation.definition.symbol.begin.ruby" + } + }, + "end": "]", + "endCaptures": { + "0": { + "name": "punctuation.definition.symbol.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", + "patterns": [ + { + "match": "\\\\]|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_brackets" + } + ] + }, + { + "begin": "%s{", + "beginCaptures": { + "0": { + "name": "punctuation.definition.symbol.begin.ruby" + } + }, + "end": "}", + "endCaptures": { + "0": { + "name": "punctuation.definition.symbol.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", + "patterns": [ + { + "match": "\\\\}|\\\\\\\\", + "name": "constant.character.escape.ruby" + }, + { + "include": "#nest_curly" + } + ] + }, + { + "begin": "%s([^\\w])", + "beginCaptures": { + "0": { + "name": "punctuation.definition.symbol.begin.ruby" + } + }, + "end": "\\1", + "endCaptures": { + "0": { + "name": "punctuation.definition.symbol.end.ruby" + } + }, + "name": "constant.language.symbol.ruby", + "patterns": [ + { + "comment": "Cant be named because its not necessarily an escape.", + "match": "\\\\." + } + ] + }, + { + "captures": { + "1": { + "name": "punctuation.definition.constant.ruby" + } + }, + "comment": "symbols", + "match": "(?x)\n(?\n [$a-zA-Z_]\\w*(?>[?!]|=(?![>=]))?\n |\n ===?|<=>|>[>=]?|<[<=]?|[%&`/\\|]|\\*\\*?|=?~|[-+]@?|\\[]=?\n |\n @@?[a-zA-Z_]\\w*\n)", + "name": "constant.language.symbol.ruby" + }, + { + "begin": "^=begin", + "captures": { + "0": { + "name": "punctuation.definition.comment.ruby" + } + }, + "comment": "multiline comments", + "end": "^=end", + "name": "comment.block.documentation.ruby" + }, + { + "include": "#yard" + }, + { + "begin": "(^[ \\t]+)?(?=#)", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.comment.leading.ruby" + } + }, + "end": "(?!\\G)", + "patterns": [ + { + "begin": "#", + "beginCaptures": { + "0": { + "name": "punctuation.definition.comment.ruby" + } + }, + "end": "\\n", + "name": "comment.line.number-sign.ruby" + } + ] + }, + { + "comment": "\n\t\t\tmatches questionmark-letters.\n\n\t\t\texamples (1st alternation = hex):\n\t\t\t?\\x1 ?\\x61\n\n\t\t\texamples (2nd alternation = octal):\n\t\t\t?\\0 ?\\07 ?\\017\n\n\t\t\texamples (3rd alternation = escaped):\n\t\t\t?\\n ?\\b\n\n\t\t\texamples (4th alternation = meta-ctrl):\n\t\t\t?\\C-a ?\\M-a ?\\C-\\M-\\C-\\M-a\n\n\t\t\texamples (4th alternation = normal):\n\t\t\t?a ?A ?0 \n\t\t\t?* ?\" ?( \n\t\t\t?. ?#\n\t\t\t\n\t\t\t\n\t\t\tthe negative lookbehind prevents against matching\n\t\t\tp(42.tainted?)\n\t\t\t", + "match": "(?<<[-~]([\"'`]?)((?:[_\\w]+_|)HTML)\\b\\1))", + "comment": "Heredoc with embedded HTML", + "end": "(?!\\G)", + "name": "meta.embedded.block.html", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)HTML)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "text.html", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "text.html.basic" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)HAML)\\b\\1))", + "comment": "Heredoc with embedded HAML", + "end": "(?!\\G)", + "name": "meta.embedded.block.haml", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)HAML)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "text.haml", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "text.haml" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)XML)\\b\\1))", + "comment": "Heredoc with embedded XML", + "end": "(?!\\G)", + "name": "meta.embedded.block.xml", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)XML)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "text.xml", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "text.xml" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)SQL)\\b\\1))", + "comment": "Heredoc with embedded SQL", + "end": "(?!\\G)", + "name": "meta.embedded.block.sql", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)SQL)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.sql", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.sql" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)(?:GRAPHQL|GQL))\\b\\1))", + "comment": "Heredoc with embedded GraphQL", + "end": "(?!\\G)", + "name": "meta.embedded.block.graphql", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)(?:GRAPHQL|GQL))\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.graphql", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.graphql" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)CSS)\\b\\1))", + "comment": "Heredoc with embedded CSS", + "end": "(?!\\G)", + "name": "meta.embedded.block.css", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)CSS)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.css", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.css" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)CPP)\\b\\1))", + "comment": "Heredoc with embedded C++", + "end": "(?!\\G)", + "name": "meta.embedded.block.cpp", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)CPP)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.cpp", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.cpp" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)C)\\b\\1))", + "comment": "Heredoc with embedded C", + "end": "(?!\\G)", + "name": "meta.embedded.block.c", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)C)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.c", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "source.c" + }, + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)(?:JS|JAVASCRIPT))\\b\\1))", + "comment": "Heredoc with embedded Javascript", + "end": "(?!\\G)", + "name": "meta.embedded.block.js", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)(?:JS|JAVASCRIPT))\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.js", + "end": "^\\s*\\2$\\n?", "endCaptures": { "0": { - "name": "punctuation.section.array.end.ruby" + "name": "punctuation.definition.string.end.ruby" } }, - "name": "meta.array.symbol.ruby", + "name": "string.unquoted.heredoc.ruby", "patterns": [ { - "begin": "\\G(?<=\\()(?!\\))", - "end": "(?=\\))", - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#symbol" - } - ] + "include": "#heredoc" }, { - "begin": "\\G(?<=\\[)(?!\\])", - "end": "(?=\\])", - "patterns": [ - { - "include": "#brackets" - }, - { - "include": "#symbol" - } - ] + "include": "#interpolated_ruby" }, { - "begin": "\\G(?<=\\{)(?!\\})", - "end": "(?=\\})", - "patterns": [ - { - "include": "#braces" - }, - { - "include": "#symbol" - } - ] + "include": "source.js" }, { - "begin": "\\G(?<=<)(?!>)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - }, - { - "include": "#symbol" - } - ] - }, + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)JQUERY)\\b\\1))", + "comment": "Heredoc with embedded jQuery Javascript", + "end": "(?!\\G)", + "name": "meta.embedded.block.js.jquery", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)JQUERY)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.js.jquery", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ { - "include": "#symbol" - } - ], - "repository": { - "angles": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\<|\\\\>", - "name": "constant.other.symbol.ruby" - }, - { - "begin": "<", - "captures": { - "0": { - "name": "constant.other.symbol.ruby" - } - }, - "end": ">", - "patterns": [ - { - "include": "#angles" - }, - { - "include": "#symbol" - } - ] - } - ] - }, - "braces": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\\\{|\\\\\\}", - "name": "constant.other.symbol.ruby" - }, - { - "begin": "\\{", - "captures": { - "0": { - "name": "constant.other.symbol.ruby" - } - }, - "end": "\\}", - "patterns": [ - { - "include": "#braces" - }, - { - "include": "#symbol" - } - ] - } - ] + "include": "#heredoc" }, - "brackets": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\\\[|\\\\\\]", - "name": "constant.other.symbol.ruby" - }, - { - "begin": "\\[", - "captures": { - "0": { - "name": "constant.other.symbol.ruby" - } - }, - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - }, - { - "include": "#symbol" - } - ] - } - ] + { + "include": "#interpolated_ruby" }, - "parens": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\\\(|\\\\\\)", - "name": "constant.other.symbol.ruby" - }, - { - "begin": "\\(", - "captures": { - "0": { - "name": "constant.other.symbol.ruby" - } - }, - "end": "\\)", - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#symbol" - } - ] - } - ] + { + "include": "source.js.jquery" }, - "symbol": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\\\\\|\\\\[ ]", - "name": "constant.other.symbol.ruby" - }, - { - "match": "\\S\\w*", - "name": "constant.other.symbol.ruby" - } - ] + { + "include": "#escaped_char" } - } - }, + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)(?:SH|SHELL))\\b\\1))", + "comment": "Heredoc with embedded Shell", + "end": "(?!\\G)", + "name": "meta.embedded.block.shell", + "patterns": [ { - "begin": "%I(?:([(\\[{<])|([^\\w\\s]|_))", + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)(?:SH|SHELL))\\b\\1)", "beginCaptures": { "0": { - "name": "punctuation.section.array.begin.ruby" + "name": "punctuation.definition.string.begin.ruby" } }, - "end": "[)\\]}>]\\2|\\1\\2", + "contentName": "source.shell", + "end": "^\\s*\\2$\\n?", "endCaptures": { "0": { - "name": "punctuation.section.array.end.ruby" + "name": "punctuation.definition.string.end.ruby" } }, - "name": "meta.array.symbol.interpolated.ruby", + "name": "string.unquoted.heredoc.ruby", "patterns": [ { - "begin": "\\G(?<=\\()(?!\\))", - "end": "(?=\\))", - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#symbol" - } - ] + "include": "#heredoc" }, { - "begin": "\\G(?<=\\[)(?!\\])", - "end": "(?=\\])", - "patterns": [ - { - "include": "#brackets" - }, - { - "include": "#symbol" - } - ] + "include": "#interpolated_ruby" }, { - "begin": "\\G(?<=\\{)(?!\\})", - "end": "(?=\\})", - "patterns": [ - { - "include": "#braces" - }, - { - "include": "#symbol" - } - ] + "include": "source.shell" }, { - "begin": "\\G(?<=<)(?!>)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - }, - { - "include": "#symbol" - } - ] + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)LUA)\\b\\1))", + "comment": "Heredoc with embedded Lua", + "end": "(?!\\G)", + "name": "meta.embedded.block.lua", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)LUA)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.lua", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" }, { - "include": "#symbol" - } - ], - "repository": { - "angles": { - "patterns": [ - { - "begin": "<", - "captures": { - "0": { - "name": "constant.other.symbol.ruby" - } - }, - "end": ">", - "patterns": [ - { - "include": "#angles" - }, - { - "include": "#symbol" - } - ] - } - ] + "include": "#interpolated_ruby" }, - "braces": { - "patterns": [ - { - "begin": "\\{", - "captures": { - "0": { - "name": "constant.other.symbol.ruby" - } - }, - "end": "\\}", - "patterns": [ - { - "include": "#braces" - }, - { - "include": "#symbol" - } - ] - } - ] + { + "include": "source.lua" }, - "brackets": { - "patterns": [ - { - "begin": "\\[", - "captures": { - "0": { - "name": "constant.other.symbol.ruby" - } - }, - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - }, - { - "include": "#symbol" - } - ] - } - ] + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)RUBY)\\b\\1))", + "comment": "Heredoc with embedded Ruby", + "end": "(?!\\G)", + "name": "meta.embedded.block.ruby", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)RUBY)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "source.ruby", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" }, - "parens": { - "patterns": [ - { - "begin": "\\(", - "captures": { - "0": { - "name": "constant.other.symbol.ruby" - } - }, - "end": "\\)", - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#symbol" - } - ] - } - ] + { + "include": "#interpolated_ruby" }, - "symbol": { - "patterns": [ - { - "begin": "(?=\\\\|#\\{)", - "end": "(?!\\G)", - "name": "constant.other.symbol.ruby", - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - } - ] - }, - { - "match": "\\S\\w*", - "name": "constant.other.symbol.ruby" - } - ] + { + "include": "source.ruby" + }, + { + "include": "#escaped_char" } - } - }, + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)(?:YAML|YML))\\b\\1))", + "comment": "Heredoc with embedded YAML", + "end": "(?!\\G)", + "name": "meta.embedded.block.yaml", + "patterns": [ { - "begin": "%q(?:([(\\[{<])|([^\\w\\s]|_))", + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)(?:YAML|YML))\\b\\1)", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.ruby" } }, - "end": "[)\\]}>]\\2|\\1\\2", + "contentName": "source.yaml", + "end": "^\\s*\\2$\\n?", "endCaptures": { "0": { "name": "punctuation.definition.string.end.ruby" } }, - "name": "string.quoted.other.ruby", + "name": "string.unquoted.heredoc.ruby", "patterns": [ { - "begin": "\\G(?<=\\()(?!\\))", - "end": "(?=\\))", - "patterns": [ - { - "include": "#parens" - } - ] + "include": "#heredoc" }, { - "begin": "\\G(?<=\\[)(?!\\])", - "end": "(?=\\])", - "patterns": [ - { - "include": "#brackets" - } - ] + "include": "#interpolated_ruby" }, { - "begin": "\\G(?<=\\{)(?!\\})", - "end": "(?=\\})", - "patterns": [ - { - "include": "#braces" - } - ] + "include": "source.yaml" }, { - "begin": "\\G(?<=<)(?!>)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - } - ] - } - ], - "repository": { - "angles": { - "patterns": [ - { - "match": "\\\\<|\\\\>|\\\\\\\\", - "name": "constant.character.escape.ruby" - }, - { - "begin": "<", - "end": ">", - "patterns": [ - { - "include": "#angles" - } - ] - } - ] + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?=(?><<[-~]([\"'`]?)((?:[_\\w]+_|)SLIM)\\b\\1))", + "comment": "Heredoc with embedded Slim", + "end": "(?!\\G)", + "name": "meta.embedded.block.slim", + "patterns": [ + { + "begin": "(?><<[-~]([\"'`]?)((?:[_\\w]+_|)SLIM)\\b\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "contentName": "text.slim", + "end": "^\\s*\\2$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" }, - "braces": { - "patterns": [ - { - "match": "\\\\\\{|\\\\\\}|\\\\\\\\", - "name": "constant.character.escape.ruby" - }, - { - "begin": "\\{", - "end": "\\}", - "patterns": [ - { - "include": "#braces" - } - ] - } - ] + { + "include": "#interpolated_ruby" }, - "brackets": { - "patterns": [ - { - "match": "\\\\\\[|\\\\\\]|\\\\\\\\", - "name": "constant.character.escape.ruby" - }, - { - "begin": "\\[", - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - } - ] - } - ] + { + "include": "text.slim" }, - "parens": { - "patterns": [ - { - "match": "\\\\\\(|\\\\\\)|\\\\\\\\", - "name": "constant.character.escape.ruby" - }, - { - "begin": "\\(", - "end": "\\)", - "patterns": [ - { - "include": "#parens" - } - ] - } - ] + { + "include": "#escaped_char" + } + ] + } + ] + }, + { + "begin": "(?>=\\s*<<([\"'`]?)(\\w+)\\1)", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.ruby" + } + }, + "end": "^\\2$", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" + }, + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + } + ] + }, + { + "begin": "(?>((<<[-~]([\"'`]?)(\\w+)\\3,\\s?)*<<[-~]([\"'`]?)(\\w+)\\5))(.*)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.string.begin.ruby" + }, + "7": { + "patterns": [ + { + "include": "source.ruby" } - } + ] + } + }, + "comment": "heredoc with multiple inputs and indented terminator", + "end": "^\\s*\\6$", + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.ruby" + } + }, + "name": "string.unquoted.heredoc.ruby", + "patterns": [ + { + "include": "#heredoc" }, { - "begin": "%Q?(?:([(\\[{<])|([^\\w\\s=]|_))", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "end": "[)\\]}>]\\2|\\1\\2", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.quoted.other.interpolated.ruby", + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + } + ] + }, + { + "begin": "(?<={|{\\s+|[^A-Za-z0-9_:@$]do|^do|[^A-Za-z0-9_:@$]do\\s+|^do\\s+)(\\|)", + "name": "meta.block.parameters.ruby", + "captures": { + "1": { + "name": "punctuation.separator.variable.ruby" + } + }, + "end": "(?)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - } - ] - }, - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - } - ], - "repository": { - "angles": { - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" + "match": "\\G((?:&|\\*\\*?)?)([a-zA-Z_][\\w_]*)", + "captures": { + "1": { + "name": "storage.type.variable.ruby" }, - { - "begin": "<", - "end": ">", - "patterns": [ - { - "include": "#angles" - } - ] + "2": { + "name": "variable.other.block.ruby" } - ] + } + } + ] + }, + { + "match": ",", + "name": "punctuation.separator.variable.ruby" + } + ] + }, + { + "match": "=>", + "name": "punctuation.separator.key-value" + }, + { + "match": "->", + "name": "support.function.kernel.ruby" + }, + { + "match": "<<=|%=|&{1,2}=|\\*=|\\*\\*=|\\+=|-=|\\^=|\\|{1,2}=|<<", + "name": "keyword.operator.assignment.augmented.ruby" + }, + { + "match": "<=>|<(?!<|=)|>(?!<|=|>)|<=|>=|===|==|=~|!=|!~|(?<=[ \\t])\\?", + "name": "keyword.operator.comparison.ruby" + }, + { + "match": "(?>", + "name": "keyword.operator.other.ruby" + }, + { + "match": ";", + "name": "punctuation.separator.statement.ruby" + }, + { + "match": ",", + "name": "punctuation.separator.object.ruby" + }, + { + "comment": "Mark as namespace separator if double colons followed by capital letter", + "match": "(::)\\s*(?=[A-Z])", + "captures": { + "1": { + "name": "punctuation.separator.namespace.ruby" + } + } + }, + { + "comment": "Mark as method separator if double colons not followed by capital letter", + "match": "(\\.|::)\\s*(?![A-Z])", + "captures": { + "1": { + "name": "punctuation.separator.method.ruby" + } + } + }, + { + "comment": "Must come after method and constant separators to prefer double colons", + "match": ":", + "name": "punctuation.separator.other.ruby" + }, + { + "match": "{", + "name": "punctuation.section.scope.begin.ruby" + }, + { + "match": "}", + "name": "punctuation.section.scope.end.ruby" + }, + { + "match": "\\[", + "name": "punctuation.section.array.begin.ruby" + }, + { + "match": "]", + "name": "punctuation.section.array.end.ruby" + }, + { + "match": "\\(|\\)", + "name": "punctuation.section.function.ruby" + }, + { + "name": "meta.function-call.ruby", + "begin": "(?<=[^\\.]\\.|::)(?=[a-zA-Z][a-zA-Z0-9_!?]*[^a-zA-Z0-9_!?])", + "end": "(?<=[a-zA-Z0-9_!?])(?=[^a-zA-Z0-9_!?])", + "patterns": [ + { + "name": "entity.name.function.ruby", + "match": "([a-zA-Z][a-zA-Z0-9_!?]*)(?=[^a-zA-Z0-9_!?])" + } + ] + }, + { + "begin": "([a-zA-Z]\\w*[!?]?)(\\()", + "beginCaptures": { + "1": { + "name": "entity.name.function.ruby" + }, + "2": { + "name": "punctuation.section.function.ruby" + } + }, + "end": "(\\))", + "endCaptures": { + "1": { + "name": "punctuation.section.function.ruby" + } + }, + "name": "meta.function-call.ruby", + "patterns": [ + { + "include": "$self" + } + ] + } + ], + "repository": { + "method_parameters": { + "patterns": [ + { + "include": "#parens" + }, + { + "include": "#braces" + }, + { + "include": "#brackets" + }, + { + "include": "#params" + }, + { + "include": "$self" + } + ], + "repository": { + "params": { + "captures": { + "1": { + "name": "storage.type.variable.ruby" }, - "braces": { - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - }, - { - "begin": "\\{", - "end": "\\}", - "patterns": [ - { - "include": "#braces" - } - ] - } - ] + "2": { + "name": "constant.other.symbol.hashkey.parameter.function.ruby" }, - "brackets": { - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - }, - { - "begin": "\\[", - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - } - ] - } - ] + "3": { + "name": "punctuation.definition.constant.ruby" }, - "parens": { - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - }, - { - "begin": "\\(", - "end": "\\)", - "patterns": [ - { - "include": "#parens" - } - ] - } - ] + "4": { + "name": "variable.parameter.function.ruby" } - } + }, + "match": "\\G(&|\\*\\*?)?(?:([_a-zA-Z]\\w*[?!]?(:))|([_a-zA-Z]\\w*))" }, - { - "begin": "%r(?:([(\\[{<])|([^\\w\\s]|_))", + "braces": { + "begin": "\\{", "beginCaptures": { "0": { - "name": "punctuation.definition.string.begin.ruby" + "name": "punctuation.section.scope.begin.ruby" } }, - "end": "([)\\]}>]\\2|\\1\\2)[eimnosux]*", + "end": "\\}", "endCaptures": { "0": { - "name": "punctuation.definition.string.end.ruby" + "name": "punctuation.section.scope.end.ruby" } }, - "name": "string.regexp.percent.ruby", "patterns": [ { - "begin": "\\G(?<=\\()(?!\\))", - "end": "(?=\\))", - "patterns": [ - { - "include": "#parens" - } - ] - }, - { - "begin": "\\G(?<=\\[)(?!\\])", - "end": "(?=\\])", - "patterns": [ - { - "include": "#brackets" - } - ] + "include": "#parens" }, { - "begin": "\\G(?<=\\{)(?!\\})", - "end": "(?=\\})", - "patterns": [ - { - "include": "#braces" - } - ] + "include": "#braces" }, { - "begin": "\\G(?<=<)(?!>)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - } - ] + "include": "#brackets" }, { - "include": "#regex_sub" - } - ], - "repository": { - "angles": { - "patterns": [ - { - "include": "#regex_sub" - }, - { - "begin": "<", - "end": ">", - "patterns": [ - { - "include": "#angles" - } - ] - } - ] - }, - "braces": { - "patterns": [ - { - "include": "#regex_sub" - }, - { - "begin": "\\{", - "end": "\\}", - "patterns": [ - { - "include": "#braces" - } - ] - } - ] - }, - "brackets": { - "patterns": [ - { - "include": "#regex_sub" - }, - { - "begin": "\\[", - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - } - ] - } - ] - }, - "parens": { - "patterns": [ - { - "include": "#regex_sub" - }, - { - "begin": "\\(", - "end": "\\)", - "patterns": [ - { - "include": "#parens" - } - ] - } - ] + "include": "$self" } - } + ] }, - { - "begin": "%s(?:([(\\[{<])|([^\\w\\s]|_))", + "brackets": { + "begin": "\\[", "beginCaptures": { "0": { - "name": "punctuation.definition.constant.begin.ruby" + "name": "punctuation.section.array.begin.ruby" } }, - "end": "[)\\]}>]\\2|\\1\\2", + "end": "\\]", "endCaptures": { "0": { - "name": "punctuation.definition.constant.end.ruby" + "name": "punctuation.section.array.end.ruby" } }, - "name": "constant.other.symbol.percent.ruby", "patterns": [ { - "begin": "\\G(?<=\\()(?!\\))", - "end": "(?=\\))", - "patterns": [ - { - "include": "#parens" - } - ] + "include": "#parens" }, { - "begin": "\\G(?<=\\[)(?!\\])", - "end": "(?=\\])", - "patterns": [ - { - "include": "#brackets" - } - ] + "include": "#braces" }, { - "begin": "\\G(?<=\\{)(?!\\})", - "end": "(?=\\})", - "patterns": [ - { - "include": "#braces" - } - ] + "include": "#brackets" }, { - "begin": "\\G(?<=<)(?!>)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - } - ] - } - ], - "repository": { - "angles": { - "patterns": [ - { - "match": "\\\\<|\\\\>|\\\\\\\\", - "name": "constant.character.escape.ruby" - }, - { - "begin": "<", - "end": ">", - "patterns": [ - { - "include": "#angles" - } - ] - } - ] - }, - "braces": { - "patterns": [ - { - "match": "\\\\\\{|\\\\\\}|\\\\\\\\", - "name": "constant.character.escape.ruby" - }, - { - "begin": "\\{", - "end": "\\}", - "patterns": [ - { - "include": "#braces" - } - ] - } - ] - }, - "brackets": { - "patterns": [ - { - "match": "\\\\\\[|\\\\\\]|\\\\\\\\", - "name": "constant.character.escape.ruby" - }, - { - "begin": "\\[", - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - } - ] - } - ] - }, - "parens": { - "patterns": [ - { - "match": "\\\\\\(|\\\\\\)|\\\\\\\\", - "name": "constant.character.escape.ruby" - }, - { - "begin": "\\(", - "end": "\\)", - "patterns": [ - { - "include": "#parens" - } - ] - } - ] + "include": "$self" } - } + ] }, - { - "begin": "%w(?:([(\\[{<])|([^\\w\\s]|_))", + "parens": { + "begin": "\\(", "beginCaptures": { "0": { - "name": "punctuation.section.array.begin.ruby" + "name": "punctuation.section.function.begin.ruby" } }, - "end": "[)\\]}>]\\2|\\1\\2", + "end": "\\)", "endCaptures": { "0": { - "name": "punctuation.section.array.end.ruby" + "name": "punctuation.section.function.end.ruby" } }, - "name": "meta.array.string.ruby", "patterns": [ { - "begin": "\\G(?<=\\()(?!\\))", - "end": "(?=\\))", - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#string" - } - ] - }, - { - "begin": "\\G(?<=\\[)(?!\\])", - "end": "(?=\\])", - "patterns": [ - { - "include": "#brackets" - }, - { - "include": "#string" - } - ] + "include": "#parens" }, { - "begin": "\\G(?<=\\{)(?!\\})", - "end": "(?=\\})", - "patterns": [ - { - "include": "#braces" - }, - { - "include": "#string" - } - ] + "include": "#braces" }, { - "begin": "\\G(?<=<)(?!>)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - }, - { - "include": "#string" - } - ] + "include": "#brackets" }, { - "include": "#string" - } - ], - "repository": { - "angles": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\<|\\\\>", - "name": "string.other.ruby" - }, - { - "begin": "<", - "captures": { - "0": { - "name": "string.other.ruby" - } - }, - "end": ">", - "patterns": [ - { - "include": "#angles" - }, - { - "include": "#string" - } - ] - } - ] - }, - "braces": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\\\{|\\\\\\}", - "name": "string.other.ruby" - }, - { - "begin": "\\{", - "captures": { - "0": { - "name": "string.other.ruby" - } - }, - "end": "\\}", - "patterns": [ - { - "include": "#braces" - }, - { - "include": "#string" - } - ] - } - ] - }, - "brackets": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\\\[|\\\\\\]", - "name": "string.other.ruby" - }, - { - "begin": "\\[", - "captures": { - "0": { - "name": "string.other.ruby" - } - }, - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - }, - { - "include": "#string" - } - ] - } - ] - }, - "parens": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\\\(|\\\\\\)", - "name": "string.other.ruby" - }, - { - "begin": "\\(", - "captures": { - "0": { - "name": "string.other.ruby" - } - }, - "end": "\\)", - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#string" - } - ] - } - ] - }, - "string": { - "patterns": [ - { - "captures": { - "0": { - "name": "constant.character.escape.ruby" - } - }, - "match": "\\\\\\\\|\\\\[ ]", - "name": "string.other.ruby" - }, - { - "match": "\\S\\w*", - "name": "string.other.ruby" - } - ] + "include": "$self" } - } - }, + ] + } + } + }, + "escaped_char": { + "match": "\\\\(?:[0-7]{1,3}|x[\\da-fA-F]{1,2}|.)", + "name": "constant.character.escape.ruby" + }, + "heredoc": { + "begin": "^<<[-~]?\\w+", + "end": "$", + "patterns": [ + { + "include": "$self" + } + ] + }, + "interpolated_ruby": { + "patterns": [ { - "begin": "%W(?:([(\\[{<])|([^\\w\\s]|_))", + "begin": "#{", "beginCaptures": { "0": { - "name": "punctuation.section.array.begin.ruby" + "name": "punctuation.section.embedded.begin.ruby" } }, - "end": "[)\\]}>]\\2|\\1\\2", + "contentName": "source.ruby", + "end": "}", "endCaptures": { "0": { - "name": "punctuation.section.array.end.ruby" + "name": "punctuation.section.embedded.end.ruby" } }, - "name": "meta.array.string.interpolated.ruby", + "name": "meta.embedded.line.ruby", "patterns": [ { - "begin": "\\G(?<=\\()(?!\\))", - "end": "(?=\\))", - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#string" - } - ] - }, - { - "begin": "\\G(?<=\\[)(?!\\])", - "end": "(?=\\])", - "patterns": [ - { - "include": "#brackets" - }, - { - "include": "#string" - } - ] - }, - { - "begin": "\\G(?<=\\{)(?!\\})", - "end": "(?=\\})", - "patterns": [ - { - "include": "#braces" - }, - { - "include": "#string" - } - ] + "include": "#nest_curly_and_self" }, { - "begin": "\\G(?<=<)(?!>)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - }, - { - "include": "#string" - } - ] - }, + "include": "$self" + } + ] + }, + { + "captures": { + "1": { + "name": "punctuation.definition.variable.ruby" + } + }, + "match": "(#@)[a-zA-Z_]\\w*", + "name": "variable.other.readwrite.instance.ruby" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.variable.ruby" + } + }, + "match": "(#@@)[a-zA-Z_]\\w*", + "name": "variable.other.readwrite.class.ruby" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.variable.ruby" + } + }, + "match": "(#\\$)[a-zA-Z_]\\w*", + "name": "variable.other.readwrite.global.ruby" + } + ] + }, + "nest_brackets": { + "begin": "\\[", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "]", + "patterns": [ + { + "include": "#nest_brackets" + } + ] + }, + "nest_brackets_i": { + "begin": "\\[", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "]", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_brackets_i" + } + ] + }, + "nest_brackets_r": { + "begin": "\\[", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "]", + "patterns": [ + { + "include": "#regex_sub" + }, + { + "include": "#nest_brackets_r" + } + ] + }, + "nest_curly": { + "begin": "{", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "}", + "patterns": [ + { + "include": "#nest_curly" + } + ] + }, + "nest_curly_and_self": { + "patterns": [ + { + "begin": "{", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "}", + "patterns": [ { - "include": "#string" - } - ], - "repository": { - "angles": { - "patterns": [ - { - "begin": "<", - "captures": { - "0": { - "name": "string.other.ruby" - } - }, - "end": ">", - "patterns": [ - { - "include": "#angles" - }, - { - "include": "#string" - } - ] - } - ] - }, - "braces": { - "patterns": [ - { - "begin": "\\{", - "captures": { - "0": { - "name": "string.other.ruby" - } - }, - "end": "\\}", - "patterns": [ - { - "include": "#braces" - }, - { - "include": "#string" - } - ] - } - ] - }, - "brackets": { - "patterns": [ - { - "begin": "\\[", - "captures": { - "0": { - "name": "string.other.ruby" - } - }, - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - }, - { - "include": "#string" - } - ] - } - ] - }, - "parens": { - "patterns": [ - { - "begin": "\\(", - "captures": { - "0": { - "name": "string.other.ruby" - } - }, - "end": "\\)", - "patterns": [ - { - "include": "#parens" - }, - { - "include": "#string" - } - ] - } - ] - }, - "string": { - "patterns": [ - { - "begin": "(?=\\\\|#\\{)", - "end": "(?!\\G)", - "name": "string.other.ruby", - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - } - ] - }, - { - "match": "\\S\\w*", - "name": "string.other.ruby" - } - ] + "include": "#nest_curly_and_self" } - } + ] + }, + { + "include": "$self" + } + ] + }, + "nest_curly_i": { + "begin": "{", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "}", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_curly_i" + } + ] + }, + "nest_curly_r": { + "begin": "{", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "}", + "patterns": [ + { + "include": "#regex_sub" + }, + { + "include": "#nest_curly_r" + } + ] + }, + "nest_ltgt": { + "begin": "<", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": ">", + "patterns": [ + { + "include": "#nest_ltgt" + } + ] + }, + "nest_ltgt_i": { + "begin": "<", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": ">", + "patterns": [ + { + "include": "#interpolated_ruby" + }, + { + "include": "#escaped_char" + }, + { + "include": "#nest_ltgt_i" + } + ] + }, + "nest_ltgt_r": { + "begin": "<", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": ">", + "patterns": [ + { + "include": "#regex_sub" + }, + { + "include": "#nest_ltgt_r" + } + ] + }, + "nest_parens": { + "begin": "\\(", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "\\)", + "patterns": [ + { + "include": "#nest_parens" + } + ] + }, + "nest_parens_i": { + "begin": "\\(", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "\\)", + "patterns": [ + { + "include": "#interpolated_ruby" }, { - "begin": "%x(?:([(\\[{<])|([^\\w\\s]|_))", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.ruby" - } - }, - "end": "[)\\]}>]\\2|\\1\\2", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.ruby" - } - }, - "name": "string.interpolated.percent.ruby", - "patterns": [ - { - "begin": "\\G(?<=\\()(?!\\))", - "end": "(?=\\))", - "patterns": [ - { - "include": "#parens" - } - ] - }, - { - "begin": "\\G(?<=\\[)(?!\\])", - "end": "(?=\\])", - "patterns": [ - { - "include": "#brackets" - } - ] - }, - { - "begin": "\\G(?<=\\{)(?!\\})", - "end": "(?=\\})", - "patterns": [ - { - "include": "#braces" - } - ] - }, - { - "begin": "\\G(?<=<)(?!>)", - "end": "(?=>)", - "patterns": [ - { - "include": "#angles" - } - ] - }, - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - } - ], - "repository": { - "angles": { - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - }, - { - "begin": "<", - "end": ">", - "patterns": [ - { - "include": "#angles" - } - ] - } - ] - }, - "braces": { - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - }, - { - "begin": "\\{", - "end": "\\}", - "patterns": [ - { - "include": "#braces" - } - ] - } - ] - }, - "brackets": { - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - }, - { - "begin": "\\[", - "end": "\\]", - "patterns": [ - { - "include": "#brackets" - } - ] - } - ] - }, - "parens": { - "patterns": [ - { - "include": "#escaped_char" - }, - { - "include": "#interpolated_ruby" - }, - { - "begin": "\\(", - "end": "\\)", - "patterns": [ - { - "include": "#parens" - } - ] - } - ] - } - } + "include": "#escaped_char" + }, + { + "include": "#nest_parens_i" + } + ] + }, + "nest_parens_r": { + "begin": "\\(", + "captures": { + "0": { + "name": "punctuation.section.scope.ruby" + } + }, + "end": "\\)", + "patterns": [ + { + "include": "#regex_sub" + }, + { + "include": "#nest_parens_r" } ] }, @@ -2694,29 +2801,24 @@ { "captures": { "1": { - "name": "punctuation.definition.quantifier.begin.ruby" + "name": "punctuation.definition.arbitrary-repetition.ruby" }, "3": { - "name": "punctuation.definition.quantifier.end.ruby" + "name": "punctuation.definition.arbitrary-repetition.ruby" } }, - "match": "(\\{)\\d+(,\\d+)?(\\})", - "name": "keyword.operator.quantifier.ruby" + "match": "({)\\d+(,\\d+)?(})", + "name": "string.regexp.arbitrary-repetition.ruby" }, { - "begin": "\\[\\^?", - "beginCaptures": { - "0": { - "name": "punctuation.definition.character-class.begin.ruby" - } - }, - "end": "\\]", - "endCaptures": { + "begin": "\\[(?:\\^?])?", + "captures": { "0": { - "name": "punctuation.definition.character-class.end.ruby" + "name": "punctuation.definition.character-class.ruby" } }, - "name": "constant.other.character-class.set.ruby", + "end": "]", + "name": "string.regexp.character-class.ruby", "patterns": [ { "include": "#escaped_char" @@ -2751,7 +2853,7 @@ } }, "end": "\\)", - "name": "meta.group.regexp.ruby", + "name": "string.regexp.group.ruby", "patterns": [ { "include": "#regex_sub" @@ -2767,9 +2869,328 @@ }, "comment": "We are restrictive in what we allow to go after the comment character to avoid false positives, since the availability of comments depend on regexp flags.", "end": "$\\n?", + "endCaptures": { + "0": { + "name": "punctuation.definition.comment.ruby" + } + }, "name": "comment.line.number-sign.ruby" } ] + }, + "yard": { + "patterns": [ + { + "include": "#yard_comment" + }, + { + "include": "#yard_param_types" + }, + { + "include": "#yard_option" + }, + { + "include": "#yard_tag" + }, + { + "include": "#yard_types" + }, + { + "include": "#yard_directive" + }, + { + "include": "#yard_see" + }, + { + "include": "#yard_macro_attribute" + } + ] + }, + "yard_see": { + "comment": "separate rule for @see because name could contain url", + "begin": "^(\\s*)(#)(\\s*)(@)(see)(?=\\s)(\\s+(.+?))?(?=\\s|$)", + "beginCaptures": { + "2": { + "name": "punctuation.definition.comment.ruby" + }, + "4": { + "name": "comment.line.keyword.punctuation.yard.ruby" + }, + "5": { + "name": "comment.line.keyword.yard.ruby" + }, + "7": { + "name": "comment.line.parameter.yard.ruby" + } + }, + "end": "^(?!\\s*#\\3\\s{2,}|\\s*#\\s*$)", + "contentName": "comment.line.string.yard.ruby", + "name": "comment.line.number-sign.ruby", + "patterns": [ + { + "include": "#yard" + }, + { + "include": "#yard_continuation" + } + ] + }, + "yard_macro_attribute": { + "comment": "separate rule for attribute and macro tags because name goes after []", + "begin": "^(\\s*)(#)(\\s*)(@!)(attribute|macro)(\\s+((\\[).+(])))?(?=\\s)(\\s+([a-z_]\\w*:?))?", + "beginCaptures": { + "2": { + "name": "punctuation.definition.comment.ruby" + }, + "4": { + "name": "comment.line.keyword.punctuation.yard.ruby" + }, + "5": { + "name": "comment.line.keyword.yard.ruby" + }, + "7": { + "name": "comment.line.type.yard.ruby" + }, + "8": { + "name": "comment.line.punctuation.yard.ruby" + }, + "9": { + "name": "comment.line.punctuation.yard.ruby" + }, + "11": { + "name": "comment.line.parameter.yard.ruby" + } + }, + "end": "^(?!\\s*#\\3\\s{2,}|\\s*#\\s*$)", + "contentName": "comment.line.string.yard.ruby", + "name": "comment.line.number-sign.ruby", + "patterns": [ + { + "include": "#yard" + }, + { + "include": "#yard_continuation" + } + ] + }, + "yard_comment": { + "comment": "For YARD tags that follow the tag-comment pattern", + "begin": "^(\\s*)(#)(\\s*)(@)(abstract|api|author|deprecated|example|macro|note|overload|since|todo|version)(?=\\s|$)", + "beginCaptures": { + "2": { + "name": "punctuation.definition.comment.ruby" + }, + "4": { + "name": "comment.line.keyword.punctuation.yard.ruby" + }, + "5": { + "name": "comment.line.keyword.yard.ruby" + } + }, + "end": "^(?!\\s*#\\3\\s{2,}|\\s*#\\s*$)", + "contentName": "comment.line.string.yard.ruby", + "name": "comment.line.number-sign.ruby", + "patterns": [ + { + "include": "#yard" + }, + { + "include": "#yard_continuation" + } + ] + }, + "yard_param_types": { + "comment": "For YARD tags that follow the tag-name-types-description or tag-types-name-description pattern", + "begin": "^(\\s*)(#)(\\s*)(@)(attr|attr_reader|attr_writer|yieldparam|param)(?=\\s)(?>\\s+(?>([a-z_]\\w*:?)|((\\[).+(]))))?(?>\\s+(?>((\\[).+(]))|([a-z_]\\w*:?)))?", + "beginCaptures": { + "2": { + "name": "punctuation.definition.comment.ruby" + }, + "4": { + "name": "comment.line.keyword.punctuation.yard.ruby" + }, + "5": { + "name": "comment.line.keyword.yard.ruby" + }, + "6": { + "name": "comment.line.parameter.yard.ruby" + }, + "7": { + "name": "comment.line.type.yard.ruby" + }, + "8": { + "name": "comment.line.punctuation.yard.ruby" + }, + "9": { + "name": "comment.line.punctuation.yard.ruby" + }, + "10": { + "name": "comment.line.type.yard.ruby" + }, + "11": { + "name": "comment.line.punctuation.yard.ruby" + }, + "12": { + "name": "comment.line.punctuation.yard.ruby" + }, + "13": { + "name": "comment.line.parameter.yard.ruby" + } + }, + "end": "^(?!\\s*#\\3\\s{2,}|\\s*#\\s*$)", + "contentName": "comment.line.string.yard.ruby", + "name": "comment.line.number-sign.ruby", + "patterns": [ + { + "include": "#yard" + }, + { + "include": "#yard_continuation" + } + ] + }, + "yard_option": { + "comment": "For YARD option tag that follow the tag-name-types-key-(value)-description pattern", + "begin": "^(\\s*)(#)(\\s*)(@)(option)(?=\\s)(?>\\s+([a-z_]\\w*:?))?(?>\\s+((\\[).+(])))?(?>\\s+((\\S*)))?(?>\\s+((\\().+(\\))))?", + "beginCaptures": { + "2": { + "name": "punctuation.definition.comment.ruby" + }, + "4": { + "name": "comment.line.keyword.punctuation.yard.ruby" + }, + "5": { + "name": "comment.line.keyword.yard.ruby" + }, + "6": { + "name": "comment.line.parameter.yard.ruby" + }, + "7": { + "name": "comment.line.type.yard.ruby" + }, + "8": { + "name": "comment.line.punctuation.yard.ruby" + }, + "9": { + "name": "comment.line.punctuation.yard.ruby" + }, + "10": { + "name": "comment.line.keyword.yard.ruby" + }, + "11": { + "name": "comment.line.hashkey.yard.ruby" + }, + "12": { + "name": "comment.line.defaultvalue.yard.ruby" + }, + "13": { + "name": "comment.line.punctuation.yard.ruby" + }, + "14": { + "name": "comment.line.punctuation.yard.ruby" + } + }, + "end": "^(?!\\s*#\\3\\s{2,}|\\s*#\\s*$)", + "contentName": "comment.line.string.yard.ruby", + "name": "comment.line.number-sign.ruby", + "patterns": [ + { + "include": "#yard" + }, + { + "include": "#yard_continuation" + } + ] + }, + "yard_tag": { + "comment": "For YARD tags that are just the tag", + "match": "^(\\s*)(#)(\\s*)(@)(private)$", + "captures": { + "2": { + "name": "punctuation.definition.comment.ruby" + }, + "4": { + "name": "comment.line.keyword.punctuation.yard.ruby" + }, + "5": { + "name": "comment.line.keyword.yard.ruby" + } + }, + "name": "comment.line.number-sign.ruby" + }, + "yard_types": { + "comment": "For YARD tags that follow the tag-types-comment pattern", + "begin": "^(\\s*)(#)(\\s*)(@)(raise|return|yield(?:return)?)(?=\\s)(\\s+((\\[).+(])))?", + "beginCaptures": { + "2": { + "name": "punctuation.definition.comment.ruby" + }, + "4": { + "name": "comment.line.keyword.punctuation.yard.ruby" + }, + "5": { + "name": "comment.line.keyword.yard.ruby" + }, + "7": { + "name": "comment.line.type.yard.ruby" + }, + "8": { + "name": "comment.line.punctuation.yard.ruby" + }, + "9": { + "name": "comment.line.punctuation.yard.ruby" + } + }, + "end": "^(?!\\s*#\\3\\s{2,}|\\s*#\\s*$)", + "contentName": "comment.line.string.yard.ruby", + "name": "comment.line.number-sign.ruby", + "patterns": [ + { + "include": "#yard" + }, + { + "include": "#yard_continuation" + } + ] + }, + "yard_directive": { + "comment": "For YARD directives", + "begin": "^(\\s*)(#)(\\s*)(@!)(endgroup|group|method|parse|scope|visibility)(\\s+((\\[).+(])))?(?=\\s)", + "beginCaptures": { + "2": { + "name": "punctuation.definition.comment.ruby" + }, + "4": { + "name": "comment.line.keyword.punctuation.yard.ruby" + }, + "5": { + "name": "comment.line.keyword.yard.ruby" + }, + "7": { + "name": "comment.line.type.yard.ruby" + }, + "8": { + "name": "comment.line.punctuation.yard.ruby" + }, + "9": { + "name": "comment.line.punctuation.yard.ruby" + } + }, + "end": "^(?!\\s*#\\3\\s{2,}|\\s*#\\s*$)", + "contentName": "comment.line.string.yard.ruby", + "name": "comment.line.number-sign.ruby", + "patterns": [ + { + "include": "#yard" + }, + { + "include": "#yard_continuation" + } + ] + }, + "yard_continuation": { + "match": "^\\s*#", + "name": "punctuation.definition.comment.ruby" } } } \ No newline at end of file diff --git a/extensions/rust/language-configuration.json b/extensions/rust/language-configuration.json index ecb0007f6ea2..490f4409c652 100644 --- a/extensions/rust/language-configuration.json +++ b/extensions/rust/language-configuration.json @@ -1,25 +1,67 @@ { "comments": { "lineComment": "//", - "blockComment": [ "/*", "*/" ] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - { "open": "\"", "close": "\"", "notIn": ["string"] } + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["<", ">"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "<", + ">" + ] ], "indentationRules": { "increaseIndentPattern": "^.*\\{[^}\"']*$|^.*\\([^\\)\"']*$", @@ -30,5 +72,20 @@ "start": "^\\s*//\\s*#?region\\b", "end": "^\\s*//\\s*#?endregion\\b" } - } + }, + "onEnterRules": [ + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, + ] } diff --git a/extensions/scss/cgmanifest.json b/extensions/scss/cgmanifest.json index 12247769ce22..a67a4f546096 100644 --- a/extensions/scss/cgmanifest.json +++ b/extensions/scss/cgmanifest.json @@ -6,12 +6,12 @@ "git": { "name": "atom/language-sass", "repositoryUrl": "https://github.com/atom/language-sass", - "commitHash": "f52ab12f7f9346cc2568129d8c4419bd3d506b47" + "commitHash": "303bbf0c250fe380b9e57375598cfd916110758b" } }, "license": "MIT", "description": "The file syntaxes/scss.json was derived from the Atom package https://github.com/atom/language-sass which was originally converted from the TextMate bundle https://github.com/alexsancho/SASS.tmbundle.", - "version": "0.62.1" + "version": "0.61.4" } ], "version": 1 diff --git a/extensions/shared.webpack.config.js b/extensions/shared.webpack.config.js index 279bc199bc4a..ad9d70c24908 100644 --- a/extensions/shared.webpack.config.js +++ b/extensions/shared.webpack.config.js @@ -56,6 +56,7 @@ function withNodeDefaults(/**@type WebpackConfig & { context: string }*/extConfi }] }, externals: { + 'electron': 'commonjs electron', // ignored to avoid bundling from node_modules 'vscode': 'commonjs vscode', // ignored because it doesn't exist, 'applicationinsights-native-metrics': 'commonjs applicationinsights-native-metrics', // ignored because we don't ship native module '@azure/functions-core': 'commonjs azure/functions-core', // optioinal dependency of appinsights that we don't use @@ -204,4 +205,3 @@ module.exports.node = withNodeDefaults; module.exports.browser = withBrowserDefaults; module.exports.nodePlugins = nodePlugins; module.exports.browserPlugins = browserPlugins; - diff --git a/extensions/shellscript/package.json b/extensions/shellscript/package.json index 93333abd313d..ab9be7b29ad1 100644 --- a/extensions/shellscript/package.json +++ b/extensions/shellscript/package.json @@ -94,7 +94,8 @@ ], "configurationDefaults": { "[shellscript]": { - "files.eol": "\n" + "files.eol": "\n", + "editor.defaultColorDecorators": "never" } } }, diff --git a/extensions/simple-browser/esbuild-preview.js b/extensions/simple-browser/esbuild-preview.js index cf6e99ca6cb9..9c94a67d56ff 100644 --- a/extensions/simple-browser/esbuild-preview.js +++ b/extensions/simple-browser/esbuild-preview.js @@ -11,7 +11,7 @@ const outDir = path.join(__dirname, 'media'); require('../esbuild-webview-common').run({ entryPoints: { 'index': path.join(srcDir, 'index.ts'), - 'codicon': path.join(__dirname, 'node_modules', 'vscode-codicons', 'dist', 'codicon.css'), + 'codicon': path.join(__dirname, 'node_modules', '@vscode', 'codicons', 'dist', 'codicon.css'), }, srcDir, outdir: outDir, diff --git a/extensions/simple-browser/package-lock.json b/extensions/simple-browser/package-lock.json index b5d97e00873e..d00ca5d995ba 100644 --- a/extensions/simple-browser/package-lock.json +++ b/extensions/simple-browser/package-lock.json @@ -9,155 +9,167 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0" + "@vscode/extension-telemetry": "^1.2.0" }, "devDependencies": { - "@types/vscode-webview": "^1.57.0", - "vscode-codicons": "^0.0.14" + "@types/vscode-webview": "^1.57.5", + "@vscode/codicons": "^0.0.44" }, "engines": { "vscode": "^1.70.0" } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.0.3.tgz", - "integrity": "sha512-FrxNLVAPsAvD7+l63TlNS/Kodvpct2WulpDSn1dI4Xuy0kF4E2H867kHdwL/iY1Bj3zA3FSy/jvE4+OcDws7ug==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.0.3.tgz", - "integrity": "sha512-uewvmUtXKd7ttypiKQGdYI6i7UUpPkOznLayzIFrJ4r2xnG6jhPjpKRncHFXPQcM4XSWO3yf5PQ3xAbPq9t7ZQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.0.4.tgz", - "integrity": "sha512-6TlfExmErQ8Y+/ChbkyWl+jyt4wg3T6p7lwXDsUCB0LgZmlEWMaCUS0YlT73JCWmE8j7vxW8yUm0lgsgmHns3A==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.0.4.tgz", - "integrity": "sha512-r5gWaw/K9+tKfuo2GtDiDiKASgOkPOCrKW+wZzFvuR06uuwvWjbVQ6yW/YbnfuhRF5M65ksUiMi0eCMwEOGq7Q==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.0.4.tgz", - "integrity": "sha512-anxy5kEkqBmVoEqJiJzaaXXA0wzqZi9U4zGd05xFJ04lWckP8dG3zyT3+GGdg7rDelqLTNGxndeYoFmDv63u1g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", "dependencies": { "@nevware21/ts-utils": ">= 0.9.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.0.4.tgz", - "integrity": "sha512-KfoxPlLlf0JT12ADb23C5iGye/yFouoMgHEKULxkSQcYY9SsW/8rVrqqvoYKAL+u215CZU2A8Kc8sR3ehEaPCQ==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.0.4", - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", - "@microsoft/dynamicproto-js": "^2.0.2", - "@nevware21/ts-async": ">= 0.3.0 < 2.x", - "@nevware21/ts-utils": ">= 0.10.1 < 2.x" + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.2.tgz", - "integrity": "sha512-MB8trWaFREpmb037k/d0bB7T2BP7Ai24w1e1tbz3ASLB0/lwphsq3Nq8S9I5AsI5vs4zAQT+SB5nC5/dLYTiOg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@nevware21/ts-async": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.3.0.tgz", - "integrity": "sha512-ZUcgUH12LN/F6nzN0cYd0F/rJaMLmXr0EHVTyYfaYmK55bdwE4338uue4UiVoRqHVqNW4KDUrJc49iGogHKeWA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.4.tgz", + "integrity": "sha512-IBTyj29GwGlxfzXw2NPnzty+w0Adx61Eze1/lknH/XIVdxtF9UnOpk76tnrHXWa6j84a1RR9hsOcHQPFv9qJjA==", + "license": "MIT", "dependencies": { - "@nevware21/ts-utils": ">= 0.10.0 < 2.x" + "@nevware21/ts-utils": ">= 0.11.6 < 2.x" } }, "node_modules/@nevware21/ts-utils": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.10.1.tgz", - "integrity": "sha512-pMny25NnF2/MJwdqC3Iyjm2pGIXNxni4AROpcqDeWa+td9JMUY4bUS9uU9XW+BoBRqTLUL+WURF9SOd/6OQzRg==" + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", + "license": "MIT" }, "node_modules/@types/vscode-webview": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/@types/vscode-webview/-/vscode-webview-1.57.0.tgz", - "integrity": "sha512-x3Cb/SMa1IwRHfSvKaZDZOTh4cNoG505c3NjTqGlMC082m++x/ETUmtYniDsw6SSmYzZXO8KBNhYxR0+VqymqA==", - "dev": true + "version": "1.57.5", + "resolved": "https://registry.npmjs.org/@types/vscode-webview/-/vscode-webview-1.57.5.tgz", + "integrity": "sha512-iBAUYNYkz+uk1kdsq05fEcoh8gJmwT3lqqFPN7MGyjQ3HVloViMdo7ZJ8DFIP8WOK74PjOEilosqAyxV2iUFUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vscode/codicons": { + "version": "0.0.44", + "resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.44.tgz", + "integrity": "sha512-F7qPRumUK3EHjNdopfICLGRf3iNPoZQt+McTHAn4AlOWPB3W2kL4H0S7uqEqbyZ6rCxaeDjpAn3MCUnwTu/VJQ==", + "dev": true, + "license": "CC-BY-4.0" }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.0.tgz", - "integrity": "sha512-37RxGHXrs3GoXPgCUKQhghEu0gxs8j27RLjQwwtSf4WhPdJKz8UrqMYzpsXlliQ05zURYmtdGZst9C6+hfWXaQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.2.0.tgz", + "integrity": "sha512-En6dTwfy5NFzSMibvOpx/lKq2jtgWuR4++KJbi3SpQ2iT8gm+PHo9868/scocW122KDwTxl4ruxZ7i4rHmJJnQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.0.3", - "@microsoft/1ds-post-js": "^4.0.3", - "@microsoft/applicationinsights-web-basic": "^3.0.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" } - }, - "node_modules/vscode-codicons": { - "version": "0.0.14", - "resolved": "https://registry.npmjs.org/vscode-codicons/-/vscode-codicons-0.0.14.tgz", - "integrity": "sha512-6CEH5KT9ct5WMw7n5dlX7rB8ya4CUI2FSq1Wk36XaW+c5RglFtAanUV0T+gvZVVFhl/WxfjTvFHq06Hz9c1SLA==", - "deprecated": "This package is deprecated, please use @vscode/codicons https://www.npmjs.com/package/@vscode/codicons", - "dev": true } } } diff --git a/extensions/simple-browser/package.json b/extensions/simple-browser/package.json index 982333b33eff..e70ac07ad0ec 100644 --- a/extensions/simple-browser/package.json +++ b/extensions/simple-browser/package.json @@ -66,11 +66,11 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.0" + "@vscode/extension-telemetry": "^1.2.0" }, "devDependencies": { - "@types/vscode-webview": "^1.57.0", - "vscode-codicons": "^0.0.14" + "@types/vscode-webview": "^1.57.5", + "@vscode/codicons": "^0.0.44" }, "repository": { "type": "git", diff --git a/extensions/simple-browser/preview-src/index.ts b/extensions/simple-browser/preview-src/index.ts index 3d804aa60fa4..106146f86579 100644 --- a/extensions/simple-browser/preview-src/index.ts +++ b/extensions/simple-browser/preview-src/index.ts @@ -90,18 +90,23 @@ onceDocumentLoaded(() => { toggleFocusLockIndicatorEnabled(settings.focusLockIndicatorEnabled); function navigateTo(rawUrl: string): void { + let safeUrl: string | null = null; try { const url = new URL(rawUrl); - - // Try to bust the cache for the iframe - // There does not appear to be any way to reliably do this except modifying the url - url.searchParams.append('vscodeBrowserReqId', Date.now().toString()); - - iframe.src = url.toString(); + if (url.protocol === 'http:' || url.protocol === 'https:') { + // Try to bust the cache for the iframe + url.searchParams.append('vscodeBrowserReqId', Date.now().toString()); + safeUrl = url.toString(); + } } catch { - iframe.src = rawUrl; + // On parse error, do not attempt to match with regex; keep safeUrl as null. + } + if (safeUrl) { + iframe.src = safeUrl; + } else { + // Optionally, display an error or set to about:blank + iframe.src = 'about:blank'; } - vscode.setState({ url: rawUrl }); } }); diff --git a/extensions/sql/cgmanifest.json b/extensions/sql/cgmanifest.json index fec241862ef0..4a1455ebd319 100644 --- a/extensions/sql/cgmanifest.json +++ b/extensions/sql/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "microsoft/vscode-mssql", "repositoryUrl": "https://github.com/microsoft/vscode-mssql", - "commitHash": "49eff02f68b6ee73025c6665c672ca1c93385dde" + "commitHash": "d07e0f838eabff968e4387841427d3c3af8aeec6" } }, "license": "MIT", - "version": "1.23.0" + "version": "1.29.0" } ], "version": 1 diff --git a/extensions/sql/syntaxes/sql.tmLanguage.json b/extensions/sql/syntaxes/sql.tmLanguage.json index 9db452210265..4341bc81528f 100644 --- a/extensions/sql/syntaxes/sql.tmLanguage.json +++ b/extensions/sql/syntaxes/sql.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-mssql/commit/49eff02f68b6ee73025c6665c672ca1c93385dde", + "version": "https://github.com/microsoft/vscode-mssql/commit/d07e0f838eabff968e4387841427d3c3af8aeec6", "name": "SQL", "scopeName": "source.sql", "patterns": [ @@ -372,7 +372,7 @@ "include": "#regexps" }, { - "match": "\\b(?i)(abort|abort_after_wait|absent|absolute|accent_sensitivity|acceptable_cursopt|acp|action|activation|add|address|admin|aes_128|aes_192|aes_256|affinity|after|aggregate|algorithm|all_constraints|all_errormsgs|all_indexes|all_levels|all_results|allow_connections|allow_dup_row|allow_encrypted_value_modifications|allow_page_locks|allow_row_locks|allow_snapshot_isolation|alter|altercolumn|always|anonymous|ansi_defaults|ansi_null_default|ansi_null_dflt_off|ansi_null_dflt_on|ansi_nulls|ansi_padding|ansi_warnings|appdomain|append|application|apply|arithabort|arithignore|array|assembly|asymmetric|asynchronous_commit|at|atan2|atomic|attach|attach_force_rebuild_log|attach_rebuild_log|audit|auth_realm|authentication|auto|auto_cleanup|auto_close|auto_create_statistics|auto_drop|auto_shrink|auto_update_statistics|auto_update_statistics_async|automated_backup_preference|automatic|autopilot|availability|availability_mode|backup|backup_priority|base64|basic|batches|batchsize|before|between|bigint|binary|binding|bit|block|blockers|blocksize|bmk|both|break|broker|broker_instance|bucket_count|buffer|buffercount|bulk_logged|by|call|caller|card|case|catalog|catch|cert|certificate|change_retention|change_tracking|change_tracking_context|changes|char|character|character_set|check_expiration|check_policy|checkconstraints|checkindex|checkpoint|checksum|cleanup_policy|clear|clear_port|close|clustered|codepage|collection|column_encryption_key|column_master_key|columnstore|columnstore_archive|colv_80_to_100|colv_100_to_80|commit_differential_base|committed|compatibility_level|compress_all_row_groups|compression|compression_delay|concat_null_yields_null|concatenate|configuration|connect|connection|containment|continue|continue_after_error|contract|contract_name|control|conversation|conversation_group_id|conversation_handle|copy|copy_only|count_rows|counter|create(\\\\s+or\\\\s+alter)?|credential|cross|cryptographic|cryptographic_provider|cube|cursor|cursor_close_on_commit|cursor_default|data|data_compression|data_flush_interval_seconds|data_mirroring|data_purity|data_source|database|database_name|database_snapshot|datafiletype|date_correlation_optimization|date|datefirst|dateformat|date_format|datetime|datetime2|datetimeoffset|day(s)?|db_chaining|dbid|dbidexec|dbo_only|deadlock_priority|deallocate|dec|decimal|declare|decrypt|decrypt_a|decryption|default_database|default_fulltext_language|default_language|default_logon_domain|default_schema|definition|delay|delayed_durability|delimitedtext|density_vector|dependent|des|description|desired_state|desx|differential|digest|disable|disable_broker|disable_def_cnst_chk|disabled|disk|distinct|distributed|distribution|drop|drop_existing|dts_buffers|dump|durability|dynamic|edition|elements|else|emergency|empty|enable|enable_broker|enabled|encoding|encrypted|encrypted_value|encryption|encryption_type|end|endpoint|endpoint_url|enhancedintegrity|entry|error_broker_conversations|errorfile|estimateonly|event|except|exec|executable|execute|exists|expand|expiredate|expiry_date|explicit|external|external_access|failover|failover_mode|failure_condition_level|fast|fast_forward|fastfirstrow|federated_service_account|fetch|field_terminator|fieldterminator|file|filelistonly|filegroup|filegrowth|filename|filestream|filestream_log|filestream_on|filetable|file_format|filter|first_row|fips_flagger|fire_triggers|first|firstrow|float|flush_interval_seconds|fmtonly|following|for|force|force_failover_allow_data_loss|force_service_allow_data_loss|forced|forceplan|formatfile|format_options|format_type|formsof|forward_only|free_cursors|free_exec_context|fullscan|fulltext|fulltextall|fulltextkey|function|generated|get|geography|geometry|global|go|goto|governor|guid|hadoop|hardening|hash|hashed|header_limit|headeronly|health_check_timeout|hidden|hierarchyid|histogram|histogram_steps|hits_cursors|hits_exec_context|hour(s)?|http|identity|identity_value|if|ifnull|ignore|ignore_constraints|ignore_dup_key|ignore_dup_row|ignore_triggers|image|immediate|implicit_transactions|include|include_null_values|incremental|index|inflectional|init|initiator|insensitive|insert|instead|int|integer|integrated|intersect|intermediate|interval_length_minutes|into|inuse_cursors|inuse_exec_context|io|is|isabout|iso_week|isolation|job_tracker_location|json|keep|keep_nulls|keep_replication|keepdefaults|keepfixed|keepidentity|keepnulls|kerberos|key|key_path|key_source|key_store_provider_name|keyset|kill|kilobytes_per_batch|labelonly|langid|language|last|lastrow|leading|legacy_cardinality_estimation|length|level|lifetime|lineage_80_to_100|lineage_100_to_80|listener_ip|listener_port|load|loadhistory|lob_compaction|local|local_service_name|locate|location|lock_escalation|lock_timeout|lockres|log|login|login_type|loop|manual|mark_in_use_for_removal|masked|master|match|matched|max_queue_readers|max_duration|max_outstanding_io_per_volume|maxdop|maxerrors|maxlength|maxtransfersize|max_plans_per_query|max_storage_size_mb|mediadescription|medianame|mediapassword|memogroup|memory_optimized|merge|message|message_forward_size|message_forwarding|microsecond|millisecond|minute(s)?|mirror_address|misses_cursors|misses_exec_context|mixed|modify|money|month|move|multi_user|must_change|name|namespace|nanosecond|native|native_compilation|nchar|ncharacter|nested_triggers|never|new_account|new_broker|newname|next|no|no_browsetable|no_checksum|no_compression|no_infomsgs|no_triggers|no_truncate|nocount|noexec|noexpand|noformat|noinit|nolock|nonatomic|nonclustered|nondurable|none|norecompute|norecovery|noreset|norewind|noskip|not|notification|nounload|now|nowait|ntext|ntlm|nulls|numeric|numeric_roundabort|nvarchar|object|objid|oem|offline|old_account|online|operation_mode|open|openjson|optimistic|option|orc|out|outer|output|over|override|owner|ownership|pad_index|page|page_checksum|page_verify|pagecount|paglock|param|parameter_sniffing|parameter_type_expansion|parameterization|parquet|parseonly|partial|partition|partner|password|path|pause|percentage|permission_set|persisted|period|physical_only|plan_forcing_mode|policy|pool|population|ports|preceding|precision|predicate|presume_abort|primary|primary_role|print|prior|priority |priority_level|private|proc(edure)?|procedure_name|profile|provider|quarter|query_capture_mode|query_governor_cost_limit|query_optimizer_hotfixes|query_store|queue|quoted_identifier|raiserror|range|raw|rcfile|rc2|rc4|rc4_128|rdbms|read_committed_snapshot|read|read_only|read_write|readcommitted|readcommittedlock|readonly|readpast|readuncommitted|readwrite|real|rebuild|receive|recmodel_70backcomp|recompile|reconfigure|recovery|recursive|recursive_triggers|redo_queue|reject_sample_value|reject_type|reject_value|relative|remote|remote_data_archive|remote_proc_transactions|remote_service_name|remove|removed_cursors|removed_exec_context|reorganize|repeat|repeatable|repeatableread|replace|replica|replicated|replnick_100_to_80|replnickarray_80_to_100|replnickarray_100_to_80|required|required_cursopt|resample|reset|resource|resource_manager_location|respect|restart|restore|restricted_user|resume|retaindays|retention|return|revert|rewind|rewindonly|returns|robust|role|rollup|root|round_robin|route|row|rowdump|rowguidcol|rowlock|row_terminator|rows|rows_per_batch|rowsets_only|rowterminator|rowversion|rsa_1024|rsa_2048|rsa_3072|rsa_4096|rsa_512|safe|safety|sample|save|scalar|schema|schemabinding|scoped|scroll|scroll_locks|sddl|second|secexpr|seconds|secondary|secondary_only|secondary_role|secret|security|securityaudit|selective|self|send|sent|sequence|serde_method|serializable|server|service|service_broker|service_name|service_objective|session_timeout|session|sessions|seterror|setopts|sets|shard_map_manager|shard_map_name|sharded|shared_memory|shortest_path|show_statistics|showplan_all|showplan_text|showplan_xml|showplan_xml_with_recompile|shrinkdb|shutdown|sid|signature|simple|single_blob|single_clob|single_nclob|single_user|singleton|site|size|size_based_cleanup_mode|skip|smalldatetime|smallint|smallmoney|snapshot|snapshot_import|snapshotrestorephase|soap|softnuma|sort_in_tempdb|sorted_data|sorted_data_reorg|spatial|sql|sql_bigint|sql_binary|sql_bit|sql_char|sql_date|sql_decimal|sql_double|sql_float|sql_guid|sql_handle|sql_longvarbinary|sql_longvarchar|sql_numeric|sql_real|sql_smallint|sql_time|sql_timestamp|sql_tinyint|sql_tsi_day|sql_tsi_frac_second|sql_tsi_hour|sql_tsi_minute|sql_tsi_month|sql_tsi_quarter|sql_tsi_second|sql_tsi_week|sql_tsi_year|sql_type_date|sql_type_time|sql_type_timestamp|sql_varbinary|sql_varchar|sql_variant|sql_wchar|sql_wlongvarchar|ssl|ssl_port|standard|standby|start|start_date|started|stat_header|state|statement|static|statistics|statistics_incremental|statistics_norecompute|statistics_only|statman|stats|stats_stream|status|stop|stop_on_error|stopat|stopatmark|stopbeforemark|stoplist|stopped|string_delimiter|subject|supplemental_logging|supported|suspend|symmetric|synchronous_commit|synonym|sysname|system|system_time|system_versioning|table|tableresults|tablock|tablockx|take|tape|target|target_index|target_partition|target_recovery_time|tcp|temporal_history_retention|text|textimage_on|then|thesaurus|throw|time|timeout|timestamp|tinyint|to|top|torn_page_detection|track_columns_updated|trailing|tran|transaction|transfer|transform_noise_words|triple_des|triple_des_3key|truncate|trustworthy|try|tsql|two_digit_year_cutoff|type|type_desc|type_warning|tzoffset|uid|unbounded|uncommitted|unique|uniqueidentifier|unlimited|unload|unlock|unsafe|updlock|url|use|useplan|useroptions|use_type_default|using|utcdatetime|valid_xml|validation|value|values|varbinary|varchar|verbose|verifyonly|version|view_metadata|virtual_device|visiblity|wait_at_low_priority|waitfor|webmethod|week|weekday|weight|well_formed_xml|when|while|widechar|widechar_ansi|widenative|window|windows|with|within|within group|witness|without|without_array_wrapper|workload|wsdl|xact_abort|xlock|xml|xmlschema|xquery|xsinil|year|zone)\\b", + "match": "\\b(?i)(abort|abort_after_wait|absent|absolute|accent_sensitivity|acceptable_cursopt|acp|action|activation|add|address|admin|aes_128|aes_192|aes_256|affinity|after|aggregate|algorithm|all_constraints|all_errormsgs|all_indexes|all_levels|all_results|allow_connections|allow_dup_row|allow_encrypted_value_modifications|allow_page_locks|allow_row_locks|allow_snapshot_isolation|alter|altercolumn|always|anonymous|ansi_defaults|ansi_null_default|ansi_null_dflt_off|ansi_null_dflt_on|ansi_nulls|ansi_padding|ansi_warnings|appdomain|append|application|apply|arithabort|arithignore|array|assembly|asymmetric|asynchronous_commit|at|atan2|atomic|attach|attach_force_rebuild_log|attach_rebuild_log|audit|auth_realm|authentication|auto|auto_cleanup|auto_close|auto_create_statistics|auto_drop|auto_shrink|auto_update_statistics|auto_update_statistics_async|automated_backup_preference|automatic|autopilot|availability|availability_mode|backup|backup_priority|base64|basic|batches|batchsize|before|between|bigint|binary|binding|bit|block|blockers|blocksize|bmk|both|break|broker|broker_instance|bucket_count|buffer|buffercount|bulk_logged|by|call|caller|card|case|catalog|catch|cert|certificate|change_retention|change_tracking|change_tracking_context|changes|char|character|character_set|check_expiration|check_policy|checkconstraints|checkindex|checkpoint|checksum|cleanup_policy|clear|clear_port|close|clustered|codepage|collection|column_encryption_key|column_master_key|columnstore|columnstore_archive|colv_80_to_100|colv_100_to_80|commit_differential_base|committed|compatibility_level|compress_all_row_groups|compression|compression_delay|concat_null_yields_null|concatenate|configuration|connect|connection|containment|continue|continue_after_error|contract|contract_name|control|conversation|conversation_group_id|conversation_handle|copy|copy_only|count_rows|counter|create(\\\\s+or\\\\s+alter)?|credential|cross|cryptographic|cryptographic_provider|cube|cursor|cursor_close_on_commit|cursor_default|data|data_compression|data_flush_interval_seconds|data_mirroring|data_purity|data_source|database|database_name|database_snapshot|datafiletype|date_correlation_optimization|date|datefirst|dateformat|date_format|datetime|datetime2|datetimeoffset|day(s)?|db_chaining|dbid|dbidexec|dbo_only|deadlock_priority|deallocate|dec|decimal|declare|decrypt|decrypt_a|decryption|default_database|default_fulltext_language|default_language|default_logon_domain|default_schema|definition|delay|delayed_durability|delimitedtext|density_vector|dependent|des|description|desired_state|desx|differential|digest|disable|disable_broker|disable_def_cnst_chk|disabled|disk|distinct|distributed|distribution|drop|drop_existing|dts_buffers|dump|durability|dynamic|edition|elements|else|emergency|empty|enable|enable_broker|enabled|encoding|encrypted|encrypted_value|encryption|encryption_type|end|endpoint|endpoint_url|enhancedintegrity|entry|error_broker_conversations|errorfile|estimateonly|event|except|exec|executable|execute|exists|expand|expiredate|expiry_date|explicit|external|external_access|failover|failover_mode|failure_condition_level|fast|fast_forward|fastfirstrow|federated_service_account|fetch|field_terminator|fieldterminator|file|filelistonly|filegroup|filegrowth|filename|filestream|filestream_log|filestream_on|filetable|file_format|filter|first_row|fips_flagger|fire_triggers|first|firstrow|float|flush_interval_seconds|fmtonly|following|for|force|force_failover_allow_data_loss|force_service_allow_data_loss|forced|forceplan|formatfile|format_options|format_type|formsof|forward_only|free_cursors|free_exec_context|fullscan|fulltext|fulltextall|fulltextkey|function|generated|get|geography|geometry|global|go|goto|governor|guid|hadoop|hardening|hash|hashed|header_limit|headeronly|health_check_timeout|hidden|hierarchyid|histogram|histogram_steps|hits_cursors|hits_exec_context|hour(s)?|http|identity|identity_value|if|ifnull|ignore|ignore_constraints|ignore_dup_key|ignore_dup_row|ignore_triggers|image|immediate|implicit_transactions|include|include_null_values|incremental|index|inflectional|init|initiator|insensitive|insert|instead|int|integer|integrated|intersect|intermediate|interval_length_minutes|into|inuse_cursors|inuse_exec_context|io|is|isabout|iso_week|isolation|job_tracker_location|json|keep|keep_nulls|keep_replication|keepdefaults|keepfixed|keepidentity|keepnulls|kerberos|key|key_path|key_source|key_store_provider_name|keyset|kill|kilobytes_per_batch|labelonly|langid|language|last|lastrow|leading|legacy_cardinality_estimation|length|level|lifetime|lineage_80_to_100|lineage_100_to_80|listener_ip|listener_port|load|loadhistory|lob_compaction|local|local_service_name|locate|location|lock_escalation|lock_timeout|lockres|log|login|login_type|loop|manual|mark_in_use_for_removal|masked|master|match|matched|max_queue_readers|max_duration|max_outstanding_io_per_volume|maxdop|maxerrors|maxlength|maxtransfersize|max_plans_per_query|max_storage_size_mb|mediadescription|medianame|mediapassword|memogroup|memory_optimized|merge|message|message_forward_size|message_forwarding|microsecond|millisecond|minute(s)?|mirror_address|misses_cursors|misses_exec_context|mixed|modify|money|month|move|multi_user|must_change|name|namespace|nanosecond|native|native_compilation|nchar|ncharacter|nested_triggers|never|new_account|new_broker|newname|next|no|no_browsetable|no_checksum|no_compression|no_infomsgs|no_triggers|no_truncate|nocount|noexec|noexpand|noformat|noinit|nolock|nonatomic|nonclustered|nondurable|none|norecompute|norecovery|noreset|norewind|noskip|not|notification|nounload|now|nowait|ntext|ntlm|nulls|numeric|numeric_roundabort|nvarchar|object|objid|oem|offline|old_account|online|operation_mode|open|openjson|optimistic|option|orc|out|outer|output|over|override|owner|ownership|pad_index|page|page_checksum|page_verify|pagecount|paglock|param|parameter_sniffing|parameter_type_expansion|parameterization|parquet|parseonly|partial|partition|partner|password|path|pause|percentage|permission_set|persisted|period|physical_only|plan_forcing_mode|policy|pool|population|ports|preceding|precision|predicate|presume_abort|primary|primary_role|print|prior|priority |priority_level|private|proc(edure)?|procedure_name|profile|provider|quarter|query_capture_mode|query_governor_cost_limit|query_optimizer_hotfixes|query_store|queue|quoted_identifier|raiserror|range|raw|rcfile|rc2|rc4|rc4_128|rdbms|read_committed_snapshot|read|read_only|read_write|readcommitted|readcommittedlock|readonly|readpast|readuncommitted|readwrite|real|rebuild|receive|recmodel_70backcomp|recompile|reconfigure|recovery|recursive|recursive_triggers|redo_queue|reject_sample_value|reject_type|reject_value|relative|remote|remote_data_archive|remote_proc_transactions|remote_service_name|remove|removed_cursors|removed_exec_context|reorganize|repeat|repeatable|repeatableread|replace|replica|replicated|replnick_100_to_80|replnickarray_80_to_100|replnickarray_100_to_80|required|required_cursopt|resample|reset|resource|resource_manager_location|respect|restart|restore|restricted_user|resume|retaindays|retention|return|revert|rewind|rewindonly|returns|robust|role|rollup|root|round_robin|route|row|rowdump|rowguidcol|rowlock|row_terminator|rows|rows_per_batch|rowsets_only|rowterminator|rowversion|rsa_1024|rsa_2048|rsa_3072|rsa_4096|rsa_512|safe|safety|sample|save|scalar|schema|schemabinding|scoped|scroll|scroll_locks|sddl|second|secexpr|seconds|secondary|secondary_only|secondary_role|secret|security|securityaudit|selective|self|send|sent|sequence|serde_method|serializable|server|service|service_broker|service_name|service_objective|session_timeout|session|sessions|seterror|setopts|sets|shard_map_manager|shard_map_name|sharded|shared_memory|shortest_path|show_statistics|showplan_all|showplan_text|showplan_xml|showplan_xml_with_recompile|shrinkdb|shutdown|sid|signature|simple|single_blob|single_clob|single_nclob|single_user|singleton|site|size|size_based_cleanup_mode|skip|smalldatetime|smallint|smallmoney|snapshot|snapshot_import|snapshotrestorephase|soap|softnuma|sort_in_tempdb|sorted_data|sorted_data_reorg|spatial|sql|sql_bigint|sql_binary|sql_bit|sql_char|sql_date|sql_decimal|sql_double|sql_float|sql_guid|sql_handle|sql_longvarbinary|sql_longvarchar|sql_numeric|sql_real|sql_smallint|sql_time|sql_timestamp|sql_tinyint|sql_tsi_day|sql_tsi_frac_second|sql_tsi_hour|sql_tsi_minute|sql_tsi_month|sql_tsi_quarter|sql_tsi_second|sql_tsi_week|sql_tsi_year|sql_type_date|sql_type_time|sql_type_timestamp|sql_varbinary|sql_varchar|sql_variant|sql_wchar|sql_wlongvarchar|ssl|ssl_port|standard|standby|start|start_date|started|stat_header|state|statement|static|statistics|statistics_incremental|statistics_norecompute|statistics_only|statman|stats|stats_stream|status|stop|stop_on_error|stopat|stopatmark|stopbeforemark|stoplist|stopped|string_delimiter|subject|supplemental_logging|supported|suspend|symmetric|synchronous_commit|synonym|sysname|system|system_time|system_versioning|table|tableresults|tablock|tablockx|take|tape|target|target_index|target_partition|target_recovery_time|tcp|temporal_history_retention|text|textimage_on|then|thesaurus|throw|time|timeout|timestamp|tinyint|to|top|torn_page_detection|track_columns_updated|trailing|tran|transaction|transfer|transform_noise_words|triple_des|triple_des_3key|truncate|trustworthy|try|tsql|two_digit_year_cutoff|type|type_desc|type_warning|tzoffset|uid|unbounded|uncommitted|unique|uniqueidentifier|unlimited|unload|unlock|unsafe|updlock|url|use|useplan|useroptions|use_type_default|using|utcdatetime|valid_xml|validation|value|values|varbinary|varchar|vector|verbose|verifyonly|version|view_metadata|virtual_device|visiblity|wait_at_low_priority|waitfor|webmethod|week|weekday|weight|well_formed_xml|when|while|widechar|widechar_ansi|widenative|window|windows|with|within|within group|witness|without|without_array_wrapper|workload|wsdl|xact_abort|xlock|xml|xmlschema|xquery|xsinil|year|zone)\\b", "name": "keyword.other.sql" }, { diff --git a/extensions/swift/cgmanifest.json b/extensions/swift/cgmanifest.json index cb1ca02310f9..d40d7c7e6e5e 100644 --- a/extensions/swift/cgmanifest.json +++ b/extensions/swift/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "jtbandes/swift-tmlanguage", "repositoryUrl": "https://github.com/jtbandes/swift-tmlanguage", - "commitHash": "860eface4241cf9f2174d5fa690bd34389ac8d26" + "commitHash": "b8d2889b4af1d8bad41578317a6adade642555a3" } }, "license": "MIT" diff --git a/extensions/swift/language-configuration.json b/extensions/swift/language-configuration.json index 54095ef5212e..e1ceb1f6bc6f 100644 --- a/extensions/swift/language-configuration.json +++ b/extensions/swift/language-configuration.json @@ -1,27 +1,99 @@ { "comments": { "lineComment": "//", - "blockComment": [ "/*", "*/" ] + "blockComment": [ + "/*", + "*/" + ] }, "brackets": [ - ["{", "}"], - ["[", "]"], - ["(", ")"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] ], "autoClosingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - { "open": "\"", "close": "\"", "notIn": ["string"] }, - { "open": "'", "close": "'", "notIn": ["string"] }, - { "open": "`", "close": "`", "notIn": ["string"] } + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string" + ] + }, + { + "open": "`", + "close": "`", + "notIn": [ + "string" + ] + } ], "surroundingPairs": [ - ["{", "}"], - ["[", "]"], - ["(", ")"], - ["\"", "\""], - ["'", "'"], - ["`", "`"] + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "\"", + "\"" + ], + [ + "'", + "'" + ], + [ + "`", + "`" + ] + ], + "onEnterRules": [ + // Add // when pressing enter from inside line comment + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, ] } diff --git a/extensions/swift/syntaxes/swift.tmLanguage.json b/extensions/swift/syntaxes/swift.tmLanguage.json index b18b340f2c68..7d6694cbead3 100644 --- a/extensions/swift/syntaxes/swift.tmLanguage.json +++ b/extensions/swift/syntaxes/swift.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/jtbandes/swift-tmlanguage/commit/860eface4241cf9f2174d5fa690bd34389ac8d26", + "version": "https://github.com/jtbandes/swift-tmlanguage/commit/b8d2889b4af1d8bad41578317a6adade642555a3", "name": "Swift", "scopeName": "source.swift", "comment": "See swift.tmbundle/grammar-test.swift for test cases.", @@ -3081,7 +3081,7 @@ { "comment": "Single-line regular expression literals must be matched all in one go\n in order to avoid ambiguities with operators, and to adhere to certain\n parsing rules in SE-0354/SE-0355, such as:\n - A regex literal will not be parsed if it contains an unbalanced ).\n - A regex may end with a space only if it began with an escaped space", "name": "string.regexp.line.swift", - "match": "(?x)\n(((\\#+)?)/) # (1) for captures, (2) for matching end, (3) for conditionals\n(?(3)|(?!/)) # is not a comment\n(?(3)|(?!\\s)) # does not start with a space or tab\n(\\\\\\s)? # (4) may start with an escaped space or tab\n(?\n (?> # no backtracking, avoids issues with negative lookbehind at end\n (?:\n \\\\Q\n (?:(?!\\\\E)(?!/\\2).)*+\n (?:\\\\E\n # A quoted sequence may not have a closing E, in which case it extends to the end of the regex\n | (?(3)|(?\\{)?+(?\\{)?+(?\\{)?+(?\\{)?+(?\\{)?+\n .+?\n \\}(?()\\})(?()\\})(?()\\})(?()\\})(?()\\})\n (?:\\[(?!\\d)\\w+\\])?\n [X<>]?\n \\)\n | (?\\[ (?:\\\\. | [^\\[\\]] | \\g)+ \\])\n | \\(\\g?+\\)\n | (?:(?!/\\2)[^()\\[\\\\])+ # any character (until end)\n )+\n )\n)?+\n# may end with a space only if it is an extended literal or contains only a single escaped space\n(?(3)|(?(5)(?\n (?> # no backtracking, avoids issues with negative lookbehind at end\n (?:\n \\\\Q\n (?:(?!\\\\E)(?!/\\2).)*+\n (?:\\\\E\n # A quoted sequence may not have a closing E, in which case it extends to the end of the regex\n | (?(3)|(?(\\{(?:\\g<-1>|(?!{).*?)\\}))\n (?:\\[(?!\\d)\\w+\\])?\n [X<>]?\n \\)\n | (?\\[ (?:\\\\. | [^\\[\\]] | \\g)+ \\])\n | \\(\\g?+\\)\n | (?:(?!/\\2)[^()\\[\\\\])+ # any character (until end)\n )+\n )\n)?+\n# may end with a space only if it is an extended literal or contains only a single escaped space\n(?(3)|(?(5)(?'\n \"\\k'\" NamedOrNumberRef \"'\"\n '\\g<' NamedOrNumberRef '>'\n \"\\g'\" NamedOrNumberRef \"'\"", - "match": "(?x)(\\\\[gk](<)|\\\\[gk]') (?: ((?!\\d)\\w+) (?:([+-])(\\d+))? | ([+-]?\\d+) (?:([+-])(\\d+))? ) ((?(2)>|'))", + "comment": "'\\k<' NamedOrNumberRef '>'\n '\\g<' NamedOrNumberRef '>'", + "match": "(?x)(\\\\[gk]<) (?: ((?!\\d)\\w+) (?:([+-])(\\d+))? | ([+-]?\\d+) (?:([+-])(\\d+))? ) (>)", "captures": { "1": { "name": "constant.character.escape.backslash.regexp" }, - "3": { + "2": { "name": "variable.other.group-name.regexp" }, - "4": { + "3": { "name": "keyword.operator.recursion-level.regexp" }, + "4": { + "name": "constant.numeric.integer.decimal.regexp" + }, "5": { "name": "constant.numeric.integer.decimal.regexp" }, "6": { - "name": "constant.numeric.integer.decimal.regexp" + "name": "keyword.operator.recursion-level.regexp" }, "7": { - "name": "keyword.operator.recursion-level.regexp" + "name": "constant.numeric.integer.decimal.regexp" }, "8": { + "name": "constant.character.escape.backslash.regexp" + } + } + }, + { + "comment": "\"\\k'\" NamedOrNumberRef \"'\"\n \"\\g'\" NamedOrNumberRef \"'\"", + "match": "(?x)(\\\\[gk]') (?: ((?!\\d)\\w+) (?:([+-])(\\d+))? | ([+-]?\\d+) (?:([+-])(\\d+))? ) (')", + "captures": { + "1": { + "name": "constant.character.escape.backslash.regexp" + }, + "2": { + "name": "variable.other.group-name.regexp" + }, + "3": { + "name": "keyword.operator.recursion-level.regexp" + }, + "4": { "name": "constant.numeric.integer.decimal.regexp" }, - "9": { + "5": { + "name": "constant.numeric.integer.decimal.regexp" + }, + "6": { + "name": "keyword.operator.recursion-level.regexp" + }, + "7": { + "name": "constant.numeric.integer.decimal.regexp" + }, + "8": { "name": "constant.character.escape.backslash.regexp" } } @@ -3291,7 +3321,7 @@ }, "literals-regular-expression-literal-callout": { "name": "meta.callout.regexp", - "match": "(?x)\n# PCRECallout\n(\\()(?\\?C)\n (?:\n (?\\d+)\n | `(?(?:[^`]|``)*)`\n | '(?(?:[^']|'')*)'\n | \"(?(?:[^\"]|\"\")*)\"\n | \\^(?(?:[^\\^]|\\^\\^)*)\\^\n | %(?(?:[^%]|%%)*)%\n | \\#(?(?:[^#]|\\#\\#)*)\\#\n | \\$(?(?:[^$]|\\$\\$)*)\\$\n | \\{(?(?:[^}]|\\}\\})*)\\}\n )?\n(\\))\n# NamedCallout\n| (\\()(?\\*)\n (?(?!\\d)\\w+)\n (?:\\[(?(?!\\d)\\w+)\\])?\n (?:\\{ [^,}]+ (?:,[^,}]+)* \\})?\n (\\))\n# InterpolatedCallout\n| (\\()(?\\?)\n # we only support a fixed maximum number of braces because otherwise we can't balance the number of open and close braces\n (\\{(?\\{)?+(?\\{)?+(?\\{)?+(?\\{)?+(?\\{)?+) .+? \\}(?()\\})(?()\\})(?()\\})(?()\\})(?()\\})\n (?:\\[(?(?!\\d)\\w+)\\])?\n (?[X<>]?)\n (\\))", + "match": "(?x)\n# PCRECallout\n(\\()(?\\?C)\n (?:\n (?\\d+)\n | `(?(?:[^`]|``)*)`\n | '(?(?:[^']|'')*)'\n | \"(?(?:[^\"]|\"\")*)\"\n | \\^(?(?:[^\\^]|\\^\\^)*)\\^\n | %(?(?:[^%]|%%)*)%\n | \\#(?(?:[^#]|\\#\\#)*)\\#\n | \\$(?(?:[^$]|\\$\\$)*)\\$\n | \\{(?(?:[^}]|\\}\\})*)\\}\n )?\n(\\))\n# NamedCallout\n| (\\()(?\\*)\n (?(?!\\d)\\w+)\n (?:\\[(?(?!\\d)\\w+)\\])?\n (?:\\{ [^,}]+ (?:,[^,}]+)* \\})?\n (\\))\n# InterpolatedCallout\n| (\\()(?\\?)\n (?>(\\{(?:\\g<-1>|(?!{).*?)\\}))\n (?:\\[(?(?!\\d)\\w+)\\])?\n (?[X<>]?)\n (\\))", "captures": { "1": { "name": "punctuation.definition.group.regexp" @@ -3350,13 +3380,13 @@ "19": { "name": "keyword.control.callout.regexp" }, - "26": { + "21": { "name": "variable.language.tag-name.regexp" }, - "27": { + "22": { "name": "keyword.control.callout.regexp" }, - "28": { + "23": { "name": "punctuation.definition.group.regexp" } } diff --git a/extensions/terminal-suggest/.gitignore b/extensions/terminal-suggest/.gitignore new file mode 100644 index 000000000000..76b510c710f0 --- /dev/null +++ b/extensions/terminal-suggest/.gitignore @@ -0,0 +1 @@ +third_party/ diff --git a/extensions/terminal-suggest/.vscode/launch.json b/extensions/terminal-suggest/.vscode/launch.json new file mode 100644 index 000000000000..017c87624153 --- /dev/null +++ b/extensions/terminal-suggest/.vscode/launch.json @@ -0,0 +1,18 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch Extension", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ], + "stopOnEntry": false, + "sourceMaps": true, + "outFiles": ["${workspaceFolder}/client/out/**/*.js"], + "preLaunchTask": "npm" + } + ] +} \ No newline at end of file diff --git a/extensions/terminal-suggest/.vscode/tasks.json b/extensions/terminal-suggest/.vscode/tasks.json new file mode 100644 index 000000000000..b7c8a281635b --- /dev/null +++ b/extensions/terminal-suggest/.vscode/tasks.json @@ -0,0 +1,11 @@ +{ + "version": "2.0.0", + "command": "npm", + "type": "shell", + "presentation": { + "reveal": "silent", + }, + "args": ["run", "compile"], + "isBackground": true, + "problemMatcher": "$tsc-watch" +} \ No newline at end of file diff --git a/extensions/terminal-suggest/.vscodeignore b/extensions/terminal-suggest/.vscodeignore new file mode 100644 index 000000000000..f05a79416be0 --- /dev/null +++ b/extensions/terminal-suggest/.vscodeignore @@ -0,0 +1,7 @@ +src/** +out/** +tsconfig.json +.vscode/** +extension.webpack.config.js +extension-browser.webpack.config.js +package-lock.json diff --git a/extensions/terminal-suggest/README.md b/extensions/terminal-suggest/README.md new file mode 100644 index 000000000000..cd1c1f4afa52 --- /dev/null +++ b/extensions/terminal-suggest/README.md @@ -0,0 +1,7 @@ +# Terminal Suggestions + +**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. To enable the completions from this extension, set `terminal.integrated.suggest.enabled` to `true`. + +## Features + +Provides terminal suggestions for zsh, bash, fish, and pwsh. diff --git a/extensions/terminal-suggest/ThirdPartyNotices.txt b/extensions/terminal-suggest/ThirdPartyNotices.txt new file mode 100644 index 000000000000..f6de57f6266a --- /dev/null +++ b/extensions/terminal-suggest/ThirdPartyNotices.txt @@ -0,0 +1,30 @@ +THIRD-PARTY SOFTWARE NOTICES AND INFORMATION +For Microsoft terminal-suggest + +This file is based on or incorporates material from the projects listed below ("Third Party OSS"). The original copyright +notice and the license under which Microsoft received such Third Party OSS, are set forth below. Such licenses and notice +are provided for informational purposes only. Microsoft licenses the Third Party OSS to you under the licensing terms for +the Microsoft product or service. Microsoft reserves all other rights not expressly granted under this agreement, whether +by implication, estoppel or otherwise.† + +1. withfig/autocomplete - IDE-style autocomplete for your existing terminal & shell (https://github.com/withfig/autocomplete) + +Copyright (c) 2021 Hercules Labs Inc. (Fig) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/extensions/terminal-suggest/cgmanifest.json b/extensions/terminal-suggest/cgmanifest.json new file mode 100644 index 000000000000..ead3479e6673 --- /dev/null +++ b/extensions/terminal-suggest/cgmanifest.json @@ -0,0 +1,145 @@ +{ + "registrations": [ + { + "component": { + "type": "git", + "git": { + "repositoryUrl": "https://github.com/withfig/autocomplete", + "commitHash": "1cc34dc1ba530bb620bd380c25cfb9eb2264be89" + } + }, + "version": "2.684.0", + "license": { + "type": "MIT", + "url": "https://github.com/withfig/autocomplete/blob/main/LICENSE.md" + }, + "description": "IDE-style autocomplete for your existing terminal & shell from withfig/autocomplete." + }, + { + "component": { + "type": "git", + "git": { + "name": "amazon-q-developer-cli", + "repositoryUrl": "https://github.com/aws/amazon-q-developer-cli", + "commitHash": "f66e0b0e917ab185eef528dc36eca56b78ca8b5d" + } + }, + "licenseDetail": [ + "MIT License", + "", + "Copyright (c) 2024 Amazon.com, Inc. or its affiliates.", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + "SOFTWARE." + ], + "version": "f66e0b0e917ab185eef528dc36eca56b78ca8b5d" + }, + { + "component": { + "type": "git", + "git": { + "name": "@fig/autocomplete-shared", + "repositoryUrl": "https://github.com/withfig/autocomplete-tools", + "commitHash": "104377c19a91ca8a312cb38c115a74468f6227cb" + } + }, + "licenseDetail": [ + "MIT License", + "", + "Copyright (c) 2021 Hercules Labs Inc. (Fig)", + "", + "Permission is hereby granted, free of charge, to any person obtaining a copy", + "of this software and associated documentation files (the \"Software\"), to deal", + "in the Software without restriction, including without limitation the rights", + "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", + "copies of the Software, and to permit persons to whom the Software is", + "furnished to do so, subject to the following conditions:", + "", + "The above copyright notice and this permission notice shall be included in all", + "copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", + "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", + "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", + "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", + "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", + "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE", + "SOFTWARE." + ], + "version": "1.1.2" + }, + { + "component": { + "type": "git", + "git": { + "repositoryUrl": "https://github.com/zsh-users/zsh", + "commitHash": "435cb1b748ce1f2f5c38edc1d64f4ee2424f9b3a" + } + }, + "version": "5.9", + "licenseDetail": [ + "Unless otherwise noted in the header of specific files, files in this distribution have the licence shown below.", + "", + "However, note that certain shell functions are licensed under versions of the GNU General Public Licence. Anyone distributing the shell as a binary including those files needs to take account of this. Search shell functions for \"Copyright\" for specific copyright information. None of the core functions are affected by this, so those files may simply be omitted.", + "", + "--", + "", + "The Z Shell is copyright (c) 1992-2017 Paul Falstad, Richard Coleman, Zoltán Hidvégi, Andrew Main, Peter Stephenson, Sven Wischnowsky, and others. All rights reserved. Individual authors, whether or not specifically named, retain copyright in all changes; in what follows, they are referred to as `the Zsh Development Group'. This is for convenience only and this body has no legal status. The Z shell is distributed under the following licence; any provisions made in individual files take precedence.", + "", + "Permission is hereby granted, without written agreement and without licence or royalty fees, to use, copy, modify, and distribute this software and to distribute modified versions of this software for any purpose, provided that the above copyright notice and the following two paragraphs appear in all copies of this software.", + "", + "In no event shall the Zsh Development Group be liable to any party for direct, indirect, special, incidental, or consequential damages arising out of the use of this software and its documentation, even if the Zsh Development Group have been advised of the possibility of such damage.", + "", + "The Zsh Development Group specifically disclaim any warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The software provided hereunder is on an \"as is\" basis, and the Zsh Development Group have no obligation to provide maintenance, support, updates, enhancements, or modifications." + ] + }, + { + "component": { + "type": "git", + "git": { + "repositoryUrl": "https://github.com/fish-shell/fish-shell", + "commitHash": "6627d403d33b4e74b49aa4db2a4f17709628cdc8" + } + }, + "version": "3.7.1", + "licenseDetail": [ + "Fish is a smart and user-friendly command line shell.", + "", + "Copyright (C) 2005-2009 Axel Liljencrantz", + "Copyright (C) 2009- fish-shell contributors", + "", + "fish is free software.", + "", + "Most of fish is licensed under the GNU General Public License version 2, and", + "you can redistribute it and/or modify it under the terms of the GNU GPL as", + "published by the Free Software Foundation.", + "", + "fish also includes software licensed under the Python Software Foundation License version 2, the MIT", + "license, and the GNU Library General Public License version 2.", + "", + "Full licensing information is contained in doc_src/license.rst.", + "", + "This program is distributed in the hope that it will be useful, but WITHOUT", + "ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or", + "FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for", + "more details." + ] + } + ], + "version": 1 +} diff --git a/extensions/terminal-suggest/extension.webpack.config.js b/extensions/terminal-suggest/extension.webpack.config.js new file mode 100644 index 000000000000..89f3ea28d874 --- /dev/null +++ b/extensions/terminal-suggest/extension.webpack.config.js @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check +'use strict'; + +const withDefaults = require('../shared.webpack.config'); + +module.exports = withDefaults({ + context: __dirname, + entry: { + extension: './src/terminalSuggestMain.ts' + }, + output: { + filename: 'terminalSuggestMain.js' + }, + resolve: { + mainFields: ['module', 'main'], + extensions: ['.ts', '.js'] // support ts-files and js-files + } +}); diff --git a/extensions/terminal-suggest/fixtures/shell-parser/basic/input.sh b/extensions/terminal-suggest/fixtures/shell-parser/basic/input.sh new file mode 100644 index 000000000000..72075545c056 --- /dev/null +++ b/extensions/terminal-suggest/fixtures/shell-parser/basic/input.sh @@ -0,0 +1,29 @@ +### Case 1 +a b\\ c + +### Case 2 +a "b" + +### Case 3 +a 'b' + +### Case 4 +a $'b' + +### Case 5 +a $commit + +### Case 6 +a $$ + +### Case 7 +a $((b)) + +### Case 8 +a $(b) + +### Case 9 +a \`b\` + +### Case 10 +a $(\`b\`) diff --git a/extensions/terminal-suggest/fixtures/shell-parser/basic/output.txt b/extensions/terminal-suggest/fixtures/shell-parser/basic/output.txt new file mode 100644 index 000000000000..8e816c5fe184 --- /dev/null +++ b/extensions/terminal-suggest/fixtures/shell-parser/basic/output.txt @@ -0,0 +1,448 @@ +// Case 1 +{ + "startIndex": 0, + "type": "program", + "endIndex": 7, + "text": "a b\\\\ c", + "innerText": "a b\\\\ c", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 7, + "text": "a b\\\\ c", + "innerText": "a b\\\\ c", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "word", + "endIndex": 5, + "text": "b\\\\", + "innerText": "b\\", + "complete": true, + "children": [] + }, + { + "startIndex": 6, + "type": "word", + "endIndex": 7, + "text": "c", + "innerText": "c", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 2 +{ + "startIndex": 0, + "type": "program", + "endIndex": 5, + "text": "a \"b\"", + "innerText": "a \"b\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 5, + "text": "a \"b\"", + "innerText": "a \"b\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "string", + "endIndex": 5, + "text": "\"b\"", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 3 +{ + "startIndex": 0, + "type": "program", + "endIndex": 5, + "text": "a 'b'", + "innerText": "a 'b'", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 5, + "text": "a 'b'", + "innerText": "a 'b'", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "raw_string", + "endIndex": 5, + "text": "'b'", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 4 +{ + "startIndex": 0, + "type": "program", + "endIndex": 6, + "text": "a $'b'", + "innerText": "a $'b'", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 6, + "text": "a $'b'", + "innerText": "a $'b'", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "ansi_c_string", + "endIndex": 6, + "text": "$'b'", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 5 +{ + "startIndex": 0, + "type": "program", + "endIndex": 9, + "text": "a $commit", + "innerText": "a $commit", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 9, + "text": "a $commit", + "innerText": "a $commit", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "simple_expansion", + "endIndex": 9, + "text": "$commit", + "innerText": "$commit", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 6 +{ + "startIndex": 0, + "type": "program", + "endIndex": 4, + "text": "a $$", + "innerText": "a $$", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 4, + "text": "a $$", + "innerText": "a $$", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "special_expansion", + "endIndex": 4, + "text": "$$", + "innerText": "$$", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 7 +{ + "startIndex": 0, + "type": "program", + "endIndex": 8, + "text": "a $((b))", + "innerText": "a $((b))", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 8, + "text": "a $((b))", + "innerText": "a $((b))", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "arithmetic_expansion", + "endIndex": 8, + "text": "$((b))", + "innerText": "$((b))", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 8 +{ + "startIndex": 0, + "type": "program", + "endIndex": 6, + "text": "a $(b)", + "innerText": "a $(b)", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 6, + "text": "a $(b)", + "innerText": "a $(b)", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "command_substitution", + "endIndex": 6, + "text": "$(b)", + "innerText": "$(b)", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "command", + "endIndex": 5, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] + } + ] + } + ] +} + +// Case 9 +{ + "startIndex": 0, + "type": "program", + "endIndex": 7, + "text": "a \\`b\\`", + "innerText": "a \\`b\\`", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 7, + "text": "a \\`b\\`", + "innerText": "a \\`b\\`", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 3, + "type": "word", + "endIndex": 7, + "text": "`b\\`", + "innerText": "`b`", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 10 +{ + "startIndex": 0, + "type": "program", + "endIndex": 10, + "text": "a $(\\`b\\`)", + "innerText": "a $(\\`b\\`)", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 10, + "text": "a $(\\`b\\`)", + "innerText": "a $(\\`b\\`)", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "command_substitution", + "endIndex": 10, + "text": "$(\\`b\\`)", + "innerText": "$(\\`b\\`)", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "command", + "endIndex": 9, + "text": "\\`b\\`", + "innerText": "\\`b\\`", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "word", + "endIndex": 9, + "text": "`b\\`", + "innerText": "`b`", + "complete": true, + "children": [] + } + ] + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/extensions/terminal-suggest/fixtures/shell-parser/multipleStatements/input.sh b/extensions/terminal-suggest/fixtures/shell-parser/multipleStatements/input.sh new file mode 100644 index 000000000000..ba6858ea1a59 --- /dev/null +++ b/extensions/terminal-suggest/fixtures/shell-parser/multipleStatements/input.sh @@ -0,0 +1,47 @@ +### Case 1 +a && b + +### Case 2 +a || b + +### Case 3 +a | b + +### Case 4 +a |& b + +### Case 5 +(a; b) + +### Case 6 +(a; b;) + +### Case 7 +{a; b} + +### Case 8 +{a; b;} + +### Case 9 +a; b + +### Case 10 +a & b + +### Case 11 +a &; b + +### Case 12 +a ; b; + +### Case 13 +a && b || c + +### Case 14 +a && b | c + +### Case 15 +a | b && c + +### Case 16 +(a) | b && c \ No newline at end of file diff --git a/extensions/terminal-suggest/fixtures/shell-parser/multipleStatements/output.txt b/extensions/terminal-suggest/fixtures/shell-parser/multipleStatements/output.txt new file mode 100644 index 000000000000..624f01663718 --- /dev/null +++ b/extensions/terminal-suggest/fixtures/shell-parser/multipleStatements/output.txt @@ -0,0 +1,1035 @@ +// Case 1 +{ + "startIndex": 0, + "type": "program", + "endIndex": 6, + "text": "a && b", + "innerText": "a && b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "list", + "endIndex": 6, + "text": "a && b", + "innerText": "a && b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 2, + "text": "a ", + "innerText": "a ", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 5, + "type": "command", + "endIndex": 6, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "word", + "endIndex": 6, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 2 +{ + "startIndex": 0, + "type": "program", + "endIndex": 6, + "text": "a || b", + "innerText": "a || b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "list", + "endIndex": 6, + "text": "a || b", + "innerText": "a || b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 2, + "text": "a ", + "innerText": "a ", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 5, + "type": "command", + "endIndex": 6, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "word", + "endIndex": 6, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 3 +{ + "startIndex": 0, + "type": "program", + "endIndex": 5, + "text": "a | b", + "innerText": "a | b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "pipeline", + "endIndex": 5, + "text": "a | b", + "innerText": "a | b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 2, + "text": "a ", + "innerText": "a ", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 4, + "type": "command", + "endIndex": 5, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 4 +{ + "startIndex": 0, + "type": "program", + "endIndex": 6, + "text": "a |& b", + "innerText": "a |& b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "pipeline", + "endIndex": 6, + "text": "a |& b", + "innerText": "a |& b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 2, + "text": "a ", + "innerText": "a ", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 5, + "type": "command", + "endIndex": 6, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "word", + "endIndex": 6, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 5 +{ + "startIndex": 0, + "type": "program", + "endIndex": 6, + "text": "(a; b)", + "innerText": "(a; b)", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "subshell", + "endIndex": 6, + "text": "(a; b)", + "innerText": "(a; b)", + "complete": true, + "children": [ + { + "startIndex": 1, + "type": "command", + "endIndex": 2, + "text": "a", + "innerText": "a", + "complete": true, + "children": [ + { + "startIndex": 1, + "type": "word", + "endIndex": 2, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 4, + "type": "command", + "endIndex": 5, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 6 +{ + "startIndex": 0, + "type": "program", + "endIndex": 7, + "text": "(a; b;)", + "innerText": "(a; b;)", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "subshell", + "endIndex": 7, + "text": "(a; b;)", + "innerText": "(a; b;)", + "complete": true, + "children": [ + { + "startIndex": 1, + "type": "command", + "endIndex": 2, + "text": "a", + "innerText": "a", + "complete": true, + "children": [ + { + "startIndex": 1, + "type": "word", + "endIndex": 2, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 4, + "type": "command", + "endIndex": 5, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 7 +{ + "startIndex": 0, + "type": "program", + "endIndex": 6, + "text": "{a; b}", + "innerText": "{a; b}", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "compound_statement", + "endIndex": 6, + "text": "{a; b}", + "innerText": "{a; b}", + "complete": false, + "children": [ + { + "startIndex": 1, + "type": "command", + "endIndex": 2, + "text": "a", + "innerText": "a", + "complete": true, + "children": [ + { + "startIndex": 1, + "type": "word", + "endIndex": 2, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 4, + "type": "command", + "endIndex": 6, + "text": "b}", + "innerText": "b}", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 6, + "text": "b}", + "innerText": "b}", + "complete": true, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 8 +{ + "startIndex": 0, + "type": "program", + "endIndex": 7, + "text": "{a; b;}", + "innerText": "{a; b;}", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "compound_statement", + "endIndex": 7, + "text": "{a; b;}", + "innerText": "{a; b;}", + "complete": true, + "children": [ + { + "startIndex": 1, + "type": "command", + "endIndex": 2, + "text": "a", + "innerText": "a", + "complete": true, + "children": [ + { + "startIndex": 1, + "type": "word", + "endIndex": 2, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 4, + "type": "command", + "endIndex": 5, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 9 +{ + "startIndex": 0, + "type": "program", + "endIndex": 4, + "text": "a; b", + "innerText": "a; b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 3, + "type": "command", + "endIndex": 4, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 3, + "type": "word", + "endIndex": 4, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 10 +{ + "startIndex": 0, + "type": "program", + "endIndex": 5, + "text": "a & b", + "innerText": "a & b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 2, + "text": "a ", + "innerText": "a ", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 4, + "type": "command", + "endIndex": 5, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 11 +{ + "startIndex": 0, + "type": "program", + "endIndex": 6, + "text": "a &; b", + "innerText": "a &; b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 2, + "text": "a ", + "innerText": "a ", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 5, + "type": "command", + "endIndex": 6, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "word", + "endIndex": 6, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 12 +{ + "startIndex": 0, + "type": "program", + "endIndex": 6, + "text": "a ; b;", + "innerText": "a ; b;", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 2, + "text": "a ", + "innerText": "a ", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 4, + "type": "command", + "endIndex": 5, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 13 +{ + "startIndex": 0, + "type": "program", + "endIndex": 11, + "text": "a && b || c", + "innerText": "a && b || c", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "list", + "endIndex": 11, + "text": "a && b || c", + "innerText": "a && b || c", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 2, + "text": "a ", + "innerText": "a ", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 5, + "type": "command", + "endIndex": 7, + "text": "b ", + "innerText": "b ", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "word", + "endIndex": 6, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 10, + "type": "command", + "endIndex": 11, + "text": "c", + "innerText": "c", + "complete": true, + "children": [ + { + "startIndex": 10, + "type": "word", + "endIndex": 11, + "text": "c", + "innerText": "c", + "complete": true, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 14 +{ + "startIndex": 0, + "type": "program", + "endIndex": 10, + "text": "a && b | c", + "innerText": "a && b | c", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "list", + "endIndex": 10, + "text": "a && b | c", + "innerText": "a && b | c", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 2, + "text": "a ", + "innerText": "a ", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 5, + "type": "pipeline", + "endIndex": 10, + "text": "b | c", + "innerText": "b | c", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "command", + "endIndex": 7, + "text": "b ", + "innerText": "b ", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "word", + "endIndex": 6, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 9, + "type": "command", + "endIndex": 10, + "text": "c", + "innerText": "c", + "complete": true, + "children": [ + { + "startIndex": 9, + "type": "word", + "endIndex": 10, + "text": "c", + "innerText": "c", + "complete": true, + "children": [] + } + ] + } + ] + } + ] + } + ] +} + +// Case 15 +{ + "startIndex": 0, + "type": "program", + "endIndex": 10, + "text": "a | b && c", + "innerText": "a | b && c", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "list", + "endIndex": 10, + "text": "a | b && c", + "innerText": "a | b && c", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "pipeline", + "endIndex": 6, + "text": "a | b ", + "innerText": "a | b ", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 2, + "text": "a ", + "innerText": "a ", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 4, + "type": "command", + "endIndex": 6, + "text": "b ", + "innerText": "b ", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] + }, + { + "startIndex": 9, + "type": "command", + "endIndex": 10, + "text": "c", + "innerText": "c", + "complete": true, + "children": [ + { + "startIndex": 9, + "type": "word", + "endIndex": 10, + "text": "c", + "innerText": "c", + "complete": true, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 16 +{ + "startIndex": 0, + "type": "program", + "endIndex": 12, + "text": "(a) | b && c", + "innerText": "(a) | b && c", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "list", + "endIndex": 12, + "text": "(a) | b && c", + "innerText": "(a) | b && c", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "pipeline", + "endIndex": 8, + "text": "(a) | b ", + "innerText": "(a) | b ", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "subshell", + "endIndex": 3, + "text": "(a)", + "innerText": "(a)", + "complete": true, + "children": [ + { + "startIndex": 1, + "type": "command", + "endIndex": 2, + "text": "a", + "innerText": "a", + "complete": true, + "children": [ + { + "startIndex": 1, + "type": "word", + "endIndex": 2, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + } + ] + }, + { + "startIndex": 6, + "type": "command", + "endIndex": 8, + "text": "b ", + "innerText": "b ", + "complete": true, + "children": [ + { + "startIndex": 6, + "type": "word", + "endIndex": 7, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] + }, + { + "startIndex": 11, + "type": "command", + "endIndex": 12, + "text": "c", + "innerText": "c", + "complete": true, + "children": [ + { + "startIndex": 11, + "type": "word", + "endIndex": 12, + "text": "c", + "innerText": "c", + "complete": true, + "children": [] + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/extensions/terminal-suggest/fixtures/shell-parser/primaryExpressions/input.sh b/extensions/terminal-suggest/fixtures/shell-parser/primaryExpressions/input.sh new file mode 100644 index 000000000000..d2c5977e3185 --- /dev/null +++ b/extensions/terminal-suggest/fixtures/shell-parser/primaryExpressions/input.sh @@ -0,0 +1,35 @@ +### Case 1 +a "\${b}" + +### Case 2 +a "'b'" + +### Case 3 +a "\${b:+"c"}" + +### Case 4 +a b"c" + +### Case 5 +a '\${b}' + +### Case 6 +a $'\${b}' + +### Case 7 +a $'b''c'd$$$e\${f}"g" + +### Case 8 +a $'b\\'c' + +### Case 9 +a 'b\\'c' + +### Case 10 +a "b$" + +### Case 11 +a "$b" + +### Case 12 +a "$(b "c" && d)" \ No newline at end of file diff --git a/extensions/terminal-suggest/fixtures/shell-parser/primaryExpressions/output.txt b/extensions/terminal-suggest/fixtures/shell-parser/primaryExpressions/output.txt new file mode 100644 index 000000000000..4783411a82b7 --- /dev/null +++ b/extensions/terminal-suggest/fixtures/shell-parser/primaryExpressions/output.txt @@ -0,0 +1,724 @@ +// Case 1 +{ + "startIndex": 0, + "type": "program", + "endIndex": 9, + "text": "a \"\\${b}\"", + "innerText": "a \"\\${b}\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 9, + "text": "a \"\\${b}\"", + "innerText": "a \"\\${b}\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "string", + "endIndex": 9, + "text": "\"\\${b}\"", + "innerText": "${b}", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 2 +{ + "startIndex": 0, + "type": "program", + "endIndex": 7, + "text": "a \"'b'\"", + "innerText": "a \"'b'\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 7, + "text": "a \"'b'\"", + "innerText": "a \"'b'\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "string", + "endIndex": 7, + "text": "\"'b'\"", + "innerText": "'b'", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 3 +{ + "startIndex": 0, + "type": "program", + "endIndex": 14, + "text": "a \"\\${b:+\"c\"}\"", + "innerText": "a \"\\${b:+\"c\"}\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 14, + "text": "a \"\\${b:+\"c\"}\"", + "innerText": "a \"\\${b:+\"c\"}\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "concatenation", + "endIndex": 14, + "text": "\"\\${b:+\"c\"}\"", + "innerText": "${b:+c}", + "complete": true, + "children": [ + { + "startIndex": 2, + "type": "string", + "endIndex": 10, + "text": "\"\\${b:+\"", + "innerText": "${b:+", + "complete": true, + "children": [] + }, + { + "startIndex": 10, + "type": "word", + "endIndex": 11, + "text": "c", + "innerText": "c", + "complete": true, + "children": [] + }, + { + "startIndex": 11, + "type": "string", + "endIndex": 14, + "text": "\"}\"", + "innerText": "}", + "complete": true, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 4 +{ + "startIndex": 0, + "type": "program", + "endIndex": 6, + "text": "a b\"c\"", + "innerText": "a b\"c\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 6, + "text": "a b\"c\"", + "innerText": "a b\"c\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "concatenation", + "endIndex": 6, + "text": "b\"c\"", + "innerText": "bc", + "complete": true, + "children": [ + { + "startIndex": 2, + "type": "word", + "endIndex": 3, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + }, + { + "startIndex": 3, + "type": "string", + "endIndex": 6, + "text": "\"c\"", + "innerText": "c", + "complete": true, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 5 +{ + "startIndex": 0, + "type": "program", + "endIndex": 9, + "text": "a '\\${b}'", + "innerText": "a '\\${b}'", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 9, + "text": "a '\\${b}'", + "innerText": "a '\\${b}'", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "raw_string", + "endIndex": 9, + "text": "'\\${b}'", + "innerText": "\\${b}", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 6 +{ + "startIndex": 0, + "type": "program", + "endIndex": 10, + "text": "a $'\\${b}'", + "innerText": "a $'\\${b}'", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 10, + "text": "a $'\\${b}'", + "innerText": "a $'\\${b}'", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "ansi_c_string", + "endIndex": 10, + "text": "$'\\${b}'", + "innerText": "\\${b}", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 7 +{ + "startIndex": 0, + "type": "program", + "endIndex": 22, + "text": "a $'b''c'd$$$e\\${f}\"g\"", + "innerText": "a $'b''c'd$$$e\\${f}\"g\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 22, + "text": "a $'b''c'd$$$e\\${f}\"g\"", + "innerText": "a $'b''c'd$$$e\\${f}\"g\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "concatenation", + "endIndex": 22, + "text": "$'b''c'd$$$e\\${f}\"g\"", + "innerText": "bcd$$$e${f}g", + "complete": true, + "children": [ + { + "startIndex": 2, + "type": "ansi_c_string", + "endIndex": 6, + "text": "$'b'", + "innerText": "b", + "complete": true, + "children": [] + }, + { + "startIndex": 6, + "type": "raw_string", + "endIndex": 9, + "text": "'c'", + "innerText": "c", + "complete": true, + "children": [] + }, + { + "startIndex": 9, + "type": "word", + "endIndex": 10, + "text": "d", + "innerText": "d", + "complete": true, + "children": [] + }, + { + "startIndex": 10, + "type": "special_expansion", + "endIndex": 12, + "text": "$$", + "innerText": "$$", + "complete": true, + "children": [] + }, + { + "startIndex": 12, + "type": "simple_expansion", + "endIndex": 14, + "text": "$e", + "innerText": "$e", + "complete": true, + "children": [] + }, + { + "startIndex": 15, + "type": "word", + "endIndex": 19, + "text": "${f}", + "innerText": "${f}", + "complete": true, + "children": [] + }, + { + "startIndex": 19, + "type": "string", + "endIndex": 22, + "text": "\"g\"", + "innerText": "g", + "complete": true, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 8 +{ + "startIndex": 0, + "type": "program", + "endIndex": 10, + "text": "a $'b\\\\'c'", + "innerText": "a $'b\\\\'c'", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 10, + "text": "a $'b\\\\'c'", + "innerText": "a $'b\\\\'c'", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "concatenation", + "endIndex": 10, + "text": "$'b\\\\'c'", + "innerText": "b\\\\c", + "complete": false, + "children": [ + { + "startIndex": 2, + "type": "ansi_c_string", + "endIndex": 8, + "text": "$'b\\\\'", + "innerText": "b\\\\", + "complete": true, + "children": [] + }, + { + "startIndex": 8, + "type": "word", + "endIndex": 9, + "text": "c", + "innerText": "c", + "complete": true, + "children": [] + }, + { + "startIndex": 9, + "type": "raw_string", + "endIndex": 10, + "text": "'", + "innerText": "", + "complete": false, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 9 +{ + "startIndex": 0, + "type": "program", + "endIndex": 9, + "text": "a 'b\\\\'c'", + "innerText": "a 'b\\\\'c'", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 9, + "text": "a 'b\\\\'c'", + "innerText": "a 'b\\\\'c'", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "concatenation", + "endIndex": 9, + "text": "'b\\\\'c'", + "innerText": "b\\\\c", + "complete": false, + "children": [ + { + "startIndex": 2, + "type": "raw_string", + "endIndex": 7, + "text": "'b\\\\'", + "innerText": "b\\\\", + "complete": true, + "children": [] + }, + { + "startIndex": 7, + "type": "word", + "endIndex": 8, + "text": "c", + "innerText": "c", + "complete": true, + "children": [] + }, + { + "startIndex": 8, + "type": "raw_string", + "endIndex": 9, + "text": "'", + "innerText": "", + "complete": false, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 10 +{ + "startIndex": 0, + "type": "program", + "endIndex": 6, + "text": "a \"b$\"", + "innerText": "a \"b$\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 6, + "text": "a \"b$\"", + "innerText": "a \"b$\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "string", + "endIndex": 6, + "text": "\"b$\"", + "innerText": "b$", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 11 +{ + "startIndex": 0, + "type": "program", + "endIndex": 6, + "text": "a \"$b\"", + "innerText": "a \"$b\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 6, + "text": "a \"$b\"", + "innerText": "a \"$b\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "string", + "endIndex": 6, + "text": "\"$b\"", + "innerText": "$b", + "complete": true, + "children": [ + { + "startIndex": 3, + "type": "simple_expansion", + "endIndex": 5, + "text": "$b", + "innerText": "$b", + "complete": true, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 12 +{ + "startIndex": 0, + "type": "program", + "endIndex": 17, + "text": "a \"$(b \"c\" && d)\"", + "innerText": "a \"$(b \"c\" && d)\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 17, + "text": "a \"$(b \"c\" && d)\"", + "innerText": "a \"$(b \"c\" && d)\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "string", + "endIndex": 17, + "text": "\"$(b \"c\" && d)\"", + "innerText": "$(b \"c\" && d)", + "complete": true, + "children": [ + { + "startIndex": 3, + "type": "command_substitution", + "endIndex": 16, + "text": "$(b \"c\" && d)", + "innerText": "$(b \"c\" && d)", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "list", + "endIndex": 15, + "text": "b \"c\" && d", + "innerText": "b \"c\" && d", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "command", + "endIndex": 11, + "text": "b \"c\" ", + "innerText": "b \"c\" ", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "word", + "endIndex": 6, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + }, + { + "startIndex": 7, + "type": "string", + "endIndex": 10, + "text": "\"c\"", + "innerText": "c", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 14, + "type": "command", + "endIndex": 15, + "text": "d", + "innerText": "d", + "complete": true, + "children": [ + { + "startIndex": 14, + "type": "word", + "endIndex": 15, + "text": "d", + "innerText": "d", + "complete": true, + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/extensions/terminal-suggest/fixtures/shell-parser/variables/input.sh b/extensions/terminal-suggest/fixtures/shell-parser/variables/input.sh new file mode 100644 index 000000000000..30b8788a90f2 --- /dev/null +++ b/extensions/terminal-suggest/fixtures/shell-parser/variables/input.sh @@ -0,0 +1,77 @@ +### Case 1 +ENV=a b + +### Case 2 +ENV=a b c d --op=e + +### Case 3 +ENV=a ENV=b a + +### Case 4 +ENV=a ENV=b a && ENV=c c + +### Case 5 +ENV="a b" c + +### Case 6 +ENV='a b' c + +### Case 7 +ENV=`cmd` a + +### Case 8 +ENV+='100' b + +### Case 9 +ENV+=a ENV=b + +### Case 10 +ENV+=a ENV=b && foo + +### Case 11 +ENV="a + +### Case 12 +ENV='a + +### Case 13 +ENV=a ENV=`b + +### Case 14 +ENV=`ENV="a" b` && ENV="c" d + +### Case 15 +c $(ENV=a foo) + +### Case 16 +ENV=a; b + +### Case 17 +ENV=a ; b + +### Case 18 +ENV=a & b + +### Case 19 +ENV=a|b + +### Case 20 +ENV[0]=a b + +### Case 21 +ENV[0]=a; b + +### Case 22 +ENV[1]=`a b + +### Case 23 +ENV[2]+="a b " + +### Case 24 +MY_VAR='echo'hi$'quote'"command: $(ps | VAR=2 grep ps)" + +### Case 25 +ENV="a"'b'c d + +### Case 26 +ENV=a"b"'c' \ No newline at end of file diff --git a/extensions/terminal-suggest/fixtures/shell-parser/variables/output.txt b/extensions/terminal-suggest/fixtures/shell-parser/variables/output.txt new file mode 100644 index 000000000000..9cbf4ab2ffb0 --- /dev/null +++ b/extensions/terminal-suggest/fixtures/shell-parser/variables/output.txt @@ -0,0 +1,2439 @@ +// Case 1 +{ + "startIndex": 0, + "type": "program", + "endIndex": 7, + "text": "ENV=a b", + "innerText": "ENV=a b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 7, + "text": "ENV=a b", + "innerText": "ENV=a b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 5, + "text": "ENV=a", + "innerText": "ENV=a", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + }, + { + "startIndex": 6, + "type": "command", + "endIndex": 7, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 6, + "type": "word", + "endIndex": 7, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ], + "hasCommand": true + } + ] +} + +// Case 2 +{ + "startIndex": 0, + "type": "program", + "endIndex": 18, + "text": "ENV=a b c d --op=e", + "innerText": "ENV=a b c d --op=e", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 18, + "text": "ENV=a b c d --op=e", + "innerText": "ENV=a b c d --op=e", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 5, + "text": "ENV=a", + "innerText": "ENV=a", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + }, + { + "startIndex": 6, + "type": "command", + "endIndex": 18, + "text": "b c d --op=e", + "innerText": "b c d --op=e", + "complete": true, + "children": [ + { + "startIndex": 6, + "type": "word", + "endIndex": 7, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + }, + { + "startIndex": 8, + "type": "word", + "endIndex": 9, + "text": "c", + "innerText": "c", + "complete": true, + "children": [] + }, + { + "startIndex": 10, + "type": "word", + "endIndex": 11, + "text": "d", + "innerText": "d", + "complete": true, + "children": [] + }, + { + "startIndex": 12, + "type": "word", + "endIndex": 18, + "text": "--op=e", + "innerText": "--op=e", + "complete": true, + "children": [] + } + ] + } + ], + "hasCommand": true + } + ] +} + +// Case 3 +{ + "startIndex": 0, + "type": "program", + "endIndex": 13, + "text": "ENV=a ENV=b a", + "innerText": "ENV=a ENV=b a", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 13, + "text": "ENV=a ENV=b a", + "innerText": "ENV=a ENV=b a", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 5, + "text": "ENV=a", + "innerText": "ENV=a", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + }, + { + "startIndex": 6, + "type": "assignment", + "endIndex": 11, + "text": "ENV=b", + "innerText": "ENV=b", + "complete": true, + "children": [ + { + "startIndex": 10, + "type": "word", + "endIndex": 11, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 6, + "type": "variable_name", + "endIndex": 9, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + }, + { + "startIndex": 12, + "type": "command", + "endIndex": 13, + "text": "a", + "innerText": "a", + "complete": true, + "children": [ + { + "startIndex": 12, + "type": "word", + "endIndex": 13, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + } + ], + "hasCommand": true + } + ] +} + +// Case 4 +{ + "startIndex": 0, + "type": "program", + "endIndex": 24, + "text": "ENV=a ENV=b a && ENV=c c", + "innerText": "ENV=a ENV=b a && ENV=c c", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "list", + "endIndex": 24, + "text": "ENV=a ENV=b a && ENV=c c", + "innerText": "ENV=a ENV=b a && ENV=c c", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 14, + "text": "ENV=a ENV=b a ", + "innerText": "ENV=a ENV=b a ", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 5, + "text": "ENV=a", + "innerText": "ENV=a", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + }, + { + "startIndex": 6, + "type": "assignment", + "endIndex": 11, + "text": "ENV=b", + "innerText": "ENV=b", + "complete": true, + "children": [ + { + "startIndex": 10, + "type": "word", + "endIndex": 11, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 6, + "type": "variable_name", + "endIndex": 9, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + }, + { + "startIndex": 12, + "type": "command", + "endIndex": 14, + "text": "a ", + "innerText": "a ", + "complete": true, + "children": [ + { + "startIndex": 12, + "type": "word", + "endIndex": 13, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + } + ], + "hasCommand": true + }, + { + "startIndex": 17, + "type": "assignment_list", + "endIndex": 24, + "text": "ENV=c c", + "innerText": "ENV=c c", + "complete": true, + "children": [ + { + "startIndex": 17, + "type": "assignment", + "endIndex": 22, + "text": "ENV=c", + "innerText": "ENV=c", + "complete": true, + "children": [ + { + "startIndex": 21, + "type": "word", + "endIndex": 22, + "text": "c", + "innerText": "c", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 17, + "type": "variable_name", + "endIndex": 20, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + }, + { + "startIndex": 23, + "type": "command", + "endIndex": 24, + "text": "c", + "innerText": "c", + "complete": true, + "children": [ + { + "startIndex": 23, + "type": "word", + "endIndex": 24, + "text": "c", + "innerText": "c", + "complete": true, + "children": [] + } + ] + } + ], + "hasCommand": true + } + ] + } + ] +} + +// Case 5 +{ + "startIndex": 0, + "type": "program", + "endIndex": 11, + "text": "ENV=\"a b\" c", + "innerText": "ENV=\"a b\" c", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 11, + "text": "ENV=\"a b\" c", + "innerText": "ENV=\"a b\" c", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 9, + "text": "ENV=\"a b\"", + "innerText": "ENV=\"a b\"", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "string", + "endIndex": 9, + "text": "\"a b\"", + "innerText": "a b", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + }, + { + "startIndex": 10, + "type": "command", + "endIndex": 11, + "text": "c", + "innerText": "c", + "complete": true, + "children": [ + { + "startIndex": 10, + "type": "word", + "endIndex": 11, + "text": "c", + "innerText": "c", + "complete": true, + "children": [] + } + ] + } + ], + "hasCommand": true + } + ] +} + +// Case 6 +{ + "startIndex": 0, + "type": "program", + "endIndex": 11, + "text": "ENV='a b' c", + "innerText": "ENV='a b' c", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 11, + "text": "ENV='a b' c", + "innerText": "ENV='a b' c", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 9, + "text": "ENV='a b'", + "innerText": "ENV='a b'", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "raw_string", + "endIndex": 9, + "text": "'a b'", + "innerText": "a b", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + }, + { + "startIndex": 10, + "type": "command", + "endIndex": 11, + "text": "c", + "innerText": "c", + "complete": true, + "children": [ + { + "startIndex": 10, + "type": "word", + "endIndex": 11, + "text": "c", + "innerText": "c", + "complete": true, + "children": [] + } + ] + } + ], + "hasCommand": true + } + ] +} + +// Case 7 +{ + "startIndex": 0, + "type": "program", + "endIndex": 11, + "text": "ENV=`cmd` a", + "innerText": "ENV=`cmd` a", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 11, + "text": "ENV=`cmd` a", + "innerText": "ENV=`cmd` a", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 9, + "text": "ENV=`cmd`", + "innerText": "ENV=`cmd`", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "command_substitution", + "endIndex": 9, + "text": "`cmd`", + "innerText": "`cmd`", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "command", + "endIndex": 8, + "text": "cmd", + "innerText": "cmd", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "word", + "endIndex": 8, + "text": "cmd", + "innerText": "cmd", + "complete": true, + "children": [] + } + ] + } + ] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + }, + { + "startIndex": 10, + "type": "command", + "endIndex": 11, + "text": "a", + "innerText": "a", + "complete": true, + "children": [ + { + "startIndex": 10, + "type": "word", + "endIndex": 11, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ] + } + ], + "hasCommand": true + } + ] +} + +// Case 8 +{ + "startIndex": 0, + "type": "program", + "endIndex": 12, + "text": "ENV+='100' b", + "innerText": "ENV+='100' b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 12, + "text": "ENV+='100' b", + "innerText": "ENV+='100' b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 10, + "text": "ENV+='100'", + "innerText": "ENV+='100'", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "raw_string", + "endIndex": 10, + "text": "'100'", + "innerText": "100", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "+=" + }, + { + "startIndex": 11, + "type": "command", + "endIndex": 12, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 11, + "type": "word", + "endIndex": 12, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ], + "hasCommand": true + } + ] +} + +// Case 9 +{ + "startIndex": 0, + "type": "program", + "endIndex": 12, + "text": "ENV+=a ENV=b", + "innerText": "ENV+=a ENV=b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 12, + "text": "ENV+=a ENV=b", + "innerText": "ENV+=a ENV=b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 6, + "text": "ENV+=a", + "innerText": "ENV+=a", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "word", + "endIndex": 6, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "+=" + }, + { + "startIndex": 7, + "type": "assignment", + "endIndex": 12, + "text": "ENV=b", + "innerText": "ENV=b", + "complete": true, + "children": [ + { + "startIndex": 11, + "type": "word", + "endIndex": 12, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 7, + "type": "variable_name", + "endIndex": 10, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + } + ], + "hasCommand": false + } + ] +} + +// Case 10 +{ + "startIndex": 0, + "type": "program", + "endIndex": 19, + "text": "ENV+=a ENV=b && foo", + "innerText": "ENV+=a ENV=b && foo", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "list", + "endIndex": 19, + "text": "ENV+=a ENV=b && foo", + "innerText": "ENV+=a ENV=b && foo", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 12, + "text": "ENV+=a ENV=b", + "innerText": "ENV+=a ENV=b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 6, + "text": "ENV+=a", + "innerText": "ENV+=a", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "word", + "endIndex": 6, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "+=" + }, + { + "startIndex": 7, + "type": "assignment", + "endIndex": 12, + "text": "ENV=b", + "innerText": "ENV=b", + "complete": true, + "children": [ + { + "startIndex": 11, + "type": "word", + "endIndex": 12, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 7, + "type": "variable_name", + "endIndex": 10, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + } + ], + "hasCommand": false + }, + { + "startIndex": 16, + "type": "command", + "endIndex": 19, + "text": "foo", + "innerText": "foo", + "complete": true, + "children": [ + { + "startIndex": 16, + "type": "word", + "endIndex": 19, + "text": "foo", + "innerText": "foo", + "complete": true, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 11 +{ + "startIndex": 0, + "type": "program", + "endIndex": 6, + "text": "ENV=\"a", + "innerText": "ENV=\"a", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 6, + "text": "ENV=\"a", + "innerText": "ENV=\"a", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 6, + "text": "ENV=\"a", + "innerText": "ENV=\"a", + "complete": false, + "children": [ + { + "startIndex": 4, + "type": "string", + "endIndex": 6, + "text": "\"a", + "innerText": "a", + "complete": false, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + } + ], + "hasCommand": false + } + ] +} + +// Case 12 +{ + "startIndex": 0, + "type": "program", + "endIndex": 6, + "text": "ENV='a", + "innerText": "ENV='a", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 6, + "text": "ENV='a", + "innerText": "ENV='a", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 6, + "text": "ENV='a", + "innerText": "ENV='a", + "complete": false, + "children": [ + { + "startIndex": 4, + "type": "raw_string", + "endIndex": 6, + "text": "'a", + "innerText": "a", + "complete": false, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + } + ], + "hasCommand": false + } + ] +} + +// Case 13 +{ + "startIndex": 0, + "type": "program", + "endIndex": 12, + "text": "ENV=a ENV=`b", + "innerText": "ENV=a ENV=`b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 12, + "text": "ENV=a ENV=`b", + "innerText": "ENV=a ENV=`b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 5, + "text": "ENV=a", + "innerText": "ENV=a", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + }, + { + "startIndex": 6, + "type": "assignment", + "endIndex": 12, + "text": "ENV=`b", + "innerText": "ENV=`b", + "complete": false, + "children": [ + { + "startIndex": 10, + "type": "command_substitution", + "endIndex": 12, + "text": "`b", + "innerText": "`b", + "complete": false, + "children": [ + { + "startIndex": 11, + "type": "command", + "endIndex": 12, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 11, + "type": "word", + "endIndex": 12, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] + } + ], + "name": { + "startIndex": 6, + "type": "variable_name", + "endIndex": 9, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + } + ], + "hasCommand": false + } + ] +} + +// Case 14 +{ + "startIndex": 0, + "type": "program", + "endIndex": 28, + "text": "ENV=`ENV=\"a\" b` && ENV=\"c\" d", + "innerText": "ENV=`ENV=\"a\" b` && ENV=\"c\" d", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "list", + "endIndex": 28, + "text": "ENV=`ENV=\"a\" b` && ENV=\"c\" d", + "innerText": "ENV=`ENV=\"a\" b` && ENV=\"c\" d", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 15, + "text": "ENV=`ENV=\"a\" b`", + "innerText": "ENV=`ENV=\"a\" b`", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 15, + "text": "ENV=`ENV=\"a\" b`", + "innerText": "ENV=`ENV=\"a\" b`", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "command_substitution", + "endIndex": 15, + "text": "`ENV=\"a\" b`", + "innerText": "`ENV=\"a\" b`", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "assignment_list", + "endIndex": 14, + "text": "ENV=\"a\" b", + "innerText": "ENV=\"a\" b", + "complete": true, + "children": [ + { + "startIndex": 5, + "type": "assignment", + "endIndex": 12, + "text": "ENV=\"a\"", + "innerText": "ENV=\"a\"", + "complete": true, + "children": [ + { + "startIndex": 9, + "type": "string", + "endIndex": 12, + "text": "\"a\"", + "innerText": "a", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 5, + "type": "variable_name", + "endIndex": 8, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + }, + { + "startIndex": 13, + "type": "command", + "endIndex": 14, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 13, + "type": "word", + "endIndex": 14, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ], + "hasCommand": true + } + ] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + } + ], + "hasCommand": false + }, + { + "startIndex": 19, + "type": "assignment_list", + "endIndex": 28, + "text": "ENV=\"c\" d", + "innerText": "ENV=\"c\" d", + "complete": true, + "children": [ + { + "startIndex": 19, + "type": "assignment", + "endIndex": 26, + "text": "ENV=\"c\"", + "innerText": "ENV=\"c\"", + "complete": true, + "children": [ + { + "startIndex": 23, + "type": "string", + "endIndex": 26, + "text": "\"c\"", + "innerText": "c", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 19, + "type": "variable_name", + "endIndex": 22, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + }, + { + "startIndex": 27, + "type": "command", + "endIndex": 28, + "text": "d", + "innerText": "d", + "complete": true, + "children": [ + { + "startIndex": 27, + "type": "word", + "endIndex": 28, + "text": "d", + "innerText": "d", + "complete": true, + "children": [] + } + ] + } + ], + "hasCommand": true + } + ] + } + ] +} + +// Case 15 +{ + "startIndex": 0, + "type": "program", + "endIndex": 14, + "text": "c $(ENV=a foo)", + "innerText": "c $(ENV=a foo)", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "command", + "endIndex": 14, + "text": "c $(ENV=a foo)", + "innerText": "c $(ENV=a foo)", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "word", + "endIndex": 1, + "text": "c", + "innerText": "c", + "complete": true, + "children": [] + }, + { + "startIndex": 2, + "type": "command_substitution", + "endIndex": 14, + "text": "$(ENV=a foo)", + "innerText": "$(ENV=a foo)", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "assignment_list", + "endIndex": 13, + "text": "ENV=a foo", + "innerText": "ENV=a foo", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "assignment", + "endIndex": 9, + "text": "ENV=a", + "innerText": "ENV=a", + "complete": true, + "children": [ + { + "startIndex": 8, + "type": "word", + "endIndex": 9, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 4, + "type": "variable_name", + "endIndex": 7, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + }, + { + "startIndex": 10, + "type": "command", + "endIndex": 13, + "text": "foo", + "innerText": "foo", + "complete": true, + "children": [ + { + "startIndex": 10, + "type": "word", + "endIndex": 13, + "text": "foo", + "innerText": "foo", + "complete": true, + "children": [] + } + ] + } + ], + "hasCommand": true + } + ] + } + ] + } + ] +} + +// Case 16 +{ + "startIndex": 0, + "type": "program", + "endIndex": 8, + "text": "ENV=a; b", + "innerText": "ENV=a; b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 5, + "text": "ENV=a", + "innerText": "ENV=a", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 5, + "text": "ENV=a", + "innerText": "ENV=a", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + } + ], + "hasCommand": false + }, + { + "startIndex": 7, + "type": "command", + "endIndex": 8, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 7, + "type": "word", + "endIndex": 8, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 17 +{ + "startIndex": 0, + "type": "program", + "endIndex": 9, + "text": "ENV=a ; b", + "innerText": "ENV=a ; b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 5, + "text": "ENV=a", + "innerText": "ENV=a", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 5, + "text": "ENV=a", + "innerText": "ENV=a", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + } + ], + "hasCommand": false + }, + { + "startIndex": 8, + "type": "command", + "endIndex": 9, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 8, + "type": "word", + "endIndex": 9, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 18 +{ + "startIndex": 0, + "type": "program", + "endIndex": 9, + "text": "ENV=a & b", + "innerText": "ENV=a & b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 5, + "text": "ENV=a", + "innerText": "ENV=a", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 5, + "text": "ENV=a", + "innerText": "ENV=a", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + } + ], + "hasCommand": false + }, + { + "startIndex": 8, + "type": "command", + "endIndex": 9, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 8, + "type": "word", + "endIndex": 9, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 19 +{ + "startIndex": 0, + "type": "program", + "endIndex": 7, + "text": "ENV=a|b", + "innerText": "ENV=a|b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "pipeline", + "endIndex": 7, + "text": "ENV=a|b", + "innerText": "ENV=a|b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 5, + "text": "ENV=a", + "innerText": "ENV=a", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 5, + "text": "ENV=a", + "innerText": "ENV=a", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + } + ], + "hasCommand": false + }, + { + "startIndex": 6, + "type": "command", + "endIndex": 7, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 6, + "type": "word", + "endIndex": 7, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] + } + ] +} + +// Case 20 +{ + "startIndex": 0, + "type": "program", + "endIndex": 10, + "text": "ENV[0]=a b", + "innerText": "ENV[0]=a b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 10, + "text": "ENV[0]=a b", + "innerText": "ENV[0]=a b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 8, + "text": "ENV[0]=a", + "innerText": "ENV[0]=a", + "complete": true, + "children": [ + { + "startIndex": 7, + "type": "word", + "endIndex": 8, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "subscript", + "endIndex": 6, + "text": "ENV[0]", + "innerText": "ENV[0]", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "0", + "innerText": "0", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + } + }, + "operator": "=" + }, + { + "startIndex": 9, + "type": "command", + "endIndex": 10, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 9, + "type": "word", + "endIndex": 10, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ], + "hasCommand": true + } + ] +} + +// Case 21 +{ + "startIndex": 0, + "type": "program", + "endIndex": 11, + "text": "ENV[0]=a; b", + "innerText": "ENV[0]=a; b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 8, + "text": "ENV[0]=a", + "innerText": "ENV[0]=a", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 8, + "text": "ENV[0]=a", + "innerText": "ENV[0]=a", + "complete": true, + "children": [ + { + "startIndex": 7, + "type": "word", + "endIndex": 8, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "subscript", + "endIndex": 6, + "text": "ENV[0]", + "innerText": "ENV[0]", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "0", + "innerText": "0", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + } + }, + "operator": "=" + } + ], + "hasCommand": false + }, + { + "startIndex": 10, + "type": "command", + "endIndex": 11, + "text": "b", + "innerText": "b", + "complete": true, + "children": [ + { + "startIndex": 10, + "type": "word", + "endIndex": 11, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] +} + +// Case 22 +{ + "startIndex": 0, + "type": "program", + "endIndex": 11, + "text": "ENV[1]=`a b", + "innerText": "ENV[1]=`a b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 11, + "text": "ENV[1]=`a b", + "innerText": "ENV[1]=`a b", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 11, + "text": "ENV[1]=`a b", + "innerText": "ENV[1]=`a b", + "complete": false, + "children": [ + { + "startIndex": 7, + "type": "command_substitution", + "endIndex": 11, + "text": "`a b", + "innerText": "`a b", + "complete": false, + "children": [ + { + "startIndex": 8, + "type": "command", + "endIndex": 11, + "text": "a b", + "innerText": "a b", + "complete": true, + "children": [ + { + "startIndex": 8, + "type": "word", + "endIndex": 9, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 10, + "type": "word", + "endIndex": 11, + "text": "b", + "innerText": "b", + "complete": true, + "children": [] + } + ] + } + ] + } + ], + "name": { + "startIndex": 0, + "type": "subscript", + "endIndex": 6, + "text": "ENV[1]", + "innerText": "ENV[1]", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "1", + "innerText": "1", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + } + }, + "operator": "=" + } + ], + "hasCommand": false + } + ] +} + +// Case 23 +{ + "startIndex": 0, + "type": "program", + "endIndex": 14, + "text": "ENV[2]+=\"a b \"", + "innerText": "ENV[2]+=\"a b \"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 14, + "text": "ENV[2]+=\"a b \"", + "innerText": "ENV[2]+=\"a b \"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 14, + "text": "ENV[2]+=\"a b \"", + "innerText": "ENV[2]+=\"a b \"", + "complete": true, + "children": [ + { + "startIndex": 8, + "type": "string", + "endIndex": 14, + "text": "\"a b \"", + "innerText": "a b ", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "subscript", + "endIndex": 6, + "text": "ENV[2]", + "innerText": "ENV[2]", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "2", + "innerText": "2", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + } + }, + "operator": "+=" + } + ], + "hasCommand": false + } + ] +} + +// Case 24 +{ + "startIndex": 0, + "type": "program", + "endIndex": 55, + "text": "MY_VAR='echo'hi$'quote'\"command: $(ps | VAR=2 grep ps)\"", + "innerText": "MY_VAR='echo'hi$'quote'\"command: $(ps | VAR=2 grep ps)\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 55, + "text": "MY_VAR='echo'hi$'quote'\"command: $(ps | VAR=2 grep ps)\"", + "innerText": "MY_VAR='echo'hi$'quote'\"command: $(ps | VAR=2 grep ps)\"", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 55, + "text": "MY_VAR='echo'hi$'quote'\"command: $(ps | VAR=2 grep ps)\"", + "innerText": "MY_VAR='echo'hi$'quote'\"command: $(ps | VAR=2 grep ps)\"", + "complete": true, + "children": [ + { + "startIndex": 7, + "type": "concatenation", + "endIndex": 55, + "text": "'echo'hi$'quote'\"command: $(ps | VAR=2 grep ps)\"", + "innerText": "echohiquotecommand: $(ps | VAR=2 grep ps)", + "complete": true, + "children": [ + { + "startIndex": 7, + "type": "raw_string", + "endIndex": 13, + "text": "'echo'", + "innerText": "echo", + "complete": true, + "children": [] + }, + { + "startIndex": 13, + "type": "word", + "endIndex": 15, + "text": "hi", + "innerText": "hi", + "complete": true, + "children": [] + }, + { + "startIndex": 15, + "type": "ansi_c_string", + "endIndex": 23, + "text": "$'quote'", + "innerText": "quote", + "complete": true, + "children": [] + }, + { + "startIndex": 23, + "type": "string", + "endIndex": 55, + "text": "\"command: $(ps | VAR=2 grep ps)\"", + "innerText": "command: $(ps | VAR=2 grep ps)", + "complete": true, + "children": [ + { + "startIndex": 33, + "type": "command_substitution", + "endIndex": 54, + "text": "$(ps | VAR=2 grep ps)", + "innerText": "$(ps | VAR=2 grep ps)", + "complete": true, + "children": [ + { + "startIndex": 35, + "type": "pipeline", + "endIndex": 53, + "text": "ps | VAR=2 grep ps", + "innerText": "ps | VAR=2 grep ps", + "complete": true, + "children": [ + { + "startIndex": 35, + "type": "command", + "endIndex": 38, + "text": "ps ", + "innerText": "ps ", + "complete": true, + "children": [ + { + "startIndex": 35, + "type": "word", + "endIndex": 37, + "text": "ps", + "innerText": "ps", + "complete": true, + "children": [] + } + ] + }, + { + "startIndex": 40, + "type": "assignment_list", + "endIndex": 53, + "text": "VAR=2 grep ps", + "innerText": "VAR=2 grep ps", + "complete": true, + "children": [ + { + "startIndex": 40, + "type": "assignment", + "endIndex": 45, + "text": "VAR=2", + "innerText": "VAR=2", + "complete": true, + "children": [ + { + "startIndex": 44, + "type": "word", + "endIndex": 45, + "text": "2", + "innerText": "2", + "complete": true, + "children": [] + } + ], + "name": { + "startIndex": 40, + "type": "variable_name", + "endIndex": 43, + "text": "VAR", + "innerText": "VAR", + "complete": true, + "children": [] + }, + "operator": "=" + }, + { + "startIndex": 46, + "type": "command", + "endIndex": 53, + "text": "grep ps", + "innerText": "grep ps", + "complete": true, + "children": [ + { + "startIndex": 46, + "type": "word", + "endIndex": 50, + "text": "grep", + "innerText": "grep", + "complete": true, + "children": [] + }, + { + "startIndex": 51, + "type": "word", + "endIndex": 53, + "text": "ps", + "innerText": "ps", + "complete": true, + "children": [] + } + ] + } + ], + "hasCommand": true + } + ] + } + ] + } + ] + } + ] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 6, + "text": "MY_VAR", + "innerText": "MY_VAR", + "complete": true, + "children": [] + }, + "operator": "=" + } + ], + "hasCommand": false + } + ] +} + +// Case 25 +{ + "startIndex": 0, + "type": "program", + "endIndex": 13, + "text": "ENV=\"a\"'b'c d", + "innerText": "ENV=\"a\"'b'c d", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 13, + "text": "ENV=\"a\"'b'c d", + "innerText": "ENV=\"a\"'b'c d", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 11, + "text": "ENV=\"a\"'b'c", + "innerText": "ENV=\"a\"'b'c", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "concatenation", + "endIndex": 11, + "text": "\"a\"'b'c", + "innerText": "abc", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "string", + "endIndex": 7, + "text": "\"a\"", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 7, + "type": "raw_string", + "endIndex": 10, + "text": "'b'", + "innerText": "b", + "complete": true, + "children": [] + }, + { + "startIndex": 10, + "type": "word", + "endIndex": 11, + "text": "c", + "innerText": "c", + "complete": true, + "children": [] + } + ] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + }, + { + "startIndex": 12, + "type": "command", + "endIndex": 13, + "text": "d", + "innerText": "d", + "complete": true, + "children": [ + { + "startIndex": 12, + "type": "word", + "endIndex": 13, + "text": "d", + "innerText": "d", + "complete": true, + "children": [] + } + ] + } + ], + "hasCommand": true + } + ] +} + +// Case 26 +{ + "startIndex": 0, + "type": "program", + "endIndex": 11, + "text": "ENV=a\"b\"'c'", + "innerText": "ENV=a\"b\"'c'", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment_list", + "endIndex": 11, + "text": "ENV=a\"b\"'c'", + "innerText": "ENV=a\"b\"'c'", + "complete": true, + "children": [ + { + "startIndex": 0, + "type": "assignment", + "endIndex": 11, + "text": "ENV=a\"b\"'c'", + "innerText": "ENV=a\"b\"'c'", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "concatenation", + "endIndex": 11, + "text": "a\"b\"'c'", + "innerText": "abc", + "complete": true, + "children": [ + { + "startIndex": 4, + "type": "word", + "endIndex": 5, + "text": "a", + "innerText": "a", + "complete": true, + "children": [] + }, + { + "startIndex": 5, + "type": "string", + "endIndex": 8, + "text": "\"b\"", + "innerText": "b", + "complete": true, + "children": [] + }, + { + "startIndex": 8, + "type": "raw_string", + "endIndex": 11, + "text": "'c'", + "innerText": "c", + "complete": true, + "children": [] + } + ] + } + ], + "name": { + "startIndex": 0, + "type": "variable_name", + "endIndex": 3, + "text": "ENV", + "innerText": "ENV", + "complete": true, + "children": [] + }, + "operator": "=" + } + ], + "hasCommand": false + } + ] +} \ No newline at end of file diff --git a/extensions/terminal-suggest/package-lock.json b/extensions/terminal-suggest/package-lock.json new file mode 100644 index 000000000000..239e60b0c25d --- /dev/null +++ b/extensions/terminal-suggest/package-lock.json @@ -0,0 +1,16 @@ +{ + "name": "terminal-suggest", + "version": "1.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "terminal-suggest", + "version": "1.0.1", + "license": "MIT", + "engines": { + "vscode": "^1.95.0" + } + } + } +} diff --git a/extensions/terminal-suggest/package.json b/extensions/terminal-suggest/package.json new file mode 100644 index 000000000000..f2e4104823c8 --- /dev/null +++ b/extensions/terminal-suggest/package.json @@ -0,0 +1,35 @@ +{ + "name": "terminal-suggest", + "publisher": "vscode", + "displayName": "%displayName%", + "description": "%description%", + "version": "1.0.1", + "private": true, + "license": "MIT", + "icon": "./src/media/icon.png", + "engines": { + "vscode": "^1.95.0" + }, + "categories": [ + "Other" + ], + "enabledApiProposals": [ + "terminalCompletionProvider", + "terminalShellEnv", + "terminalShellType" + ], + "scripts": { + "compile": "npx gulp compile-extension:terminal-suggest", + "watch": "npx gulp watch-extension:terminal-suggest", + "pull-zshbuiltins": "ts-node ./scripts/pullZshBuiltins.ts", + "pull-fishbuiltins": "ts-node ./scripts/pullFishBuiltins.ts" + }, + "main": "./out/terminalSuggestMain", + "activationEvents": [ + "onTerminalCompletionsRequested" + ], + "repository": { + "type": "git", + "url": "https://github.com/microsoft/vscode.git" + } +} diff --git a/extensions/terminal-suggest/package.nls.json b/extensions/terminal-suggest/package.nls.json new file mode 100644 index 000000000000..2fff64874d73 --- /dev/null +++ b/extensions/terminal-suggest/package.nls.json @@ -0,0 +1,5 @@ +{ + "description": "Extension to add terminal completions for zsh, bash, and fish terminals.", + "displayName": "Terminal Suggest for VS Code", + "view.name": "Terminal Suggest" +} diff --git a/extensions/terminal-suggest/scripts/clone-fig.ps1 b/extensions/terminal-suggest/scripts/clone-fig.ps1 new file mode 100644 index 000000000000..11ec13e560eb --- /dev/null +++ b/extensions/terminal-suggest/scripts/clone-fig.ps1 @@ -0,0 +1 @@ +git clone https://github.com/withfig/autocomplete third_party/autocomplete diff --git a/extensions/terminal-suggest/scripts/clone-fig.sh b/extensions/terminal-suggest/scripts/clone-fig.sh new file mode 100755 index 000000000000..11ec13e560eb --- /dev/null +++ b/extensions/terminal-suggest/scripts/clone-fig.sh @@ -0,0 +1 @@ +git clone https://github.com/withfig/autocomplete third_party/autocomplete diff --git a/extensions/terminal-suggest/scripts/pullFishBuiltins.ts b/extensions/terminal-suggest/scripts/pullFishBuiltins.ts new file mode 100644 index 000000000000..8ac6b870d451 --- /dev/null +++ b/extensions/terminal-suggest/scripts/pullFishBuiltins.ts @@ -0,0 +1,260 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs/promises'; +import * as path from 'path'; +import { cleanupText, checkWindows, execAsync, copyright } from './terminalScriptHelpers'; + +checkWindows(); + +interface ICommandDetails { + description: string; + args: string | undefined; + shortDescription?: string; +} + +let fishBuiltinsCommandDescriptionsCache = new Map(); + +// Fallback descriptions for commands that don't return proper help information +const fallbackDescriptions: Record = { + '[': { + shortDescription: 'Test if a statement is true', + description: 'Evaluate an expression and return a status of true (0) or false (non-zero). Unlike the `test` command, the `[` command requires a closing `]`.', + args: 'EXPRESSION ]' + }, + 'break': { + shortDescription: 'Exit the current loop', + description: 'Terminate the execution of the nearest enclosing `while` or `for` loop and proceed with the next command after the loop.', + args: undefined + }, + 'breakpoint': { + shortDescription: 'Launch debug mode', + description: 'Pause execution and launch an interactive debug prompt. This is useful for inspecting the state of a script at a specific point.', + args: undefined + }, + 'case': { + shortDescription: 'Match a value against patterns', + description: 'Within a `switch` block, the `case` command specifies patterns to match against the given value, executing the associated block if a match is found.', + args: 'PATTERN...' + }, + 'continue': { + shortDescription: 'Skip to the next iteration of a loop', + description: 'Within a `while` or `for` loop, `continue` skips the remaining commands in the current iteration and proceeds to the next iteration of the loop.', + args: undefined + }, + 'else': { + shortDescription: 'Execute commands if the previous condition was false', + description: 'In an `if` block, the `else` section contains commands that execute if none of the preceding `if` or `else if` conditions were true.', + args: undefined + }, + 'end': { + shortDescription: 'Terminate a block of code', + description: 'Conclude a block of code initiated by constructs like `if`, `switch`, `while`, `for`, or `function`.', + args: undefined + }, + 'eval': { + shortDescription: 'Execute arguments as a command', + description: 'Concatenate all arguments into a single command and execute it. This allows for dynamic construction and execution of commands.', + args: 'COMMAND...' + }, + 'false': { + shortDescription: 'Return an unsuccessful result', + description: 'A command that returns a non-zero exit status, indicating failure. It is often used in scripts to represent a false condition.', + args: undefined + }, + 'realpath': { + shortDescription: 'Resolve and print the absolute path', + description: 'Convert each provided path to its absolute, canonical form by resolving symbolic links and relative path components.', + args: 'PATH...' + }, + ':': { + shortDescription: 'No operation command', + description: 'The `:` command is a no-op (no operation) command that returns a successful (zero) exit status. It can be used as a placeholder in scripts where a command is syntactically required but no action is desired.', + args: undefined + }, + 'test': { + shortDescription: 'Evaluate conditional expressions', + description: 'The `test` command evaluates conditional expressions and sets the exit status to 0 if the expression is true, and 1 if it is false. It supports various operators to evaluate expressions related to strings, numbers, and file attributes.', + args: 'EXPRESSION' + }, + 'true': { + shortDescription: 'Return a successful result', + description: 'The `true` command always returns a successful (zero) exit status. It is often used in scripts and conditional statements where an unconditional success result is needed.', + args: undefined + }, + 'printf': { + shortDescription: 'Display formatted text', + description: 'The `printf` command formats and prints text according to a specified format string. Unlike `echo`, `printf` does not append a newline unless explicitly included in the format.', + args: 'FORMAT [ARGUMENT...]' + } +}; + + +async function createCommandDescriptionsCache(): Promise { + const cachedCommandDescriptions: Map = new Map(); + + try { + // Get list of all builtins + const builtinsOutput = await execAsync('fish -c "builtin -n"').then(r => r.stdout.trim()); + const builtins = builtinsOutput.split('\n'); + + console.log(`Found ${builtins.length} Fish builtin commands`); + + for (const cmd of builtins) { + try { + // Get help info for each builtin + const helpOutput = await execAsync(`fish -c "${cmd} --help 2>&1"`).then(r => r.stdout); + let set = false; + if (helpOutput && !helpOutput.includes('No help for function') && !helpOutput.includes('See the web documentation')) { + const cleanHelpText = cleanupText(helpOutput); + + // Split the text into lines to process + const lines = cleanHelpText.split('\n'); + + + // Extract the short description, args, and full description + const { shortDescription, args, description } = extractHelpContent(cmd, lines); + + cachedCommandDescriptions.set(cmd, { + shortDescription, + description, + args + }); + set = description !== ''; + } + if (!set) { + // Use fallback descriptions for commands that don't return proper help + if (fallbackDescriptions[cmd]) { + console.info(`Using fallback description for ${cmd}`); + cachedCommandDescriptions.set(cmd, fallbackDescriptions[cmd]); + } else { + console.info(`No fallback description exists for ${cmd}`); + } + } + } catch { + // Use fallback descriptions for commands that throw an error + if (fallbackDescriptions[cmd]) { + console.info('Using fallback description for', cmd); + cachedCommandDescriptions.set(cmd, fallbackDescriptions[cmd]); + } else { + console.info(`Error getting help for ${cmd}`); + } + } + } + } catch (e) { + console.error('Error creating Fish builtins cache:', e); + process.exit(1); + } + + fishBuiltinsCommandDescriptionsCache = cachedCommandDescriptions; +} + +/** + * Extracts short description, args, and full description from help text lines + */ +function extractHelpContent(cmd: string, lines: string[]): { shortDescription: string; args: string | undefined; description: string } { + let shortDescription = ''; + let args: string | undefined; + let description = ''; + + // Skip the first line (usually just command name and basic usage) + let i = 1; + + // Skip any leading empty lines + while (i < lines.length && lines[i].trim().length === 0) { + i++; + } + + // The next non-empty line after the command name is typically + // either the short description or additional usage info + const startLine = i; + + // Find where the short description starts + if (i < lines.length) { + // First, check if the line has a command prefix and remove it + let firstContentLine = lines[i].trim(); + const cmdPrefixRegex = new RegExp(`^${cmd}\\s*-\\s*`, 'i'); + firstContentLine = firstContentLine.replace(cmdPrefixRegex, ''); + + // First non-empty line is the short description + shortDescription = firstContentLine; + i++; + + // Next non-empty line (after short description) is typically args + while (i < lines.length && lines[i].trim().length === 0) { + i++; + } + + if (i < lines.length) { + // Found a line after the short description - that's our args + args = lines[i].trim(); + i++; + } + } + + // Find the DESCRIPTION marker which marks the end of args section + let descriptionIndex = -1; + for (let j = i; j < lines.length; j++) { + if (lines[j].trim() === 'DESCRIPTION') { + descriptionIndex = j; + break; + } + } + + // If DESCRIPTION marker is found, consider everything between i and descriptionIndex as part of args + if (descriptionIndex > i) { + // Combine lines from i up to (but not including) descriptionIndex + const additionalArgs = lines.slice(i, descriptionIndex).join('\n').trim(); + if (additionalArgs) { + args = args ? `${args}\n${additionalArgs}` : additionalArgs; + } + i = descriptionIndex + 1; // Move past the DESCRIPTION line + } + + // The rest is the full description (skipping any empty lines after args) + while (i < lines.length && lines[i].trim().length === 0) { + i++; + } + + // Combine the remaining lines into the full description + description = lines.slice(Math.max(i, startLine)).join('\n').trim(); + + // If description is empty, use the short description + if (!description && shortDescription) { + description = shortDescription; + } + + // Extract just the first sentence for short description + const firstPeriodIndex = shortDescription.indexOf('.'); + if (firstPeriodIndex > 0) { + shortDescription = shortDescription.substring(0, firstPeriodIndex + 1).trim(); + } else if (shortDescription.length > 100) { + shortDescription = shortDescription.substring(0, 100) + '...'; + } + + return { + shortDescription, + args, + description + }; +} + +const main = async () => { + try { + await createCommandDescriptionsCache(); + console.log('Created Fish command descriptions cache with', fishBuiltinsCommandDescriptionsCache.size, 'entries'); + + // Save the cache to a TypeScript file + const cacheFilePath = path.join(__dirname, '../src/shell/fishBuiltinsCache.ts'); + const cacheObject = Object.fromEntries(fishBuiltinsCommandDescriptionsCache); + const tsContent = `${copyright}\n\nexport const fishBuiltinsCommandDescriptionsCache = ${JSON.stringify(cacheObject, null, 2)} as const;`; + await fs.writeFile(cacheFilePath, tsContent, 'utf8'); + console.log('Saved Fish command descriptions cache to fishBuiltinsCache.ts with', Object.keys(cacheObject).length, 'entries'); + } catch (error) { + console.error('Error:', error); + } +}; + +main(); diff --git a/extensions/terminal-suggest/scripts/pullZshBuiltins.ts b/extensions/terminal-suggest/scripts/pullZshBuiltins.ts new file mode 100644 index 000000000000..a05866b16bc5 --- /dev/null +++ b/extensions/terminal-suggest/scripts/pullZshBuiltins.ts @@ -0,0 +1,257 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs/promises'; +import * as path from 'path'; +import { checkWindows, execAsync, copyright } from './terminalScriptHelpers'; + +checkWindows(); + +const latestZshVersion = 5.9; + +const shortDescriptions: Map = new Map([ + ['.', 'Source a file'], + [':', 'No effect'], + ['alias', 'Define or view aliases'], + ['autoload', 'Autoload a function'], + ['bg', 'Put a job in the background'], + ['bindkey', 'Manipulate keymap names'], + ['break', 'Exit from a loop'], + ['builtin', 'Executes a builtin'], + ['bye', 'Exit the shell'], + ['cap', 'Manipulating POSIX capability sets'], + ['cd', 'Change the current directory'], + ['chdir', 'Change the current directory'], + ['clone', 'Clone shell onto another terminal'], + ['command', 'Execute a command'], + ['comparguments', 'Complete arguments'], + ['compcall', 'Complete call'], + ['compctl', 'Complete control'], + ['compdescribe', 'Complete describe'], + ['compfiles', 'Complete files'], + ['compgroups', 'Complete groups'], + ['compquote', 'Complete quote'], + ['comptags', 'Complete tags'], + ['comptry', 'Complete try'], + ['compvalues', 'Complete values'], + ['continue', 'Resume the next loop iteration'], + ['declare', 'Set or display parameter attributes/values'], + ['dirs', 'Interact with directory stack'], + ['disable', 'Disable shell features'], + ['disown', 'Remove job from job table'], + ['echo', 'Write on standard output'], + ['echotc', 'Echo terminal capabilities'], + ['echoti', 'Echo terminal info'], + ['emulate', 'Emulate a shell'], + ['enable', 'Enable shell features'], + ['eval', 'Execute arguments in shell'], + ['exec', 'Replace shell with command'], + ['exit', 'Exit the shell'], + ['export', 'Export to environment'], + ['false', 'Return exit status of 1'], + ['fc', 'Fix command'], + ['fg', 'Put a job in the foreground'], + ['float', 'Floating point arithmetic'], + ['functions', 'List functions'], + ['getcap', 'Get capabilities'], + ['getln', 'Get line from buffer'], + ['getopts', 'Parse positional parameters'], + ['hash', 'Remember command locations'], + ['history', 'Command history'], + ['integer', 'Integer arithmetic'], + ['jobs', 'List active jobs'], + ['kill', 'Send a signal to a process'], + ['let', 'Evaluate arithmetic expression'], + ['limit', 'Set or display resource limits'], + ['local', 'Create a local variable'], + ['logout', 'Exit the shell'], + ['noglob', 'Disable filename expansion'], + ['popd', 'Remove directory from stack'], + ['print', 'Print arguments'], + ['printf', 'Format and print data'], + ['pushd', 'Add directory to stack'], + ['pushln', 'Push arguments onto the buffer'], + ['pwd', 'Print working directory'], + ['r', 'Re-execute command'], + ['read', 'Read a line from input'], + ['readonly', 'Mark variables as read-only'], + ['rehash', 'Recompute command hash table'], + ['return', 'Return from a function'], + ['sched', 'Schedule commands'], + ['set', 'Set shell options'], + ['setcap', 'Set capabilities'], + ['setopt', 'Set shell options'], + ['shift', 'Shift positional parameters'], + ['source', 'Source a file'], + ['stat', 'Display file status'], + ['suspend', 'Suspend the shell'], + ['test', 'Evaluate a conditional expression'], + ['times', 'Display shell times'], + ['trap', 'Set signal handlers'], + ['true', 'Return exit status of 0'], + ['ttyctl', 'Control terminal attributes'], + ['type', 'Describe a command'], + ['typeset', 'Set or display parameter attributes/values'], + ['ulimit', 'Set or display resource limits'], + ['umask', 'Set file creation mask'], + ['unalias', 'Removes aliases'], + ['unfunction', 'Remove function definition'], + ['unhash', 'Remove command from hash table'], + ['unlimit', 'Remove resource limits'], + ['unset', 'Unset values and attributes of variables'], + ['unsetopt', 'Unset shell options'], + ['vared', 'Edit shell variables'], + ['wait', 'Wait for a process'], + ['whence', 'Locate a command'], + ['where', 'Locate a command'], + ['which', 'Locate a command'], + ['zcompile', 'Compile functions'], + ['zformat', 'Format strings'], + ['zftp', 'Zsh FTP client'], + ['zle', 'Zsh line editor'], + ['zmodload', 'Load a module'], + ['zparseopts', 'Parse options'], + ['zprof', 'Zsh profiler'], + ['zpty', 'Zsh pseudo terminal'], + ['zregexparse', 'Parse regex'], + ['zsocket', 'Zsh socket interface'], + ['zstyle', 'Define styles'], + ['ztcp', 'Manipulate TCP sockets'], +]); + +interface ICommandDetails { + description: string; + args: string | undefined; + shortDescription?: string; +} + +let zshBuiltinsCommandDescriptionsCache = new Map(); + +async function createCommandDescriptionsCache(): Promise { + const cachedCommandDescriptions: Map = new Map(); + let output = ''; + const zshVersionOutput = await execAsync('zsh --version').then(r => r.stdout); + const zshVersionMatch = zshVersionOutput.match(/zsh (\d+\.\d+)/); + if (!zshVersionMatch) { + console.error('\x1b[31mFailed to determine zsh version\x1b[0m'); + process.exit(1); + } + const zshVersion = parseFloat(zshVersionMatch[1]); + if (zshVersion < latestZshVersion) { + console.error(`\x1b[31mZsh version must be ${latestZshVersion} or higher\x1b[0m`); + process.exit(1); + } + try { + output = await execAsync('pandoc --from man --to markdown --wrap=none < $(man -w zshbuiltins)').then(r => r.stdout); + } catch { + } + + const commands: Map = new Map(); + const commandRegex = /^\*\*(?[a-z\.:]+)\*\*(?:\s\*.+\*)?(?:\s\\\[.+\\\])?$/; + if (output) { + const lines = output.split('\n'); + let currentCommand: string | undefined; + let currentCommandStart = 0; + let seenOutput = false; + let i = 0; + for (; i < lines.length; i++) { + if (!currentCommand || seenOutput) { + const match = lines[i].match(commandRegex); + if (match?.groups?.command) { + if (currentCommand) { + commands.set(currentCommand, lines.slice(currentCommandStart, i)); + } + currentCommand = match.groups.command; + currentCommandStart = i; + seenOutput = false; + } + } + if (!currentCommand) { + continue; + } + // There may be several examples of usage + if (!seenOutput) { + seenOutput = lines[i].length > 0 && !lines[i].match(commandRegex); + } + } + if (currentCommand) { + commands.set(currentCommand, lines.slice(currentCommandStart, i - 1)); + } + } + + if (commands.size === 0) { + console.error('\x1b[31mFailed to parse command descriptions\x1b[30m'); + process.exit(1); + } + + for (const [command, lines] of commands) { + const shortDescription = shortDescriptions.get(command); + let argsEnd = 0; + try { + while (true) { + const line = lines[++argsEnd]; + if (line.trim().length > 0 && !line.match(commandRegex)) { + break; + } + } + } catch (e) { + console.log(e); + } + const formattedArgs = lines.slice(0, argsEnd - 1).join('\n'); + const args = (await execAsync(`pandoc --from markdown --to plain <<< "${formattedArgs}"`)).stdout.trim(); + const description = lines.slice(argsEnd).map(e => formatLineAsMarkdown(e)).join('\n').trim(); + if (shortDescription) { + cachedCommandDescriptions.set(command, { + shortDescription, + description, + args + }); + } else { + cachedCommandDescriptions.set(command, { + description, + args + }); + } + } + + zshBuiltinsCommandDescriptionsCache = cachedCommandDescriptions; +} + +function formatLineAsMarkdown(text: string): string { + // Detect any inline code blocks which use the form `code' (backtick, single quote) and convert + // them to standard markdown `code` (backtick, backtick). This doesn't attempt to remove + // formatting inside the code blocks. We probably need to use the original .troff format to do + // this + const formattedText = text.replace(/\\`([^']+)\\'/g, '`$1`'); + return formattedText; +} + +const main = async () => { + try { + await createCommandDescriptionsCache(); + console.log('created command descriptions cache with ', zshBuiltinsCommandDescriptionsCache.size, 'entries'); + + const missingShortDescription: string[] = []; + for (const [command, entry] of zshBuiltinsCommandDescriptionsCache.entries()) { + if (entry.shortDescription === undefined) { + missingShortDescription.push(command); + } + } + if (missingShortDescription.length > 0) { + console.log('\x1b[31mmissing short description for commands:\n' + missingShortDescription.join('\n') + '\x1b[0m'); + } + + // Save the cache to a TypeScript file + const cacheFilePath = path.join(__dirname, '../src/shell/zshBuiltinsCache.ts'); + const cacheObject = Object.fromEntries(zshBuiltinsCommandDescriptionsCache); + const tsContent = `${copyright}\n\nexport const zshBuiltinsCommandDescriptionsCache = ${JSON.stringify(cacheObject, null, 2)} as const;`; + await fs.writeFile(cacheFilePath, tsContent, 'utf8'); + console.log('saved command descriptions cache to zshBuiltinsCache.ts with ', Object.keys(cacheObject).length, 'entries'); + } catch (error) { + console.error('Error:', error); + } +}; + +main(); diff --git a/extensions/terminal-suggest/scripts/terminalScriptHelpers.ts b/extensions/terminal-suggest/scripts/terminalScriptHelpers.ts new file mode 100644 index 000000000000..402e6d5d288a --- /dev/null +++ b/extensions/terminal-suggest/scripts/terminalScriptHelpers.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { platform } from 'os'; +import { exec } from 'child_process'; +import { promisify } from 'util'; + +export const execAsync = promisify(exec); + +/** + * Cleans up text from terminal control sequences and formatting artifacts + */ +export function cleanupText(text: string): string { + // Remove ANSI escape codes + let cleanedText = text.replace(/\x1b\[\d+m/g, ''); + + // Remove backspace sequences (like a\bb which tries to print a, move back, print b) + // This regex looks for a character followed by a backspace and another character + const backspaceRegex = /.\x08./g; + while (backspaceRegex.test(cleanedText)) { + cleanedText = cleanedText.replace(backspaceRegex, match => match.charAt(2)); + } + + // Remove any remaining backspaces and their preceding characters + cleanedText = cleanedText.replace(/.\x08/g, ''); + + // Remove underscores that are used for formatting in some fish help output + cleanedText = cleanedText.replace(/_\b/g, ''); + + return cleanedText; +} + +/** + * Copyright notice for generated files + */ +export const copyright = `/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/`; + +/** + * Checks if the script is running on Windows and exits if so + */ +export function checkWindows(): void { + if (platform() === 'win32') { + console.error('\x1b[31mThis command is not supported on Windows\x1b[0m'); + process.exit(1); + } +} diff --git a/extensions/terminal-suggest/scripts/update-specs.js b/extensions/terminal-suggest/scripts/update-specs.js new file mode 100644 index 000000000000..775c5dd32648 --- /dev/null +++ b/extensions/terminal-suggest/scripts/update-specs.js @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// @ts-check + +const fs = require('fs'); +const path = require('path'); + +const upstreamSpecs = require('../out/constants.js').upstreamSpecs; +const extRoot = path.resolve(path.join(__dirname, '..')); +const replaceStrings = [ + [ + 'import { filepaths } from "@fig/autocomplete-generators";', + 'import { filepaths } from \'../../helpers/filepaths\';' + ], +]; +const indentSearch = [20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1].map(e => new RegExp('^' + ' '.repeat(e * 2), 'gm')); +const indentReplaceValue = [20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1].map(e => '\t'.repeat(e)); + +const specSpecificReplaceStrings = new Map([ + ['git', [ + [ + 'import { ai } from "@fig/autocomplete-generators";', + 'function ai(...args: any[]): undefined { return undefined; }' + ], [ + 'prompt: async ({ executeCommand }) => {', + 'prompt: async ({ executeCommand }: any) => {' + ], [ + 'message: async ({ executeCommand }) =>', + 'message: async ({ executeCommand }: any) =>' + ] + ]], +]); + +for (const spec of upstreamSpecs) { + const source = path.join(extRoot, `third_party/autocomplete/src/${spec}.ts`); + const destination = path.join(extRoot, `src/completions/upstream/${spec}.ts`); + fs.copyFileSync(source, destination); + + let content = fs.readFileSync(destination).toString(); + for (const replaceString of replaceStrings) { + content = content.replaceAll(replaceString[0], replaceString[1]); + } + for (let i = 0; i < indentSearch.length; i++) { + content = content.replaceAll(indentSearch[i], indentReplaceValue[i]); + } + const thisSpecReplaceStrings = specSpecificReplaceStrings.get(spec); + if (thisSpecReplaceStrings) { + for (const replaceString of thisSpecReplaceStrings) { + content = content.replaceAll(replaceString[0], replaceString[1]); + } + } + + fs.writeFileSync(destination, content); +} diff --git a/extensions/terminal-suggest/scripts/update-specs.ps1 b/extensions/terminal-suggest/scripts/update-specs.ps1 new file mode 100644 index 000000000000..0f1291903791 --- /dev/null +++ b/extensions/terminal-suggest/scripts/update-specs.ps1 @@ -0,0 +1 @@ +node "$PSScriptRoot/update-specs.js" diff --git a/extensions/terminal-suggest/scripts/update-specs.sh b/extensions/terminal-suggest/scripts/update-specs.sh new file mode 100755 index 000000000000..4efd5bbf20dc --- /dev/null +++ b/extensions/terminal-suggest/scripts/update-specs.sh @@ -0,0 +1 @@ +node ./update-specs.js diff --git a/extensions/terminal-suggest/src/completions/cd.ts b/extensions/terminal-suggest/src/completions/cd.ts new file mode 100644 index 000000000000..ee38ca1a526a --- /dev/null +++ b/extensions/terminal-suggest/src/completions/cd.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const cdSpec: Fig.Spec = { + name: 'cd', + description: 'Change the shell working directory', + args: { + name: 'folder', + template: 'folders', + + suggestions: [ + { + name: '-', + description: 'Switch to the last used folder', + hidden: true, + }, + ], + } +}; + +export default cdSpec; diff --git a/extensions/terminal-suggest/src/completions/code-insiders.ts b/extensions/terminal-suggest/src/completions/code-insiders.ts new file mode 100644 index 000000000000..1eb4f04acb17 --- /dev/null +++ b/extensions/terminal-suggest/src/completions/code-insiders.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import code, { commonOptions, extensionManagementOptions, troubleshootingOptions } from './code'; + +const codeInsidersCompletionSpec: Fig.Spec = { + ...code, + name: 'code-insiders', + description: 'Visual Studio Code Insiders', + options: [ + ...commonOptions, + ...extensionManagementOptions('code-insiders'), + ...troubleshootingOptions('code-insiders'), + ], +}; + +export default codeInsidersCompletionSpec; diff --git a/extensions/terminal-suggest/src/completions/code.ts b/extensions/terminal-suggest/src/completions/code.ts new file mode 100644 index 000000000000..919253c387f6 --- /dev/null +++ b/extensions/terminal-suggest/src/completions/code.ts @@ -0,0 +1,361 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { filepaths } from '../helpers/filepaths'; + +export const commonOptions: Fig.Option[] = [ + { + name: '-', + description: `Read from stdin (e.g. 'ps aux | grep code | code -')`, + }, + { + name: ['-d', '--diff'], + description: 'Compare two files with each other', + args: [ + { + name: 'file', + template: 'filepaths', + }, + { + name: 'file', + template: 'filepaths', + }, + ], + }, + { + name: ['-m', '--merge'], + description: + 'Perform a three-way merge by providing paths for two modified versions of a file, the common origin of both modified versions and the output file to save merge results', + args: [ + { + name: 'path1', + template: 'filepaths', + }, + { + name: 'path2', + template: 'filepaths', + }, + { + name: 'base', + template: 'filepaths', + }, + { + name: 'result', + template: 'filepaths', + }, + ], + }, + { + name: ['-a', '--add'], + description: 'Add folder(s) to the last active window', + args: { + name: 'folder', + template: 'folders', + isVariadic: true, + }, + }, + { + name: ['-g', '--goto'], + description: + 'Open a file at the path on the specified line and character position', + args: { + name: 'file:line[:character]', + template: 'filepaths', + }, + }, + { + name: ['-n', '--new-window'], + description: 'Force to open a new window', + }, + { + name: ['-r', '--reuse-window'], + description: 'Force to open a file or folder in an already opened window', + }, + { + name: ['-w', '--wait'], + description: 'Wait for the files to be closed before returning', + }, + { + name: '--locale', + description: 'The locale to use (e.g. en-US or zh-TW)', + args: { + name: 'locale', + suggestions: [ + // Supported locales: https://code.visualstudio.com/docs/getstarted/locales#_available-locales + // allow-any-unicode-next-line + { name: 'en', icon: '🇺🇸', description: 'English (US)' }, + // allow-any-unicode-next-line + { name: 'zh-CN', icon: '🇨🇳', description: 'Simplified Chinese' }, + // allow-any-unicode-next-line + { name: 'zh-TW', icon: '🇹🇼', description: 'Traditional Chinese' }, + // allow-any-unicode-next-line + { name: 'fr', icon: '🇫🇷', description: 'French' }, + // allow-any-unicode-next-line + { name: 'de', icon: '🇩🇪', description: 'German' }, + // allow-any-unicode-next-line + { name: 'it', icon: '🇮🇹', description: 'Italian' }, + // allow-any-unicode-next-line + { name: 'es', icon: '🇪🇸', description: 'Spanish' }, + // allow-any-unicode-next-line + { name: 'ja', icon: '🇯🇵', description: 'Japanese' }, + // allow-any-unicode-next-line + { name: 'ko', icon: '🇰🇷', description: 'Korean' }, + // allow-any-unicode-next-line + { name: 'ru', icon: '🇷🇺', description: 'Russian' }, + // allow-any-unicode-next-line + { name: 'bg', icon: '🇧🇬', description: 'Bulgarian' }, + // allow-any-unicode-next-line + { name: 'hu', icon: '🇭🇺', description: 'Hungarian' }, + // allow-any-unicode-next-line + { name: 'pt-br', icon: '🇧🇷', description: 'Portuguese (Brazil)' }, + // allow-any-unicode-next-line + { name: 'tr', icon: '🇹🇷', description: 'Turkish' }, + ], + }, + }, + { + name: '--user-data-dir', + description: + 'Specifies the directory that user data is kept in. Can be used to open multiple distinct instances of Code', + args: { + name: 'dir', + template: 'folders', + }, + }, + { + name: '--profile', + description: + 'Opens the provided folder or workspace with the given profile and associates the profile with the workspace. If the profile does not exist, a new empty one is created. A folder or workspace must be provided for the profile to take effect', + args: { + name: 'settingsProfileName', + }, + }, + { + name: ['-h', '--help'], + description: 'Print usage', + }, + { + name: '--locate-shell-integration-path', + description: + 'Print the path to the shell integration script for the provided shell', + args: { + isOptional: false, + name: 'shell', + description: 'The shell to locate the integration script for', + suggestions: [ + 'bash', + 'fish', + 'pwsh', + 'zsh', + ] + } + } +]; + +export const extensionManagementOptions = (cliName: string): Fig.Option[] => [ + { + name: '--extensions-dir', + description: 'Set the root path for extensions', + args: { + name: 'dir', + template: 'folders', + }, + }, + { + name: '--list-extensions', + description: 'List the installed extensions', + }, + { + name: '--show-versions', + description: + 'Show versions of installed extensions, when using --list-extensions', + }, + { + name: '--category', + description: + 'Filters installed extensions by provided category, when using --list-extensions', + args: { + name: 'category', + suggestions: [ + 'azure', + 'data science', + 'debuggers', + 'extension packs', + 'education', + 'formatters', + 'keymaps', + 'language packs', + 'linters', + 'machine learning', + 'notebooks', + 'programming languages', + 'scm providers', + 'snippets', + 'testing', + 'themes', + 'visualization', + 'other', + ], + }, + }, + { + name: '--install-extension', + description: + `Installs or updates an extension. The argument is either an extension id or a path to a VSIX. The identifier of an extension is '\${ publisher }.\${ name }'. Use '--force' argument to update to latest version. To install a specific version provide '@\${version}'. For example: 'vscode.csharp@1.2.3'`, + args: { + name: 'extension-id[@version] | path-to-vsix', + generators: [ + createCodeGenerators(cliName), + filepaths({ + extensions: ['vsix'], + }), + ], + }, + }, + { + name: '--pre-release', + description: + 'Installs the pre-release version of the extension, when using --install-extension', + }, + { + name: '--uninstall-extension', + description: 'Uninstalls an extension', + args: { + name: 'extension-id', + generators: createCodeGenerators(cliName) + }, + }, + { + name: '--enable-proposed-api', + description: + 'Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually', + }, +]; + +export const troubleshootingOptions = (cliName: string): Fig.Option[] => [ + { + name: ['-v', '--version'], + description: 'Print version', + }, + { + name: '--verbose', + description: 'Print verbose output (implies --wait)', + }, + { + name: '--log', + description: `Log level to use. Default is 'info' when unspecified`, + args: { + name: 'level', + default: 'info', + suggestions: [ + 'critical', + 'error', + 'warn', + 'info', + 'debug', + 'trace', + 'off', + ], + }, + }, + { + name: ['-s', '--status'], + description: 'Print process usage and diagnostics information', + }, + { + name: '--prof-startup', + description: 'Run CPU profiler during startup', + }, + { + name: '--disable-extensions', + description: 'Disable all installed extensions', + }, + { + name: '--disable-extension', + description: 'Disable an extension', + args: { + name: 'extension-id', + generators: createCodeGenerators(cliName) + }, + }, + { + name: '--sync', + description: 'Turn sync on or off', + args: { + name: 'sync', + description: 'Whether to enable sync', + suggestions: ['on', 'off'], + }, + }, + { + name: '--inspect-extensions', + description: + 'Allow debugging and profiling of extensions. Check the developer tools for the connection URI', + args: { + name: 'port', + }, + }, + { + name: '--inspect-brk-extensions', + description: + 'Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection URI', + args: { + name: 'port', + }, + }, + { + name: '--disable-gpu', + description: 'Disable GPU hardware acceleration', + }, + { + name: '--max-memory', + description: 'Max memory size for a window (in Mbytes)', + args: { + name: 'memory', + description: 'Memory in megabytes', + }, + }, + { + name: '--telemetry', + description: 'Shows all telemetry events which VS code collects', + }, +]; + +export function createCodeGenerators(cliName: string): Fig.Generator { + return { + script: [cliName, '--list-extensions', '--show-versions'], + postProcess: parseInstalledExtensions + }; +} + +export function parseInstalledExtensions(out: string): Fig.Suggestion[] | undefined { + const extensions = out.split('\n').filter(Boolean).map((line) => { + const [id, version] = line.split('@'); + return { + name: id, + type: 'option' as Fig.SuggestionType, + description: `Version: ${version}` + }; + }); + return extensions; +} + + +const codeCompletionSpec: Fig.Spec = { + name: 'code', + description: 'Visual Studio Code', + args: { + template: ['filepaths', 'folders'], + isVariadic: true, + }, + options: [ + ...commonOptions, + ...extensionManagementOptions('code'), + ...troubleshootingOptions('code'), + ], +}; + +export default codeCompletionSpec; + diff --git a/extensions/terminal-suggest/src/completions/index.d.ts b/extensions/terminal-suggest/src/completions/index.d.ts new file mode 100644 index 000000000000..44dcb9814456 --- /dev/null +++ b/extensions/terminal-suggest/src/completions/index.d.ts @@ -0,0 +1,1300 @@ +/* eslint-disable @typescript-eslint/ban-types */ +declare namespace Fig { + /** + * Templates are generators prebuilt by Fig. + * @remarks + * Here are the three templates: + * - filepaths: show folders and filepaths. Allow autoexecute on filepaths + * - folders: show folders only. Allow autoexecute on folders + * - history: show suggestions for all items in history matching this pattern + * - help: show subcommands. Only includes the 'siblings' of the nearest 'parent' subcommand + */ + type TemplateStrings = "filepaths" | "folders" | "history" | "help"; + + /** + * A template which is a single TemplateString or an array of TemplateStrings + * + * @remarks + * Templates are generators prebuilt by Fig. Here are the three templates: + * - filepaths: show folders and filepaths. Allow autoexecute on filepaths + * - folders: show folders only. Allow autoexecute on folders + * - history: show suggestions for all items in history matching this pattern + * - help: show subcommands. Only includes the 'siblings' of the nearest 'parent' subcommand + * + * @example + * `cd` uses the "folders" template + * `ls` used ["filepaths", "folders"]. Why both? Because if I `ls` a directory, we want to enable a user to autoexecute on this directory. If we just did "filepaths" they couldn't autoexecute. + * + */ + type Template = TemplateStrings | TemplateStrings[]; + + type HistoryContext = { + currentWorkingDirectory: string; + time: number; + exitCode: number; + shell: string; + }; + + type TemplateSuggestionContext = + | { templateType: "filepaths" } + | { templateType: "folders" } + | { templateType: "help" } + | ({ templateType: "history" } & Partial); + + type TemplateSuggestion = Modify< + Suggestion, + { name?: string; context: TemplateSuggestionContext } + >; + + /** + * + * The SpecLocation object defines well... the location of the completion spec we want to load. + * Specs can be "global" (ie hosted by Fig's cloud) or "local" (ie stored on your local machine) + * + * @remarks + * **The `SpecLocation` Object** + * + * The SpecLocation object defines well... the location of the completion spec we want to load. + * Specs can be "global" (ie hosted by Fig's cloud) or "local" (ie stored on your local machine). + * + * - Global `SpecLocation`: + * Load specs hosted in Fig's Cloud. Assume the current working directory is here: https://github.com/withfig/autocomplete/tree/master/src. Now set the value for the "name" prop to the relative location of your spec (without the .js file extension) + * ```js + * // e.g. + * { type: "global", name: "aws/s3" } // Loads up the aws s3 completion spec + * { type: "global", name: "python/http.server" } // Loads up the http.server completion spec + * ``` + * + * - Local `SpecLocation`: + * Load specs saved on your local system / machine. Assume the current working directory is the user's current working directory. + * The `name` prop should take the name of the spec (without the .js file extension) e.g. my_cli_tool + * The `path` prop should take an absolute path OR a relative path (relative to the user's current working directory). The path should be to the directory that contains the `.fig` folder. Fig will then assume your spec is located in `.fig/autocomplete/build/` + * ```js + * // e.g. + * { type: "global", path: "node_modules/cowsay", name: "cowsay_cli" } // will look for `cwd/node_modules/cowsay/.fig/autocomplete/build/cowsay_cli.js` + * { type: "global", path: "~", name: "my_cli" } // will look for `~/.fig/autocomplete/build/my_cli.js` + * ``` + * @irreplaceable + */ + type SpecLocation = + | { type: "local"; path?: string; name: string } + | { type: "global"; name: string }; + + /** + * Dynamically load up another completion spec at runtime. + * + * See [`loadSpec` property in Subcommand Object](https://fig.io/docs/reference/subcommand#loadspec). + */ + type LoadSpec = + | string + | Subcommand + | (( + token: string, + executeCommand: ExecuteCommandFunction + ) => Promise); + + /** + * The type of a suggestion object. + * @remarks + * The type determines: + * - the default icon Fig uses (e.g. a file or folder searches for the system icon, a subcommand has a specific icon etc) + * - whether we allow users to auto-execute a command + */ + type SuggestionType = + | "folder" + | "file" + | "arg" + | "subcommand" + | "option" + | "special" + | "mixin" + | "shortcut"; + + /** + * A single object of type `T` or an array of objects of type `T`. + */ + type SingleOrArray = T | T[]; + + /** + * An async function that returns the version of a given CLI tool. + * @remarks + * This is used in completion specs that want to version themselves the same way CLI tools are versioned. See fig.io/docs + * + * @param executeCommand -an async function that allows you to execute a shell command on the user's system and get the output as a string. + * @returns The version of a CLI tool + * + * @example + * `1.0.22` + * + * @example + * `v26` + * + */ + type GetVersionCommand = (executeCommand: ExecuteCommandFunction) => Promise; + + /** + * Context about a current shell session. + */ + type ShellContext = { + /** + * The current directory the shell is in + */ + currentWorkingDirectory: string; + /** + * Exported environment variables from the shell + */ + environmentVariables: Record; + /** + * The name of the current process + */ + currentProcess: string; + /** + * @hidden + * @deprecated + */ + sshPrefix: string; + }; + + type GeneratorContext = ShellContext & { + isDangerous?: boolean; + searchTerm: string; + }; + + /** + * A function which can have a `T` argument and a `R` result. + * @param param - A param of type `R` + * @returns Something of type `R` + */ + type Function = (param: T) => R; + + /** + * A utility type to modify a property type + * @irreplaceable + */ + type Modify = Omit & R; + + /** + * A `string` OR a `function` which can have a `T` argument and a `R` result. + * @param param - A param of type `R` + * @returns Something of type `R` + */ + type StringOrFunction = string | Function; + + /** + * @excluded + * @irreplaceable + */ + type ArgDiff = Modify; + + /** + * @excluded + * @irreplaceable + */ + type OptionDiff = Modify< + Fig.Option, + { + args?: ArgDiff | ArgDiff[]; + remove?: true; + } + >; + + /** + * @excluded + * @irreplaceable + */ + type SubcommandDiff = Modify< + Fig.Subcommand, + { + subcommands?: SubcommandDiff[]; + options?: OptionDiff[]; + args?: ArgDiff | ArgDiff[]; + remove?: true; + } + >; + + /** + * @excluded + * @irreplaceable + */ + type SpecDiff = Omit; + + /** + * @excluded + * @irreplaceable + */ + type VersionDiffMap = Record; + + /** + * A spec object. + * Can be one of + * 1. A subcommand + * 2. A function that dynamically computes a subcommand + * 3. A function that returns the path to a versioned spec files (that exports a base subcommand and { versions: VersionDiffMap } + */ + type Spec = + | Subcommand + | ((version?: string) => Subcommand) + | ((version?: string) => { + versionedSpecPath: string; + version?: string; + }); + + type ExecuteCommandInput = { + /** + * The command to execute + */ + command: string; + /** + * The arguments to the command to be run + */ + args: string[]; + /** + * The directory to run the command in + */ + cwd?: string; + /** + * The environment variables to set when executing the command, `undefined` will unset the variable if it set + */ + env?: Record; + /** + * Duration of timeout in milliseconds, if the command takes longer than the timeout a error will be thrown. + * @defaultValue 5000 + */ + timeout?: number; + }; + + /** + * The output of running a command + */ + type ExecuteCommandOutput = { + /** + * The stdout (1) of running a command + */ + stdout: string; + /** + * The stderr (2) of running a command + */ + stderr: string; + /** + * The exit status of running a command + */ + status: number; + }; + + /** + * An async function to execute a command + * @returns The output of the command + */ + type ExecuteCommandFunction = (args: ExecuteCommandInput) => Promise; + + type CacheMaxAge = { + strategy: "max-age"; + /** + * The time to live for the cache in milliseconds. + * @example + * 3600 + */ + ttl: number; + }; + + type CacheStaleWhileRevalidate = { + strategy?: "stale-while-revalidate"; + /** + * The time to live for the cache in milliseconds. + * @example + * 3600 + */ + ttl?: number; + }; + + type Cache = (CacheMaxAge | CacheStaleWhileRevalidate) & { + /** + * Whether the cache should be based on the directory the user was currently in or not. + * @defaultValue false + */ + cacheByDirectory?: boolean; + + /** + * Hardcoded cache key that can be used to cache a single generator across + * multiple argument locations in a spec. + */ + cacheKey?: string; + }; + + type TriggerOnChange = { + /** Trigger on any change to the token */ + on: "change"; + }; + + type TriggerOnThreshold = { + /** Trigger when the length of the token changes past a threshold */ + on: "threshold"; + length: number; + }; + + type TriggerOnMatch = { + /** Trigger when the index of a string changes */ + on: "match"; + string: string | string[]; + }; + + type Trigger = + | string + | ((newToken: string, oldToken: string) => boolean) + | TriggerOnChange + | TriggerOnThreshold + | TriggerOnMatch; + + /** + * The BaseSuggestion object is the root of the Suggestion, Subcommand, and Option objects. + * It is where key properties like description, icon, and displayName are found + * @excluded + */ + interface BaseSuggestion { + /** + * The string that is displayed in the UI for a given suggestion. + * @defaultValue the name prop + * + * @example + * The npm CLI has a subcommand called `install`. If we wanted + * to display some custom text like `Install an NPM package 📦` we would set + * `name: "install"` and `displayName: "Install an NPM package 📦"` + */ + displayName?: string; + /** + * The value that's inserted into the terminal when a user presses enter/tab or clicks on a menu item. + * + * @remarks + * You can use `\n` to insert a newline or `\b` to insert a backspace. + * You can also optionally specify {cursor} in the string and Fig will automatically place the cursor there after insert. + * + * @defaultValue The value of the name prop. + * + * @example + * For the `git commit` subcommand, the `-m` option has an insert value of `-m '{cursor}'` + */ + insertValue?: string; + /** + * When the suggestion is inserted, replace the command with this string + * + * @remarks + * You can use `\n` to insert a newline or `\b` to insert a backspace. + * You can also optionally specify {cursor} in the string and Fig will automatically place the cursor there after insert. + * Note that currently the entire edit buffer will be replaced. Eventually, only the root command will be replaced, preserving pipes and continuations. + */ + replaceValue?: string; + /** + * The text that gets rendered at the bottom of the autocomplete box (or the side if you hit ⌘i) + * + * @example + * "Your commit message" + */ + description?: string; + /** + * The icon that is rendered is based on the type. + * + * @remarks + * Icons can be a 1 character string, a URL, or Fig's [icon protocol](https://fig.io/docs/reference/suggestion/icon-api) (fig://) which lets you generate + * colorful and fun systems icons. + * + * @defaultValue related to the type of the object (e.g. `Suggestion`, `Subcommand`, `Option`, `Arg`) + * + * @example + * `A` + * @example + * `😊` + * @example + * `https://www.herokucdn.com/favicon.ico` + * @example + * `fig://icon?type=file` + * + */ + icon?: string; + /** + * Specifies whether the suggestion is "dangerous". + * + * @remarks + * If true, Fig will not enable its autoexecute functionality. Autoexecute means if a user selects a suggestion it will insert the text and run the command. We signal this by changing the icon to red. + * Setting `isDangerous` to `true` will make it harder for a user to accidentally run a dangerous command. + * + * @defaultValue false + * + * @example + * This is used in the `rm` spec. Why? Because we don't want users to accidentally delete their files so we make it just a little bit harder... + */ + isDangerous?: boolean; + /** + * The number used to rank suggestions in autocomplete. Number must be from 0-100. Higher priorities rank higher. + * + * @defaultValue 50 + * @remarks + * Fig ranks suggestions by recency. To do this, we check if a suggestion has been selected before. If yes and the suggestions has: + * - a priority between 50-75, the priority will be replaced with 75, then we will add the timestamp of when that suggestion was selected as a decimal. + * - a priority outside of 50-75, the priority will be increased by the timestamp of when that suggestion was selected as a decimal. + * If it has not been selected before, Fig will keep the same priority as was set in the completion spec + * If it was not set in the spec, it will default to 50. + * + * @example + * Let's say a user has previously selected a suggestion at unix timestamp 1634087677: + * - If completion spec did not set a priority (Fig treats this as priority 50), its priority would change to 75 + 0.1634087677 = 75.1634087677; + * - If completion spec set a priority of 49 or less, its priority would change to 49 + 0.1634087677 = 49.1634087677; + * - If completion spec set a priority of 76 or more, its priority would change to 76 + 0.1634087677 = 76.1634087677; + * - If a user had never selected a suggestion, then its priority would just stay as is (or if not set, default to 50). + * + * @example + * If you want your suggestions to always be: + * - at the top order, rank them 76 or above. + * - at the bottom, rank them 49 or below + */ + priority?: number; + /** + * Specifies whether a suggestion should be hidden from results. + * @remarks + * Fig will only show it if the user exactly types the name. + * @defaultValue false + * @example + * The "-" suggestion is hidden in the `cd` spec. You will only see it if you type exactly `cd -` + */ + hidden?: boolean; + /** + * + * Specifies whether a suggestion is deprecated. + * @remarks + * It is possible to specify a suggestion to replace the deprecated one. + * - The `description` of the deprecated object (e.g `deprecated: { description: 'The --no-ansi option has been deprecated in v2' }`) is used to provide infos about the deprecation. + * - `deprecated: true` and `deprecated: { }` behave the same and will just display the suggestion as deprecated. + * @example + * ```js + * deprecated: { insertValue: '--ansi never', description: 'The --no-ansi option has been deprecated in v2' } + * ``` + */ + deprecated?: boolean | Omit; + + /** + * Specifies which component to use to render the preview window. + * + * @remarks This should be the path within the `src` directory to the component without the extension. + * + * @example 'ls/filepathPreview' + */ + previewComponent?: string; + + /** + * This is a way to pass data to the Autocomplete Engine that is not formalized in the spec, do not use this in specs as it may change at any time + * + * @ignore + */ + _internal?: Record; + } + + /** + * Each item in Fig's autocomplete popup window is a Suggestion object. It is probably the most important object in Fig. + * Subcommand and Option objects compile down to Suggestion objects. Generators return Suggestion objects. + * The main things you can customize in your suggestion object is the text that's displayed, the icon, and what's inserted after being selected. In saying that, most of these have very sane defaults. + */ + interface Suggestion extends BaseSuggestion { + /** + * The string Fig uses when filtering over a list of suggestions to check for a match. + * @remarks + * When a a user is typing in the terminal, the query term (the token they are currently typing) filters over all suggestions in a list by checking if the queryTerm matches the prefix of the name. + * The `displayName` prop also defaults to the value of name. + * + * The `name` props of suggestion, subcommand, option, and arg objects are all different. It's important to read them all carefully. + * + * @example + * If a user types git `c`, any Suggestion objects with a name prop that has a value starting with "c" will match. + * + */ + name?: SingleOrArray; + /** + * The type of a suggestion object. + * @remarks + * The type determines + * - the default icon Fig uses (e.g. a file or folder searches for the system icon, a subcommand has a specific icon etc) + * - whether we allow users to auto-execute a command + */ + type?: SuggestionType; + } + + /** + * The subcommand object represent the tree structure of a completion spec. We sometimes also call it the skeleton. + * + * A subcommand can nest options, arguments, and more subcommands (it's recursive) + */ + interface Subcommand extends BaseSuggestion { + /** + * The name of the subcommand. Should exactly match the name defined by the CLI tool. + * + * @remarks + * If a subcommand has multiple aliases, they should be included as an array. + * + * Note that Fig's autocomplete engine requires this `name` to match the text typed by the user in the shell. + * + * To customize the title that is displayed to the user, use `displayName`. + * + * + * @example + * For `git checkout`, the subcommand `checkout` would have `name: "checkout"` + * @example + * For `npm install`, the subcommand `install` would have `name: ["install", "i"]` as these two values both represent the same subcommand. + */ + name: SingleOrArray; + + /** + * An array of `Subcommand` objects representing all the subcommands that exist beneath the current command. + * * + * To support large CLI tools, `Subcommands` can be nested recursively. + * + * @example + * A CLI tool like `aws` is composed of many top-level subcommands (`s3`, `ec2`, `eks`...), each of which include child subcommands of their own. + */ + subcommands?: Subcommand[]; + + /** + * Specifies whether the command requires a subcommand. This is false by default. + * + * A space will always be inserted after this command if `requiresSubcommand` is true. + * If the property is omitted, a space will be inserted if there is at least one required argument. + */ + requiresSubcommand?: boolean; + + /** + * An array of `Option` objects representing the options that are available on this subcommand. + * + * @example + * A command like `git commit` accepts various flags and options, such as `--message` and `--all`. These `Option` objects would be included in the `options` field. + */ + options?: Option[]; + + /** + * An array of `Arg` objects representing the various parameters or "arguments" that can be passed to this subcommand. + * + */ + args?: SingleOrArray; + /** + * This option allows to enforce the suggestion filtering strategy for a specific subcommand. + * @remarks + * Users always want to have the most accurate results at the top of the suggestions list. + * For example we can enable fuzzy search on a subcommand that always requires fuzzy search to show the best suggestions. + * This property is also useful when subcommands or options have a prefix (e.g. the npm package scope) because enabling fuzzy search users can omit that part (see the second example below) + * @example + * yarn workspace [name] with fuzzy search is way more useful since we can omit the npm package scope + * @example + * fig settings uses fuzzy search to prevent having to add the `autocomplete.` prefix to each searched setting + * ```typescript + * const figSpec: Fig.Spec { + * name: "fig", + * subcommands: [ + * { + * name: "settings", + * filterStrategy: "fuzzy", + * subcommands: [ + * { + * name: "autocomplete.theme", // if a user writes `fig settings theme` it gets the correct suggestions + * }, + * // ... other settings + * ] + * }, + * // ... other fig subcommands + * ] + * } + * ``` + */ + filterStrategy?: "fuzzy" | "prefix" | "default"; + /** + * A list of Suggestion objects that are appended to the suggestions shown beneath a subcommand. + * + * @remarks + * You can use this field to suggest common workflows. + * + */ + additionalSuggestions?: (string | Suggestion)[]; + /** + * Dynamically load another completion spec at runtime. + * + * @param tokens - a tokenized array of the text the user has typed in the shell. + * @param executeCommand - an async function that can execute a shell command on behalf of the user. The output is a string. + * @returns A `SpecLocation` object or an array of `SpecLocation` objects. + * + * @remarks + * `loadSpec` can be invoked as string (recommended) or a function (advanced). + * + * The API tells the autocomplete engine where to look for a completion spec. If you pass a string, the engine will attempt to locate a matching spec that is hosted by Fig. + * + * @example + * Suppose you have an internal CLI tool that wraps `kubectl`. Instead of copying the `kubectl` completion spec, you can include the spec at runtime. + * ```typescript + * { + * name: "kube", + * description: "a wrapper around kubectl" + * loadSpec: "kubectl" + * } + * ``` + * @example + * In the `aws` completion spec, `loadSpec` is used to optimize performance. The completion spec is split into multiple files, each of which can be loaded separately. + * ```typescript + * { + * name: "s3", + * loadSpec: "aws/s3" + * } + * ``` + */ + loadSpec?: LoadSpec; + /** + * Dynamically *generate* a `Subcommand` object a runtime. The generated `Subcommand` is merged with the current subcommand. + * + * @remarks + * This API is often used by CLI tools where the structure of the CLI tool is not *static*. For instance, if the tool can be extended by plugins or otherwise shows different subcommands or options depending on the environment. + * + * @param tokens - a tokenized array of the text the user has typed in the shell. + * @param executeCommand - an async function that can execute a shell command on behalf of the user. The output is a string. + * @returns a `Fig.Spec` object + * + * @example + * The `python` spec uses `generateSpec` to include the`django-admin` spec if `django manage.py` exists. + * ```typescript + * generateSpec: async (tokens, executeCommand) => { + * // Load the contents of manage.py + * const managePyContents = await executeCommand("cat manage.py"); + * // Heuristic to determine if project uses django + * if (managePyContents.contains("django")) { + * return { + * name: "python", + * subcommands: [{ name: "manage.py", loadSpec: "django-admin" }], + * }; + * } + * }, + * ``` + */ + generateSpec?: (tokens: string[], executeCommand: ExecuteCommandFunction) => Promise; + + /** + * Generating a spec can be expensive, but due to current guarantees they are not cached. + * This function generates a cache key which is used to cache the result of generateSpec. + * If `undefined` is returned, the cache will not be used. + */ + generateSpecCacheKey?: Function<{ tokens: string[] }, string | undefined> | string; + + /** + * Configure how the autocomplete engine will map the raw tokens to a given completion spec. + * + * @param flagsArePosixNoncompliant - Indicates that flags with one hyphen may have *more* than one character. Enabling this directive, turns off support for option chaining. + * @param optionsMustPrecedeArguments - Options will not be suggested after any argument of the Subcommand has been typed. + * @param optionArgSeparators - Indicate that options which take arguments will require one of the specified separators between the 'verbose' option name and the argument. + * + * @example + * The `-work` option from the `go` spec is parsed as a single flag when `parserDirectives.flagsArePosixNoncompliant` is set to true. Normally, this would be chained and parsed as `-w -o -r -k` if `flagsArePosixNoncompliant` is not set to true. + */ + parserDirectives?: { + flagsArePosixNoncompliant?: boolean; + optionsMustPrecedeArguments?: boolean; + optionArgSeparators?: SingleOrArray; + }; + + /** + * Specifies whether or not to cache the result of loadSpec and generateSpec + * + * @remarks + * Caching is good because it reduces the time to completion on subsequent calls to a dynamic subcommand, but when the data does not outlive the cache this allows a mechanism for opting out of it. + */ + cache?: boolean; + } + + /** + * The option object represent CLI options (sometimes called flags). + * + * A option can have an argument. An option can NOT have subcommands or other option + */ + interface Option extends BaseSuggestion { + /** + * The exact name of the subcommand as defined in the CLI tool. + * + * @remarks + * Fig's parser relies on your option name being exactly what the user would type. (e.g. if the user types `git "-m"`, you must have `name: "-m"` and not something like `name: "your message"` or even with an `=` sign like`name: "-m="`) + * + * If you want to customize what the text the popup says, use `displayName`. + * + * The name prop in an Option object compiles down to the name prop in a Suggestion object + * + * Final note: the name prop can be a string (most common) or an array of strings + * + * + * @example + * For `git commit -m` in the, message option nested beneath `commit` would have `name: ["-m", "--message"]` + * @example + * For `ls -l` the `-l` option would have `name: "-l"` + */ + name: SingleOrArray; + + /** + * An array of arg objects or a single arg object + * + * @remarks + * If a subcommand takes an argument, please at least include an empty Arg Object. (e.g. `{ }`). Why? If you don't, Fig will assume the subcommand does not take an argument. When the user types their argument + * If the argument is optional, signal this by saying `isOptional: true`. + * + * @example + * `npm run` takes one mandatory argument. This can be represented by `args: { }` + * @example + * `git push` takes two optional arguments. This can be represented by: `args: [{ isOptional: true }, { isOptional: true }]` + * @example + * `git clone` takes one mandatory argument and one optional argument. This can be represented by: `args: [{ }, { isOptional: true }]` + */ + args?: SingleOrArray; + /** + * + * Signals whether an option is persistent, meaning that it will still be available + * as an option for all child subcommands. + * + * @remarks + * As of now there is no way to disable this + * persistence for certain children. Also see + * https://github.com/spf13/cobra/blob/master/user_guide.md#persistent-flags. + * + * @defaultValue false + * + * @example + * Say the `git` spec had an option at the top level with `{ name: "--help", isPersistent: true }`. + * Then the spec would recognize both `git --help` and `git commit --help` + * as a valid as we are passing the `--help` option to all `git` subcommands. + * + */ + isPersistent?: boolean; + /** + * Signals whether an option is required. + * + * @defaultValue false (option is NOT required) + * @example + * The `-m` option of `git commit` is required + * + */ + isRequired?: boolean; + /** + * + * Signals whether an equals sign is required to pass an argument to an option (e.g. `git commit --message="msg"`) + * @defaultValue false (does NOT require an equal) + * + * @example + * When `requiresEqual: true` the user MUST do `--opt=value` and cannot do `--opt value` + * + * @deprecated use `requiresSeparator` instead + * + */ + requiresEquals?: boolean; + /** + * + * Signals whether one of the separators specified in parserDirectives is required to pass an argument to an option (e.g. `git commit --message[separator]"msg"`) + * If set to true this will automatically insert an equal after the option name. + * If set to a separator (string) this will automatically insert the separator specified after the option name. + * @defaultValue false (does NOT require a separator) + * + * @example + * When `requiresSeparator: true` the user MUST do `--opt=value` and cannot do `--opt value` + * @example + * When `requiresSeparator: ':'` the user MUST do `--opt:value` and cannot do `--opt value` + */ + requiresSeparator?: boolean | string; + /** + * + * Signals whether an option can be passed multiple times. + * + * @defaultValue false (option is NOT repeatable) + * + * @remarks + * Passing `isRepeatable: true` will allow an option to be passed any number + * of times, while passing `isRepeatable: 2` will allow it to be passed + * twice, etc. Passing `isRepeatable: false` is the same as passing + * `isRepeatable: 1`. + * + * If you explicitly specify the isRepeatable option in a spec, this + * constraint will be enforced at the parser level, meaning after the option + * (say `-o`) has been passed the maximum number of times, Fig's parser will + * not recognize `-o` as an option if the user types it again. + * + * @example + * In `npm install` doesn't specify `isRepeatable` for `{ name: ["-D", "--save-dev"] }`. + * When the user types `npm install -D`, Fig will no longer suggest `-D`. + * If the user types `npm install -D -D`. Fig will still parse the second + * `-D` as an option. + * + * Suppose `npm install` explicitly specified `{ name: ["-D", "--save-dev"], isRepeatable: false }`. + * Now if the user types `npm install -D -D`, Fig will instead parse the second + * `-D` as the argument to the `install` subcommand instead of as an option. + * + * @example + * SSH has `{ name: "-v", isRepeatable: 3 }`. When the user types `ssh -vv`, Fig + * will still suggest `-v`, when the user types `ssh -vvv` Fig will stop + * suggesting `-v` as an option. Finally if the user types `ssh -vvvv` Fig's + * parser will recognize that this is not a valid string of chained options + * and will treat this as an argument to `ssh`. + * + */ + isRepeatable?: boolean | number; + /** + * + * Signals whether an option is mutually exclusive with other options (ie if the user has this option, Fig should not show the options specified). + * @defaultValue false + * + * @remarks + * Options that are mutually exclusive with flags the user has already passed will not be shown in the suggestions list. + * + * @example + * You might see `[-a | --interactive | --patch]` in a man page. This means each of these options are mutually exclusive on each other. + * If we were defining the exclusive prop of the "-a" option, then we would have `exclusive: ["--interactive", "--patch"]` + * + */ + exclusiveOn?: string[]; + /** + * + * + * Signals whether an option depends on other options (ie if the user has this option, Fig should only show these options until they are all inserted). + * + * @defaultValue false + * + * @remarks + * If the user has an unmet dependency for a flag they've already typed, this dependency will have boosted priority in the suggestion list. + * + * @example + * In a tool like firebase, we may want to delete a specific extension. The command might be `firebase delete --project ABC --extension 123` This would mean we delete the 123 extension from the ABC project. + * In this case, `--extension` dependsOn `--project` + * + */ + dependsOn?: string[]; + } + + /** + * The arg object represent CLI arguments (sometimes called positional arguments). + * + * An argument is different to a subcommand object and option object. It does not compile down to a suggestion object. Rather, it represents custom user input. If you want to generate suggestions for this custom user input, you should use the generator prop nested beneath an Arg object + */ + interface Arg { + /** + * The name of an argument. This is different to the `name` prop for subcommands, options, and suggestion objects so please read carefully. + * This `name` prop signals a normal, human readable string. It usually signals to the user the type of argument they are inserting if there are no available suggestions. + * Unlike subcommands and options, Fig does NOT use this value for parsing. Therefore, it can be whatever you want. + * + * @example + * The name prop for the `git commit -m ` arg object is "msg". But you could also make it "message" or "your message". It is only used for description purposes (you see it when you type the message), not for parsing! + */ + name?: string; + + /** + * The text that gets rendered at the bottom of the autocomplete box a) when the user is inputting an argument and there are no suggestions and b) for all generated suggestions for an argument + * Keep it short and direct! + * + * @example + * "Your commit message" + */ + description?: string; + + /** + * Specifies whether the suggestions generated for this argument are "dangerous". + * + * @remarks + * If true, Fig will not enable its autoexecute functionality. Autoexecute means if a user selects a suggestion it will insert the text and run the command. We signal this by changing the icon to red. + * Turning on isDangerous will make it harder for a user to accidentally run a dangerous command. + * + * @defaultValue false + * + * @example + * This is used for all arguments in the `rm` spec. + */ + isDangerous?: boolean; + + /** + * A list of Suggestion objects that are shown when a user is typing an argument. + * + * @remarks + * These suggestions are static meaning you know them beforehand and they are not generated at runtime. If you want to generate suggestions at runtime, use a generator + * + * @example + * For `git reset `, a two common arguments to pass are "head" and "head^". Therefore, the spec suggests both of these by using the suggestion prop + */ + suggestions?: (string | Suggestion)[]; + /** + * A template which is a single TemplateString or an array of TemplateStrings + * + * @remarks + * Templates are generators prebuilt by Fig. Here are the three templates: + * - filepaths: show folders and filepaths. Allow autoexecute on filepaths + * - folders: show folders only. Allow autoexecute on folders + * - history: show suggestions for all items in history matching this pattern + * - help: show subcommands. Only includes the 'siblings' of the nearest 'parent' subcommand + * + * @example + * `cd` uses the "folders" template + * @example + * `ls` used ["filepaths", "folders"]. Why both? Because if I `ls` a directory, we want to enable a user to autoexecute on this directory. If we just did "filepaths" they couldn't autoexecute. + * + */ + template?: Template; + /** + * + * Generators let you dynamically generate suggestions for arguments by running shell commands on a user's device. + * + * This takes a single generator or an array of generators + */ + generators?: SingleOrArray; + /** + * This option allows to enforce the suggestion filtering strategy for a specific argument suggestions. + * @remarks + * Users always want to have the most accurate results at the top of the suggestions list. + * For example we can enable fuzzy search on an argument that always requires fuzzy search to show the best suggestions. + * This property is also useful when argument suggestions have a prefix (e.g. the npm package scope) because enabling fuzzy search users can omit that part (see the second example below) + * @example + * npm uninstall [packages...] uses fuzzy search to allow searching for installed packages ignoring the package scope + * ```typescript + * const figSpec: Fig.Spec { + * name: "npm", + * subcommands: [ + * { + * args: { + * name: "packages", + * filterStrategy: "fuzzy", // search in suggestions provided by the generator (in this case) using fuzzy search + * generators: generateNpmDeps, + * isVariadic: true, + * }, + * }, + * // ... other npm commands + * ], + * } + * ``` + */ + filterStrategy?: "fuzzy" | "prefix" | "default"; + /** + * Provide a suggestion at the top of the list with the current token that is being typed by the user. + */ + suggestCurrentToken?: boolean; + /** + * Specifies that the argument is variadic and therefore repeats infinitely. + * + * @remarks + * Man pages represent variadic arguments with an ellipsis e.g. `git add ` + * + * @example + * `echo` takes a variadic argument (`echo hello world ...`) + * @example + * `git add` also takes a variadic argument + */ + isVariadic?: boolean; + + /** + * Specifies whether options can interrupt variadic arguments. There is + * slightly different behavior when this is used on an option argument and + * on a subcommand argument: + * + * - When an option breaks a *variadic subcommand argument*, after the option + * and any arguments are parsed, the parser will continue parsing variadic + * arguments to the subcommand + * - When an option breaks a *variadic option argument*, after the breaking + * option and any arguments are parsed, the original variadic options + * arguments will be terminated. See the second examples below for details. + * + * + * @defaultValue true + * + * @example + * When true for git add's argument: + * `git add file1 -v file2` will interpret `-v` as an option NOT an + * argument, and will continue interpreting file2 as a variadic argument to + * add after + * + * @example + * When true for -T's argument, where -T is a variadic list of tags: + * `cmd -T tag1 tag2 -p project tag3` will interpret `-p` as an option, but + * will then terminate the list of tags. So tag3 is not parsed as an + * argument to `-T`, but rather as a subcommand argument to `cmd` if `cmd` + * takes any arguments. + * + * @example + * When false: + * `echo hello -n world` will treat -n as an argument NOT an option. + * However, in `echo -n hello world` it will treat -n as an option as + * variadic arguments haven't started yet + * + */ + optionsCanBreakVariadicArg?: boolean; + + /** + * `true` if an argument is optional (ie the CLI spec says it is not mandatory to include an argument, but you can if you want to). + * + * @remarks + * NOTE: It is important you include this for our parsing. If you don't, Fig will assume the argument is mandatory. When we assume an argument is mandatory, we force the user to input the argument and hide all other suggestions. + * + * @example + * `git push [remote] [branch]` takes two optional args. + */ + isOptional?: boolean; + /** + * Syntactic sugar over the `loadSpec` prop. + * + * @remarks + * Specifies that the argument is an entirely new command which Fig should start completing on from scratch. + * + * @example + * `time` and `builtin` have only one argument and this argument has the `isCommand` property. If I type `time git`, Fig will load up the git completion spec because the isCommand property is set. + */ + isCommand?: boolean; + /** + * The same as the `isCommand` prop, except Fig will look for a completion spec in the `.fig/autocomplete/build` folder in the user's current working directory. + * + * @remarks + * See our docs for more on building completion specs for local scripts [Fig for Teams](https://fig.io/docs/) + * @example + * `python` take one argument which is a `.py` file. If I have a `main.py` file on my desktop and my current working directory is my desktop, if I type `python main.py[space]` Fig will look for a completion spec in `~/Desktop/.fig/autocomplete/build/main.py.js` + */ + isScript?: boolean; + /** + * The same as the `isCommand` prop, except you specify a string to prepend to what the user inputs and fig will load the completion spec accordingly. + * @remarks + * If isModule: "python/", Fig would load up the `python/USER_INPUT.js` completion spec from the `~/.fig/autocomplete` folder. + * @example + * For `python -m`, the user can input a specific module such as http.server. Each module is effectively a mini CLI tool that should have its own completions. Therefore the argument object for -m has `isModule: "python/"`. Whatever the modules user inputs, Fig will look under the `~/.fig/autocomplete/python/` directory for completion spec. + * + * @deprecated use `loadSpec` instead + */ + isModule?: string; + + /** + * This will debounce every keystroke event for this particular arg. + * @remarks + * If there are no keystroke events after 100ms, Fig will execute all the generators in this arg and return the suggestions. + * + * @example + * `npm install` and `pip install` send debounced network requests after inactive typing from users. + */ + debounce?: boolean; + /** + * The default value for an optional argument. + * + * @remarks + * Note: This is currently not used anywhere in Fig's autocomplete popup, but will be soon. + * + */ + default?: string; + /** + * See [`loadSpec` in Subcommand Object](https://fig.io/docs/reference/subcommand#loadspec). + * + * @remarks + * There is a very high chance you want to use one of the following: + * 1. `isCommand` (See [Arg Object](https://fig.io/docs/reference/arg#iscommand)) + * 2. `isScript` (See [Arg Object](https://fig.io/docs/reference/arg#isscript)) + * + */ + loadSpec?: LoadSpec; + + /** + * The `arg.parserDirective.alias` prop defines whether Fig's tokenizer should expand out an alias into separate tokens then offer completions accordingly. + * + * @remarks + * This is similar to how Fig is able to offer autocomplete for user defined shell aliases, but occurs at the completion spec level. + * + * @param token - The token that the user has just typed that is an alias for something else + * @param executeCommand -an async function that allows you to execute a shell command on the user's system and get the output as a string. + * @returns The expansion of the alias that Fig's bash parser will reparse as if it were typed out in full, rather than the alias. + * + * If for some reason you know exactly what it will be, you may also just pass in the expanded alias, not a function that returns the expanded alias. + * + * @example + * git takes git aliases. These aliases are defined in a user's gitconfig file. Let's say a user has an alias for `p=push`, then if a user typed `git p[space]`, this function would take the `p` token, return `push` and then offer suggestions as if the user had typed `git push[space]` + * + * @example + * `npm run - - diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter.html b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter.html deleted file mode 100644 index 2f87d2489cef..000000000000 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter.ts deleted file mode 100644 index 629a6185ccb7..000000000000 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter.ts +++ /dev/null @@ -1,26 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/* eslint-disable no-restricted-globals */ - -(async function () { - - type IBootstrapWindow = import('vs/platform/window/electron-sandbox/window.js').IBootstrapWindow; - type IIssueReporterMain = import('vs/workbench/contrib/issue/electron-sandbox/issueReporterMain').IIssueReporterMain; - type OldIssueReporterWindowConfiguration = import('vs/platform/issue/common/issue.js').OldIssueReporterWindowConfiguration; - - const bootstrapWindow: IBootstrapWindow = (window as any).MonacoBootstrapWindow; // defined by bootstrap-window.ts - - const { result, configuration } = await bootstrapWindow.load('vs/workbench/contrib/issue/electron-sandbox/issueReporterMain', { - configureDeveloperSettings: function () { - return { - forceEnableDeveloperKeybindings: true, - disallowReloadKeybinding: true - }; - } - }); - - result.startup(configuration); -}()); diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterMain.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterMain.ts deleted file mode 100644 index fa1628e3be88..000000000000 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterMain.ts +++ /dev/null @@ -1,57 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { safeInnerHtml } from '../../../../base/browser/dom.js'; -import '../../../../base/browser/ui/codicons/codiconStyles.js'; // make sure codicon css is loaded -import { mainWindow } from '../../../../base/browser/window.js'; -import { isLinux, isWindows } from '../../../../base/common/platform.js'; -import './media/issueReporter.css'; -import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; -import { getSingletonServiceDescriptors } from '../../../../platform/instantiation/common/extensions.js'; -import { InstantiationService } from '../../../../platform/instantiation/common/instantiationService.js'; -import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; -import { IMainProcessService } from '../../../../platform/ipc/common/mainProcessService.js'; -import { ElectronIPCMainProcessService } from '../../../../platform/ipc/electron-sandbox/mainProcessService.js'; -import { registerMainProcessRemoteService } from '../../../../platform/ipc/electron-sandbox/services.js'; -import { INativeHostService } from '../../../../platform/native/common/native.js'; -import { NativeHostService } from '../../../../platform/native/common/nativeHostService.js'; -import BaseHtml from '../browser/issueReporterPage.js'; -import { IProcessMainService, IIssueMainService, OldIssueReporterWindowConfiguration } from '../../../../platform/issue/common/issue.js'; -import { IssueReporter } from './issueReporterService.js'; - -export interface IIssueReporterMain { - startup(configuration: OldIssueReporterWindowConfiguration): void; -} - -export function startup(configuration: OldIssueReporterWindowConfiguration): void { - const platformClass = isWindows ? 'windows' : isLinux ? 'linux' : 'mac'; - mainWindow.document.body.classList.add(platformClass); // used by our fonts - - safeInnerHtml(mainWindow.document.body, BaseHtml()); - - const instantiationService = initServices(configuration.windowId); - - const issueReporter = instantiationService.createInstance(IssueReporter, configuration); - issueReporter.render(); - mainWindow.document.body.style.display = 'block'; - issueReporter.setInitialFocus(); -} - -function initServices(windowId: number) { - const services = new ServiceCollection(); - - const contributedServices = getSingletonServiceDescriptors(); - for (const [id, descriptor] of contributedServices) { - services.set(id, descriptor); - } - - services.set(IMainProcessService, new SyncDescriptor(ElectronIPCMainProcessService, [windowId])); - services.set(INativeHostService, new SyncDescriptor(NativeHostService, [windowId])); - - return new InstantiationService(services, true); -} - -registerMainProcessRemoteService(IIssueMainService, 'issue'); -registerMainProcessRemoteService(IProcessMainService, 'process'); diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterService.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterService.ts index bc6f5eb9de27..bb27e35e45dd 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterService.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterService.ts @@ -2,112 +2,57 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { $, createStyleSheet, isHTMLInputElement, isHTMLTextAreaElement, reset, windowOpenNoOpener } from '../../../../base/browser/dom.js'; -import { Button, unthemedButtonStyles } from '../../../../base/browser/ui/button/button.js'; -import { renderIcon } from '../../../../base/browser/ui/iconLabel/iconLabels.js'; -import { mainWindow } from '../../../../base/browser/window.js'; -import { Delayer, RunOnceScheduler } from '../../../../base/common/async.js'; -import { Codicon } from '../../../../base/common/codicons.js'; -import { groupBy } from '../../../../base/common/collections.js'; -import { debounce } from '../../../../base/common/decorators.js'; +import { $, reset } from '../../../../base/browser/dom.js'; +import { VSBuffer } from '../../../../base/common/buffer.js'; import { CancellationError } from '../../../../base/common/errors.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; -import { isLinuxSnap, isMacintosh } from '../../../../base/common/platform.js'; -import { escape } from '../../../../base/common/strings.js'; -import { ThemeIcon } from '../../../../base/common/themables.js'; +import { Schemas } from '../../../../base/common/network.js'; +import { IProductConfiguration } from '../../../../base/common/product.js'; +import { joinPath } from '../../../../base/common/resources.js'; import { URI } from '../../../../base/common/uri.js'; import { localize } from '../../../../nls.js'; import { isRemoteDiagnosticError } from '../../../../platform/diagnostics/common/diagnostics.js'; -import { IIssueMainService, IProcessMainService, OldIssueReporterData, OldIssueReporterExtensionData, OldIssueReporterStyles, OldIssueReporterWindowConfiguration, OldIssueType } from '../../../../platform/issue/common/issue.js'; +import { IFileDialogService } from '../../../../platform/dialogs/common/dialogs.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; import { INativeHostService } from '../../../../platform/native/common/native.js'; -import { getIconsStyleSheet } from '../../../../platform/theme/browser/iconsStyleSheet.js'; -import { applyZoom, zoomIn, zoomOut } from '../../../../platform/window/electron-sandbox/window.js'; -import { IssueReporterData, IssueReporterModel, IssueReporterData as IssueReporterModelData } from '../browser/issueReporterModel.js'; -import { normalizeGitHubUrl } from '../common/issueReporterUtil.js'; +import { IProcessMainService } from '../../../../platform/process/common/process.js'; +import { IThemeService } from '../../../../platform/theme/common/themeService.js'; +import { IUpdateService, StateType } from '../../../../platform/update/common/update.js'; +import { applyZoom } from '../../../../platform/window/electron-sandbox/window.js'; +import { BaseIssueReporterService } from '../browser/baseIssueReporterService.js'; +import { IssueReporterData as IssueReporterModelData } from '../browser/issueReporterModel.js'; +import { IIssueFormService, IssueReporterData, IssueType } from '../common/issue.js'; // GitHub has let us know that we could up our limit here to 8k. We chose 7500 to play it safe. // ref https://github.com/microsoft/vscode/issues/159191 const MAX_URL_LENGTH = 7500; -interface SearchResult { - html_url: string; - title: string; - state?: string; -} - -enum IssueSource { - VSCode = 'vscode', - Extension = 'extension', - Marketplace = 'marketplace', - Unknown = 'unknown' -} +// Github API and issues on web has a limit of 65536. We chose 65500 to play it safe. +// ref https://github.com/github/issues/issues/12858 +const MAX_GITHUB_API_LENGTH = 65500; -export class IssueReporter extends Disposable { - private readonly issueReporterModel: IssueReporterModel; - private numberOfSearchResultsDisplayed = 0; - private receivedSystemInfo = false; - private receivedPerformanceInfo = false; - private shouldQueueSearch = false; - private hasBeenSubmitted = false; - private openReporter = false; - private loadingExtensionData = false; - private selectedExtension = ''; - private delayedSubmit = new Delayer(300); - private readonly previewButton!: Button; - private nonGitHubIssueUrl = false; +export class IssueReporter extends BaseIssueReporterService { + private readonly processMainService: IProcessMainService; constructor( - private readonly configuration: OldIssueReporterWindowConfiguration, + disableExtensions: boolean, + data: IssueReporterData, + os: { + type: string; + arch: string; + release: string; + }, + product: IProductConfiguration, + window: Window, @INativeHostService private readonly nativeHostService: INativeHostService, - @IIssueMainService private readonly issueMainService: IIssueMainService, - @IProcessMainService private readonly processMainService: IProcessMainService + @IIssueFormService issueFormService: IIssueFormService, + @IProcessMainService processMainService: IProcessMainService, + @IThemeService themeService: IThemeService, + @IFileService fileService: IFileService, + @IFileDialogService fileDialogService: IFileDialogService, + @IUpdateService private readonly updateService: IUpdateService ) { - super(); - const targetExtension = configuration.data.extensionId ? configuration.data.enabledExtensions.find(extension => extension.id.toLocaleLowerCase() === configuration.data.extensionId?.toLocaleLowerCase()) : undefined; - this.issueReporterModel = new IssueReporterModel({ - ...configuration.data, - issueType: configuration.data.issueType || OldIssueType.Bug, - versionInfo: { - vscodeVersion: `${configuration.product.nameShort} ${!!configuration.product.darwinUniversalAssetId ? `${configuration.product.version} (Universal)` : configuration.product.version} (${configuration.product.commit || 'Commit unknown'}, ${configuration.product.date || 'Date unknown'})`, - os: `${this.configuration.os.type} ${this.configuration.os.arch} ${this.configuration.os.release}${isLinuxSnap ? ' snap' : ''}` - }, - extensionsDisabled: !!configuration.disableExtensions, - fileOnExtension: configuration.data.extensionId ? !targetExtension?.isBuiltin : undefined, - selectedExtension: targetExtension - }); - - const fileOnMarketplace = configuration.data.issueSource === IssueSource.Marketplace; - const fileOnProduct = configuration.data.issueSource === IssueSource.VSCode; - this.issueReporterModel.update({ fileOnMarketplace, fileOnProduct }); - - //TODO: Handle case where extension is not activated - const issueReporterElement = this.getElementById('issue-reporter'); - if (issueReporterElement) { - this.previewButton = new Button(issueReporterElement, unthemedButtonStyles); - const issueRepoName = document.createElement('a'); - issueReporterElement.appendChild(issueRepoName); - issueRepoName.id = 'show-repo-name'; - issueRepoName.classList.add('hidden'); - this.updatePreviewButtonState(); - } - - const issueTitle = configuration.data.issueTitle; - if (issueTitle) { - const issueTitleElement = this.getElementById('issue-title'); - if (issueTitleElement) { - issueTitleElement.value = issueTitle; - } - } - - const issueBody = configuration.data.issueBody; - if (issueBody) { - const description = this.getElementById('description'); - if (description) { - description.value = issueBody; - this.issueReporterModel.update({ issueDescription: issueBody }); - } - } - + super(disableExtensions, data, os, product, window, false, issueFormService, themeService, fileService, fileDialogService); + this.processMainService = processMainService; this.processMainService.$getSystemInfo().then(info => { this.issueReporterModel.update({ systemInfo: info }); this.receivedSystemInfo = true; @@ -115,178 +60,41 @@ export class IssueReporter extends Disposable { this.updateSystemInfo(this.issueReporterModel.getData()); this.updatePreviewButtonState(); }); - if (configuration.data.issueType === OldIssueType.PerformanceIssue) { + if (this.data.issueType === IssueType.PerformanceIssue) { this.processMainService.$getPerformanceInfo().then(info => { this.updatePerformanceInfo(info as Partial); }); } - if (mainWindow.document.documentElement.lang !== 'en') { - show(this.getElementById('english')); - } - - const codiconStyleSheet = createStyleSheet(); - codiconStyleSheet.id = 'codiconStyles'; - - // TODO: Is there a way to use the IThemeService here instead - const iconsStyleSheet = this._register(getIconsStyleSheet(undefined)); - function updateAll() { - codiconStyleSheet.textContent = iconsStyleSheet.getCSS(); - } - - const delayer = new RunOnceScheduler(updateAll, 0); - iconsStyleSheet.onDidChange(() => delayer.schedule()); - delayer.schedule(); - - this.setUpTypes(); + this.checkForUpdates(); this.setEventHandlers(); - applyZoom(configuration.data.zoomLevel, mainWindow); - this.applyStyles(configuration.data.styles); - this.handleExtensionData(configuration.data.enabledExtensions); - this.updateExperimentsInfo(configuration.data.experiments); - this.updateRestrictedMode(configuration.data.restrictedMode); - this.updateUnsupportedMode(configuration.data.isUnsupported); - - // Handle case where extension is pre-selected through the command - if ((configuration.data.data || configuration.data.uri) && targetExtension) { - this.updateExtensionStatus(targetExtension); - } - } - - render(): void { - this.renderBlocks(); + applyZoom(this.data.zoomLevel, this.window); + this.updateExperimentsInfo(this.data.experiments); + this.updateRestrictedMode(this.data.restrictedMode); + this.updateUnsupportedMode(this.data.isUnsupported); } - setInitialFocus() { - const { fileOnExtension } = this.issueReporterModel.getData(); - if (fileOnExtension) { - const issueTitle = mainWindow.document.getElementById('issue-title'); - issueTitle?.focus(); - } else { - const issueType = mainWindow.document.getElementById('issue-type'); - issueType?.focus(); - } - } - - // TODO @justschen: After migration to Aux Window, switch to dedicated css. - private applyStyles(styles: OldIssueReporterStyles) { - const styleTag = document.createElement('style'); - const content: string[] = []; - - if (styles.inputBackground) { - content.push(`input[type="text"], textarea, select, .issues-container > .issue > .issue-state, .block-info { background-color: ${styles.inputBackground}; }`); - } - - if (styles.inputBorder) { - content.push(`input[type="text"], textarea, select { border: 1px solid ${styles.inputBorder}; }`); - } else { - content.push(`input[type="text"], textarea, select { border: 1px solid transparent; }`); - } - - if (styles.inputForeground) { - content.push(`input[type="text"], textarea, select, .issues-container > .issue > .issue-state, .block-info { color: ${styles.inputForeground}; }`); - } - - if (styles.inputErrorBorder) { - content.push(`.invalid-input, .invalid-input:focus, .validation-error { border: 1px solid ${styles.inputErrorBorder} !important; }`); - content.push(`.required-input { color: ${styles.inputErrorBorder}; }`); - } - - if (styles.inputErrorBackground) { - content.push(`.validation-error { background: ${styles.inputErrorBackground}; }`); - } - - if (styles.inputErrorForeground) { - content.push(`.validation-error { color: ${styles.inputErrorForeground}; }`); - } - - if (styles.inputActiveBorder) { - content.push(`input[type='text']:focus, textarea:focus, select:focus, summary:focus, button:focus, a:focus, .workbenchCommand:focus { border: 1px solid ${styles.inputActiveBorder}; outline-style: none; }`); - } - - if (styles.textLinkColor) { - content.push(`a, .workbenchCommand { color: ${styles.textLinkColor}; }`); - } - - if (styles.textLinkColor) { - content.push(`a { color: ${styles.textLinkColor}; }`); - } - - if (styles.textLinkActiveForeground) { - content.push(`a:hover, .workbenchCommand:hover { color: ${styles.textLinkActiveForeground}; }`); - } - - if (styles.sliderBackgroundColor) { - content.push(`::-webkit-scrollbar-thumb { background-color: ${styles.sliderBackgroundColor}; }`); - } - - if (styles.sliderActiveColor) { - content.push(`::-webkit-scrollbar-thumb:active { background-color: ${styles.sliderActiveColor}; }`); - } - - if (styles.sliderHoverColor) { - content.push(`::--webkit-scrollbar-thumb:hover { background-color: ${styles.sliderHoverColor}; }`); - } - - if (styles.buttonBackground) { - content.push(`.monaco-text-button { background-color: ${styles.buttonBackground} !important; }`); - } - - if (styles.buttonForeground) { - content.push(`.monaco-text-button { color: ${styles.buttonForeground} !important; }`); - } - - if (styles.buttonHoverBackground) { - content.push(`.monaco-text-button:not(.disabled):hover, .monaco-text-button:focus { background-color: ${styles.buttonHoverBackground} !important; }`); - } - - styleTag.textContent = content.join('\n'); - mainWindow.document.head.appendChild(styleTag); - mainWindow.document.body.style.color = styles.color || ''; - } - - private handleExtensionData(extensions: OldIssueReporterExtensionData[]) { - const installedExtensions = extensions.filter(x => !x.isBuiltin); - const { nonThemes, themes } = groupBy(installedExtensions, ext => { - return ext.isTheme ? 'themes' : 'nonThemes'; - }); - - const numberOfThemeExtesions = themes && themes.length; - this.issueReporterModel.update({ numberOfThemeExtesions, enabledNonThemeExtesions: nonThemes, allExtensions: installedExtensions }); - this.updateExtensionTable(nonThemes, numberOfThemeExtesions); - if (this.configuration.disableExtensions || installedExtensions.length === 0) { - (this.getElementById('disableExtensions')).disabled = true; - } - - this.updateExtensionSelector(installedExtensions); - } - - private async updateIssueReporterUri(extension: OldIssueReporterExtensionData): Promise { - try { - if (extension.uri) { - const uri = URI.revive(extension.uri); - extension.bugsUrl = uri.toString(); + private async checkForUpdates(): Promise { + const updateState = this.updateService.state; + if (updateState.type === StateType.Ready || updateState.type === StateType.Downloaded) { + this.needsUpdate = true; + const includeAcknowledgement = this.getElementById('version-acknowledgements'); + const updateBanner = this.getElementById('update-banner'); + if (updateBanner && includeAcknowledgement) { + includeAcknowledgement.classList.remove('hidden'); + updateBanner.classList.remove('hidden'); + updateBanner.textContent = localize('updateAvailable', "A new version of {0} is available.", this.product.nameLong); } - } catch (e) { - this.renderBlocks(); } } - private async sendReporterMenu(extension: OldIssueReporterExtensionData): Promise { - try { - const data = await this.issueMainService.$sendReporterMenu(extension.id, extension.name); - return data; - } catch (e) { - console.error(e); - return undefined; - } - } + public override setEventHandlers(): void { + super.setEventHandlers(); - private setEventHandlers(): void { this.addEventListener('issue-type', 'change', (event: Event) => { const issueType = parseInt((event.target).value); this.issueReporterModel.update({ issueType: issueType }); - if (issueType === OldIssueType.PerformanceIssue && !this.receivedPerformanceInfo) { + if (issueType === IssueType.PerformanceIssue && !this.receivedPerformanceInfo) { this.processMainService.$getPerformanceInfo().then(info => { this.updatePerformanceInfo(info as Partial); }); @@ -302,630 +110,36 @@ export class IssueReporter extends Disposable { this.setSourceOptions(); this.render(); }); - - (['includeSystemInfo', 'includeProcessInfo', 'includeWorkspaceInfo', 'includeExtensions', 'includeExperiments', 'includeExtensionData'] as const).forEach(elementId => { - this.addEventListener(elementId, 'click', (event: Event) => { - event.stopPropagation(); - this.issueReporterModel.update({ [elementId]: !this.issueReporterModel.getData()[elementId] }); - }); - }); - - const showInfoElements = mainWindow.document.getElementsByClassName('showInfo'); - for (let i = 0; i < showInfoElements.length; i++) { - const showInfo = showInfoElements.item(i)!; - (showInfo as HTMLAnchorElement).addEventListener('click', (e: MouseEvent) => { - e.preventDefault(); - const label = (e.target); - if (label) { - const containingElement = label.parentElement && label.parentElement.parentElement; - const info = containingElement && containingElement.lastElementChild; - if (info && info.classList.contains('hidden')) { - show(info); - label.textContent = localize('hide', "hide"); - } else { - hide(info); - label.textContent = localize('show', "show"); - } - } - }); - } - - this.addEventListener('issue-source', 'change', (e: Event) => { - const value = (e.target).value; - const problemSourceHelpText = this.getElementById('problem-source-help-text')!; - if (value === '') { - this.issueReporterModel.update({ fileOnExtension: undefined }); - show(problemSourceHelpText); - this.clearSearchResults(); - this.render(); - return; - } else { - hide(problemSourceHelpText); - } - - const descriptionTextArea = this.getElementById('issue-title'); - if (value === IssueSource.VSCode) { - descriptionTextArea.placeholder = localize('vscodePlaceholder', "E.g Workbench is missing problems panel"); - } else if (value === IssueSource.Extension) { - descriptionTextArea.placeholder = localize('extensionPlaceholder', "E.g. Missing alt text on extension readme image"); - } else if (value === IssueSource.Marketplace) { - descriptionTextArea.placeholder = localize('marketplacePlaceholder', "E.g Cannot disable installed extension"); - } else { - descriptionTextArea.placeholder = localize('undefinedPlaceholder', "Please enter a title"); - } - - let fileOnExtension, fileOnMarketplace = false; - if (value === IssueSource.Extension) { - fileOnExtension = true; - } else if (value === IssueSource.Marketplace) { - fileOnMarketplace = true; - } - - this.issueReporterModel.update({ fileOnExtension, fileOnMarketplace }); - this.render(); - - const title = (this.getElementById('issue-title')).value; - this.searchIssues(title, fileOnExtension, fileOnMarketplace); - }); - - this.addEventListener('description', 'input', (e: Event) => { - const issueDescription = (e.target).value; - this.issueReporterModel.update({ issueDescription }); - - // Only search for extension issues on title change - if (this.issueReporterModel.fileOnExtension() === false) { - const title = (this.getElementById('issue-title')).value; - this.searchVSCodeIssues(title, issueDescription); - } - }); - - this.addEventListener('issue-title', 'input', (e: Event) => { - const title = (e.target).value; - const lengthValidationMessage = this.getElementById('issue-title-length-validation-error'); - const issueUrl = this.getIssueUrl(); - if (title && this.getIssueUrlWithTitle(title, issueUrl).length > MAX_URL_LENGTH) { - show(lengthValidationMessage); - } else { - hide(lengthValidationMessage); - } - const issueSource = this.getElementById('issue-source'); - if (!issueSource || issueSource.value === '') { - return; - } - - const { fileOnExtension, fileOnMarketplace } = this.issueReporterModel.getData(); - this.searchIssues(title, fileOnExtension, fileOnMarketplace); - }); - - this.previewButton.onDidClick(async () => { - this.delayedSubmit.trigger(async () => { - this.createIssue(); - }); - }); - - this.addEventListener('disableExtensions', 'click', () => { - this.issueMainService.$reloadWithExtensionsDisabled(); - }); - - this.addEventListener('extensionBugsLink', 'click', (e: Event) => { - const url = (e.target).innerText; - windowOpenNoOpener(url); - }); - - this.addEventListener('disableExtensions', 'keydown', (e: Event) => { - e.stopPropagation(); - if ((e as KeyboardEvent).keyCode === 13 || (e as KeyboardEvent).keyCode === 32) { - this.issueMainService.$reloadWithExtensionsDisabled(); - } - }); - - mainWindow.document.onkeydown = async (e: KeyboardEvent) => { - const cmdOrCtrlKey = isMacintosh ? e.metaKey : e.ctrlKey; - // Cmd/Ctrl+Enter previews issue and closes window - if (cmdOrCtrlKey && e.keyCode === 13) { - this.delayedSubmit.trigger(async () => { - if (await this.createIssue()) { - this.close(); - } - }); - } - - // Cmd/Ctrl + w closes issue window - if (cmdOrCtrlKey && e.keyCode === 87) { - e.stopPropagation(); - e.preventDefault(); - - const issueTitle = (this.getElementById('issue-title'))!.value; - const { issueDescription } = this.issueReporterModel.getData(); - if (!this.hasBeenSubmitted && (issueTitle || issueDescription)) { - // fire and forget - this.issueMainService.$showConfirmCloseDialog(); - } else { - this.close(); - } - } - - // Cmd/Ctrl + zooms in - if (cmdOrCtrlKey && e.keyCode === 187) { - zoomIn(mainWindow); - } - - // Cmd/Ctrl - zooms out - if (cmdOrCtrlKey && e.keyCode === 189) { - zoomOut(mainWindow); - } - - // With latest electron upgrade, cmd+a is no longer propagating correctly for inputs in this window on mac - // Manually perform the selection - if (isMacintosh) { - if (cmdOrCtrlKey && e.keyCode === 65 && e.target) { - if (isHTMLInputElement(e.target) || isHTMLTextAreaElement(e.target)) { - (e.target).select(); - } - } - } - }; - } - - private updatePerformanceInfo(info: Partial) { - this.issueReporterModel.update(info); - this.receivedPerformanceInfo = true; - - const state = this.issueReporterModel.getData(); - this.updateProcessInfo(state); - this.updateWorkspaceInfo(state); - this.updatePreviewButtonState(); } - private updatePreviewButtonState() { - if (this.isPreviewEnabled()) { - if (this.configuration.data.githubAccessToken) { - this.previewButton.label = localize('createOnGitHub', "Create on GitHub"); - } else { - this.previewButton.label = localize('previewOnGitHub', "Preview on GitHub"); - } - this.previewButton.enabled = true; - } else { - this.previewButton.enabled = false; - this.previewButton.label = localize('loadingData', "Loading data..."); - } - - const issueRepoName = this.getElementById('show-repo-name')! as HTMLAnchorElement; - const selectedExtension = this.issueReporterModel.getData().selectedExtension; - if (selectedExtension && selectedExtension.uri) { - const urlString = URI.revive(selectedExtension.uri).toString(); - issueRepoName.href = urlString; - issueRepoName.addEventListener('click', (e) => this.openLink(e)); - issueRepoName.addEventListener('auxclick', (e) => this.openLink(e)); - const gitHubInfo = this.parseGitHubUrl(urlString); - issueRepoName.textContent = gitHubInfo ? gitHubInfo.owner + '/' + gitHubInfo.repositoryName : urlString; - Object.assign(issueRepoName.style, { - alignSelf: 'flex-end', - display: 'block', - fontSize: '13px', - marginBottom: '10px', - padding: '4px 0px', - textDecoration: 'none', - width: 'auto' - }); - show(issueRepoName); - } else { - // clear styles - issueRepoName.removeAttribute('style'); - hide(issueRepoName); - } - - // Initial check when first opened. - this.getExtensionGitHubUrl(); - } - - private isPreviewEnabled() { - const issueType = this.issueReporterModel.getData().issueType; - - if (this.loadingExtensionData) { - return false; - } - - if (issueType === OldIssueType.Bug && this.receivedSystemInfo) { - return true; - } - - if (issueType === OldIssueType.PerformanceIssue && this.receivedSystemInfo && this.receivedPerformanceInfo) { - return true; - } - - if (issueType === OldIssueType.FeatureRequest) { - return true; - } - - return false; - } - - private getExtensionRepositoryUrl(): string | undefined { - const selectedExtension = this.issueReporterModel.getData().selectedExtension; - return selectedExtension && selectedExtension.repositoryUrl; - } - - private getExtensionBugsUrl(): string | undefined { - const selectedExtension = this.issueReporterModel.getData().selectedExtension; - return selectedExtension && selectedExtension.bugsUrl; - } - - private searchVSCodeIssues(title: string, issueDescription?: string): void { - if (title) { - this.searchDuplicates(title, issueDescription); - } else { - this.clearSearchResults(); - } - } - - private searchIssues(title: string, fileOnExtension: boolean | undefined, fileOnMarketplace: boolean | undefined): void { - if (fileOnExtension) { - return this.searchExtensionIssues(title); - } - - if (fileOnMarketplace) { - return this.searchMarketplaceIssues(title); - } - - const description = this.issueReporterModel.getData().issueDescription; - this.searchVSCodeIssues(title, description); - } - - private searchExtensionIssues(title: string): void { - const url = this.getExtensionGitHubUrl(); - if (title) { - const matches = /^https?:\/\/github\.com\/(.*)/.exec(url); - if (matches && matches.length) { - const repo = matches[1]; - return this.searchGitHub(repo, title); - } - - // If the extension has no repository, display empty search results - if (this.issueReporterModel.getData().selectedExtension) { - this.clearSearchResults(); - return this.displaySearchResults([]); - - } - } - - this.clearSearchResults(); - } - - private searchMarketplaceIssues(title: string): void { - if (title) { - const gitHubInfo = this.parseGitHubUrl(this.configuration.product.reportMarketplaceIssueUrl!); - if (gitHubInfo) { - return this.searchGitHub(`${gitHubInfo.owner}/${gitHubInfo.repositoryName}`, title); - } - } - } - - private async close(): Promise { - await this.issueMainService.$closeReporter(); - } - - private clearSearchResults(): void { - const similarIssues = this.getElementById('similar-issues')!; - similarIssues.innerText = ''; - this.numberOfSearchResultsDisplayed = 0; - } - - @debounce(300) - private searchGitHub(repo: string, title: string): void { - const query = `is:issue+repo:${repo}+${title}`; - const similarIssues = this.getElementById('similar-issues')!; - - fetch(`https://api.github.com/search/issues?q=${query}`).then((response) => { - response.json().then(result => { - similarIssues.innerText = ''; - if (result && result.items) { - this.displaySearchResults(result.items); - } else { - // If the items property isn't present, the rate limit has been hit - const message = $('div.list-title'); - message.textContent = localize('rateLimited', "GitHub query limit exceeded. Please wait."); - similarIssues.appendChild(message); - - const resetTime = response.headers.get('X-RateLimit-Reset'); - const timeToWait = resetTime ? parseInt(resetTime) - Math.floor(Date.now() / 1000) : 1; - if (this.shouldQueueSearch) { - this.shouldQueueSearch = false; - setTimeout(() => { - this.searchGitHub(repo, title); - this.shouldQueueSearch = true; - }, timeToWait * 1000); + public override async submitToGitHub(issueTitle: string, issueBody: string, gitHubDetails: { owner: string; repositoryName: string }): Promise { + if (issueBody.length > MAX_GITHUB_API_LENGTH) { + const extensionData = this.issueReporterModel.getData().extensionData; + if (extensionData) { + issueBody = issueBody.replace(extensionData, ''); + const date = new Date(); + const formattedDate = date.toISOString().split('T')[0]; // YYYY-MM-DD + const formattedTime = date.toTimeString().split(' ')[0].replace(/:/g, '-'); // HH-MM-SS + const fileName = `extensionData_${formattedDate}_${formattedTime}.md`; + try { + const downloadPath = await this.fileDialogService.showSaveDialog({ + title: localize('saveExtensionData', "Save Extension Data"), + availableFileSystems: [Schemas.file], + defaultUri: joinPath(await this.fileDialogService.defaultFilePath(Schemas.file), fileName), + }); + + if (downloadPath) { + await this.fileService.writeFile(downloadPath, VSBuffer.fromString(extensionData)); } + } catch (e) { + console.error('Writing extension data to file failed'); + return false; } - }).catch(_ => { - // Ignore - }); - }).catch(_ => { - // Ignore - }); - } - - @debounce(300) - private searchDuplicates(title: string, body?: string): void { - const url = 'https://vscode-probot.westus.cloudapp.azure.com:7890/duplicate_candidates'; - const init = { - method: 'POST', - body: JSON.stringify({ - title, - body - }), - headers: new Headers({ - 'Content-Type': 'application/json' - }) - }; - - fetch(url, init).then((response) => { - response.json().then(result => { - this.clearSearchResults(); - - if (result && result.candidates) { - this.displaySearchResults(result.candidates); - } else { - throw new Error('Unexpected response, no candidates property'); - } - }).catch(_ => { - // Ignore - }); - }).catch(_ => { - // Ignore - }); - } - - private displaySearchResults(results: SearchResult[]) { - const similarIssues = this.getElementById('similar-issues')!; - if (results.length) { - const issues = $('div.issues-container'); - const issuesText = $('div.list-title'); - issuesText.textContent = localize('similarIssues', "Similar issues"); - - this.numberOfSearchResultsDisplayed = results.length < 5 ? results.length : 5; - for (let i = 0; i < this.numberOfSearchResultsDisplayed; i++) { - const issue = results[i]; - const link = $('a.issue-link', { href: issue.html_url }); - link.textContent = issue.title; - link.title = issue.title; - link.addEventListener('click', (e) => this.openLink(e)); - link.addEventListener('auxclick', (e) => this.openLink(e)); - - let issueState: HTMLElement; - let item: HTMLElement; - if (issue.state) { - issueState = $('span.issue-state'); - - const issueIcon = $('span.issue-icon'); - issueIcon.appendChild(renderIcon(issue.state === 'open' ? Codicon.issueOpened : Codicon.issueClosed)); - - const issueStateLabel = $('span.issue-state.label'); - issueStateLabel.textContent = issue.state === 'open' ? localize('open', "Open") : localize('closed', "Closed"); - - issueState.title = issue.state === 'open' ? localize('open', "Open") : localize('closed', "Closed"); - issueState.appendChild(issueIcon); - issueState.appendChild(issueStateLabel); - - item = $('div.issue', undefined, issueState, link); - } else { - item = $('div.issue', undefined, link); - } - - issues.appendChild(item); - } - - similarIssues.appendChild(issuesText); - similarIssues.appendChild(issues); - } else { - const message = $('div.list-title'); - message.textContent = localize('noSimilarIssues', "No similar issues found"); - similarIssues.appendChild(message); - } - } - - private setUpTypes(): void { - const makeOption = (issueType: OldIssueType, description: string) => $('option', { 'value': issueType.valueOf() }, escape(description)); - - const typeSelect = this.getElementById('issue-type')! as HTMLSelectElement; - const { issueType } = this.issueReporterModel.getData(); - reset(typeSelect, - makeOption(OldIssueType.Bug, localize('bugReporter', "Bug Report")), - makeOption(OldIssueType.FeatureRequest, localize('featureRequest', "Feature Request")), - makeOption(OldIssueType.PerformanceIssue, localize('performanceIssue', "Performance Issue (freeze, slow, crash)")) - ); - - typeSelect.value = issueType.toString(); - - this.setSourceOptions(); - } - - private makeOption(value: string, description: string, disabled: boolean): HTMLOptionElement { - const option: HTMLOptionElement = document.createElement('option'); - option.disabled = disabled; - option.value = value; - option.textContent = description; - - return option; - } - - private setSourceOptions(): void { - const sourceSelect = this.getElementById('issue-source')! as HTMLSelectElement; - const { issueType, fileOnExtension, selectedExtension, fileOnMarketplace, fileOnProduct } = this.issueReporterModel.getData(); - let selected = sourceSelect.selectedIndex; - if (selected === -1) { - if (fileOnExtension !== undefined) { - selected = fileOnExtension ? 2 : 1; - } else if (selectedExtension?.isBuiltin) { - selected = 1; - } else if (fileOnMarketplace) { - selected = 3; - } else if (fileOnProduct) { - selected = 1; - } - } - - sourceSelect.innerText = ''; - sourceSelect.append(this.makeOption('', localize('selectSource', "Select source"), true)); - sourceSelect.append(this.makeOption(IssueSource.VSCode, localize('vscode', "Visual Studio Code"), false)); - sourceSelect.append(this.makeOption(IssueSource.Extension, localize('extension', "A VS Code extension"), false)); - if (this.configuration.product.reportMarketplaceIssueUrl) { - sourceSelect.append(this.makeOption(IssueSource.Marketplace, localize('marketplace', "Extensions Marketplace"), false)); - } - - if (issueType !== OldIssueType.FeatureRequest) { - sourceSelect.append(this.makeOption(IssueSource.Unknown, localize('unknown', "Don't know"), false)); - } - - if (selected !== -1 && selected < sourceSelect.options.length) { - sourceSelect.selectedIndex = selected; - } else { - sourceSelect.selectedIndex = 0; - hide(this.getElementById('problem-source-help-text')); - } - } - - private renderBlocks(): void { - // Depending on Issue Type, we render different blocks and text - const { issueType, fileOnExtension, fileOnMarketplace, selectedExtension } = this.issueReporterModel.getData(); - const blockContainer = this.getElementById('block-container'); - const systemBlock = mainWindow.document.querySelector('.block-system'); - const processBlock = mainWindow.document.querySelector('.block-process'); - const workspaceBlock = mainWindow.document.querySelector('.block-workspace'); - const extensionsBlock = mainWindow.document.querySelector('.block-extensions'); - const experimentsBlock = mainWindow.document.querySelector('.block-experiments'); - const extensionDataBlock = mainWindow.document.querySelector('.block-extension-data'); - - const problemSource = this.getElementById('problem-source')!; - const descriptionTitle = this.getElementById('issue-description-label')!; - const descriptionSubtitle = this.getElementById('issue-description-subtitle')!; - const extensionSelector = this.getElementById('extension-selection')!; - - const titleTextArea = this.getElementById('issue-title-container')!; - const descriptionTextArea = this.getElementById('description')!; - const extensionDataTextArea = this.getElementById('extension-data')!; - - // Hide all by default - hide(blockContainer); - hide(systemBlock); - hide(processBlock); - hide(workspaceBlock); - hide(extensionsBlock); - hide(experimentsBlock); - hide(extensionSelector); - hide(extensionDataTextArea); - hide(extensionDataBlock); - - show(problemSource); - show(titleTextArea); - show(descriptionTextArea); - - if (fileOnExtension) { - show(extensionSelector); - } - - - if (selectedExtension && this.nonGitHubIssueUrl) { - hide(titleTextArea); - hide(descriptionTextArea); - reset(descriptionTitle, localize('handlesIssuesElsewhere', "This extension handles issues outside of VS Code")); - reset(descriptionSubtitle, localize('elsewhereDescription', "The '{0}' extension prefers to use an external issue reporter. To be taken to that issue reporting experience, click the button below.", selectedExtension.displayName)); - this.previewButton.label = localize('openIssueReporter', "Open External Issue Reporter"); - return; - } - - if (fileOnExtension && selectedExtension?.data) { - const data = selectedExtension?.data; - (extensionDataTextArea as HTMLElement).innerText = data.toString(); - (extensionDataTextArea as HTMLTextAreaElement).readOnly = true; - show(extensionDataBlock); - } - - // only if we know comes from the open reporter command - if (fileOnExtension && this.openReporter) { - (extensionDataTextArea as HTMLTextAreaElement).readOnly = true; - setTimeout(() => { - // delay to make sure from command or not - if (this.openReporter) { - show(extensionDataBlock); - } - }, 100); - } - - if (issueType === OldIssueType.Bug) { - if (!fileOnMarketplace) { - show(blockContainer); - show(systemBlock); - show(experimentsBlock); - if (!fileOnExtension) { - show(extensionsBlock); - } - } - - reset(descriptionTitle, localize('stepsToReproduce', "Steps to Reproduce") + ' ', $('span.required-input', undefined, '*')); - reset(descriptionSubtitle, localize('bugDescription', "Share the steps needed to reliably reproduce the problem. Please include actual and expected results. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub.")); - } else if (issueType === OldIssueType.PerformanceIssue) { - if (!fileOnMarketplace) { - show(blockContainer); - show(systemBlock); - show(processBlock); - show(workspaceBlock); - show(experimentsBlock); - } - - if (fileOnExtension) { - show(extensionSelector); - } else if (!fileOnMarketplace) { - show(extensionsBlock); - } - - reset(descriptionTitle, localize('stepsToReproduce', "Steps to Reproduce") + ' ', $('span.required-input', undefined, '*')); - reset(descriptionSubtitle, localize('performanceIssueDesciption', "When did this performance issue happen? Does it occur on startup or after a specific series of actions? We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub.")); - } else if (issueType === OldIssueType.FeatureRequest) { - reset(descriptionTitle, localize('description', "Description") + ' ', $('span.required-input', undefined, '*')); - reset(descriptionSubtitle, localize('featureRequestDescription', "Please describe the feature you would like to see. We support GitHub-flavored Markdown. You will be able to edit your issue and add screenshots when we preview it on GitHub.")); - } - } - - private validateInput(inputId: string): boolean { - const inputElement = (this.getElementById(inputId)); - const inputValidationMessage = this.getElementById(`${inputId}-empty-error`); - const descriptionShortMessage = this.getElementById(`description-short-error`); - if (!inputElement.value) { - inputElement.classList.add('invalid-input'); - inputValidationMessage?.classList.remove('hidden'); - descriptionShortMessage?.classList.add('hidden'); - return false; - } else if (inputId === 'description' && inputElement.value.length < 10) { - inputElement.classList.add('invalid-input'); - descriptionShortMessage?.classList.remove('hidden'); - inputValidationMessage?.classList.add('hidden'); - return false; - } - else { - inputElement.classList.remove('invalid-input'); - inputValidationMessage?.classList.add('hidden'); - if (inputId === 'description') { - descriptionShortMessage?.classList.add('hidden'); + } else { + console.error('Issue body too large to submit to GitHub'); + return false; } - return true; } - } - - private validateInputs(): boolean { - let isValid = true; - ['issue-title', 'description', 'issue-source'].forEach(elementId => { - isValid = this.validateInput(elementId) && isValid; - }); - - if (this.issueReporterModel.fileOnExtension()) { - isValid = this.validateInput('extension-selector') && isValid; - } - - return isValid; - } - - private async submitToGitHub(issueTitle: string, issueBody: string, gitHubDetails: { owner: string; repositoryName: string }): Promise { const url = `https://api.github.com/repos/${gitHubDetails.owner}/${gitHubDetails.repositoryName}/issues`; const init = { method: 'POST', @@ -935,7 +149,7 @@ export class IssueReporter extends Disposable { }), headers: new Headers({ 'Content-Type': 'application/json', - 'Authorization': `Bearer ${this.configuration.data.githubAccessToken}` + 'Authorization': `Bearer ${this.data.githubAccessToken}` }) }; @@ -950,7 +164,7 @@ export class IssueReporter extends Disposable { return true; } - private async createIssue(): Promise { + public override async createIssue(): Promise { const selectedExtension = this.issueReporterModel.getData().selectedExtension; const hasUri = this.nonGitHubIssueUrl; // Short circuit if the extension provides a custom issue handler @@ -966,7 +180,7 @@ export class IssueReporter extends Disposable { if (!this.validateInputs()) { // If inputs are invalid, set focus to the first one and add listeners on them // to detect further changes - const invalidInput = mainWindow.document.getElementsByClassName('invalid-input'); + const invalidInput = this.window.document.getElementsByClassName('invalid-input'); if (invalidInput.length) { (invalidInput[0]).focus(); } @@ -986,6 +200,7 @@ export class IssueReporter extends Disposable { if (this.issueReporterModel.fileOnExtension()) { this.addEventListener('extension-selector', 'change', _ => { this.validateInput('extension-selector'); + this.validateInput('description'); }); } @@ -1013,23 +228,27 @@ export class IssueReporter extends Disposable { const baseUrl = this.getIssueUrlWithTitle((this.getElementById('issue-title')).value, issueUrl); let url = baseUrl + `&body=${encodeURIComponent(issueBody)}`; - if (url.length > MAX_URL_LENGTH) { - try { + if (this.data.githubAccessToken && gitHubDetails) { + if (await this.submitToGitHub(issueTitle, issueBody, gitHubDetails)) { + return true; + } + } + + try { + if (url.length > MAX_URL_LENGTH || issueBody.length > MAX_GITHUB_API_LENGTH) { url = await this.writeToClipboard(baseUrl, issueBody); - } catch (_) { - console.error('Writing to clipboard failed'); - return false; } - } else if (this.configuration.data.githubAccessToken && gitHubDetails) { - return this.submitToGitHub(issueTitle, issueBody, gitHubDetails); + } catch (_) { + console.error('Writing to clipboard failed'); + return false; } await this.nativeHostService.openExternal(url); return true; } - private async writeToClipboard(baseUrl: string, issueBody: string): Promise { - const shouldWrite = await this.issueMainService.$showClipboardDialog(); + public override async writeToClipboard(baseUrl: string, issueBody: string): Promise { + const shouldWrite = await this.issueFormService.showClipboardDialog(); if (!shouldWrite) { throw new CancellationError(); } @@ -1039,60 +258,8 @@ export class IssueReporter extends Disposable { return baseUrl + `&body=${encodeURIComponent(localize('pasteData', "We have written the needed data into your clipboard because it was too large to send. Please paste."))}`; } - private getIssueUrl(): string { - return this.issueReporterModel.fileOnExtension() - ? this.getExtensionGitHubUrl() - : this.issueReporterModel.getData().fileOnMarketplace - ? this.configuration.product.reportMarketplaceIssueUrl! - : this.configuration.product.reportIssueUrl!; - } - - private parseGitHubUrl(url: string): undefined | { repositoryName: string; owner: string } { - // Assumes a GitHub url to a particular repo, https://github.com/repositoryName/owner. - // Repository name and owner cannot contain '/' - const match = /^https?:\/\/github\.com\/([^\/]*)\/([^\/]*).*/.exec(url); - if (match && match.length) { - return { - owner: match[1], - repositoryName: match[2] - }; - } else { - console.error('No GitHub issues match'); - } - - return undefined; - } - - private getExtensionGitHubUrl(): string { - let repositoryUrl = ''; - const bugsUrl = this.getExtensionBugsUrl(); - const extensionUrl = this.getExtensionRepositoryUrl(); - // If given, try to match the extension's bug url - if (bugsUrl && bugsUrl.match(/^https?:\/\/github\.com\/([^\/]*)\/([^\/]*)\/?(\/issues)?$/)) { - // matches exactly: https://github.com/owner/repo/issues - repositoryUrl = normalizeGitHubUrl(bugsUrl); - } else if (extensionUrl && extensionUrl.match(/^https?:\/\/github\.com\/([^\/]*)\/([^\/]*)$/)) { - // matches exactly: https://github.com/owner/repo - repositoryUrl = normalizeGitHubUrl(extensionUrl); - } else { - this.nonGitHubIssueUrl = true; - repositoryUrl = bugsUrl || extensionUrl || ''; - } - - return repositoryUrl; - } - - private getIssueUrlWithTitle(issueTitle: string, repositoryUrl: string): string { - if (this.issueReporterModel.fileOnExtension()) { - repositoryUrl = repositoryUrl + '/issues/new'; - } - - const queryStringPrefix = repositoryUrl.indexOf('?') === -1 ? '?' : '&'; - return `${repositoryUrl}${queryStringPrefix}title=${encodeURIComponent(issueTitle)}`; - } - private updateSystemInfo(state: IssueReporterModelData) { - const target = mainWindow.document.querySelector('.block-system .block-info'); + const target = this.window.document.querySelector('.block-system .block-info'); if (target) { const systemInfo = state.systemInfo!; @@ -1171,270 +338,6 @@ export class IssueReporter extends Disposable { } } - private updateExtensionSelector(extensions: OldIssueReporterExtensionData[]): void { - interface IOption { - name: string; - id: string; - } - - const extensionOptions: IOption[] = extensions.map(extension => { - return { - name: extension.displayName || extension.name || '', - id: extension.id - }; - }); - - // Sort extensions by name - extensionOptions.sort((a, b) => { - const aName = a.name.toLowerCase(); - const bName = b.name.toLowerCase(); - if (aName > bName) { - return 1; - } - - if (aName < bName) { - return -1; - } - - return 0; - }); - - const makeOption = (extension: IOption, selectedExtension?: OldIssueReporterExtensionData): HTMLOptionElement => { - const selected = selectedExtension && extension.id === selectedExtension.id; - return $('option', { - 'value': extension.id, - 'selected': selected || '' - }, extension.name); - }; - - const extensionsSelector = this.getElementById('extension-selector'); - if (extensionsSelector) { - const { selectedExtension } = this.issueReporterModel.getData(); - reset(extensionsSelector, this.makeOption('', localize('selectExtension', "Select extension"), true), ...extensionOptions.map(extension => makeOption(extension, selectedExtension))); - - if (!selectedExtension) { - extensionsSelector.selectedIndex = 0; - } - - this.addEventListener('extension-selector', 'change', async (e: Event) => { - this.clearExtensionData(); - const selectedExtensionId = (e.target).value; - this.selectedExtension = selectedExtensionId; - const extensions = this.issueReporterModel.getData().allExtensions; - const matches = extensions.filter(extension => extension.id === selectedExtensionId); - if (matches.length) { - this.issueReporterModel.update({ selectedExtension: matches[0] }); - const selectedExtension = this.issueReporterModel.getData().selectedExtension; - if (selectedExtension) { - const iconElement = document.createElement('span'); - iconElement.classList.add(...ThemeIcon.asClassNameArray(Codicon.loading), 'codicon-modifier-spin'); - this.setLoading(iconElement); - const openReporterData = await this.sendReporterMenu(selectedExtension); - if (openReporterData) { - if (this.selectedExtension === selectedExtensionId) { - this.removeLoading(iconElement, true); - this.configuration.data = openReporterData; - } else if (this.selectedExtension !== selectedExtensionId) { - } - } - else { - if (!this.loadingExtensionData) { - iconElement.classList.remove(...ThemeIcon.asClassNameArray(Codicon.loading), 'codicon-modifier-spin'); - } - this.removeLoading(iconElement); - // if not using command, should have no configuration data in fields we care about and check later. - this.clearExtensionData(); - - // case when previous extension was opened from normal openIssueReporter command - selectedExtension.data = undefined; - selectedExtension.uri = undefined; - } - if (this.selectedExtension === selectedExtensionId) { - // repopulates the fields with the new data given the selected extension. - this.updateExtensionStatus(matches[0]); - this.openReporter = false; - } - } else { - this.issueReporterModel.update({ selectedExtension: undefined }); - this.clearSearchResults(); - this.clearExtensionData(); - this.validateSelectedExtension(); - this.updateExtensionStatus(matches[0]); - } - } - }); - } - - this.addEventListener('problem-source', 'change', (_) => { - this.validateSelectedExtension(); - }); - } - - private clearExtensionData(): void { - this.nonGitHubIssueUrl = false; - this.issueReporterModel.update({ extensionData: undefined }); - this.configuration.data.issueBody = undefined; - this.configuration.data.data = undefined; - this.configuration.data.uri = undefined; - } - - private async updateExtensionStatus(extension: OldIssueReporterExtensionData) { - this.issueReporterModel.update({ selectedExtension: extension }); - - // uses this.configuuration.data to ensure that data is coming from `openReporter` command. - const template = this.configuration.data.issueBody; - if (template) { - const descriptionTextArea = this.getElementById('description')!; - const descriptionText = (descriptionTextArea as HTMLTextAreaElement).value; - if (descriptionText === '' || !descriptionText.includes(template.toString())) { - const fullTextArea = descriptionText + (descriptionText === '' ? '' : '\n') + template.toString(); - (descriptionTextArea as HTMLTextAreaElement).value = fullTextArea; - this.issueReporterModel.update({ issueDescription: fullTextArea }); - } - } - - const data = this.configuration.data.data; - if (data) { - this.issueReporterModel.update({ extensionData: data }); - extension.data = data; - const extensionDataBlock = mainWindow.document.querySelector('.block-extension-data')!; - show(extensionDataBlock); - this.renderBlocks(); - } - - const uri = this.configuration.data.uri; - if (uri) { - extension.uri = uri; - this.updateIssueReporterUri(extension); - } - - this.validateSelectedExtension(); - const title = (this.getElementById('issue-title')).value; - this.searchExtensionIssues(title); - - this.updatePreviewButtonState(); - this.renderBlocks(); - } - - private validateSelectedExtension(): void { - const extensionValidationMessage = this.getElementById('extension-selection-validation-error')!; - const extensionValidationNoUrlsMessage = this.getElementById('extension-selection-validation-error-no-url')!; - hide(extensionValidationMessage); - hide(extensionValidationNoUrlsMessage); - - const extension = this.issueReporterModel.getData().selectedExtension; - if (!extension) { - this.previewButton.enabled = true; - return; - } - - if (this.loadingExtensionData) { - return; - } - - const hasValidGitHubUrl = this.getExtensionGitHubUrl(); - if (hasValidGitHubUrl) { - this.previewButton.enabled = true; - } else { - this.setExtensionValidationMessage(); - this.previewButton.enabled = false; - } - } - - private setLoading(element: HTMLElement) { - // Show loading - this.openReporter = true; - this.loadingExtensionData = true; - this.updatePreviewButtonState(); - - const extensionDataCaption = this.getElementById('extension-id')!; - hide(extensionDataCaption); - - const extensionDataCaption2 = Array.from(mainWindow.document.querySelectorAll('.ext-parens')); - extensionDataCaption2.forEach(extensionDataCaption2 => hide(extensionDataCaption2)); - - const showLoading = this.getElementById('ext-loading')!; - show(showLoading); - while (showLoading.firstChild) { - showLoading.firstChild.remove(); - } - showLoading.append(element); - - this.renderBlocks(); - } - - private removeLoading(element: HTMLElement, fromReporter: boolean = false) { - this.openReporter = fromReporter; - this.loadingExtensionData = false; - this.updatePreviewButtonState(); - - const extensionDataCaption = this.getElementById('extension-id')!; - show(extensionDataCaption); - - const extensionDataCaption2 = Array.from(mainWindow.document.querySelectorAll('.ext-parens')); - extensionDataCaption2.forEach(extensionDataCaption2 => show(extensionDataCaption2)); - - const hideLoading = this.getElementById('ext-loading')!; - hide(hideLoading); - if (hideLoading.firstChild) { - element.remove(); - } - this.renderBlocks(); - } - - private setExtensionValidationMessage(): void { - const extensionValidationMessage = this.getElementById('extension-selection-validation-error')!; - const extensionValidationNoUrlsMessage = this.getElementById('extension-selection-validation-error-no-url')!; - const bugsUrl = this.getExtensionBugsUrl(); - if (bugsUrl) { - show(extensionValidationMessage); - const link = this.getElementById('extensionBugsLink')!; - link.textContent = bugsUrl; - return; - } - - const extensionUrl = this.getExtensionRepositoryUrl(); - if (extensionUrl) { - show(extensionValidationMessage); - const link = this.getElementById('extensionBugsLink'); - link!.textContent = extensionUrl; - return; - } - - show(extensionValidationNoUrlsMessage); - } - - private updateProcessInfo(state: IssueReporterModelData) { - const target = mainWindow.document.querySelector('.block-process .block-info') as HTMLElement; - if (target) { - reset(target, $('code', undefined, state.processInfo ?? '')); - } - } - - private updateWorkspaceInfo(state: IssueReporterModelData) { - mainWindow.document.querySelector('.block-workspace .block-info code')!.textContent = '\n' + state.workspaceInfo; - } - - private updateExtensionTable(extensions: OldIssueReporterExtensionData[], numThemeExtensions: number): void { - const target = mainWindow.document.querySelector('.block-extensions .block-info'); - if (target) { - if (this.configuration.disableExtensions) { - reset(target, localize('disabledExtensions', "Extensions are disabled")); - return; - } - - const themeExclusionStr = numThemeExtensions ? `\n(${numThemeExtensions} theme extensions excluded)` : ''; - extensions = extensions || []; - - if (!extensions.length) { - target.innerText = 'Extensions: none' + themeExclusionStr; - return; - } - - reset(target, this.getExtensionTableHtml(extensions), document.createTextNode(themeExclusionStr)); - } - } - private updateRestrictedMode(restrictedMode: boolean) { this.issueReporterModel.update({ restrictedMode }); } @@ -1445,56 +348,9 @@ export class IssueReporter extends Disposable { private updateExperimentsInfo(experimentInfo: string | undefined) { this.issueReporterModel.update({ experimentInfo }); - const target = mainWindow.document.querySelector('.block-experiments .block-info'); + const target = this.window.document.querySelector('.block-experiments .block-info'); if (target) { target.textContent = experimentInfo ? experimentInfo : localize('noCurrentExperiments', "No current experiments."); } } - - private getExtensionTableHtml(extensions: OldIssueReporterExtensionData[]): HTMLTableElement { - return $('table', undefined, - $('tr', undefined, - $('th', undefined, 'Extension'), - $('th', undefined, 'Author (truncated)' as string), - $('th', undefined, 'Version') - ), - ...extensions.map(extension => $('tr', undefined, - $('td', undefined, extension.name), - $('td', undefined, extension.publisher?.substr(0, 3) ?? 'N/A'), - $('td', undefined, extension.version) - )) - ); - } - - private openLink(event: MouseEvent): void { - event.preventDefault(); - event.stopPropagation(); - // Exclude right click - if (event.which < 3) { - windowOpenNoOpener((event.target).href); - } - } - - private getElementById(elementId: string): T | undefined { - const element = mainWindow.document.getElementById(elementId) as T | undefined; - if (element) { - return element; - } else { - return undefined; - } - } - - private addEventListener(elementId: string, eventType: string, handler: (event: Event) => void): void { - const element = this.getElementById(elementId); - element?.addEventListener(eventType, handler); - } -} - -// helper functions - -export function hide(el: Element | undefined | null) { - el?.classList.add('hidden'); -} -export function show(el: Element | undefined | null) { - el?.classList.remove('hidden'); } diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterService2.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterService2.ts deleted file mode 100644 index 305ee9a7bc95..000000000000 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterService2.ts +++ /dev/null @@ -1,297 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import { $, reset } from '../../../../base/browser/dom.js'; -import { CancellationError } from '../../../../base/common/errors.js'; -import { IProductConfiguration } from '../../../../base/common/product.js'; -import { URI } from '../../../../base/common/uri.js'; -import { localize } from '../../../../nls.js'; -import { isRemoteDiagnosticError } from '../../../../platform/diagnostics/common/diagnostics.js'; -import { IProcessMainService } from '../../../../platform/issue/common/issue.js'; -import { INativeHostService } from '../../../../platform/native/common/native.js'; -import { IThemeService } from '../../../../platform/theme/common/themeService.js'; -import { applyZoom } from '../../../../platform/window/electron-sandbox/window.js'; -import { BaseIssueReporterService } from '../browser/baseIssueReporterService.js'; -import { IssueReporterData as IssueReporterModelData } from '../browser/issueReporterModel.js'; -import { IIssueFormService, IssueReporterData, IssueType } from '../common/issue.js'; - -// GitHub has let us know that we could up our limit here to 8k. We chose 7500 to play it safe. -// ref https://github.com/microsoft/vscode/issues/159191 -const MAX_URL_LENGTH = 7500; - - -export class IssueReporter2 extends BaseIssueReporterService { - private readonly processMainService: IProcessMainService; - constructor( - disableExtensions: boolean, - data: IssueReporterData, - os: { - type: string; - arch: string; - release: string; - }, - product: IProductConfiguration, - window: Window, - @INativeHostService private readonly nativeHostService: INativeHostService, - @IIssueFormService issueFormService: IIssueFormService, - @IProcessMainService processMainService: IProcessMainService, - @IThemeService themeService: IThemeService - ) { - super(disableExtensions, data, os, product, window, false, issueFormService, themeService); - this.processMainService = processMainService; - this.processMainService.$getSystemInfo().then(info => { - this.issueReporterModel.update({ systemInfo: info }); - this.receivedSystemInfo = true; - - this.updateSystemInfo(this.issueReporterModel.getData()); - this.updatePreviewButtonState(); - }); - if (this.data.issueType === IssueType.PerformanceIssue) { - this.processMainService.$getPerformanceInfo().then(info => { - this.updatePerformanceInfo(info as Partial); - }); - } - - this.setEventHandlers(); - applyZoom(this.data.zoomLevel, this.window); - this.updateExperimentsInfo(this.data.experiments); - this.updateRestrictedMode(this.data.restrictedMode); - this.updateUnsupportedMode(this.data.isUnsupported); - } - - public override setEventHandlers(): void { - super.setEventHandlers(); - - this.addEventListener('issue-type', 'change', (event: Event) => { - const issueType = parseInt((event.target).value); - this.issueReporterModel.update({ issueType: issueType }); - if (issueType === IssueType.PerformanceIssue && !this.receivedPerformanceInfo) { - this.processMainService.$getPerformanceInfo().then(info => { - this.updatePerformanceInfo(info as Partial); - }); - } - - // Resets placeholder - const descriptionTextArea = this.getElementById('issue-title'); - if (descriptionTextArea) { - descriptionTextArea.placeholder = localize('undefinedPlaceholder', "Please enter a title"); - } - - this.updatePreviewButtonState(); - this.setSourceOptions(); - this.render(); - }); - } - - public override async submitToGitHub(issueTitle: string, issueBody: string, gitHubDetails: { owner: string; repositoryName: string }): Promise { - const url = `https://api.github.com/repos/${gitHubDetails.owner}/${gitHubDetails.repositoryName}/issues`; - const init = { - method: 'POST', - body: JSON.stringify({ - title: issueTitle, - body: issueBody - }), - headers: new Headers({ - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${this.data.githubAccessToken}` - }) - }; - - const response = await fetch(url, init); - if (!response.ok) { - console.error('Invalid GitHub URL provided.'); - return false; - } - const result = await response.json(); - await this.nativeHostService.openExternal(result.html_url); - this.close(); - return true; - } - - public override async createIssue(): Promise { - const selectedExtension = this.issueReporterModel.getData().selectedExtension; - const hasUri = this.nonGitHubIssueUrl; - // Short circuit if the extension provides a custom issue handler - if (hasUri) { - const url = this.getExtensionBugsUrl(); - if (url) { - this.hasBeenSubmitted = true; - await this.nativeHostService.openExternal(url); - return true; - } - } - - if (!this.validateInputs()) { - // If inputs are invalid, set focus to the first one and add listeners on them - // to detect further changes - const invalidInput = this.window.document.getElementsByClassName('invalid-input'); - if (invalidInput.length) { - (invalidInput[0]).focus(); - } - - this.addEventListener('issue-title', 'input', _ => { - this.validateInput('issue-title'); - }); - - this.addEventListener('description', 'input', _ => { - this.validateInput('description'); - }); - - this.addEventListener('issue-source', 'change', _ => { - this.validateInput('issue-source'); - }); - - if (this.issueReporterModel.fileOnExtension()) { - this.addEventListener('extension-selector', 'change', _ => { - this.validateInput('extension-selector'); - this.validateInput('description'); - }); - } - - return false; - } - - this.hasBeenSubmitted = true; - - const issueTitle = (this.getElementById('issue-title')).value; - const issueBody = this.issueReporterModel.serialize(); - - let issueUrl = this.getIssueUrl(); - if (!issueUrl) { - console.error('No issue url found'); - return false; - } - - if (selectedExtension?.uri) { - const uri = URI.revive(selectedExtension.uri); - issueUrl = uri.toString(); - } - - const gitHubDetails = this.parseGitHubUrl(issueUrl); - - const baseUrl = this.getIssueUrlWithTitle((this.getElementById('issue-title')).value, issueUrl); - let url = baseUrl + `&body=${encodeURIComponent(issueBody)}`; - - if (url.length > MAX_URL_LENGTH) { - try { - url = await this.writeToClipboard(baseUrl, issueBody); - } catch (_) { - console.error('Writing to clipboard failed'); - return false; - } - } else if (this.data.githubAccessToken && gitHubDetails) { - return this.submitToGitHub(issueTitle, issueBody, gitHubDetails); - } - - await this.nativeHostService.openExternal(url); - return true; - } - - public override async writeToClipboard(baseUrl: string, issueBody: string): Promise { - const shouldWrite = await this.issueFormService.showClipboardDialog(); - if (!shouldWrite) { - throw new CancellationError(); - } - - await this.nativeHostService.writeClipboardText(issueBody); - - return baseUrl + `&body=${encodeURIComponent(localize('pasteData', "We have written the needed data into your clipboard because it was too large to send. Please paste."))}`; - } - - private updateSystemInfo(state: IssueReporterModelData) { - const target = this.window.document.querySelector('.block-system .block-info'); - - if (target) { - const systemInfo = state.systemInfo!; - const renderedDataTable = $('table', undefined, - $('tr', undefined, - $('td', undefined, 'CPUs'), - $('td', undefined, systemInfo.cpus || '') - ), - $('tr', undefined, - $('td', undefined, 'GPU Status' as string), - $('td', undefined, Object.keys(systemInfo.gpuStatus).map(key => `${key}: ${systemInfo.gpuStatus[key]}`).join('\n')) - ), - $('tr', undefined, - $('td', undefined, 'Load (avg)' as string), - $('td', undefined, systemInfo.load || '') - ), - $('tr', undefined, - $('td', undefined, 'Memory (System)' as string), - $('td', undefined, systemInfo.memory) - ), - $('tr', undefined, - $('td', undefined, 'Process Argv' as string), - $('td', undefined, systemInfo.processArgs) - ), - $('tr', undefined, - $('td', undefined, 'Screen Reader' as string), - $('td', undefined, systemInfo.screenReader) - ), - $('tr', undefined, - $('td', undefined, 'VM'), - $('td', undefined, systemInfo.vmHint) - ) - ); - reset(target, renderedDataTable); - - systemInfo.remoteData.forEach(remote => { - target.appendChild($('hr')); - if (isRemoteDiagnosticError(remote)) { - const remoteDataTable = $('table', undefined, - $('tr', undefined, - $('td', undefined, 'Remote'), - $('td', undefined, remote.hostName) - ), - $('tr', undefined, - $('td', undefined, ''), - $('td', undefined, remote.errorMessage) - ) - ); - target.appendChild(remoteDataTable); - } else { - const remoteDataTable = $('table', undefined, - $('tr', undefined, - $('td', undefined, 'Remote'), - $('td', undefined, remote.latency ? `${remote.hostName} (latency: ${remote.latency.current.toFixed(2)}ms last, ${remote.latency.average.toFixed(2)}ms average)` : remote.hostName) - ), - $('tr', undefined, - $('td', undefined, 'OS'), - $('td', undefined, remote.machineInfo.os) - ), - $('tr', undefined, - $('td', undefined, 'CPUs'), - $('td', undefined, remote.machineInfo.cpus || '') - ), - $('tr', undefined, - $('td', undefined, 'Memory (System)' as string), - $('td', undefined, remote.machineInfo.memory) - ), - $('tr', undefined, - $('td', undefined, 'VM'), - $('td', undefined, remote.machineInfo.vmHint) - ) - ); - target.appendChild(remoteDataTable); - } - }); - } - } - - private updateRestrictedMode(restrictedMode: boolean) { - this.issueReporterModel.update({ restrictedMode }); - } - - private updateUnsupportedMode(isUnsupported: boolean) { - this.issueReporterModel.update({ isUnsupported }); - } - - private updateExperimentsInfo(experimentInfo: string | undefined) { - this.issueReporterModel.update({ experimentInfo }); - const target = this.window.document.querySelector('.block-experiments .block-info'); - if (target) { - target.textContent = experimentInfo ? experimentInfo : localize('noCurrentExperiments', "No current experiments."); - } - } -} diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts index 2ee3df7cf706..7587d24af5be 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts @@ -5,15 +5,10 @@ import { getZoomLevel } from '../../../../base/browser/browser.js'; import { mainWindow } from '../../../../base/browser/window.js'; -import { ipcRenderer } from '../../../../base/parts/sandbox/electron-sandbox/globals.js'; -import { IMenuService, MenuId } from '../../../../platform/actions/common/actions.js'; -import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IExtensionManagementService } from '../../../../platform/extensionManagement/common/extensionManagement.js'; -import { ExtensionIdentifier, ExtensionIdentifierSet, ExtensionType } from '../../../../platform/extensions/common/extensions.js'; +import { ExtensionType } from '../../../../platform/extensions/common/extensions.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { IIssueMainService, OldIssueReporterData, OldIssueReporterExtensionData, OldIssueReporterStyles } from '../../../../platform/issue/common/issue.js'; -import { buttonBackground, buttonForeground, buttonHoverBackground, foreground, inputActiveOptionBorder, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, textLinkActiveForeground, textLinkForeground } from '../../../../platform/theme/common/colorRegistry.js'; +import { buttonBackground, buttonForeground, buttonHoverBackground, foreground, inputActiveOptionBorder, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, scrollbarSliderActiveBackground, scrollbarSliderHoverBackground, textLinkActiveForeground, textLinkForeground } from '../../../../platform/theme/common/colorRegistry.js'; import { IColorTheme, IThemeService } from '../../../../platform/theme/common/themeService.js'; import { IWorkspaceTrustManagementService } from '../../../../platform/workspace/common/workspaceTrust.js'; import { SIDE_BAR_BACKGROUND } from '../../../common/theme.js'; @@ -25,10 +20,8 @@ import { IIntegrityService } from '../../../services/integrity/common/integrity. export class NativeIssueService implements IWorkbenchIssueService { declare readonly _serviceBrand: undefined; - private extensionIdentifierSet: ExtensionIdentifierSet = new ExtensionIdentifierSet(); constructor( - @IIssueMainService private readonly issueMainService: IIssueMainService, @IIssueFormService private readonly issueFormService: IIssueFormService, @IThemeService private readonly themeService: IThemeService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @@ -37,39 +30,10 @@ export class NativeIssueService implements IWorkbenchIssueService { @IWorkbenchAssignmentService private readonly experimentService: IWorkbenchAssignmentService, @IAuthenticationService private readonly authenticationService: IAuthenticationService, @IIntegrityService private readonly integrityService: IIntegrityService, - @IMenuService private readonly menuService: IMenuService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IConfigurationService private readonly configurationService: IConfigurationService - ) { - ipcRenderer.on('vscode:triggerReporterMenu', async (event, arg) => { - const extensionId = arg.extensionId; - - // gets menu from contributed - const actions = this.menuService.getMenuActions(MenuId.IssueReporter, this.contextKeyService, { renderShortTitle: true }).flatMap(entry => entry[1]); - - // render menu and dispose - actions.forEach(async action => { - try { - if (action.item && 'source' in action.item && action.item.source?.id === extensionId) { - this.extensionIdentifierSet.add(extensionId); - await action.run(); - } - } catch (error) { - console.error(error); - } - }); - - if (!this.extensionIdentifierSet.has(extensionId)) { - // send undefined to indicate no action was taken - ipcRenderer.send(`vscode:triggerReporterMenuResponse:${extensionId}`, undefined); - } - }); - } + ) { } async openReporter(dataOverrides: Partial = {}): Promise { const extensionData: IssueReporterExtensionData[] = []; - const oldExtensionData: OldIssueReporterExtensionData[] = []; - const oldDataOverrides = dataOverrides as Partial; try { const extensions = await this.extensionManagementService.getInstalled(); const enabledExtensions = extensions.filter(extension => this.extensionEnablementService.isEnabled(extension) || (dataOverrides.extensionId && extension.identifier.id === dataOverrides.extensionId)); @@ -93,26 +57,6 @@ export class NativeIssueService implements IWorkbenchIssueService { extensionData: 'Extensions data loading', }; })); - oldExtensionData.push(...enabledExtensions.map((extension): OldIssueReporterExtensionData => { - const { manifest } = extension; - const manifestKeys = manifest.contributes ? Object.keys(manifest.contributes) : []; - const isTheme = !manifest.main && !manifest.browser && manifestKeys.length === 1 && manifestKeys[0] === 'themes'; - const isBuiltin = extension.type === ExtensionType.System; - return { - name: manifest.name, - publisher: manifest.publisher, - version: manifest.version, - repositoryUrl: manifest.repository && manifest.repository.url, - bugsUrl: manifest.bugs && manifest.bugs.url, - displayName: manifest.displayName, - id: extension.identifier.id, - data: dataOverrides.data, - uri: dataOverrides.uri, - isTheme, - isBuiltin, - extensionData: 'Extensions data loading', - }; - })); } catch (e) { extensionData.push({ name: 'Workbench Issue Service', @@ -126,18 +70,6 @@ export class NativeIssueService implements IWorkbenchIssueService { isTheme: false, isBuiltin: true }); - oldExtensionData.push({ - name: 'Workbench Issue Service', - publisher: 'Unknown', - version: '0.0.0', - repositoryUrl: undefined, - bugsUrl: undefined, - extensionData: 'Extensions data loading', - displayName: `Extensions not loaded: ${e}`, - id: 'workbench.issue', - isTheme: false, - isBuiltin: true - }); } const experiments = await this.experimentService.getCurrentExperiments(); @@ -169,34 +101,7 @@ export class NativeIssueService implements IWorkbenchIssueService { githubAccessToken }, dataOverrides); - const oldIssueReporterData: OldIssueReporterData = Object.assign({ - styles: oldGetIssueReporterStyles(theme), - zoomLevel: getZoomLevel(mainWindow), - enabledExtensions: oldExtensionData, - experiments: experiments?.join('\n'), - restrictedMode: !this.workspaceTrustManagementService.isWorkspaceTrusted(), - isUnsupported, - githubAccessToken - }, oldDataOverrides); - - if (issueReporterData.extensionId) { - const extensionExists = extensionData.some(extension => ExtensionIdentifier.equals(extension.id, issueReporterData.extensionId)); - if (!extensionExists) { - console.error(`Extension with ID ${issueReporterData.extensionId} does not exist.`); - } - } - - if (issueReporterData.extensionId && this.extensionIdentifierSet.has(issueReporterData.extensionId)) { - ipcRenderer.send(`vscode:triggerReporterMenuResponse:${issueReporterData.extensionId}`, issueReporterData); - this.extensionIdentifierSet.delete(new ExtensionIdentifier(issueReporterData.extensionId)); - } - - - if (this.configurationService.getValue('issueReporter.experimental.auxWindow')) { - return this.issueFormService.openReporter(issueReporterData); - } - - return this.issueMainService.openReporter(oldIssueReporterData); + return this.issueFormService.openReporter(issueReporterData); } } @@ -223,28 +128,6 @@ export function getIssueReporterStyles(theme: IColorTheme): IssueReporterStyles }; } -export function oldGetIssueReporterStyles(theme: IColorTheme): OldIssueReporterStyles { - return { - backgroundColor: getColor(theme, SIDE_BAR_BACKGROUND), - color: getColor(theme, foreground), - textLinkColor: getColor(theme, textLinkForeground), - textLinkActiveForeground: getColor(theme, textLinkActiveForeground), - inputBackground: getColor(theme, inputBackground), - inputForeground: getColor(theme, inputForeground), - inputBorder: getColor(theme, inputBorder), - inputActiveBorder: getColor(theme, inputActiveOptionBorder), - inputErrorBorder: getColor(theme, inputValidationErrorBorder), - inputErrorBackground: getColor(theme, inputValidationErrorBackground), - inputErrorForeground: getColor(theme, inputValidationErrorForeground), - buttonBackground: getColor(theme, buttonBackground), - buttonForeground: getColor(theme, buttonForeground), - buttonHoverBackground: getColor(theme, buttonHoverBackground), - sliderActiveColor: getColor(theme, scrollbarSliderActiveBackground), - sliderBackgroundColor: getColor(theme, scrollbarSliderBackground), - sliderHoverColor: getColor(theme, scrollbarSliderHoverBackground), - }; -} - function getColor(theme: IColorTheme, key: string): string | undefined { const color = theme.getColor(key); return color ? color.toString() : undefined; diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/media/issueReporter.css b/src/vs/workbench/contrib/issue/electron-sandbox/media/issueReporter.css index 6f8e7abe204f..aebbe9804280 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/media/issueReporter.css +++ b/src/vs/workbench/contrib/issue/electron-sandbox/media/issueReporter.css @@ -3,56 +3,54 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/* -* TODO: @justschen - remove this file once the new issue reporter is enabled by default. -* Split between this and a `newIssueReporter` because of specificy from new issue reporter monaco-workbench stylesheets. -*/ - /** * Table */ -table { +.issue-reporter-body table { width: 100%; max-width: 100%; background-color: transparent; border-collapse: collapse; } -th { + +.issue-reporter-body th { vertical-align: bottom; border-bottom: 1px solid; padding: 5px; text-align: inherit; } -td { + +.issue-reporter-body td { padding: 5px; vertical-align: top; } -tr td:first-child { +.issue-reporter-body tr td:first-child { width: 30%; } -label { +.issue-reporter-body label { user-select: none; } -.block-settingsSearchResults-details { +.issue-reporter-body .block-settingsSearchResults-details { padding-bottom: .5rem; } -.block-settingsSearchResults-details > div { +.issue-reporter-body .block-settingsSearchResults-details > div { padding: .5rem .75rem; } -.section { +.issue-reporter-body .section { margin-bottom: .5em; } /** * Forms */ -input[type="text"], textarea { +.issue-reporter-body input[type="text"], +.issue-reporter-body textarea { display: block; width: 100%; padding: .375rem .75rem; @@ -63,7 +61,7 @@ input[type="text"], textarea { border: 1px solid #ced4da; } -textarea { +.issue-reporter-body textarea { overflow: auto; resize: vertical; } @@ -72,7 +70,7 @@ textarea { * Button */ -.monaco-text-button { +.issue-reporter-body .monaco-text-button { display: block; width: auto; padding: 4px 10px; @@ -81,7 +79,7 @@ textarea { font-size: 13px; } -select { +.issue-reporter-body select { height: calc(2.25rem + 2px); display: inline-block; padding: 3px 3px; @@ -92,60 +90,104 @@ select { border: none; } -* { +.issue-reporter-body * { box-sizing: border-box; } -textarea, input, select { +.issue-reporter-body textarea, +.issue-reporter-body input, +.issue-reporter-body select { font-family: inherit; } -html { +.issue-reporter-body html { color: #CCCCCC; height: 100%; } -.extension-caption .codicon-modifier-spin { +.issue-reporter-body .extension-caption .codicon-modifier-spin { padding-bottom: 3px; margin-left: 2px; } /* Font Families (with CJK support) */ -.mac { font-family: -apple-system, BlinkMacSystemFont, sans-serif; } -.mac:lang(zh-Hans) { font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", sans-serif; } -.mac:lang(zh-Hant) { font-family: -apple-system, BlinkMacSystemFont, "PingFang TC", sans-serif; } -.mac:lang(ja) { font-family: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic Pro", sans-serif; } -.mac:lang(ko) { font-family: -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Nanum Gothic", "AppleGothic", sans-serif; } +.issue-reporter-body .mac { + font-family: -apple-system, BlinkMacSystemFont, sans-serif; +} + +.issue-reporter-body .mac:lang(zh-Hans) { + font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", sans-serif; +} + +.issue-reporter-body .mac:lang(zh-Hant) { + font-family: -apple-system, BlinkMacSystemFont, "PingFang TC", sans-serif; +} + +.issue-reporter-body .mac:lang(ja) { + font-family: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic Pro", sans-serif; +} + +.issue-reporter-body .mac:lang(ko) { + font-family: -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Nanum Gothic", "AppleGothic", sans-serif; +} + +.issue-reporter-body .windows { + font-family: "Segoe WPC", "Segoe UI", sans-serif; +} -.windows { font-family: "Segoe WPC", "Segoe UI", sans-serif; } -.windows:lang(zh-Hans) { font-family: "Segoe WPC", "Segoe UI", "Microsoft YaHei", sans-serif; } -.windows:lang(zh-Hant) { font-family: "Segoe WPC", "Segoe UI", "Microsoft Jhenghei", sans-serif; } -.windows:lang(ja) { font-family: "Segoe WPC", "Segoe UI", "Yu Gothic UI", "Meiryo UI", sans-serif; } -.windows:lang(ko) { font-family: "Segoe WPC", "Segoe UI", "Malgun Gothic", "Dotom", sans-serif; } +.issue-reporter-body .windows:lang(zh-Hans) { + font-family: "Segoe WPC", "Segoe UI", "Microsoft YaHei", sans-serif; +} + +.issue-reporter-body .windows:lang(zh-Hant) { + font-family: "Segoe WPC", "Segoe UI", "Microsoft Jhenghei", sans-serif; +} + +.issue-reporter-body .windows:lang(ja) { + font-family: "Segoe WPC", "Segoe UI", "Yu Gothic UI", "Meiryo UI", sans-serif; +} + +.issue-reporter-body .windows:lang(ko) { + font-family: "Segoe WPC", "Segoe UI", "Malgun Gothic", "Dotom", sans-serif; +} /* Linux: add `system-ui` as first font and not `Ubuntu` to allow other distribution pick their standard OS font */ -.linux { font-family: system-ui, "Ubuntu", "Droid Sans", sans-serif; } -.linux:lang(zh-Hans) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; } -.linux:lang(zh-Hant) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans TC", "Source Han Sans TW", "Source Han Sans", sans-serif; } -.linux:lang(ja) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", sans-serif; } -.linux:lang(ko) { font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; } - -body { - margin: 0; - overflow-y: scroll; - height: 100%; +.issue-reporter-body .linux { + font-family: system-ui, "Ubuntu", "Droid Sans", sans-serif; } -.hidden { +.issue-reporter-body .linux:lang(zh-Hans) { + font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; +} + +.issue-reporter-body .linux:lang(zh-Hant) { + font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans TC", "Source Han Sans TW", "Source Han Sans", sans-serif; +} + +.issue-reporter-body .linux:lang(ja) { + font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", sans-serif; +} + +.issue-reporter-body .linux:lang(ko) { + font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; +} + +body.issue-reporter-body { + margin: 0 !important; + overflow-y: auto !important; + height: 100% !important; +} + +.issue-reporter-body .hidden { display: none; } -.block { +.issue-reporter-body .block { font-size: 12px; } -.block .block-info { +.issue-reporter-body .block .block-info { width: 100%; font-size: 12px; overflow: auto; @@ -154,7 +196,7 @@ body { padding: 10px; } -#issue-reporter { +.issue-reporter-body #issue-reporter { max-width: 85vw; margin-left: auto; margin-right: auto; @@ -162,69 +204,71 @@ body { padding-bottom: 2em; display: flex; flex-direction: column; - min-height: 100%; overflow: visible; } -.description-section { - flex-grow: 1; +.issue-reporter-body .description-section { + flex-grow: 0; display: flex; flex-direction: column; flex-shrink: 0; } -textarea { - flex-grow: 1; - min-height: 150px; +.issue-reporter-body textarea { + flex-grow: 0; + height: 200px; } -.block-info-text { +.issue-reporter-body .block-info-text { display: flex; - flex-grow: 1; + flex-grow: 0; + flex-direction: column; } -#github-submit-btn { +.issue-reporter-body #github-submit-btn { flex-shrink: 0; margin-left: auto; margin-top: 10px; margin-bottom: 10px; } -.two-col { +.issue-reporter-body .two-col { display: inline-block; width: 49%; } -#vscode-version { +.issue-reporter-body #vscode-version { width: 90%; } -.input-group { +.issue-reporter-body .input-group { margin-bottom: 1em; } -#extension-selection { +.issue-reporter-body #extension-selection { margin-top: 1em; } -select, input, textarea { +.issue-reporter-body .issue-reporter select, +.issue-reporter-body .issue-reporter input, +.issue-reporter-body .issue-reporter textarea { border: 1px solid transparent; margin-top: 10px; } -#issue-reporter .validation-error { +.issue-reporter-body #issue-reporter .validation-error { font-size: 12px; padding: 10px; border-top: 0px !important; } -#issue-reporter .system-info { +.issue-reporter-body #issue-reporter .system-info { margin-bottom: 10px; } -input[type="checkbox"] { +.issue-reporter-body input[type="checkbox"] { width: auto; display: inline-block; margin-top: 0; @@ -232,136 +276,166 @@ input[type="checkbox"] { cursor: pointer; } -input:disabled { +.issue-reporter-body input:disabled { opacity: 0.6; } -.list-title { +.issue-reporter-body .list-title { margin-top: 1em; margin-left: 1em; } -.instructions { +.issue-reporter-body .instructions { font-size: 12px; margin-top: .5em; } -a, .workbenchCommand { +.issue-reporter-body a, +.issue-reporter-body .workbenchCommand { cursor: pointer; border: 1px solid transparent; } -.workbenchCommand:disabled { +.issue-reporter-body .workbenchCommand:disabled { color: #868e96; cursor: default } -.block-extensions .block-info { +.issue-reporter-body .block-extensions .block-info { margin-bottom: 1.5em; } /* Default styles, overwritten if a theme is provided */ -input, select, textarea { +.issue-reporter-body input, +.issue-reporter-body select, +.issue-reporter-body textarea { background-color: #3c3c3c; border: none; color: #cccccc; } -a { +.issue-reporter-body a { color: #CCCCCC; text-decoration: none; } -.section .input-group .validation-error { +.issue-reporter-body .showInfo, +.issue-reporter-body .input-group a { + color: var(--vscode-textLink-foreground); +} + +.issue-reporter-body .section .input-group .validation-error { margin-left: 100px; } -.section .inline-form-control, .section .inline-label { +.issue-reporter-body .section .inline-form-control, +.issue-reporter-body .section .inline-label { display: inline-block; + font-size: initial; } -.section .inline-label { +.issue-reporter-body .section .inline-label { width: 95px; } -.section .inline-form-control, .section .input-group .validation-error { +.issue-reporter-body .issue-reporter .inline-label, +.issue-reporter-body .issue-reporter #issue-description-label { + font-size: initial; + cursor: default; +} + +.issue-reporter-body .monaco-workbench .issue-reporter label { + cursor: default; +} + +.issue-reporter-body .section .inline-form-control, +.issue-reporter-body .section .input-group .validation-error { width: calc(100% - 100px); } -#issue-type { +.issue-reporter-body #issue-type, +.issue-reporter-body #issue-source, +.issue-reporter-body #extension-selector { cursor: pointer; + appearance: auto; + border: none; + border-right: 6px solid transparent; + padding-left: 10px; } -#similar-issues { +.issue-reporter-body #similar-issues { margin-left: 15%; display: block; } -#problem-source-help-text { +.issue-reporter-body #problem-source-help-text { margin-left: calc(15% + 1em); } @media (max-width: 950px) { - .section .inline-label { + .issue-reporter-body .section .inline-label { width: 15%; } - #problem-source-help-text { + .issue-reporter-body #problem-source-help-text { margin-left: calc(15% + 1em); } - .section .inline-form-control, .section .input-group .validation-error { + .issue-reporter-body .section .inline-form-control, + .issue-reporter-body .section .input-group .validation-error { width: calc(85% - 5px); } - .section .input-group .validation-error { + .issue-reporter-body .section .input-group .validation-error { margin-left: calc(15% + 4px); } } @media (max-width: 620px) { - .section .inline-label { + .issue-reporter-body .section .inline-label { display: none !important; } - #problem-source-help-text { + .issue-reporter-body #problem-source-help-text { margin-left: 1em; } - .section .inline-form-control, .section .input-group .validation-error { + .issue-reporter-body .section .inline-form-control, + .issue-reporter-body .section .input-group .validation-error { width: 100%; } - #similar-issues, .section .input-group .validation-error { + .issue-reporter-body #similar-issues, + .issue-reporter-body .section .input-group .validation-error { margin-left: 0; } } -::-webkit-scrollbar { +.issue-reporter-body::-webkit-scrollbar { width: 14px; } -::-webkit-scrollbar-thumb { +.issue-reporter-body::-webkit-scrollbar-thumb { min-height: 20px; } -::-webkit-scrollbar-corner { +.issue-reporter-body::-webkit-scrollbar-corner { display: none; } -.issues-container { +.issue-reporter-body .issues-container { margin-left: 1.5em; margin-top: .5em; max-height: 92px; overflow-y: auto; } -.issues-container > .issue { +.issue-reporter-body .issues-container > .issue { padding: 4px 0; display: flex; } -.issues-container > .issue > .issue-link { +.issue-reporter-body .issues-container > .issue > .issue-link { width: calc(100% - 82px); overflow: hidden; padding-top: 3px; @@ -369,11 +443,11 @@ a { text-overflow: ellipsis; } -.issues-container > .issue > .issue-state .codicon { +.issue-reporter-body .issues-container > .issue > .issue-state .codicon { width: 16px; } -.issues-container > .issue > .issue-state { +.issue-reporter-body .issues-container > .issue > .issue-state { display: flex; width: 77px; padding: 3px 6px; @@ -383,7 +457,7 @@ a { border-radius: .25rem; } -.issues-container > .issue .label { +.issue-reporter-body .issues-container > .issue .label { padding-top: 2px; margin-left: 5px; width: 44px; @@ -391,6 +465,16 @@ a { overflow: hidden; } -.issues-container > .issue .issue-icon{ +.issue-reporter-body .issues-container > .issue .issue-icon { padding-top: 2px; } + +.issue-reporter-update-banner { + color: var(--vscode-button-foreground); + background-color: var(--vscode-button-background); + padding: 10px; + text-align: center; + position: sticky; + top: 0; + z-index: 1000; +} diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/media/newIssueReporter.css b/src/vs/workbench/contrib/issue/electron-sandbox/media/newIssueReporter.css deleted file mode 100644 index c88a95a95e02..000000000000 --- a/src/vs/workbench/contrib/issue/electron-sandbox/media/newIssueReporter.css +++ /dev/null @@ -1,470 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/** - * Table - */ - -.issue-reporter-body table { - width: 100%; - max-width: 100%; - background-color: transparent; - border-collapse: collapse; -} - -.issue-reporter-body th { - vertical-align: bottom; - border-bottom: 1px solid; - padding: 5px; - text-align: inherit; -} - -.issue-reporter-body td { - padding: 5px; - vertical-align: top; -} - -.issue-reporter-body tr td:first-child { - width: 30%; -} - -.issue-reporter-body label { - user-select: none; -} - -.issue-reporter-body .block-settingsSearchResults-details { - padding-bottom: .5rem; -} - -.issue-reporter-body .block-settingsSearchResults-details > div { - padding: .5rem .75rem; -} - -.issue-reporter-body .section { - margin-bottom: .5em; -} - -/** - * Forms - */ -.issue-reporter-body input[type="text"], -.issue-reporter-body textarea { - display: block; - width: 100%; - padding: .375rem .75rem; - font-size: 1rem; - line-height: 1.5; - color: #495057; - background-color: #fff; - border: 1px solid #ced4da; -} - -.issue-reporter-body textarea { - overflow: auto; - resize: vertical; -} - -/** - * Button - */ - -.issue-reporter-body .monaco-text-button { - display: block; - width: auto; - padding: 4px 10px; - align-self: flex-end; - margin-bottom: 1em; - font-size: 13px; -} - -.issue-reporter-body select { - height: calc(2.25rem + 2px); - display: inline-block; - padding: 3px 3px; - font-size: 14px; - line-height: 1.5; - color: #495057; - background-color: #fff; - border: none; -} - -.issue-reporter-body * { - box-sizing: border-box; -} - -.issue-reporter-body textarea, -.issue-reporter-body input, -.issue-reporter-body select { - font-family: inherit; -} - -.issue-reporter-body html { - color: #CCCCCC; - height: 100%; -} - -.issue-reporter-body .extension-caption .codicon-modifier-spin { - padding-bottom: 3px; - margin-left: 2px; -} - -/* Font Families (with CJK support) */ - -.issue-reporter-body .mac { - font-family: -apple-system, BlinkMacSystemFont, sans-serif; -} - -.issue-reporter-body .mac:lang(zh-Hans) { - font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Hiragino Sans GB", sans-serif; -} - -.issue-reporter-body .mac:lang(zh-Hant) { - font-family: -apple-system, BlinkMacSystemFont, "PingFang TC", sans-serif; -} - -.issue-reporter-body .mac:lang(ja) { - font-family: -apple-system, BlinkMacSystemFont, "Hiragino Kaku Gothic Pro", sans-serif; -} - -.issue-reporter-body .mac:lang(ko) { - font-family: -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Nanum Gothic", "AppleGothic", sans-serif; -} - -.issue-reporter-body .windows { - font-family: "Segoe WPC", "Segoe UI", sans-serif; -} - -.issue-reporter-body .windows:lang(zh-Hans) { - font-family: "Segoe WPC", "Segoe UI", "Microsoft YaHei", sans-serif; -} - -.issue-reporter-body .windows:lang(zh-Hant) { - font-family: "Segoe WPC", "Segoe UI", "Microsoft Jhenghei", sans-serif; -} - -.issue-reporter-body .windows:lang(ja) { - font-family: "Segoe WPC", "Segoe UI", "Yu Gothic UI", "Meiryo UI", sans-serif; -} - -.issue-reporter-body .windows:lang(ko) { - font-family: "Segoe WPC", "Segoe UI", "Malgun Gothic", "Dotom", sans-serif; -} - -/* Linux: add `system-ui` as first font and not `Ubuntu` to allow other distribution pick their standard OS font */ -.issue-reporter-body .linux { - font-family: system-ui, "Ubuntu", "Droid Sans", sans-serif; -} - -.issue-reporter-body .linux:lang(zh-Hans) { - font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans SC", "Source Han Sans CN", "Source Han Sans", sans-serif; -} - -.issue-reporter-body .linux:lang(zh-Hant) { - font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans TC", "Source Han Sans TW", "Source Han Sans", sans-serif; -} - -.issue-reporter-body .linux:lang(ja) { - font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans J", "Source Han Sans JP", "Source Han Sans", sans-serif; -} - -.issue-reporter-body .linux:lang(ko) { - font-family: system-ui, "Ubuntu", "Droid Sans", "Source Han Sans K", "Source Han Sans JR", "Source Han Sans", "UnDotum", "FBaekmuk Gulim", sans-serif; -} - -body.issue-reporter-body { - margin: 0 !important; - overflow-y: scroll !important; - height: 100% !important; -} - -.issue-reporter-body .hidden { - display: none; -} - -.issue-reporter-body .block { - font-size: 12px; -} - -.issue-reporter-body .block .block-info { - width: 100%; - font-size: 12px; - overflow: auto; - overflow-wrap: break-word; - margin: 5px; - padding: 10px; -} - -.issue-reporter-body #issue-reporter { - max-width: 85vw; - margin-left: auto; - margin-right: auto; - padding-top: 2em; - padding-bottom: 2em; - display: flex; - flex-direction: column; - min-height: 100%; - overflow: visible; -} - -.issue-reporter-body .description-section { - flex-grow: 1; - display: flex; - flex-direction: column; - flex-shrink: 0; -} - -.issue-reporter-body textarea { - flex-grow: 1; - min-height: 150px; -} - -.issue-reporter-body .block-info-text { - display: flex; - flex-grow: 1; -} - -.issue-reporter-body #github-submit-btn { - flex-shrink: 0; - margin-left: auto; - margin-top: 10px; - margin-bottom: 10px; -} - -.issue-reporter-body .two-col { - display: inline-block; - width: 49%; -} - -.issue-reporter-body #vscode-version { - width: 90%; -} - -.issue-reporter-body .input-group { - margin-bottom: 1em; -} - -.issue-reporter-body #extension-selection { - margin-top: 1em; -} - -.issue-reporter-body .issue-reporter select, -.issue-reporter-body .issue-reporter input, -.issue-reporter-body .issue-reporter textarea { - border: 1px solid transparent; - margin-top: 10px; -} - - -.issue-reporter-body #issue-reporter .validation-error { - font-size: 12px; - padding: 10px; - border-top: 0px !important; -} - -.issue-reporter-body #issue-reporter .system-info { - margin-bottom: 10px; -} - - -.issue-reporter-body input[type="checkbox"] { - width: auto; - display: inline-block; - margin-top: 0; - vertical-align: middle; - cursor: pointer; -} - -.issue-reporter-body input:disabled { - opacity: 0.6; -} - -.issue-reporter-body .list-title { - margin-top: 1em; - margin-left: 1em; -} - -.issue-reporter-body .instructions { - font-size: 12px; - margin-top: .5em; -} - -.issue-reporter-body a, -.issue-reporter-body .workbenchCommand { - cursor: pointer; - border: 1px solid transparent; -} - -.issue-reporter-body .workbenchCommand:disabled { - color: #868e96; - cursor: default -} - -.issue-reporter-body .block-extensions .block-info { - margin-bottom: 1.5em; -} - -/* Default styles, overwritten if a theme is provided */ -.issue-reporter-body input, -.issue-reporter-body select, -.issue-reporter-body textarea { - background-color: #3c3c3c; - border: none; - color: #cccccc; -} - -.issue-reporter-body a { - color: #CCCCCC; - text-decoration: none; -} - -.issue-reporter-body .showInfo, -.issue-reporter-body .input-group a { - color: var(--vscode-textLink-foreground); -} - -.issue-reporter-body .section .input-group .validation-error { - margin-left: 100px; -} - -.issue-reporter-body .section .inline-form-control, -.issue-reporter-body .section .inline-label { - display: inline-block; - font-size: initial; -} - -.issue-reporter-body .section .inline-label { - width: 95px; -} - -.issue-reporter-body .issue-reporter .inline-label, -.issue-reporter-body .issue-reporter #issue-description-label { - font-size: initial; - cursor: default; -} - -.issue-reporter-body .monaco-workbench .issue-reporter label { - cursor: default; -} - -.issue-reporter-body .section .inline-form-control, -.issue-reporter-body .section .input-group .validation-error { - width: calc(100% - 100px); -} - -.issue-reporter-body #issue-type, -.issue-reporter-body #issue-source, -.issue-reporter-body #extension-selector { - cursor: pointer; - appearance: auto; - border: none; - border-right: 6px solid transparent; - padding-left: 10px; -} - -.issue-reporter-body #similar-issues { - margin-left: 15%; - display: block; -} - -.issue-reporter-body #problem-source-help-text { - margin-left: calc(15% + 1em); -} - -@media (max-width: 950px) { - .issue-reporter-body .section .inline-label { - width: 15%; - } - - .issue-reporter-body #problem-source-help-text { - margin-left: calc(15% + 1em); - } - - .issue-reporter-body .section .inline-form-control, - .issue-reporter-body .section .input-group .validation-error { - width: calc(85% - 5px); - } - - .issue-reporter-body .section .input-group .validation-error { - margin-left: calc(15% + 4px); - } -} - -@media (max-width: 620px) { - .issue-reporter-body .section .inline-label { - display: none !important; - } - - .issue-reporter-body #problem-source-help-text { - margin-left: 1em; - } - - .issue-reporter-body .section .inline-form-control, - .issue-reporter-body .section .input-group .validation-error { - width: 100%; - } - - .issue-reporter-body #similar-issues, - .issue-reporter-body .section .input-group .validation-error { - margin-left: 0; - } -} - -.issue-reporter-body::-webkit-scrollbar { - width: 14px; -} - -.issue-reporter-body::-webkit-scrollbar-thumb { - min-height: 20px; -} - -.issue-reporter-body::-webkit-scrollbar-corner { - display: none; -} - -.issue-reporter-body .issues-container { - margin-left: 1.5em; - margin-top: .5em; - max-height: 92px; - overflow-y: auto; -} - -.issue-reporter-body .issues-container > .issue { - padding: 4px 0; - display: flex; -} - -.issue-reporter-body .issues-container > .issue > .issue-link { - width: calc(100% - 82px); - overflow: hidden; - padding-top: 3px; - white-space: nowrap; - text-overflow: ellipsis; -} - -.issue-reporter-body .issues-container > .issue > .issue-state .codicon { - width: 16px; -} - -.issue-reporter-body .issues-container > .issue > .issue-state { - display: flex; - width: 77px; - padding: 3px 6px; - margin-right: 5px; - color: #CCCCCC; - background-color: #3c3c3c; - border-radius: .25rem; -} - -.issue-reporter-body .issues-container > .issue .label { - padding-top: 2px; - margin-left: 5px; - width: 44px; - text-overflow: ellipsis; - overflow: hidden; -} - -.issue-reporter-body .issues-container > .issue .issue-icon { - padding-top: 2px; -} diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/nativeIssueFormService.ts b/src/vs/workbench/contrib/issue/electron-sandbox/nativeIssueFormService.ts index 508137162ca0..f6ae15199b8b 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/nativeIssueFormService.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/nativeIssueFormService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import './media/newIssueReporter.css'; +import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { IMenuService } from '../../../../platform/actions/common/actions.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; @@ -12,13 +12,16 @@ import { IInstantiationService } from '../../../../platform/instantiation/common import { ILogService } from '../../../../platform/log/common/log.js'; import { INativeHostService } from '../../../../platform/native/common/native.js'; import product from '../../../../platform/product/common/product.js'; -import { IssueFormService } from '../browser/issueFormService.js'; -import { IIssueFormService, IssueReporterData } from '../common/issue.js'; -import { IssueReporter2 } from './issueReporterService2.js'; import { IAuxiliaryWindowService } from '../../../services/auxiliaryWindow/browser/auxiliaryWindowService.js'; import { IHostService } from '../../../services/host/browser/host.js'; +import { IssueFormService } from '../browser/issueFormService.js'; +import { IIssueFormService, IssueReporterData } from '../common/issue.js'; +import { IssueReporter } from './issueReporterService.js'; +import './media/issueReporter.css'; export class NativeIssueFormService extends IssueFormService implements IIssueFormService { + private readonly store = new DisposableStore(); + constructor( @IInstantiationService instantiationService: IInstantiationService, @IAuxiliaryWindowService auxiliaryWindowService: IAuxiliaryWindowService, @@ -53,8 +56,10 @@ export class NativeIssueFormService extends IssueFormService implements IIssueFo // create issue reporter and instantiate if (this.issueReporterWindow) { - const issueReporter = this.instantiationService.createInstance(IssueReporter2, !!this.environmentService.disableExtensions, data, { type: this.type, arch: this.arch, release: this.release }, product, this.issueReporterWindow); + const issueReporter = this.store.add(this.instantiationService.createInstance(IssueReporter, !!this.environmentService.disableExtensions, data, { type: this.type, arch: this.arch, release: this.release }, product, this.issueReporterWindow)); issueReporter.render(); + } else { + this.store.dispose(); } } } diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/process.contribution.ts b/src/vs/workbench/contrib/issue/electron-sandbox/process.contribution.ts index 6c33d9f81339..75826f51d8ec 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/process.contribution.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/process.contribution.ts @@ -13,10 +13,9 @@ import { INativeEnvironmentService } from '../../../../platform/environment/comm import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { INativeHostService } from '../../../../platform/native/common/native.js'; import { IProgressService, ProgressLocation } from '../../../../platform/progress/common/progress.js'; -import { IProcessMainService } from '../../../../platform/issue/common/issue.js'; +import { IProcessMainService } from '../../../../platform/process/common/process.js'; import './processService.js'; -import './issueMainService.js'; - +import './processMainService.js'; //#region Commands diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueMainService.ts b/src/vs/workbench/contrib/issue/electron-sandbox/processMainService.ts similarity index 76% rename from src/vs/workbench/contrib/issue/electron-sandbox/issueMainService.ts rename to src/vs/workbench/contrib/issue/electron-sandbox/processMainService.ts index 32cbb69428d9..d2073829039b 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueMainService.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/processMainService.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { registerMainProcessRemoteService } from '../../../../platform/ipc/electron-sandbox/services.js'; -import { IProcessMainService, IIssueMainService } from '../../../../platform/issue/common/issue.js'; +import { IProcessMainService } from '../../../../platform/process/common/process.js'; -registerMainProcessRemoteService(IIssueMainService, 'issue'); registerMainProcessRemoteService(IProcessMainService, 'process'); diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/processService.ts b/src/vs/workbench/contrib/issue/electron-sandbox/processService.ts index c781a4af73c9..d5837297bed6 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/processService.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/processService.ts @@ -6,7 +6,7 @@ import { getZoomLevel } from '../../../../base/browser/browser.js'; import { platform } from '../../../../base/common/process.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { IProcessMainService, ProcessExplorerData } from '../../../../platform/issue/common/issue.js'; +import { IProcessMainService, ProcessExplorerData } from '../../../../platform/process/common/process.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; import { activeContrastBorder, editorBackground, editorForeground, listActiveSelectionBackground, listActiveSelectionForeground, listFocusBackground, listFocusForeground, listFocusOutline, listHoverBackground, listHoverForeground, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground } from '../../../../platform/theme/common/colorRegistry.js'; import { IColorTheme, IThemeService } from '../../../../platform/theme/common/themeService.js'; diff --git a/src/vs/workbench/contrib/keybindings/browser/keybindings.contribution.ts b/src/vs/workbench/contrib/keybindings/browser/keybindings.contribution.ts index 096f1bbba549..8631d6f7adcd 100644 --- a/src/vs/workbench/contrib/keybindings/browser/keybindings.contribution.ts +++ b/src/vs/workbench/contrib/keybindings/browser/keybindings.contribution.ts @@ -10,8 +10,15 @@ import { IKeybindingService } from '../../../../platform/keybinding/common/keybi import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { showWindowLogActionId } from '../../../services/log/common/logConstants.js'; +import { DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { ILayoutService } from '../../../../platform/layout/browser/layoutService.js'; +import { $, append, getDomNodePagePosition, getWindows, onDidRegisterWindow } from '../../../../base/browser/dom.js'; +import { createCSSRule, createStyleSheet } from '../../../../base/browser/domStylesheets.js'; +import { Emitter } from '../../../../base/common/event.js'; +import { DomEmitter } from '../../../../base/browser/event.js'; class ToggleKeybindingsLogAction extends Action2 { + static disposable: IDisposable | undefined; constructor() { super({ @@ -28,6 +35,66 @@ class ToggleKeybindingsLogAction extends Action2 { const commandService = accessor.get(ICommandService); commandService.executeCommand(showWindowLogActionId); } + + if (ToggleKeybindingsLogAction.disposable) { + ToggleKeybindingsLogAction.disposable.dispose(); + ToggleKeybindingsLogAction.disposable = undefined; + return; + } + + const layoutService = accessor.get(ILayoutService); + const disposables = new DisposableStore(); + + const container = layoutService.activeContainer; + const focusMarker = append(container, $('.focus-troubleshooting-marker')); + disposables.add(toDisposable(() => focusMarker.remove())); + + // Add CSS rule for focus marker + const stylesheet = createStyleSheet(undefined, undefined, disposables); + createCSSRule('.focus-troubleshooting-marker', ` + position: fixed; + pointer-events: none; + z-index: 100000; + background-color: rgba(255, 0, 0, 0.2); + border: 2px solid rgba(255, 0, 0, 0.8); + border-radius: 2px; + display: none; + `, stylesheet); + + const onKeyDown = disposables.add(new Emitter()); + + function registerWindowListeners(window: Window, disposables: DisposableStore): void { + disposables.add(disposables.add(new DomEmitter(window, 'keydown', true)).event(e => onKeyDown.fire(e))); + } + + for (const { window, disposables } of getWindows()) { + registerWindowListeners(window, disposables); + } + + disposables.add(onDidRegisterWindow(({ window, disposables }) => registerWindowListeners(window, disposables))); + + disposables.add(layoutService.onDidChangeActiveContainer(() => { + layoutService.activeContainer.appendChild(focusMarker); + })); + + disposables.add(onKeyDown.event(e => { + const target = e.target as HTMLElement; + if (target) { + const position = getDomNodePagePosition(target); + focusMarker.style.top = `${position.top}px`; + focusMarker.style.left = `${position.left}px`; + focusMarker.style.width = `${position.width}px`; + focusMarker.style.height = `${position.height}px`; + focusMarker.style.display = 'block'; + + // Hide after timeout + setTimeout(() => { + focusMarker.style.display = 'none'; + }, 800); + } + })); + + ToggleKeybindingsLogAction.disposable = disposables; } } diff --git a/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts b/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts index d32ed42432f7..729a6932d6ed 100644 --- a/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts +++ b/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts @@ -103,7 +103,7 @@ class LanguageDetectionStatusContribution implements IWorkbenchContribution { text: '$(lightbulb-autofix)', }; if (!this._combinedEntry) { - this._combinedEntry = this._statusBarService.addEntry(props, LanguageDetectionStatusContribution._id, StatusbarAlignment.RIGHT, { id: 'status.editor.mode', alignment: StatusbarAlignment.RIGHT, compact: true }); + this._combinedEntry = this._statusBarService.addEntry(props, LanguageDetectionStatusContribution._id, StatusbarAlignment.RIGHT, { location: { id: 'status.editor.mode', priority: 100.1 }, alignment: StatusbarAlignment.RIGHT, compact: true }); } else { this._combinedEntry.update(props); } diff --git a/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts b/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts index d28a5e86dcca..d1edba9a891e 100644 --- a/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts +++ b/src/vs/workbench/contrib/languageStatus/browser/languageStatus.contribution.ts @@ -3,437 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import './media/languageStatus.css'; -import * as dom from '../../../../base/browser/dom.js'; -import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/iconLabels.js'; -import { Disposable, DisposableStore, dispose, toDisposable } from '../../../../base/common/lifecycle.js'; -import Severity from '../../../../base/common/severity.js'; -import { getCodeEditor, ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; -import { localize, localize2 } from '../../../../nls.js'; -import { Registry } from '../../../../platform/registry/common/platform.js'; -import { ThemeIcon } from '../../../../base/common/themables.js'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from '../../../common/contributions.js'; -import { IEditorService } from '../../../services/editor/common/editorService.js'; -import { ILanguageStatus, ILanguageStatusService } from '../../../services/languageStatus/common/languageStatusService.js'; -import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; -import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, ShowTooltipCommand, StatusbarAlignment, StatusbarEntryKind } from '../../../services/statusbar/browser/statusbar.js'; -import { parseLinkedText } from '../../../../base/common/linkedText.js'; -import { Link } from '../../../../platform/opener/browser/link.js'; -import { IOpenerService } from '../../../../platform/opener/common/opener.js'; -import { MarkdownString } from '../../../../base/common/htmlContent.js'; -import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js'; -import { Action } from '../../../../base/common/actions.js'; -import { Codicon } from '../../../../base/common/codicons.js'; -import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; -import { equals } from '../../../../base/common/arrays.js'; -import { URI } from '../../../../base/common/uri.js'; -import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js'; -import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; -import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; -import { IAccessibilityInformation } from '../../../../platform/accessibility/common/accessibility.js'; -import { IEditorGroupsService, IEditorPart } from '../../../services/editor/common/editorGroupsService.js'; -import { IHoverService, nativeHoverDelegate } from '../../../../platform/hover/browser/hover.js'; -import { Event } from '../../../../base/common/event.js'; +import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js'; +import { registerAction2 } from '../../../../platform/actions/common/actions.js'; +import { LanguageStatusContribution, ResetAction } from './languageStatus.js'; -class LanguageStatusViewModel { - constructor( - readonly combined: readonly ILanguageStatus[], - readonly dedicated: readonly ILanguageStatus[] - ) { } - - isEqual(other: LanguageStatusViewModel) { - return equals(this.combined, other.combined) && equals(this.dedicated, other.dedicated); - } -} - -class StoredCounter { - - constructor(@IStorageService private readonly _storageService: IStorageService, private readonly _key: string) { } - - get value() { - return this._storageService.getNumber(this._key, StorageScope.PROFILE, 0); - } - - increment(): number { - const n = this.value + 1; - this._storageService.store(this._key, n, StorageScope.PROFILE, StorageTarget.MACHINE); - return n; - } -} - -class LanguageStatusContribution extends Disposable implements IWorkbenchContribution { - - constructor( - @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, - ) { - super(); - - for (const part of editorGroupService.parts) { - this.createLanguageStatus(part); - } - - this._register(editorGroupService.onDidCreateAuxiliaryEditorPart(part => this.createLanguageStatus(part))); - } - - private createLanguageStatus(part: IEditorPart): void { - const disposables = new DisposableStore(); - Event.once(part.onWillDispose)(() => disposables.dispose()); - - const scopedInstantiationService = this.editorGroupService.getScopedInstantiationService(part); - disposables.add(scopedInstantiationService.createInstance(LanguageStatus)); - } -} - -class LanguageStatus { - - private static readonly _id = 'status.languageStatus'; - - private static readonly _keyDedicatedItems = 'languageStatus.dedicated'; - - private readonly _disposables = new DisposableStore(); - private readonly _interactionCounter: StoredCounter; - - private _dedicated = new Set(); - - private _model?: LanguageStatusViewModel; - private _combinedEntry?: IStatusbarEntryAccessor; - private _dedicatedEntries = new Map(); - private readonly _renderDisposables = new DisposableStore(); - - constructor( - @ILanguageStatusService private readonly _languageStatusService: ILanguageStatusService, - @IStatusbarService private readonly _statusBarService: IStatusbarService, - @IEditorService private readonly _editorService: IEditorService, - @IHoverService private readonly _hoverService: IHoverService, - @IOpenerService private readonly _openerService: IOpenerService, - @IStorageService private readonly _storageService: IStorageService, - ) { - _storageService.onDidChangeValue(StorageScope.PROFILE, LanguageStatus._keyDedicatedItems, this._disposables)(this._handleStorageChange, this, this._disposables); - this._restoreState(); - this._interactionCounter = new StoredCounter(_storageService, 'languageStatus.interactCount'); - - _languageStatusService.onDidChange(this._update, this, this._disposables); - _editorService.onDidActiveEditorChange(this._update, this, this._disposables); - this._update(); - - _statusBarService.onDidChangeEntryVisibility(e => { - if (!e.visible && this._dedicated.has(e.id)) { - this._dedicated.delete(e.id); - this._update(); - this._storeState(); - } - }, undefined, this._disposables); - - } - - dispose(): void { - this._disposables.dispose(); - this._combinedEntry?.dispose(); - dispose(this._dedicatedEntries.values()); - this._renderDisposables.dispose(); - } - - // --- persisting dedicated items - - private _handleStorageChange() { - this._restoreState(); - this._update(); - } - - private _restoreState(): void { - const raw = this._storageService.get(LanguageStatus._keyDedicatedItems, StorageScope.PROFILE, '[]'); - try { - const ids = JSON.parse(raw); - this._dedicated = new Set(ids); - } catch { - this._dedicated.clear(); - } - } - - private _storeState(): void { - if (this._dedicated.size === 0) { - this._storageService.remove(LanguageStatus._keyDedicatedItems, StorageScope.PROFILE); - } else { - const raw = JSON.stringify(Array.from(this._dedicated.keys())); - this._storageService.store(LanguageStatus._keyDedicatedItems, raw, StorageScope.PROFILE, StorageTarget.USER); - } - } - - // --- language status model and UI - - private _createViewModel(editor: ICodeEditor | null): LanguageStatusViewModel { - if (!editor?.hasModel()) { - return new LanguageStatusViewModel([], []); - } - const all = this._languageStatusService.getLanguageStatus(editor.getModel()); - const combined: ILanguageStatus[] = []; - const dedicated: ILanguageStatus[] = []; - for (const item of all) { - if (this._dedicated.has(item.id)) { - dedicated.push(item); - } - combined.push(item); - } - return new LanguageStatusViewModel(combined, dedicated); - } - - private _update(): void { - const editor = getCodeEditor(this._editorService.activeTextEditorControl); - const model = this._createViewModel(editor); - - if (this._model?.isEqual(model)) { - return; - } - this._renderDisposables.clear(); - - this._model = model; - - // update when editor language changes - editor?.onDidChangeModelLanguage(this._update, this, this._renderDisposables); - - // combined status bar item is a single item which hover shows - // each status item - if (model.combined.length === 0) { - // nothing - this._combinedEntry?.dispose(); - this._combinedEntry = undefined; - - } else { - const [first] = model.combined; - const showSeverity = first.severity >= Severity.Warning; - const text = LanguageStatus._severityToComboCodicon(first.severity); - - let isOneBusy = false; - const ariaLabels: string[] = []; - const element = document.createElement('div'); - for (const status of model.combined) { - const isPinned = model.dedicated.includes(status); - element.appendChild(this._renderStatus(status, showSeverity, isPinned, this._renderDisposables)); - ariaLabels.push(LanguageStatus._accessibilityInformation(status).label); - isOneBusy = isOneBusy || (!isPinned && status.busy); // unpinned items contribute to the busy-indicator of the composite status item - } - const props: IStatusbarEntry = { - name: localize('langStatus.name', "Editor Language Status"), - ariaLabel: localize('langStatus.aria', "Editor Language Status: {0}", ariaLabels.join(', next: ')), - tooltip: element, - command: ShowTooltipCommand, - text: isOneBusy ? `${text}\u00A0\u00A0$(loading~spin)` : text, - }; - if (!this._combinedEntry) { - this._combinedEntry = this._statusBarService.addEntry(props, LanguageStatus._id, StatusbarAlignment.RIGHT, { id: 'status.editor.mode', alignment: StatusbarAlignment.LEFT, compact: true }); - } else { - this._combinedEntry.update(props); - } - - // animate the status bar icon whenever language status changes, repeat animation - // when severity is warning or error, don't show animation when showing progress/busy - const userHasInteractedWithStatus = this._interactionCounter.value >= 3; - const targetWindow = dom.getWindow(editor?.getContainerDomNode()); - const node = targetWindow.document.querySelector('.monaco-workbench .statusbar DIV#status\\.languageStatus A>SPAN.codicon'); - const container = targetWindow.document.querySelector('.monaco-workbench .statusbar DIV#status\\.languageStatus'); - if (dom.isHTMLElement(node) && container) { - const _wiggle = 'wiggle'; - const _flash = 'flash'; - if (!isOneBusy) { - // wiggle icon when severe or "new" - node.classList.toggle(_wiggle, showSeverity || !userHasInteractedWithStatus); - this._renderDisposables.add(dom.addDisposableListener(node, 'animationend', _e => node.classList.remove(_wiggle))); - // flash background when severe - container.classList.toggle(_flash, showSeverity); - this._renderDisposables.add(dom.addDisposableListener(container, 'animationend', _e => container.classList.remove(_flash))); - } else { - node.classList.remove(_wiggle); - container.classList.remove(_flash); - } - } - - // track when the hover shows (this is automagic and DOM mutation spying is needed...) - // use that as signal that the user has interacted/learned language status items work - if (!userHasInteractedWithStatus) { - const hoverTarget = targetWindow.document.querySelector('.monaco-workbench .context-view'); - if (dom.isHTMLElement(hoverTarget)) { - const observer = new MutationObserver(() => { - if (targetWindow.document.contains(element)) { - this._interactionCounter.increment(); - observer.disconnect(); - } - }); - observer.observe(hoverTarget, { childList: true, subtree: true }); - this._renderDisposables.add(toDisposable(() => observer.disconnect())); - } - } - } - - // dedicated status bar items are shows as-is in the status bar - const newDedicatedEntries = new Map(); - for (const status of model.dedicated) { - const props = LanguageStatus._asStatusbarEntry(status); - let entry = this._dedicatedEntries.get(status.id); - if (!entry) { - entry = this._statusBarService.addEntry(props, status.id, StatusbarAlignment.RIGHT, { id: 'status.editor.mode', alignment: StatusbarAlignment.RIGHT }); - } else { - entry.update(props); - this._dedicatedEntries.delete(status.id); - } - newDedicatedEntries.set(status.id, entry); - } - dispose(this._dedicatedEntries.values()); - this._dedicatedEntries = newDedicatedEntries; - } - - private _renderStatus(status: ILanguageStatus, showSeverity: boolean, isPinned: boolean, store: DisposableStore): HTMLElement { - - const parent = document.createElement('div'); - parent.classList.add('hover-language-status'); - - const severity = document.createElement('div'); - severity.classList.add('severity', `sev${status.severity}`); - severity.classList.toggle('show', showSeverity); - const severityText = LanguageStatus._severityToSingleCodicon(status.severity); - dom.append(severity, ...renderLabelWithIcons(severityText)); - parent.appendChild(severity); - - const element = document.createElement('div'); - element.classList.add('element'); - parent.appendChild(element); - - const left = document.createElement('div'); - left.classList.add('left'); - element.appendChild(left); - - const label = document.createElement('span'); - label.classList.add('label'); - const labelValue = typeof status.label === 'string' ? status.label : status.label.value; - dom.append(label, ...renderLabelWithIcons(status.busy ? `$(loading~spin)\u00A0\u00A0${labelValue}` : labelValue)); - left.appendChild(label); - - const detail = document.createElement('span'); - detail.classList.add('detail'); - this._renderTextPlus(detail, status.detail, store); - left.appendChild(detail); - - const right = document.createElement('div'); - right.classList.add('right'); - element.appendChild(right); - - // -- command (if available) - const { command } = status; - if (command) { - store.add(new Link(right, { - label: command.title, - title: command.tooltip, - href: URI.from({ - scheme: 'command', path: command.id, query: command.arguments && JSON.stringify(command.arguments) - }).toString() - }, { hoverDelegate: nativeHoverDelegate }, this._hoverService, this._openerService)); - } - - // -- pin - const actionBar = new ActionBar(right, { hoverDelegate: nativeHoverDelegate }); - const actionLabel: string = isPinned ? localize('unpin', "Remove from Status Bar") : localize('pin', "Add to Status Bar"); - actionBar.setAriaLabel(actionLabel); - store.add(actionBar); - let action: Action; - if (!isPinned) { - action = new Action('pin', actionLabel, ThemeIcon.asClassName(Codicon.pin), true, () => { - this._dedicated.add(status.id); - this._statusBarService.updateEntryVisibility(status.id, true); - this._update(); - this._storeState(); - }); - } else { - action = new Action('unpin', actionLabel, ThemeIcon.asClassName(Codicon.pinned), true, () => { - this._dedicated.delete(status.id); - this._statusBarService.updateEntryVisibility(status.id, false); - this._update(); - this._storeState(); - }); - } - actionBar.push(action, { icon: true, label: false }); - store.add(action); - - return parent; - } - - private static _severityToComboCodicon(sev: Severity): string { - switch (sev) { - case Severity.Error: return '$(bracket-error)'; - case Severity.Warning: return '$(bracket-dot)'; - default: return '$(bracket)'; - } - } - - private static _severityToSingleCodicon(sev: Severity): string { - switch (sev) { - case Severity.Error: return '$(error)'; - case Severity.Warning: return '$(info)'; - default: return '$(check)'; - } - } - - private _renderTextPlus(target: HTMLElement, text: string, store: DisposableStore): void { - for (const node of parseLinkedText(text).nodes) { - if (typeof node === 'string') { - const parts = renderLabelWithIcons(node); - dom.append(target, ...parts); - } else { - store.add(new Link(target, node, undefined, this._hoverService, this._openerService)); - } - } - } - - private static _accessibilityInformation(status: ILanguageStatus): IAccessibilityInformation { - if (status.accessibilityInfo) { - return status.accessibilityInfo; - } - const textValue = typeof status.label === 'string' ? status.label : status.label.value; - if (status.detail) { - return { label: localize('aria.1', '{0}, {1}', textValue, status.detail) }; - } else { - return { label: localize('aria.2', '{0}', textValue) }; - } - } - - // --- - - private static _asStatusbarEntry(item: ILanguageStatus): IStatusbarEntry { - - let kind: StatusbarEntryKind | undefined; - if (item.severity === Severity.Warning) { - kind = 'warning'; - } else if (item.severity === Severity.Error) { - kind = 'error'; - } - - const textValue = typeof item.label === 'string' ? item.label : item.label.shortValue; - - return { - name: localize('name.pattern', '{0} (Language Status)', item.name), - text: item.busy ? `${textValue}\u00A0\u00A0$(loading~spin)` : textValue, - ariaLabel: LanguageStatus._accessibilityInformation(item).label, - role: item.accessibilityInfo?.role, - tooltip: item.command?.tooltip || new MarkdownString(item.detail, { isTrusted: true, supportThemeIcons: true }), - kind, - command: item.command - }; - } -} - -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LanguageStatusContribution, LifecyclePhase.Restored); - -registerAction2(class extends Action2 { - - constructor() { - super({ - id: 'editor.inlayHints.Reset', - title: localize2('reset', "Reset Language Status Interaction Counter"), - category: Categories.View, - f1: true - }); - } - - run(accessor: ServicesAccessor): void { - accessor.get(IStorageService).remove('languageStatus.interactCount', StorageScope.PROFILE); - } -}); +registerWorkbenchContribution2(LanguageStatusContribution.Id, LanguageStatusContribution, WorkbenchPhase.AfterRestored); +registerAction2(ResetAction); diff --git a/src/vs/workbench/contrib/languageStatus/browser/languageStatus.ts b/src/vs/workbench/contrib/languageStatus/browser/languageStatus.ts new file mode 100644 index 000000000000..17bff692d742 --- /dev/null +++ b/src/vs/workbench/contrib/languageStatus/browser/languageStatus.ts @@ -0,0 +1,447 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import './media/languageStatus.css'; +import * as dom from '../../../../base/browser/dom.js'; +import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/iconLabels.js'; +import { Disposable, DisposableStore, dispose, toDisposable } from '../../../../base/common/lifecycle.js'; +import Severity from '../../../../base/common/severity.js'; +import { getCodeEditor, ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; +import { localize, localize2 } from '../../../../nls.js'; +import { ThemeIcon } from '../../../../base/common/themables.js'; +import { IWorkbenchContribution } from '../../../common/contributions.js'; +import { IEditorService } from '../../../services/editor/common/editorService.js'; +import { ILanguageStatus, ILanguageStatusService } from '../../../services/languageStatus/common/languageStatusService.js'; +import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, ShowTooltipCommand, StatusbarAlignment, StatusbarEntryKind } from '../../../services/statusbar/browser/statusbar.js'; +import { parseLinkedText } from '../../../../base/common/linkedText.js'; +import { Link } from '../../../../platform/opener/browser/link.js'; +import { IOpenerService } from '../../../../platform/opener/common/opener.js'; +import { MarkdownString } from '../../../../base/common/htmlContent.js'; +import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js'; +import { Action } from '../../../../base/common/actions.js'; +import { Codicon } from '../../../../base/common/codicons.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +import { equals } from '../../../../base/common/arrays.js'; +import { URI } from '../../../../base/common/uri.js'; +import { Action2 } from '../../../../platform/actions/common/actions.js'; +import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; +import { IAccessibilityInformation } from '../../../../platform/accessibility/common/accessibility.js'; +import { IEditorGroupsService, IEditorPart } from '../../../services/editor/common/editorGroupsService.js'; +import { IHoverService, nativeHoverDelegate } from '../../../../platform/hover/browser/hover.js'; +import { Event } from '../../../../base/common/event.js'; +import { joinStrings } from '../../../../base/common/strings.js'; + +class LanguageStatusViewModel { + + constructor( + readonly combined: readonly ILanguageStatus[], + readonly dedicated: readonly ILanguageStatus[] + ) { } + + isEqual(other: LanguageStatusViewModel) { + return equals(this.combined, other.combined) && equals(this.dedicated, other.dedicated); + } +} + +class StoredCounter { + + constructor(@IStorageService private readonly _storageService: IStorageService, private readonly _key: string) { } + + get value() { + return this._storageService.getNumber(this._key, StorageScope.PROFILE, 0); + } + + increment(): number { + const n = this.value + 1; + this._storageService.store(this._key, n, StorageScope.PROFILE, StorageTarget.MACHINE); + return n; + } +} + +export class LanguageStatusContribution extends Disposable implements IWorkbenchContribution { + + static readonly Id = 'status.languageStatus'; + + constructor( + @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, + ) { + super(); + + for (const part of editorGroupService.parts) { + this.createLanguageStatus(part); + } + + this._register(editorGroupService.onDidCreateAuxiliaryEditorPart(part => this.createLanguageStatus(part))); + } + + private createLanguageStatus(part: IEditorPart): void { + const disposables = new DisposableStore(); + Event.once(part.onWillDispose)(() => disposables.dispose()); + + const scopedInstantiationService = this.editorGroupService.getScopedInstantiationService(part); + disposables.add(scopedInstantiationService.createInstance(LanguageStatus)); + } +} + +class LanguageStatus { + + private static readonly _id = 'status.languageStatus'; + + private static readonly _keyDedicatedItems = 'languageStatus.dedicated'; + + private readonly _disposables = new DisposableStore(); + private readonly _interactionCounter: StoredCounter; + + private _dedicated = new Set(); + + private _model?: LanguageStatusViewModel; + private _combinedEntry?: IStatusbarEntryAccessor; + private _dedicatedEntries = new Map(); + private readonly _renderDisposables = new DisposableStore(); + + private readonly _combinedEntryTooltip = document.createElement('div'); + + constructor( + @ILanguageStatusService private readonly _languageStatusService: ILanguageStatusService, + @IStatusbarService private readonly _statusBarService: IStatusbarService, + @IEditorService private readonly _editorService: IEditorService, + @IHoverService private readonly _hoverService: IHoverService, + @IOpenerService private readonly _openerService: IOpenerService, + @IStorageService private readonly _storageService: IStorageService, + ) { + _storageService.onDidChangeValue(StorageScope.PROFILE, LanguageStatus._keyDedicatedItems, this._disposables)(this._handleStorageChange, this, this._disposables); + this._restoreState(); + this._interactionCounter = new StoredCounter(_storageService, 'languageStatus.interactCount'); + + _languageStatusService.onDidChange(this._update, this, this._disposables); + _editorService.onDidActiveEditorChange(this._update, this, this._disposables); + this._update(); + + _statusBarService.onDidChangeEntryVisibility(e => { + if (!e.visible && this._dedicated.has(e.id)) { + this._dedicated.delete(e.id); + this._update(); + this._storeState(); + } + }, undefined, this._disposables); + + } + + dispose(): void { + this._disposables.dispose(); + this._combinedEntry?.dispose(); + dispose(this._dedicatedEntries.values()); + this._renderDisposables.dispose(); + } + + // --- persisting dedicated items + + private _handleStorageChange() { + this._restoreState(); + this._update(); + } + + private _restoreState(): void { + const raw = this._storageService.get(LanguageStatus._keyDedicatedItems, StorageScope.PROFILE, '[]'); + try { + const ids = JSON.parse(raw); + this._dedicated = new Set(ids); + } catch { + this._dedicated.clear(); + } + } + + private _storeState(): void { + if (this._dedicated.size === 0) { + this._storageService.remove(LanguageStatus._keyDedicatedItems, StorageScope.PROFILE); + } else { + const raw = JSON.stringify(Array.from(this._dedicated.keys())); + this._storageService.store(LanguageStatus._keyDedicatedItems, raw, StorageScope.PROFILE, StorageTarget.USER); + } + } + + // --- language status model and UI + + private _createViewModel(editor: ICodeEditor | null): LanguageStatusViewModel { + if (!editor?.hasModel()) { + return new LanguageStatusViewModel([], []); + } + const all = this._languageStatusService.getLanguageStatus(editor.getModel()); + const combined: ILanguageStatus[] = []; + const dedicated: ILanguageStatus[] = []; + for (const item of all) { + if (this._dedicated.has(item.id)) { + dedicated.push(item); + } + combined.push(item); + } + return new LanguageStatusViewModel(combined, dedicated); + } + + private _update(): void { + const editor = getCodeEditor(this._editorService.activeTextEditorControl); + const model = this._createViewModel(editor); + + if (this._model?.isEqual(model)) { + return; + } + this._renderDisposables.clear(); + + this._model = model; + + // update when editor language changes + editor?.onDidChangeModelLanguage(this._update, this, this._renderDisposables); + + // combined status bar item is a single item which hover shows + // each status item + if (model.combined.length === 0) { + // nothing + this._combinedEntry?.dispose(); + this._combinedEntry = undefined; + + } else { + const [first] = model.combined; + const showSeverity = first.severity >= Severity.Warning; + const text = LanguageStatus._severityToComboCodicon(first.severity); + + let isOneBusy = false; + const ariaLabels: string[] = []; + for (const status of model.combined) { + const isPinned = model.dedicated.includes(status); + this._renderStatus(this._combinedEntryTooltip, status, showSeverity, isPinned, this._renderDisposables); + ariaLabels.push(LanguageStatus._accessibilityInformation(status).label); + isOneBusy = isOneBusy || (!isPinned && status.busy); // unpinned items contribute to the busy-indicator of the composite status item + } + + const props: IStatusbarEntry = { + name: localize('langStatus.name', "Editor Language Status"), + ariaLabel: localize('langStatus.aria', "Editor Language Status: {0}", ariaLabels.join(', next: ')), + tooltip: this._combinedEntryTooltip, + command: ShowTooltipCommand, + text: isOneBusy ? '$(loading~spin)' : text, + }; + if (!this._combinedEntry) { + this._combinedEntry = this._statusBarService.addEntry(props, LanguageStatus._id, StatusbarAlignment.RIGHT, { location: { id: 'status.editor.mode', priority: 100.1 }, alignment: StatusbarAlignment.LEFT, compact: true }); + } else { + this._combinedEntry.update(props); + } + + // animate the status bar icon whenever language status changes, repeat animation + // when severity is warning or error, don't show animation when showing progress/busy + const userHasInteractedWithStatus = this._interactionCounter.value >= 3; + const targetWindow = dom.getWindow(editor?.getContainerDomNode()); + const node = targetWindow.document.querySelector('.monaco-workbench .statusbar DIV#status\\.languageStatus A>SPAN.codicon'); + const container = targetWindow.document.querySelector('.monaco-workbench .statusbar DIV#status\\.languageStatus'); + if (dom.isHTMLElement(node) && container) { + const _wiggle = 'wiggle'; + const _flash = 'flash'; + if (!isOneBusy) { + // wiggle icon when severe or "new" + node.classList.toggle(_wiggle, showSeverity || !userHasInteractedWithStatus); + this._renderDisposables.add(dom.addDisposableListener(node, 'animationend', _e => node.classList.remove(_wiggle))); + // flash background when severe + container.classList.toggle(_flash, showSeverity); + this._renderDisposables.add(dom.addDisposableListener(container, 'animationend', _e => container.classList.remove(_flash))); + } else { + node.classList.remove(_wiggle); + container.classList.remove(_flash); + } + } + + // track when the hover shows (this is automagic and DOM mutation spying is needed...) + // use that as signal that the user has interacted/learned language status items work + if (!userHasInteractedWithStatus) { + const hoverTarget = targetWindow.document.querySelector('.monaco-workbench .context-view'); + if (dom.isHTMLElement(hoverTarget)) { + const observer = new MutationObserver(() => { + if (targetWindow.document.contains(this._combinedEntryTooltip)) { + this._interactionCounter.increment(); + observer.disconnect(); + } + }); + observer.observe(hoverTarget, { childList: true, subtree: true }); + this._renderDisposables.add(toDisposable(() => observer.disconnect())); + } + } + } + + // dedicated status bar items are shows as-is in the status bar + const newDedicatedEntries = new Map(); + for (const status of model.dedicated) { + const props = LanguageStatus._asStatusbarEntry(status); + let entry = this._dedicatedEntries.get(status.id); + if (!entry) { + entry = this._statusBarService.addEntry(props, status.id, StatusbarAlignment.RIGHT, { location: { id: 'status.editor.mode', priority: 100.1 }, alignment: StatusbarAlignment.RIGHT }); + } else { + entry.update(props); + this._dedicatedEntries.delete(status.id); + } + newDedicatedEntries.set(status.id, entry); + } + dispose(this._dedicatedEntries.values()); + this._dedicatedEntries = newDedicatedEntries; + } + + private _renderStatus(container: HTMLElement, status: ILanguageStatus, showSeverity: boolean, isPinned: boolean, store: DisposableStore): HTMLElement { + + const parent = document.createElement('div'); + parent.classList.add('hover-language-status'); + + container.appendChild(parent); + store.add(toDisposable(() => parent.remove())); + + const severity = document.createElement('div'); + severity.classList.add('severity', `sev${status.severity}`); + severity.classList.toggle('show', showSeverity); + const severityText = LanguageStatus._severityToSingleCodicon(status.severity); + dom.append(severity, ...renderLabelWithIcons(severityText)); + parent.appendChild(severity); + + const element = document.createElement('div'); + element.classList.add('element'); + parent.appendChild(element); + + const left = document.createElement('div'); + left.classList.add('left'); + element.appendChild(left); + + const label = document.createElement('span'); + label.classList.add('label'); + const labelValue = typeof status.label === 'string' ? status.label : status.label.value; + dom.append(label, ...renderLabelWithIcons(computeText(labelValue, status.busy))); + left.appendChild(label); + + const detail = document.createElement('span'); + detail.classList.add('detail'); + this._renderTextPlus(detail, status.detail, store); + left.appendChild(detail); + + const right = document.createElement('div'); + right.classList.add('right'); + element.appendChild(right); + + // -- command (if available) + const { command } = status; + if (command) { + store.add(new Link(right, { + label: command.title, + title: command.tooltip, + href: URI.from({ + scheme: 'command', path: command.id, query: command.arguments && JSON.stringify(command.arguments) + }).toString() + }, { hoverDelegate: nativeHoverDelegate }, this._hoverService, this._openerService)); + } + + // -- pin + const actionBar = new ActionBar(right, { hoverDelegate: nativeHoverDelegate }); + const actionLabel: string = isPinned ? localize('unpin', "Remove from Status Bar") : localize('pin', "Add to Status Bar"); + actionBar.setAriaLabel(actionLabel); + store.add(actionBar); + let action: Action; + if (!isPinned) { + action = new Action('pin', actionLabel, ThemeIcon.asClassName(Codicon.pin), true, () => { + this._dedicated.add(status.id); + this._statusBarService.updateEntryVisibility(status.id, true); + this._update(); + this._storeState(); + }); + } else { + action = new Action('unpin', actionLabel, ThemeIcon.asClassName(Codicon.pinned), true, () => { + this._dedicated.delete(status.id); + this._statusBarService.updateEntryVisibility(status.id, false); + this._update(); + this._storeState(); + }); + } + actionBar.push(action, { icon: true, label: false }); + store.add(action); + + return parent; + } + + private static _severityToComboCodicon(sev: Severity): string { + switch (sev) { + case Severity.Error: return '$(bracket-error)'; + case Severity.Warning: return '$(bracket-dot)'; + default: return '$(bracket)'; + } + } + + private static _severityToSingleCodicon(sev: Severity): string { + switch (sev) { + case Severity.Error: return '$(error)'; + case Severity.Warning: return '$(info)'; + default: return '$(check)'; + } + } + + private _renderTextPlus(target: HTMLElement, text: string, store: DisposableStore): void { + for (const node of parseLinkedText(text).nodes) { + if (typeof node === 'string') { + const parts = renderLabelWithIcons(node); + dom.append(target, ...parts); + } else { + store.add(new Link(target, node, undefined, this._hoverService, this._openerService)); + } + } + } + + private static _accessibilityInformation(status: ILanguageStatus): IAccessibilityInformation { + if (status.accessibilityInfo) { + return status.accessibilityInfo; + } + const textValue = typeof status.label === 'string' ? status.label : status.label.value; + if (status.detail) { + return { label: localize('aria.1', '{0}, {1}', textValue, status.detail) }; + } else { + return { label: localize('aria.2', '{0}', textValue) }; + } + } + + // --- + + private static _asStatusbarEntry(item: ILanguageStatus): IStatusbarEntry { + + let kind: StatusbarEntryKind | undefined; + if (item.severity === Severity.Warning) { + kind = 'warning'; + } else if (item.severity === Severity.Error) { + kind = 'error'; + } + + const textValue = typeof item.label === 'string' ? item.label : item.label.shortValue; + + return { + name: localize('name.pattern', '{0} (Language Status)', item.name), + text: computeText(textValue, item.busy), + ariaLabel: LanguageStatus._accessibilityInformation(item).label, + role: item.accessibilityInfo?.role, + tooltip: item.command?.tooltip || new MarkdownString(item.detail, { isTrusted: true, supportThemeIcons: true }), + kind, + command: item.command + }; + } +} + +export class ResetAction extends Action2 { + + constructor() { + super({ + id: 'editor.inlayHints.Reset', + title: localize2('reset', "Reset Language Status Interaction Counter"), + category: Categories.View, + f1: true + }); + } + + run(accessor: ServicesAccessor): void { + accessor.get(IStorageService).remove('languageStatus.interactCount', StorageScope.PROFILE); + } +} + +function computeText(text: string, loading: boolean): string { + return joinStrings([text !== '' && text, loading && '$(loading~spin)'], '\u00A0\u00A0'); +} diff --git a/src/vs/workbench/contrib/limitIndicator/browser/limitIndicator.contribution.ts b/src/vs/workbench/contrib/limitIndicator/browser/limitIndicator.contribution.ts index 538e67d5f0fb..0640e07026d3 100644 --- a/src/vs/workbench/contrib/limitIndicator/browser/limitIndicator.contribution.ts +++ b/src/vs/workbench/contrib/limitIndicator/browser/limitIndicator.contribution.ts @@ -75,7 +75,7 @@ interface LanguageFeatureAccessor { class ColorDecorationAccessor implements LanguageFeatureAccessor { readonly id = 'decoratorsLimitInfo'; readonly name = nls.localize('colorDecoratorsStatusItem.name', 'Color Decorator Status'); - readonly label = nls.localize('status.limitedColorDecorators.short', 'Color Decorators'); + readonly label = nls.localize('status.limitedColorDecorators.short', 'Color decorators'); readonly source = nls.localize('colorDecoratorsStatusItem.source', 'Color Decorators'); readonly settingsId = 'editor.colorDecoratorsLimit'; @@ -87,7 +87,7 @@ class ColorDecorationAccessor implements LanguageFeatureAccessor { class FoldingRangeAccessor implements LanguageFeatureAccessor { readonly id = 'foldingLimitInfo'; readonly name = nls.localize('foldingRangesStatusItem.name', 'Folding Status'); - readonly label = nls.localize('status.limitedFoldingRanges.short', 'Folding Ranges'); + readonly label = nls.localize('status.limitedFoldingRanges.short', 'Folding ranges'); readonly source = nls.localize('foldingRangesStatusItem.source', 'Folding'); readonly settingsId = 'editor.foldingMaximumRegions'; diff --git a/src/vs/workbench/contrib/localHistory/browser/localHistory.ts b/src/vs/workbench/contrib/localHistory/browser/localHistory.ts index 0ca0ac192497..7ad95e421207 100644 --- a/src/vs/workbench/contrib/localHistory/browser/localHistory.ts +++ b/src/vs/workbench/contrib/localHistory/browser/localHistory.ts @@ -8,6 +8,7 @@ import { Codicon } from '../../../../base/common/codicons.js'; import { language } from '../../../../base/common/platform.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; +import { safeIntl } from '../../../../base/common/date.js'; interface ILocalHistoryDateFormatter { format: (timestamp: number) => string; @@ -18,14 +19,7 @@ let localHistoryDateFormatter: ILocalHistoryDateFormatter | undefined = undefine export function getLocalHistoryDateFormatter(): ILocalHistoryDateFormatter { if (!localHistoryDateFormatter) { const options: Intl.DateTimeFormatOptions = { year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' }; - - let formatter: Intl.DateTimeFormat; - try { - formatter = new Intl.DateTimeFormat(language, options); - } catch (error) { - formatter = new Intl.DateTimeFormat(undefined, options); // error can happen when language is invalid (https://github.com/microsoft/vscode/issues/147086) - } - + const formatter = safeIntl.DateTimeFormat(language, options); localHistoryDateFormatter = { format: date => formatter.format(date) }; diff --git a/src/vs/workbench/contrib/logs/common/defaultLogLevels.ts b/src/vs/workbench/contrib/logs/common/defaultLogLevels.ts index 1302428ebdfb..4d6ba9f5e01d 100644 --- a/src/vs/workbench/contrib/logs/common/defaultLogLevels.ts +++ b/src/vs/workbench/contrib/logs/common/defaultLogLevels.ts @@ -38,8 +38,6 @@ export interface IDefaultLogLevelsService { getDefaultLogLevel(extensionId?: string): Promise; setDefaultLogLevel(logLevel: LogLevel, extensionId?: string): Promise; - - migrateLogLevels(): void; } class DefaultLogLevelsService extends Disposable implements IDefaultLogLevelsService { @@ -148,17 +146,6 @@ class DefaultLogLevelsService extends Disposable implements IDefaultLogLevelsSer return !isUndefined(result.default) || result.extensions?.length ? result : undefined; } - async migrateLogLevels(): Promise { - const logLevels = await this._readLogLevelsFromArgv(); - const regex = /^([^.]+\..+):(.+)$/; - if (logLevels.some(extensionLogLevel => regex.test(extensionLogLevel))) { - const argvLogLevel = await this._parseLogLevelsFromArgv(); - if (argvLogLevel) { - await this._writeLogLevelsToArgv(argvLogLevel); - } - } - } - private async _readLogLevelsFromArgv(): Promise { try { const content = await this.fileService.readFile(this.environmentService.argvResource); diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index 4901650bec94..05b5f7a03299 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -9,18 +9,13 @@ import { Categories } from '../../../../platform/action/common/actionCommonCateg import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { SetLogLevelAction } from './logsActions.js'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from '../../../common/contributions.js'; -import { IFileService, whenProviderRegistered } from '../../../../platform/files/common/files.js'; -import { IOutputChannelRegistry, IOutputService, Extensions } from '../../../services/output/common/output.js'; -import { Disposable, DisposableMap, DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js'; -import { CONTEXT_LOG_LEVEL, ILogService, ILoggerResource, ILoggerService, LogLevel, LogLevelToString, isLogLevel } from '../../../../platform/log/common/log.js'; +import { IOutputChannelRegistry, IOutputService, Extensions, isMultiSourceOutputChannelDescriptor, isSingleSourceOutputChannelDescriptor } from '../../../services/output/common/output.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { CONTEXT_LOG_LEVEL, ILoggerResource, ILoggerService, LogLevel, LogLevelToString, isLogLevel } from '../../../../platform/log/common/log.js'; import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; -import { URI } from '../../../../base/common/uri.js'; import { Event } from '../../../../base/common/event.js'; import { windowLogId, showWindowLogActionId } from '../../../services/log/common/logConstants.js'; -import { createCancelablePromise, timeout } from '../../../../base/common/async.js'; -import { CancellationError, getErrorMessage, isCancellationError } from '../../../../base/common/errors.js'; -import { CancellationToken } from '../../../../base/common/cancellation.js'; import { IDefaultLogLevelsService } from './defaultLogLevels.js'; import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { CounterSet } from '../../../../base/common/map.js'; @@ -58,13 +53,10 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { private readonly contextKeys = new CounterSet(); private readonly outputChannelRegistry = Registry.as(Extensions.OutputChannels); - private readonly loggerDisposables = this._register(new DisposableMap()); constructor( - @ILogService private readonly logService: ILogService, @ILoggerService private readonly loggerService: ILoggerService, @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IFileService private readonly fileService: IFileService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, ) { super(); @@ -142,56 +134,48 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { } private registerLogChannel(logger: ILoggerResource): void { + if (logger.group) { + this.registerCompoundLogChannel(logger.group.id, logger.group.name, logger); + return; + } + const channel = this.outputChannelRegistry.getChannel(logger.id); - if (channel && this.uriIdentityService.extUri.isEqual(channel.file, logger.resource)) { + if (channel && isSingleSourceOutputChannelDescriptor(channel) && this.uriIdentityService.extUri.isEqual(channel.source.resource, logger.resource)) { return; } - const disposables = new DisposableStore(); - const promise = createCancelablePromise(async token => { - await whenProviderRegistered(logger.resource, this.fileService); - try { - await this.whenFileExists(logger.resource, 1, token); - const existingChannel = this.outputChannelRegistry.getChannel(logger.id); - const remoteLogger = existingChannel?.file?.scheme === Schemas.vscodeRemote ? this.loggerService.getRegisteredLogger(existingChannel.file) : undefined; - if (remoteLogger) { - this.deregisterLogChannel(remoteLogger); - } - const hasToAppendRemote = existingChannel && logger.resource.scheme === Schemas.vscodeRemote; - const id = hasToAppendRemote ? `${logger.id}.remote` : logger.id; - const label = hasToAppendRemote ? nls.localize('remote name', "{0} (Remote)", logger.name ?? logger.id) : logger.name ?? logger.id; - this.outputChannelRegistry.registerChannel({ id, label, file: logger.resource, log: true, extensionId: logger.extensionId }); - disposables.add(toDisposable(() => this.outputChannelRegistry.removeChannel(id))); - if (remoteLogger) { - this.registerLogChannel(remoteLogger); - } - } catch (error) { - if (!isCancellationError(error)) { - this.logService.error('Error while registering log channel', logger.resource.toString(), getErrorMessage(error)); - } - } - }); - disposables.add(toDisposable(() => promise.cancel())); - this.loggerDisposables.set(logger.resource.toString(), disposables); - } - private deregisterLogChannel(logger: ILoggerResource): void { - this.loggerDisposables.deleteAndDispose(logger.resource.toString()); + const existingChannel = this.outputChannelRegistry.getChannel(logger.id); + const remoteLogger = existingChannel && isSingleSourceOutputChannelDescriptor(existingChannel) && existingChannel.source.resource.scheme === Schemas.vscodeRemote ? this.loggerService.getRegisteredLogger(existingChannel.source.resource) : undefined; + if (remoteLogger) { + this.deregisterLogChannel(remoteLogger); + } + const hasToAppendRemote = existingChannel && logger.resource.scheme === Schemas.vscodeRemote; + const id = hasToAppendRemote ? `${logger.id}.remote` : logger.id; + const label = hasToAppendRemote ? nls.localize('remote name', "{0} (Remote)", logger.name ?? logger.id) : logger.name ?? logger.id; + this.outputChannelRegistry.registerChannel({ id, label, source: { resource: logger.resource }, log: true, extensionId: logger.extensionId }); } - private async whenFileExists(file: URI, trial: number, token: CancellationToken): Promise { - const exists = await this.fileService.exists(file); - if (exists) { - return; - } - if (token.isCancellationRequested) { - throw new CancellationError(); + private registerCompoundLogChannel(id: string, name: string, logger: ILoggerResource): void { + const channel = this.outputChannelRegistry.getChannel(id); + const source = { resource: logger.resource, name: logger.name ?? logger.id }; + if (channel) { + if (isMultiSourceOutputChannelDescriptor(channel) && !channel.source.some(({ resource }) => this.uriIdentityService.extUri.isEqual(resource, logger.resource))) { + this.outputChannelRegistry.updateChannelSources(id, [...channel.source, source]); + } + } else { + this.outputChannelRegistry.registerChannel({ id, label: name, log: true, source: [source] }); } - if (trial > 10) { - throw new Error(`Timed out while waiting for file to be created`); + } + + private deregisterLogChannel(logger: ILoggerResource): void { + if (logger.group) { + const channel = this.outputChannelRegistry.getChannel(logger.group.id); + if (channel && isMultiSourceOutputChannelDescriptor(channel)) { + this.outputChannelRegistry.updateChannelSources(logger.group.id, channel.source.filter(({ resource }) => !this.uriIdentityService.extUri.isEqual(resource, logger.resource))); + } + } else { + this.outputChannelRegistry.removeChannel(logger.id); } - this.logService.debug(`[Registering Log Channel] File does not exist. Waiting for 1s to retry.`, file.toString()); - await timeout(1000, token); - await this.whenFileExists(file, trial + 1, token); } private registerShowWindowLogAction(): void { @@ -212,13 +196,4 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { } } -class LogLevelMigration implements IWorkbenchContribution { - constructor( - @IDefaultLogLevelsService defaultLogLevelsService: IDefaultLogLevelsService - ) { - defaultLogLevelsService.migrateLogLevels(); - } -} - Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LogOutputChannels, LifecyclePhase.Restored); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LogLevelMigration, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/logs/common/logsActions.ts b/src/vs/workbench/contrib/logs/common/logsActions.ts index c28c6ca91406..23bf63df8916 100644 --- a/src/vs/workbench/contrib/logs/common/logsActions.ts +++ b/src/vs/workbench/contrib/logs/common/logsActions.ts @@ -12,15 +12,14 @@ import { IFileService } from '../../../../platform/files/common/files.js'; import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; import { dirname, basename, isEqual } from '../../../../base/common/resources.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; -import { IOutputChannelDescriptor, IOutputService } from '../../../services/output/common/output.js'; -import { extensionTelemetryLogChannelId, telemetryLogId } from '../../../../platform/telemetry/common/telemetryUtils.js'; +import { IOutputChannelDescriptor, IOutputService, isMultiSourceOutputChannelDescriptor, isSingleSourceOutputChannelDescriptor } from '../../../services/output/common/output.js'; import { IDefaultLogLevelsService } from './defaultLogLevels.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; type LogLevelQuickPickItem = IQuickPickItem & { level: LogLevel }; -type LogChannelQuickPickItem = IQuickPickItem & { id: string; resource: URI; extensionId?: string }; +type LogChannelQuickPickItem = IQuickPickItem & { id: string; channel: IOutputChannelDescriptor }; export class SetLogLevelAction extends Action { @@ -52,11 +51,20 @@ export class SetLogLevelAction extends Action { const extensionLogs: LogChannelQuickPickItem[] = [], logs: LogChannelQuickPickItem[] = []; const logLevel = this.loggerService.getLogLevel(); for (const channel of this.outputService.getChannelDescriptors()) { - if (!SetLogLevelAction.isLevelSettable(channel) || !channel.file) { + if (!this.outputService.canSetLogLevel(channel)) { continue; } - const channelLogLevel = this.loggerService.getLogLevel(channel.file) ?? logLevel; - const item: LogChannelQuickPickItem = { id: channel.id, resource: channel.file, label: channel.label, description: channelLogLevel !== logLevel ? this.getLabel(channelLogLevel) : undefined, extensionId: channel.extensionId }; + const sources = isSingleSourceOutputChannelDescriptor(channel) ? [channel.source] : isMultiSourceOutputChannelDescriptor(channel) ? channel.source : []; + if (!sources.length) { + continue; + } + const channelLogLevel = sources.reduce((prev, curr) => Math.min(prev, this.loggerService.getLogLevel(curr.resource) ?? logLevel), logLevel); + const item: LogChannelQuickPickItem = { + id: channel.id, + label: channel.label, + description: channelLogLevel !== logLevel ? this.getLabel(channelLogLevel) : undefined, + channel + }; if (channel.extensionId) { extensionLogs.push(item); } else { @@ -96,15 +104,10 @@ export class SetLogLevelAction extends Action { }); } - static isLevelSettable(channel: IOutputChannelDescriptor): boolean { - return channel.log && channel.file !== undefined && channel.id !== telemetryLogId && channel.id !== extensionTelemetryLogChannelId; - } - private async setLogLevelForChannel(logChannel: LogChannelQuickPickItem): Promise { const defaultLogLevels = await this.defaultLogLevelsService.getDefaultLogLevels(); - const defaultLogLevel = defaultLogLevels.extensions.find(e => e[0] === logChannel.extensionId?.toLowerCase())?.[1] ?? defaultLogLevels.default; - const currentLogLevel = this.loggerService.getLogLevel(logChannel.resource) ?? defaultLogLevel; - const entries = this.getLogLevelEntries(defaultLogLevel, currentLogLevel, !!logChannel.extensionId); + const defaultLogLevel = defaultLogLevels.extensions.find(e => e[0] === logChannel.channel.extensionId?.toLowerCase())?.[1] ?? defaultLogLevels.default; + const entries = this.getLogLevelEntries(defaultLogLevel, this.outputService.getLogLevel(logChannel.channel) ?? defaultLogLevel, !!logChannel.channel.extensionId); return new Promise((resolve, reject) => { const disposables = new DisposableStore(); @@ -115,7 +118,7 @@ export class SetLogLevelAction extends Action { let selectedItem: LogLevelQuickPickItem | undefined; disposables.add(quickPick.onDidTriggerItemButton(e => { quickPick.hide(); - this.defaultLogLevelsService.setDefaultLogLevel((e.item).level, logChannel.extensionId); + this.defaultLogLevelsService.setDefaultLogLevel((e.item).level, logChannel.channel.extensionId); })); disposables.add(quickPick.onDidAccept(e => { selectedItem = quickPick.selectedItems[0] as LogLevelQuickPickItem; @@ -123,7 +126,7 @@ export class SetLogLevelAction extends Action { })); disposables.add(quickPick.onDidHide(() => { if (selectedItem) { - this.loggerService.setLogLevel(logChannel.resource, selectedItem.level); + this.outputService.setLogLevel(logChannel.channel, selectedItem.level); } disposables.dispose(); resolve(); diff --git a/src/vs/workbench/contrib/mappedEdits/common/mappedEdits.contribution.ts b/src/vs/workbench/contrib/mappedEdits/common/mappedEdits.contribution.ts deleted file mode 100644 index 3470efba634f..000000000000 --- a/src/vs/workbench/contrib/mappedEdits/common/mappedEdits.contribution.ts +++ /dev/null @@ -1,51 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { CancellationTokenSource } from '../../../../base/common/cancellation.js'; -import { URI } from '../../../../base/common/uri.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; -import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; -import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; -import * as languages from '../../../../editor/common/languages.js'; - -CommandsRegistry.registerCommand( - '_executeMappedEditsProvider', - async ( - accessor: ServicesAccessor, - documentUri: URI, - codeBlocks: string[], - context: languages.MappedEditsContext - ): Promise => { - - const modelService = accessor.get(ITextModelService); - const langFeaturesService = accessor.get(ILanguageFeaturesService); - - const document = await modelService.createModelReference(documentUri); - - let result: languages.WorkspaceEdit | null = null; - - try { - const providers = langFeaturesService.mappedEditsProvider.ordered(document.object.textEditorModel); - - if (providers.length > 0) { - const mostRelevantProvider = providers[0]; - - const cancellationTokenSource = new CancellationTokenSource(); - - result = await mostRelevantProvider.provideMappedEdits( - document.object.textEditorModel, - codeBlocks, - context, - cancellationTokenSource.token - ); - } - } finally { - document.dispose(); - } - - return result; - } -); diff --git a/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts b/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts index 585e5f0833ab..e80722d7993d 100644 --- a/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts +++ b/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { basicMarkupHtmlTags, hookDomPurifyHrefAndSrcSanitizer } from '../../../../base/browser/dom.js'; -import * as dompurify from '../../../../base/browser/dompurify/dompurify.js'; +import dompurify from '../../../../base/browser/dompurify/dompurify.js'; import { allowedMarkdownAttr } from '../../../../base/browser/markdownRenderer.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import * as marked from '../../../../base/common/marked/marked.js'; @@ -234,7 +234,7 @@ export async function renderMarkdownDocument( namespace MarkedHighlight { // Copied from https://github.com/markedjs/marked-highlight/blob/main/src/index.js - export function markedHighlight(options: marked.MarkedOptions & { highlight: (code: string, lang: string, info: string) => string | Promise }): marked.MarkedExtension { + export function markedHighlight(options: marked.MarkedOptions & { highlight: (code: string, lang: string) => string | Promise }): marked.MarkedExtension { if (typeof options === 'function') { options = { highlight: options, @@ -252,13 +252,11 @@ namespace MarkedHighlight { return; } - const lang = getLang(token.lang); - if (options.async) { - return Promise.resolve(options.highlight(token.text, lang, token.lang || '')).then(updateToken(token)); + return Promise.resolve(options.highlight(token.text, token.lang)).then(updateToken(token)); } - const code = options.highlight(token.text, lang, token.lang || ''); + const code = options.highlight(token.text, token.lang); if (code instanceof Promise) { throw new Error('markedHighlight is not set to async but the highlight function is async. Set the async option to true on markedHighlight to await the async highlight function.'); } @@ -276,10 +274,6 @@ namespace MarkedHighlight { }; } - function getLang(lang: string) { - return (lang || '').match(/\S*/)![0]; - } - function updateToken(token: any) { return (code: string) => { if (typeof code === 'string' && code !== token.text) { diff --git a/src/vs/workbench/contrib/markdown/browser/markdownSettingRenderer.ts b/src/vs/workbench/contrib/markdown/browser/markdownSettingRenderer.ts index c2d223869d9f..5cf78c4d4759 100644 --- a/src/vs/workbench/contrib/markdown/browser/markdownSettingRenderer.ts +++ b/src/vs/workbench/contrib/markdown/browser/markdownSettingRenderer.ts @@ -121,7 +121,7 @@ export class SimpleSettingRenderer { private render(settingId: string, newValue: string): string | undefined { const setting = this.getSetting(settingId); if (!setting) { - return ''; + return `${settingId}`; } return this.renderSetting(setting, newValue); @@ -299,7 +299,7 @@ export class SimpleSettingRenderer { if (uri.scheme === Schemas.codeSetting) { type ReleaseNotesSettingUsedClassification = { owner: 'alexr00'; - comment: 'Used to understand if the the action to update settings from the release notes is used.'; + comment: 'Used to understand if the action to update settings from the release notes is used.'; settingId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The id of the setting that was clicked on in the release notes' }; }; type ReleaseNotesSettingUsed = { diff --git a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts index 5c1b30c121ae..6543d59ee29a 100644 --- a/src/vs/workbench/contrib/markers/browser/markers.contribution.ts +++ b/src/vs/workbench/contrib/markers/browser/markers.contribution.ts @@ -140,7 +140,7 @@ Registry.as(ViewContainerExtensions.ViewsRegistry).registerViews id: Markers.MARKERS_VIEW_ID, containerIcon: markersViewIcon, name: Messages.MARKERS_PANEL_TITLE_PROBLEMS, - canToggleVisibility: false, + canToggleVisibility: true, canMoveView: true, ctorDescriptor: new SyncDescriptor(MarkersView), openCommandActionDescriptor: { @@ -160,6 +160,9 @@ registerAction2(class extends ViewAction { super({ id: `workbench.actions.table.${Markers.MARKERS_VIEW_ID}.viewAsTree`, title: localize('viewAsTree', "View as Tree"), + metadata: { + description: localize2('viewAsTreeDescription', "Show the problems view as a tree.") + }, menu: { id: MenuId.ViewTitle, when: ContextKeyExpr.and(ContextKeyExpr.equals('view', Markers.MARKERS_VIEW_ID), MarkersContextKeys.MarkersViewModeContextKey.isEqualTo(MarkersViewMode.Table)), @@ -181,6 +184,9 @@ registerAction2(class extends ViewAction { super({ id: `workbench.actions.table.${Markers.MARKERS_VIEW_ID}.viewAsTable`, title: localize('viewAsTable', "View as Table"), + metadata: { + description: localize2('viewAsTableDescription', "Show the problems view as a table.") + }, menu: { id: MenuId.ViewTitle, when: ContextKeyExpr.and(ContextKeyExpr.equals('view', Markers.MARKERS_VIEW_ID), MarkersContextKeys.MarkersViewModeContextKey.isEqualTo(MarkersViewMode.Tree)), @@ -202,6 +208,9 @@ registerAction2(class extends ViewAction { super({ id: `workbench.actions.${Markers.MARKERS_VIEW_ID}.toggleErrors`, title: localize('show errors', "Show Errors"), + metadata: { + description: localize2('toggleErrorsDescription', "Show or hide errors in the problems view.") + }, category: localize('problems', "Problems"), toggled: MarkersContextKeys.ShowErrorsFilterContextKey, menu: { @@ -224,6 +233,9 @@ registerAction2(class extends ViewAction { super({ id: `workbench.actions.${Markers.MARKERS_VIEW_ID}.toggleWarnings`, title: localize('show warnings', "Show Warnings"), + metadata: { + description: localize2('toggleWarningsDescription', "Show or hide warnings in the problems view.") + }, category: localize('problems', "Problems"), toggled: MarkersContextKeys.ShowWarningsFilterContextKey, menu: { @@ -248,6 +260,9 @@ registerAction2(class extends ViewAction { title: localize('show infos', "Show Infos"), category: localize('problems', "Problems"), toggled: MarkersContextKeys.ShowInfoFilterContextKey, + metadata: { + description: localize2('toggleInfosDescription', "Show or hide infos in the problems view.") + }, menu: { id: viewFilterSubmenu, group: '1_filter', @@ -268,6 +283,9 @@ registerAction2(class extends ViewAction { super({ id: `workbench.actions.${Markers.MARKERS_VIEW_ID}.toggleActiveFile`, title: localize('show active file', "Show Active File Only"), + metadata: { + description: localize2('toggleActiveFileDescription', "Show or hide problems (errors, warnings, info) only from the active file in the problems view.") + }, category: localize('problems', "Problems"), toggled: MarkersContextKeys.ShowActiveFileFilterContextKey, menu: { @@ -290,6 +308,9 @@ registerAction2(class extends ViewAction { super({ id: `workbench.actions.${Markers.MARKERS_VIEW_ID}.toggleExcludedFiles`, title: localize('show excluded files', "Show Excluded Files"), + metadata: { + description: localize2('toggleExcludedFilesDescription', "Show or hide excluded files in the problems view.") + }, category: localize('problems', "Problems"), toggled: MarkersContextKeys.ShowExcludedFilesFilterContextKey.negate(), menu: { diff --git a/src/vs/workbench/contrib/markers/browser/markersModel.ts b/src/vs/workbench/contrib/markers/browser/markersModel.ts index 2e9cea1c76b7..91c033d4e187 100644 --- a/src/vs/workbench/contrib/markers/browser/markersModel.ts +++ b/src/vs/workbench/contrib/markers/browser/markersModel.ts @@ -124,8 +124,7 @@ export class MarkerTableItem extends Marker { readonly sourceMatches?: IMatch[], readonly codeMatches?: IMatch[], readonly messageMatches?: IMatch[], - readonly fileMatches?: IMatch[], - readonly ownerMatches?: IMatch[], + readonly fileMatches?: IMatch[] ) { super(marker.id, marker.marker, marker.relatedInformation); } diff --git a/src/vs/workbench/contrib/markers/browser/markersTable.ts b/src/vs/workbench/contrib/markers/browser/markersTable.ts index e454955cc238..3a10bafb3272 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTable.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTable.ts @@ -13,7 +13,7 @@ import { IOpenEvent, IWorkbenchTableOptions, WorkbenchTable } from '../../../../ import { HighlightedLabel } from '../../../../base/browser/ui/highlightedlabel/highlightedLabel.js'; import { compareMarkersByUri, Marker, MarkerTableItem, ResourceMarkers } from './markersModel.js'; import { MarkerSeverity } from '../../../../platform/markers/common/markers.js'; -import { SeverityIcon } from '../../../../platform/severityIcon/browser/severityIcon.js'; +import { SeverityIcon } from '../../../../base/browser/ui/severityIcon/severityIcon.js'; import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js'; import { ILabelService } from '../../../../platform/label/common/label.js'; import { FilterOptions } from './markersFilterOptions.js'; @@ -230,21 +230,21 @@ class MarkerFileColumnRenderer implements ITableRenderer { +class MarkerSourceColumnRenderer implements ITableRenderer { - static readonly TEMPLATE_ID = 'owner'; + static readonly TEMPLATE_ID = 'source'; - readonly templateId: string = MarkerOwnerColumnRenderer.TEMPLATE_ID; + readonly templateId: string = MarkerSourceColumnRenderer.TEMPLATE_ID; renderTemplate(container: HTMLElement): IMarkerHighlightedLabelColumnTemplateData { - const columnElement = DOM.append(container, $('.owner')); + const columnElement = DOM.append(container, $('.source')); const highlightedLabel = new HighlightedLabel(columnElement); return { columnElement, highlightedLabel }; } renderElement(element: MarkerTableItem, index: number, templateData: IMarkerHighlightedLabelColumnTemplateData, height: number | undefined): void { - templateData.columnElement.title = element.marker.owner; - templateData.highlightedLabel.set(element.marker.owner, element.ownerMatches); + templateData.columnElement.title = element.marker.source ?? ''; + templateData.highlightedLabel.set(element.marker.source ?? '', element.sourceMatches); } disposeTemplate(templateData: IMarkerHighlightedLabelColumnTemplateData): void { @@ -321,7 +321,7 @@ export class MarkersTable extends Disposable implements IProblemsWidget { weight: 1, minimumWidth: 100, maximumWidth: 300, - templateId: MarkerOwnerColumnRenderer.TEMPLATE_ID, + templateId: MarkerSourceColumnRenderer.TEMPLATE_ID, project(row: Marker): Marker { return row; } } ], @@ -330,7 +330,7 @@ export class MarkersTable extends Disposable implements IProblemsWidget { this.instantiationService.createInstance(MarkerCodeColumnRenderer), this.instantiationService.createInstance(MarkerMessageColumnRenderer), this.instantiationService.createInstance(MarkerFileColumnRenderer), - this.instantiationService.createInstance(MarkerOwnerColumnRenderer), + this.instantiationService.createInstance(MarkerSourceColumnRenderer), ], options ) as WorkbenchTable; @@ -454,11 +454,10 @@ export class MarkersTable extends Disposable implements IProblemsWidget { const codeMatches = marker.marker.code ? FilterOptions._filter(this.filterOptions.textFilter.text, typeof marker.marker.code === 'string' ? marker.marker.code : marker.marker.code.value) ?? undefined : undefined; const messageMatches = FilterOptions._messageFilter(this.filterOptions.textFilter.text, marker.marker.message) ?? undefined; const fileMatches = FilterOptions._messageFilter(this.filterOptions.textFilter.text, this.labelService.getUriLabel(marker.resource, { relative: true })) ?? undefined; - const ownerMatches = FilterOptions._messageFilter(this.filterOptions.textFilter.text, marker.marker.owner) ?? undefined; - const matched = sourceMatches || codeMatches || messageMatches || fileMatches || ownerMatches; + const matched = sourceMatches || codeMatches || messageMatches || fileMatches; if ((matched && !this.filterOptions.textFilter.negate) || (!matched && this.filterOptions.textFilter.negate)) { - items.push(new MarkerTableItem(marker, sourceMatches, codeMatches, messageMatches, fileMatches, ownerMatches)); + items.push(new MarkerTableItem(marker, sourceMatches, codeMatches, messageMatches, fileMatches)); } continue; diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 9820ad9e0028..4ed27e35584a 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -26,7 +26,7 @@ import { Event, Emitter } from '../../../../base/common/event.js'; import { IListAccessibilityProvider } from '../../../../base/browser/ui/list/listWidget.js'; import { isUndefinedOrNull } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; -import { Action, IAction } from '../../../../base/common/actions.js'; +import { Action, IAction, toAction } from '../../../../base/common/actions.js'; import { localize } from '../../../../nls.js'; import { CancelablePromise, createCancelablePromise, Delayer } from '../../../../base/common/async.js'; import { IModelService } from '../../../../editor/common/services/model.js'; @@ -35,7 +35,7 @@ import { applyCodeAction, ApplyCodeActionReason, getCodeActions } from '../../.. import { CodeActionKind, CodeActionSet, CodeActionTriggerSource } from '../../../../editor/contrib/codeAction/common/types.js'; import { ITextModel } from '../../../../editor/common/model.js'; import { IEditorService, ACTIVE_GROUP } from '../../../services/editor/common/editorService.js'; -import { SeverityIcon } from '../../../../platform/severityIcon/browser/severityIcon.js'; +import { SeverityIcon } from '../../../../base/browser/ui/severityIcon/severityIcon.js'; import { CodeActionTriggerType } from '../../../../editor/common/languages.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { Progress } from '../../../../platform/progress/common/progress.js'; @@ -201,6 +201,7 @@ export class ResourceMarkersRenderer implements ITreeRenderer): void { @@ -348,11 +349,11 @@ class MarkerWidget extends Disposable { return undefined; } })); - this.disposables.add(toDisposable(() => multilineActionbar.dispose())); + this.disposables.add(multilineActionbar); const viewModel = this.markersViewModel.getViewModel(marker); const multiline = viewModel && viewModel.multiline; - const action = new Action(toggleMultilineAction); + const action = this.disposables.add(new Action(toggleMultilineAction)); action.enabled = !!viewModel && marker.lines.length > 1; action.tooltip = multiline ? localize('single line', "Show message in single line") : localize('multi line', "Show message in multiple lines"); action.class = ThemeIcon.asClassName(multiline ? expandedIcon : collapsedIcon); @@ -644,15 +645,14 @@ export class MarkerViewModel extends Disposable { } private toActions(codeActions: CodeActionSet): IAction[] { - return codeActions.validActions.map(item => new Action( - item.action.command ? item.action.command.id : item.action.title, - item.action.title, - undefined, - true, - () => { - return this.openFileAtMarker(this.marker) - .then(() => this.instantiationService.invokeFunction(applyCodeAction, item, ApplyCodeActionReason.FromProblemsView)); - })); + return codeActions.validActions.map(item => toAction({ + id: item.action.command ? item.action.command.id : item.action.title, + label: item.action.title, + run: async () => { + await this.openFileAtMarker(this.marker); + return await this.instantiationService.invokeFunction(applyCodeAction, item, ApplyCodeActionReason.FromProblemsView); + } + })); } private openFileAtMarker(element: Marker): Promise { diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index 85ad02f34be6..d62dbd05f116 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -5,57 +5,58 @@ import './media/markers.css'; -import { URI } from '../../../../base/common/uri.js'; import * as dom from '../../../../base/browser/dom.js'; +import { IKeyboardEvent, StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; +import { ActionViewItem } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; +import { IIdentityProvider, IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js'; +import { ITableContextMenuEvent, ITableEvent } from '../../../../base/browser/ui/table/table.js'; +import { ITreeContextMenuEvent, ITreeElement, ITreeEvent, ITreeNode, ITreeRenderer } from '../../../../base/browser/ui/tree/tree.js'; import { IAction, Separator } from '../../../../base/common/actions.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; -import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from '../../../services/editor/common/editorService.js'; -import { Marker, ResourceMarkers, RelatedInformation, MarkerChangesEvent, MarkersModel, compareMarkersByUri, MarkerElement, MarkerTableItem } from './markersModel.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { MarkersFilters, IMarkersFiltersChangeEvent } from './markersViewActions.js'; -import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import Messages from './messages.js'; -import { RangeHighlightDecorations } from '../../../browser/codeeditor.js'; -import { IThemeService } from '../../../../platform/theme/common/themeService.js'; +import { groupBy } from '../../../../base/common/arrays.js'; +import { Event, Relay } from '../../../../base/common/event.js'; +import { IExpression } from '../../../../base/common/glob.js'; +import { Iterable } from '../../../../base/common/iterator.js'; +import { KeyCode } from '../../../../base/common/keyCodes.js'; +import { DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { ResourceMap } from '../../../../base/common/map.js'; +import { deepClone } from '../../../../base/common/objects.js'; +import { isDefined } from '../../../../base/common/types.js'; +import { URI } from '../../../../base/common/uri.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; -import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { localize } from '../../../../nls.js'; +import { MenuId } from '../../../../platform/actions/common/actions.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { Iterable } from '../../../../base/common/iterator.js'; -import { ITreeElement, ITreeNode, ITreeContextMenuEvent, ITreeRenderer, ITreeEvent } from '../../../../base/browser/ui/tree/tree.js'; -import { Relay, Event } from '../../../../base/common/event.js'; -import { WorkbenchObjectTree, IListService, IWorkbenchObjectTreeOptions, IOpenEvent } from '../../../../platform/list/browser/listService.js'; -import { FilterOptions } from './markersFilterOptions.js'; -import { IExpression } from '../../../../base/common/glob.js'; -import { deepClone } from '../../../../base/common/objects.js'; -import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; -import { FilterData, Filter, VirtualDelegate, ResourceMarkersRenderer, MarkerRenderer, RelatedInformationRenderer, MarkersWidgetAccessibilityProvider, MarkersViewModel } from './markersTreeViewer.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; -import { MenuId } from '../../../../platform/actions/common/actions.js'; +import { fillInMarkersDragData, MarkerTransferData } from '../../../../platform/dnd/browser/dnd.js'; +import { IHoverService } from '../../../../platform/hover/browser/hover.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; -import { StandardKeyboardEvent, IKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; -import { ResourceLabels } from '../../../browser/labels.js'; +import { ResultKind } from '../../../../platform/keybinding/common/keybindingResolver.js'; +import { IListService, IOpenEvent, IWorkbenchObjectTreeOptions, WorkbenchObjectTree } from '../../../../platform/list/browser/listService.js'; import { IMarkerService, MarkerSeverity } from '../../../../platform/markers/common/markers.js'; -import { MementoObject, Memento } from '../../../common/memento.js'; -import { IIdentityProvider, IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js'; -import { KeyCode } from '../../../../base/common/keyCodes.js'; -import { IViewPaneOptions, FilterViewPane } from '../../../browser/parts/views/viewPane.js'; -import { IViewDescriptorService } from '../../../common/views.js'; import { IOpenerService, withSelection } from '../../../../platform/opener/common/opener.js'; -import { ActionViewItem } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; -import { DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; -import { groupBy } from '../../../../base/common/arrays.js'; -import { ResourceMap } from '../../../../base/common/map.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { registerNavigableContainer } from '../../../browser/actions/widgetNavigationCommands.js'; +import { RangeHighlightDecorations } from '../../../browser/codeeditor.js'; +import { ResourceListDnDHandler } from '../../../browser/dnd.js'; +import { ResourceLabels } from '../../../browser/labels.js'; +import { FilterViewPane, IViewPaneOptions } from '../../../browser/parts/views/viewPane.js'; import { EditorResourceAccessor, SideBySideEditor } from '../../../common/editor.js'; +import { Memento, MementoObject } from '../../../common/memento.js'; +import { IViewDescriptorService } from '../../../common/views.js'; +import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from '../../../services/editor/common/editorService.js'; +import { Markers, MarkersContextKeys, MarkersViewMode } from '../common/markers.js'; import { IMarkersView } from './markers.js'; -import { ResourceListDnDHandler } from '../../../browser/dnd.js'; -import { ITableContextMenuEvent, ITableEvent } from '../../../../base/browser/ui/table/table.js'; +import { FilterOptions } from './markersFilterOptions.js'; +import { compareMarkersByUri, Marker, MarkerChangesEvent, MarkerElement, MarkersModel, MarkerTableItem, RelatedInformation, ResourceMarkers } from './markersModel.js'; import { MarkersTable } from './markersTable.js'; -import { Markers, MarkersContextKeys, MarkersViewMode } from '../common/markers.js'; -import { registerNavigableContainer } from '../../../browser/actions/widgetNavigationCommands.js'; -import { IHoverService } from '../../../../platform/hover/browser/hover.js'; -import { ResultKind } from '../../../../platform/keybinding/common/keybindingResolver.js'; +import { Filter, FilterData, MarkerRenderer, MarkersViewModel, MarkersWidgetAccessibilityProvider, RelatedInformationRenderer, ResourceMarkersRenderer, VirtualDelegate } from './markersTreeViewer.js'; +import { IMarkersFiltersChangeEvent, MarkersFilters } from './markersViewActions.js'; +import Messages from './messages.js'; function createResourceMarkersIterator(resourceMarkers: ResourceMarkers): Iterable> { return Iterable.map(resourceMarkers.markers, m => { @@ -130,7 +131,6 @@ export class MarkersView extends FilterViewPane implements IMarkersView { @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IEditorService private readonly editorService: IEditorService, @IConfigurationService configurationService: IConfigurationService, - @ITelemetryService telemetryService: ITelemetryService, @IMarkerService private readonly markerService: IMarkerService, @IContextKeyService contextKeyService: IContextKeyService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @@ -153,7 +153,7 @@ export class MarkersView extends FilterViewPane implements IMarkersView { text: panelState['filter'] || '', history: panelState['filterHistory'] || [] } - }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); + }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); this.memento = memento; this.panelState = panelState; @@ -495,18 +495,7 @@ export class MarkersView extends FilterViewPane implements IMarkersView { filter: this.filter, accessibilityProvider: this.widgetAccessibilityProvider, identityProvider: this.widgetIdentityProvider, - dnd: this.instantiationService.createInstance(ResourceListDnDHandler, (element) => { - if (element instanceof ResourceMarkers) { - return element.resource; - } - if (element instanceof Marker) { - return withSelection(element.resource, element.range); - } - if (element instanceof RelatedInformation) { - return withSelection(element.raw.resource, element.raw); - } - return null; - }), + dnd: this.instantiationService.createInstance(MarkersListDnDHandler), expandOnlyOnTwistieClick: (e: MarkerElement) => e instanceof Marker && e.relatedInformation.length > 0, overrideStyles: this.getLocationBasedColors().listOverrideStyles, selectionNavigation: true, @@ -1078,3 +1067,40 @@ class MarkersTree extends WorkbenchObjectTree impleme super.layout(height, width); } } + +class MarkersListDnDHandler extends ResourceListDnDHandler { + constructor( + @IInstantiationService instantiationService: IInstantiationService + ) { + super(element => { + if (element instanceof MarkerTableItem) { + return withSelection(element.resource, element.range); + } else if (element instanceof ResourceMarkers) { + return element.resource; + } else if (element instanceof Marker) { + return withSelection(element.resource, element.range); + } else if (element instanceof RelatedInformation) { + return withSelection(element.raw.resource, element.raw); + } + return null; + }, instantiationService); + } + + protected override onWillDragElements(elements: (MarkerElement | MarkerTableItem)[], originalEvent: DragEvent) { + const data = elements.map((e): MarkerTransferData | undefined => { + if (e instanceof RelatedInformation || e instanceof Marker) { + return e.marker; + } + if (e instanceof ResourceMarkers) { + return { uri: e.resource }; + } + return undefined; + }).filter(isDefined); + + if (!data.length) { + return; + } + + fillInMarkersDragData(data, originalEvent); + } +} diff --git a/src/vs/workbench/contrib/markers/browser/media/markers.css b/src/vs/workbench/contrib/markers/browser/media/markers.css index ce84d620ce87..93c8e7abaa4a 100644 --- a/src/vs/workbench/contrib/markers/browser/media/markers.css +++ b/src/vs/workbench/contrib/markers/browser/media/markers.css @@ -75,7 +75,7 @@ .markers-panel .markers-panel-container .tree-container .monaco-tl-contents .marker-message-details-container > .marker-message-line > .marker-message { overflow: hidden; text-overflow: ellipsis; - white-space: nowrap; + white-space: pre; } .markers-panel .markers-panel-container .tree-container .monaco-tl-contents .marker-message-details-container > .marker-message-line.details-container { @@ -182,7 +182,7 @@ .markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .code, .markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .message, .markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .file, -.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .owner { +.markers-panel .markers-table-container .monaco-table .monaco-list-row .monaco-table-tr > .monaco-table-td > .source { overflow: hidden; text-overflow: ellipsis; } diff --git a/src/vs/workbench/contrib/mcp/browser/mcp.contribution.ts b/src/vs/workbench/contrib/mcp/browser/mcp.contribution.ts new file mode 100644 index 000000000000..f886d782765c --- /dev/null +++ b/src/vs/workbench/contrib/mcp/browser/mcp.contribution.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerAction2 } from '../../../../platform/actions/common/actions.js'; +import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; +import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; +import * as jsonContributionRegistry from '../../../../platform/jsonschemas/common/jsonContributionRegistry.js'; +import { Registry } from '../../../../platform/registry/common/platform.js'; +import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js'; +import { mcpSchemaId } from '../../../services/configuration/common/configuration.js'; +import { ConfigMcpDiscovery } from '../common/discovery/configMcpDiscovery.js'; +import { ExtensionMcpDiscovery } from '../common/discovery/extensionMcpDiscovery.js'; +import { mcpDiscoveryRegistry } from '../common/discovery/mcpDiscovery.js'; +import { RemoteNativeMpcDiscovery } from '../common/discovery/nativeMcpRemoteDiscovery.js'; +import { mcpServerSchema } from '../common/mcpConfiguration.js'; +import { McpContextKeysController } from '../common/mcpContextKeys.js'; +import { McpRegistry } from '../common/mcpRegistry.js'; +import { IMcpRegistry } from '../common/mcpRegistryTypes.js'; +import { McpService } from '../common/mcpService.js'; +import { IMcpService } from '../common/mcpTypes.js'; +import { AddConfigurationAction, ListMcpServerCommand, MCPServerActionRendering, McpServerOptionsCommand, ResetMcpCachedTools, ResetMcpTrustCommand } from './mcpCommands.js'; +import { McpDiscovery } from './mcpDiscovery.js'; + +registerSingleton(IMcpRegistry, McpRegistry, InstantiationType.Delayed); +registerSingleton(IMcpService, McpService, InstantiationType.Delayed); + +mcpDiscoveryRegistry.register(new SyncDescriptor(RemoteNativeMpcDiscovery)); +mcpDiscoveryRegistry.register(new SyncDescriptor(ConfigMcpDiscovery)); +mcpDiscoveryRegistry.register(new SyncDescriptor(ExtensionMcpDiscovery)); + +registerWorkbenchContribution2('mcpDiscovery', McpDiscovery, WorkbenchPhase.AfterRestored); +registerWorkbenchContribution2('mcpContextKeys', McpContextKeysController, WorkbenchPhase.BlockRestore); + +registerAction2(ListMcpServerCommand); +registerAction2(McpServerOptionsCommand); +registerAction2(ResetMcpTrustCommand); +registerAction2(ResetMcpCachedTools); +registerAction2(AddConfigurationAction); + +registerWorkbenchContribution2('mcpActionRendering', MCPServerActionRendering, WorkbenchPhase.BlockRestore); + +const jsonRegistry = Registry.as(jsonContributionRegistry.Extensions.JSONContribution); +jsonRegistry.registerSchema(mcpSchemaId, mcpServerSchema); diff --git a/src/vs/workbench/contrib/mcp/browser/mcpCommands.ts b/src/vs/workbench/contrib/mcp/browser/mcpCommands.ts new file mode 100644 index 000000000000..7e45d1506a46 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/browser/mcpCommands.ts @@ -0,0 +1,389 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { h } from '../../../../base/browser/dom.js'; +import { Codicon } from '../../../../base/common/codicons.js'; +import { groupBy } from '../../../../base/common/collections.js'; +import { Event } from '../../../../base/common/event.js'; +import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; +import { autorun, derived } from '../../../../base/common/observable.js'; +import { ThemeIcon } from '../../../../base/common/themables.js'; +import { ILocalizedString, localize, localize2 } from '../../../../nls.js'; +import { IActionViewItemService } from '../../../../platform/actions/browser/actionViewItemService.js'; +import { MenuEntryActionViewItem } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; +import { Action2, MenuId, MenuItemAction } from '../../../../platform/actions/common/actions.js'; +import { ICommandService } from '../../../../platform/commands/common/commands.js'; +import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; +import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +import { IQuickInputService, IQuickPickItem } from '../../../../platform/quickinput/common/quickInput.js'; +import { spinningLoading } from '../../../../platform/theme/common/iconRegistry.js'; +import { ActiveEditorContext, ResourceContextKey } from '../../../common/contextkeys.js'; +import { IWorkbenchContribution } from '../../../common/contributions.js'; +import { ChatContextKeys } from '../../chat/common/chatContextKeys.js'; +import { ChatMode } from '../../chat/common/constants.js'; +import { TEXT_FILE_EDITOR_ID } from '../../files/common/files.js'; +import { McpContextKeys } from '../common/mcpContextKeys.js'; +import { IMcpRegistry } from '../common/mcpRegistryTypes.js'; +import { IMcpServer, IMcpService, LazyCollectionState, McpConnectionState, McpServerToolsState } from '../common/mcpTypes.js'; +import { McpAddConfigurationCommand } from './mcpCommandsAddConfiguration.js'; + +// acroynms do not get localized +const category: ILocalizedString = { + original: 'MCP', + value: 'MCP', +}; + +export class ListMcpServerCommand extends Action2 { + public static readonly id = 'workbench.mcp.listServer'; + constructor() { + super({ + id: ListMcpServerCommand.id, + title: localize2('mcp.list', 'List Servers'), + icon: Codicon.server, + category, + f1: true, + menu: { + when: ContextKeyExpr.and( + ContextKeyExpr.or(McpContextKeys.hasUnknownTools, McpContextKeys.hasServersWithErrors), + ChatContextKeys.chatMode.isEqualTo(ChatMode.Agent) + ), + id: MenuId.ChatInputAttachmentToolbar, + group: 'navigation', + order: 0 + }, + }); + } + + override async run(accessor: ServicesAccessor) { + const mcpService = accessor.get(IMcpService); + const commandService = accessor.get(ICommandService); + const quickInput = accessor.get(IQuickInputService); + + type ItemType = { id: string } & IQuickPickItem; + + const store = new DisposableStore(); + const pick = quickInput.createQuickPick({ useSeparators: true }); + pick.title = localize('mcp.selectServer', 'Select an MCP Server'); + + store.add(pick); + store.add(autorun(reader => { + const servers = groupBy(mcpService.servers.read(reader).slice().sort((a, b) => (a.collection.presentation?.order || 0) - (b.collection.presentation?.order || 0)), s => s.collection.id); + pick.items = Object.values(servers).flatMap(servers => { + return [ + { type: 'separator', label: servers[0].collection.label, id: servers[0].collection.id }, + ...servers.map(server => ({ + id: server.definition.id, + label: server.definition.label, + description: McpConnectionState.toString(server.connectionState.read(reader)), + })), + ]; + }); + })); + + + const picked = await new Promise(resolve => { + store.add(pick.onDidAccept(() => { + resolve(pick.activeItems[0]); + })); + store.add(pick.onDidHide(() => { + resolve(undefined); + })); + pick.show(); + }); + + store.dispose(); + + if (picked) { + commandService.executeCommand(McpServerOptionsCommand.id, picked.id); + } + } +} + + +export class McpServerOptionsCommand extends Action2 { + + static readonly id = 'workbench.mcp.serverOptions'; + + constructor() { + super({ + id: McpServerOptionsCommand.id, + title: localize2('mcp.options', 'Server Options'), + category, + f1: false, + }); + } + + override async run(accessor: ServicesAccessor, id: string): Promise { + const mcpService = accessor.get(IMcpService); + const quickInputService = accessor.get(IQuickInputService); + const server = mcpService.servers.get().find(s => s.definition.id === id); + if (!server) { + return; + } + + interface ActionItem extends IQuickPickItem { + action: 'start' | 'stop' | 'restart' | 'showOutput'; + } + + const items: ActionItem[] = []; + const serverState = server.connectionState.get(); + + // Only show start when server is stopped or in error state + if (McpConnectionState.canBeStarted(serverState.state)) { + items.push({ + label: localize2('mcp.start', 'Start Server').value, + action: 'start' + }); + } else { + items.push({ + label: localize2('mcp.stop', 'Stop Server').value, + action: 'stop' + }); + items.push({ + label: localize2('mcp.restart', 'Restart Server').value, + action: 'restart' + }); + } + + items.push({ + label: localize2('mcp.showOutput', 'Show Output').value, + action: 'showOutput' + }); + + const pick = await quickInputService.pick(items, { + title: server.definition.label, + placeHolder: localize('mcp.selectAction', 'Select Server Action') + }); + + if (!pick) { + return; + } + + switch (pick.action) { + case 'start': + await server.start(true); + server.showOutput(); + break; + case 'stop': + await server.stop(); + break; + case 'restart': + await server.stop(); + await server.start(true); + break; + case 'showOutput': + server.showOutput(); + break; + } + } +} + + +export class MCPServerActionRendering extends Disposable implements IWorkbenchContribution { + public static readonly ID = 'workbench.contrib.mcp.discovery'; + + constructor( + @IActionViewItemService actionViewItemService: IActionViewItemService, + @IMcpService mcpService: IMcpService, + @IInstantiationService instaService: IInstantiationService, + @ICommandService commandService: ICommandService, + ) { + super(); + + const enum DisplayedState { + None, + NewTools, + Error, + Refreshing, + } + + + + const displayedState = derived((reader) => { + const servers = mcpService.servers.read(reader); + const serversPerState: IMcpServer[][] = []; + for (const server of servers) { + let thisState = DisplayedState.None; + switch (server.toolsState.read(reader)) { + case McpServerToolsState.Unknown: + if (server.trusted.read(reader) === false) { + thisState = DisplayedState.None; + } else { + thisState = server.connectionState.read(reader).state === McpConnectionState.Kind.Error ? DisplayedState.Error : DisplayedState.NewTools; + } + break; + case McpServerToolsState.RefreshingFromUnknown: + thisState = DisplayedState.Refreshing; + break; + default: + thisState = server.connectionState.read(reader).state === McpConnectionState.Kind.Error ? DisplayedState.Error : DisplayedState.None; + break; + } + + serversPerState[thisState] ??= []; + serversPerState[thisState].push(server); + } + + const unknownServerStates = mcpService.lazyCollectionState.read(reader); + if (unknownServerStates === LazyCollectionState.LoadingUnknown) { + serversPerState[DisplayedState.Refreshing] ??= []; + } else if (unknownServerStates === LazyCollectionState.HasUnknown) { + serversPerState[DisplayedState.NewTools] ??= []; + } + + const maxState = (serversPerState.length - 1) as DisplayedState; + return { state: maxState, servers: serversPerState[maxState] || [] }; + }); + + this._store.add(actionViewItemService.register(MenuId.ChatInputAttachmentToolbar, ListMcpServerCommand.id, (action, options) => { + if (!(action instanceof MenuItemAction)) { + return undefined; + } + + return instaService.createInstance(class extends MenuEntryActionViewItem { + + override render(container: HTMLElement): void { + + super.render(container); + container.classList.add('chat-mcp'); + + const action = h('button.chat-mcp-action', [h('span@icon')]); + + this._register(autorun(r => { + const { state } = displayedState.read(r); + const { root, icon } = action; + this.updateTooltip(); + container.classList.toggle('chat-mcp-has-action', state !== DisplayedState.None); + + if (!root.parentElement) { + container.appendChild(root); + } + + root.ariaLabel = this.getLabelForState(displayedState.read(r)); + root.className = 'chat-mcp-action'; + icon.className = ''; + if (state === DisplayedState.NewTools) { + root.classList.add('chat-mcp-action-new'); + icon.classList.add(...ThemeIcon.asClassNameArray(Codicon.refresh)); + } else if (state === DisplayedState.Error) { + root.classList.add('chat-mcp-action-error'); + icon.classList.add(...ThemeIcon.asClassNameArray(Codicon.warning)); + } else if (state === DisplayedState.Refreshing) { + root.classList.add('chat-mcp-action-refreshing'); + icon.classList.add(...ThemeIcon.asClassNameArray(spinningLoading)); + } else { + root.remove(); + } + })); + } + + override async onClick(e: MouseEvent): Promise { + e.preventDefault(); + e.stopPropagation(); + + const { state, servers } = displayedState.get(); + if (state === DisplayedState.NewTools) { + servers.forEach(server => server.start()); + mcpService.activateCollections(); + } else if (state === DisplayedState.Refreshing) { + servers.at(-1)?.showOutput(); + } else if (state === DisplayedState.Error) { + const server = servers.at(-1); + if (server) { + commandService.executeCommand(McpServerOptionsCommand.id, server.definition.id); + } + } else { + commandService.executeCommand(ListMcpServerCommand.id); + } + } + + protected override getTooltip(): string { + return this.getLabelForState() || super.getTooltip(); + } + + private getLabelForState({ state, servers } = displayedState.get()) { + if (state === DisplayedState.NewTools) { + return localize('mcp.newTools', "New tools available ({0})", servers.length || 1); + } else if (state === DisplayedState.Error) { + return localize('mcp.toolError', "Error loading {0} tool(s)", servers.length || 1); + } else if (state === DisplayedState.Refreshing) { + return localize('mcp.toolRefresh', "Discovering tools..."); + } else { + return null; + } + } + + + }, action, { ...options, keybindingNotRenderedWithLabel: true }); + + }, Event.fromObservable(displayedState))); + } +} + +export class ResetMcpTrustCommand extends Action2 { + static readonly ID = 'workbench.mcp.resetTrust'; + + constructor() { + super({ + id: ResetMcpTrustCommand.ID, + title: localize2('mcp.resetTrust', "Reset Trust"), + category, + f1: true, + precondition: McpContextKeys.toolsCount.greater(0), + }); + } + + run(accessor: ServicesAccessor): void { + const mcpService = accessor.get(IMcpRegistry); + mcpService.resetTrust(); + } +} + + +export class ResetMcpCachedTools extends Action2 { + static readonly ID = 'workbench.mcp.resetCachedTools'; + + constructor() { + super({ + id: ResetMcpCachedTools.ID, + title: localize2('mcp.resetCachedTools', "Reset Cached Tools"), + category, + f1: true, + precondition: McpContextKeys.toolsCount.greater(0), + }); + } + + run(accessor: ServicesAccessor): void { + const mcpService = accessor.get(IMcpService); + mcpService.resetCaches(); + } +} + +export class AddConfigurationAction extends Action2 { + static readonly ID = 'workbench.mcp.addConfiguration'; + + constructor() { + super({ + id: AddConfigurationAction.ID, + title: localize2('mcp.addConfiguration', "Add Server..."), + metadata: { + description: localize2('mcp.addConfiguration.description', "Installs a new Model Context protocol to the mcp.json settings"), + }, + category, + f1: true, + menu: { + id: MenuId.EditorContent, + when: ContextKeyExpr.and( + ContextKeyExpr.regex(ResourceContextKey.Path.key, /\.vscode[/\\]mcp\.json$/), + ActiveEditorContext.isEqualTo(TEXT_FILE_EDITOR_ID) + ) + } + }); + } + + async run(accessor: ServicesAccessor, configUri?: string): Promise { + return accessor.get(IInstantiationService).createInstance(McpAddConfigurationCommand, configUri).run(); + } +} diff --git a/src/vs/workbench/contrib/mcp/browser/mcpCommandsAddConfiguration.ts b/src/vs/workbench/contrib/mcp/browser/mcpCommandsAddConfiguration.ts new file mode 100644 index 000000000000..cdfb4803143e --- /dev/null +++ b/src/vs/workbench/contrib/mcp/browser/mcpCommandsAddConfiguration.ts @@ -0,0 +1,315 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { assertNever } from '../../../../base/common/assert.js'; +import { DisposableStore } from '../../../../base/common/lifecycle.js'; +import { URI } from '../../../../base/common/uri.js'; +import { generateUuid } from '../../../../base/common/uuid.js'; +import { localize } from '../../../../nls.js'; +import { ICommandService } from '../../../../platform/commands/common/commands.js'; +import { ConfigurationTarget, getConfigValueInTarget, IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { IMcpConfiguration, IMcpConfigurationSSE, McpConfigurationServer } from '../../../../platform/mcp/common/mcpPlatformTypes.js'; +import { IQuickInputService, IQuickPickItem, QuickPickInput } from '../../../../platform/quickinput/common/quickInput.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { IJSONEditingService } from '../../../services/configuration/common/jsonEditing.js'; +import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; +import { IMcpConfigurationStdio, mcpStdioServerSchema } from '../common/mcpConfiguration.js'; + + +const enum AddConfigurationType { + Stdio, + SSE, + + NpmPackage, + PipPackage, +} + +const enum AddConfigurationCopilotCommand { + /** Returns whether MCP enhanced setup is enabled. */ + IsSupported = 'github.copilot.chat.mcp.setup.check', + + /** Takes an npm/pip package name, validates its owner. */ + ValidatePackage = 'github.copilot.chat.mcp.setup.validatePackage', + + /** Returns the resolved MCP configuration. */ + StartFlow = 'github.copilot.chat.mcp.setup.flow', +} + +type ValidatePackageResult = { state: 'ok'; publisher: string } | { state: 'error'; error: string }; + +export class McpAddConfigurationCommand { + constructor( + private readonly _explicitConfigUri: string | undefined, + @IQuickInputService private readonly _quickInputService: IQuickInputService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IJSONEditingService private readonly _jsonEditingService: IJSONEditingService, + @IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService, + @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, + @ICommandService private readonly _commandService: ICommandService, + ) { } + + private async getServerType(): Promise { + const items: QuickPickInput<{ kind: AddConfigurationType } & IQuickPickItem>[] = [ + { kind: AddConfigurationType.Stdio, label: localize('mcp.serverType.command', "Command (stdio)"), description: localize('mcp.serverType.command.description', "Run a local command that implements the MCP protocol") }, + { kind: AddConfigurationType.SSE, label: localize('mcp.serverType.http', "HTTP (server-sent events)"), description: localize('mcp.serverType.http.description', "Connect to a remote HTTP server that implements the MCP protocol") } + ]; + + let aiSupported: boolean | undefined; + try { + aiSupported = await this._commandService.executeCommand(AddConfigurationCopilotCommand.IsSupported); + } catch { + // ignored + } + + if (aiSupported) { + items.unshift({ type: 'separator', label: localize('mcp.serverType.manual', "Manual Install") }); + items.push( + { type: 'separator', label: localize('mcp.serverType.copilot', "Model-Assisted") }, + { kind: AddConfigurationType.NpmPackage, label: localize('mcp.serverType.npm', "NPM Package"), description: localize('mcp.serverType.npm.description', "Install from an NPM package name") }, + { kind: AddConfigurationType.PipPackage, label: localize('mcp.serverType.pip', "PIP Package"), description: localize('mcp.serverType.pip.description', "Install from a PIP package name") } + ); + } + + const result = await this._quickInputService.pick<{ kind: AddConfigurationType } & IQuickPickItem>(items, { + title: localize('mcp.serverType.title', "Select Server Type"), + placeHolder: localize('mcp.serverType.placeholder', "Choose the type of MCP server to add"), + }); + + return result?.kind; + } + + private async getStdioConfig(): Promise { + const command = await this._quickInputService.input({ + title: localize('mcp.command.title', "Enter Command"), + placeHolder: localize('mcp.command.placeholder', "Command to run (with optional arguments)"), + ignoreFocusLost: true, + }); + + if (!command) { + return undefined; + } + + // Split command into command and args, handling quotes + const parts = command.match(/(?:[^\s"]+|"[^"]*")+/g)!; + return { + type: 'stdio', + command: parts[0].replace(/"/g, ''), + + args: parts.slice(1).map(arg => arg.replace(/"/g, '')) + }; + } + + private async getSSEConfig(): Promise { + const url = await this._quickInputService.input({ + title: localize('mcp.url.title', "Enter Server URL"), + placeHolder: localize('mcp.url.placeholder', "URL of the MCP server (e.g., http://localhost:3000)"), + ignoreFocusLost: true, + }); + + if (!url) { + return undefined; + } + + return { + type: 'sse', + url + }; + } + + private async getServerId(suggestion = `my-mcp-server-${generateUuid().split('-')[0]}`): Promise { + const id = await this._quickInputService.input({ + title: localize('mcp.serverId.title', "Enter Server ID"), + placeHolder: localize('mcp.serverId.placeholder', "Unique identifier for this server"), + value: suggestion, + ignoreFocusLost: true, + }); + + return id; + } + + private async getConfigurationTarget(): Promise { + const options: (IQuickPickItem & { target: ConfigurationTarget })[] = [ + { target: ConfigurationTarget.USER, label: localize('mcp.target.user', "User Settings"), description: localize('mcp.target.user.description', "Available in all workspaces") } + ]; + + if (!!this._environmentService.remoteAuthority) { + options.push({ target: ConfigurationTarget.USER_REMOTE, label: localize('mcp.target.remote', "Remote Settings"), description: localize('mcp.target..remote.description', "Available on this remote machine") }); + } + + if (this._workspaceService.getWorkspace().folders.length > 0) { + options.push({ target: ConfigurationTarget.WORKSPACE, label: localize('mcp.target.workspace', "Workspace Settings"), description: localize('mcp.target.workspace.description', "Available in this workspace") }); + } + + if (options.length === 1) { + return options[0].target; + } + + + const targetPick = await this._quickInputService.pick(options, { + title: localize('mcp.target.title', "Choose where to save the configuration"), + }); + + return targetPick?.target; + } + + private async getAssistedConfig(type: AddConfigurationType): Promise<{ name: string; config: McpConfigurationServer } | undefined> { + const packageName = await this._quickInputService.input({ + ignoreFocusLost: true, + title: type === AddConfigurationType.NpmPackage + ? localize('mcp.npm.title', "Enter NPM Package Name") + : localize('mcp.pip.title', "Enter Pip Package Name"), + placeHolder: type === AddConfigurationType.NpmPackage + ? localize('mcp.npm.placeholder', "Package name (e.g., @org/package)") + : localize('mcp.pip.placeholder', "Package name (e.g., package-name)") + }); + + if (!packageName) { + return undefined; + } + + const enum LoadAction { + Retry = 'retry', + Cancel = 'cancel', + Allow = 'allow' + } + + const loadingQuickPickStore = new DisposableStore(); + const loadingQuickPick = loadingQuickPickStore.add(this._quickInputService.createQuickPick()); + loadingQuickPick.title = localize('mcp.loading.title', "Loading package details..."); + loadingQuickPick.busy = true; + loadingQuickPick.ignoreFocusOut = true; + + this._commandService.executeCommand( + AddConfigurationCopilotCommand.ValidatePackage, + { + type: type === AddConfigurationType.NpmPackage ? 'npm' : 'pip', + name: packageName, + targetConfig: { + ...mcpStdioServerSchema, + properties: { + ...mcpStdioServerSchema.properties, + name: { + type: 'string', + description: 'Suggested name of the server, alphanumeric and hyphen only', + } + }, + required: [...(mcpStdioServerSchema.required || []), 'name'], + }, + } + ).then(result => { + if (!result || result.state === 'error') { + loadingQuickPick.title = result?.error || 'Unknown error loading package'; + loadingQuickPick.items = [{ id: LoadAction.Retry, label: localize('mcp.error.retry', 'Try a different package') }, { id: LoadAction.Cancel, label: localize('cancel', 'Cancel') }]; + } else { + loadingQuickPick.title = localize('mcp.confirmPublish', 'Install {0} from {1}?', packageName, result.publisher); + loadingQuickPick.items = [ + { id: LoadAction.Allow, label: localize('allow', "Allow") }, + { id: LoadAction.Cancel, label: localize('cancel', 'Cancel') } + ]; + } + loadingQuickPick.busy = false; + }); + + const loadingAction = await new Promise(resolve => { + loadingQuickPick.onDidAccept(() => resolve(loadingQuickPick.selectedItems[0]?.id)); + loadingQuickPick.onDidHide(() => resolve(undefined)); + loadingQuickPick.show(); + }).finally(() => loadingQuickPick.dispose()); + + switch (loadingAction) { + case LoadAction.Retry: + return this.getAssistedConfig(type); + case LoadAction.Allow: + break; + case LoadAction.Cancel: + default: + return undefined; + } + + const configWithName = await this._commandService.executeCommand( + AddConfigurationCopilotCommand.StartFlow, + { name: packageName } + ); + + if (!configWithName) { + return undefined; + } + + const { name, ...config } = configWithName; + return { name, config }; + } + + public async run(): Promise { + // Step 1: Choose server type + const serverType = await this.getServerType(); + if (serverType === undefined) { + return; + } + + // Step 2: Get server details based on type + let serverConfig: McpConfigurationServer | undefined; + let suggestedName: string | undefined; + switch (serverType) { + case AddConfigurationType.Stdio: + serverConfig = await this.getStdioConfig(); + break; + case AddConfigurationType.SSE: + serverConfig = await this.getSSEConfig(); + break; + case AddConfigurationType.NpmPackage: { + const r = await this.getAssistedConfig(AddConfigurationType.NpmPackage); + serverConfig = r?.config; + suggestedName = r?.name; + break; + } + case AddConfigurationType.PipPackage: { + const r = await this.getAssistedConfig(AddConfigurationType.PipPackage); + serverConfig = r?.config; + suggestedName = r?.name; + break; + } + default: + assertNever(serverType); + } + + if (!serverConfig) { + return; + } + + // Step 3: Get server ID + const serverId = await this.getServerId(suggestedName); + if (!serverId) { + return; + } + + // Step 4: Choose configuration target if no configUri provided + let target: ConfigurationTarget | undefined; + const workspace = this._workspaceService.getWorkspace(); + if (!this._explicitConfigUri) { + target = await this.getConfigurationTarget(); + if (!target) { + return; + } + } + + // Step 5: Update configuration + const writeToUriDirect = this._explicitConfigUri + ? URI.parse(this._explicitConfigUri) + : target === ConfigurationTarget.WORKSPACE && workspace.folders.length === 1 + ? URI.joinPath(workspace.folders[0].uri, '.vscode', 'mcp.json') + : undefined; + + if (writeToUriDirect) { + await this._jsonEditingService.write(writeToUriDirect, [{ + path: ['servers', serverId], + value: serverConfig + }], true); + } else { + const settings: IMcpConfiguration = { ...getConfigValueInTarget(this._configurationService.inspect('mcp'), target!) }; + settings.servers = { ...settings.servers, [serverId]: serverConfig }; + await this._configurationService.updateValue('mcp', settings, target!); + } + } +} diff --git a/src/vs/workbench/contrib/mcp/browser/mcpDiscovery.ts b/src/vs/workbench/contrib/mcp/browser/mcpDiscovery.ts new file mode 100644 index 000000000000..f9d339b79626 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/browser/mcpDiscovery.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { IWorkbenchContribution } from '../../../common/contributions.js'; +import { mcpDiscoveryRegistry } from '../common/discovery/mcpDiscovery.js'; + +export class McpDiscovery extends Disposable implements IWorkbenchContribution { + public static readonly ID = 'workbench.contrib.mcp.discovery'; + + constructor( + @IInstantiationService instantiationService: IInstantiationService, + ) { + super(); + for (const discovery of mcpDiscoveryRegistry.getAll()) { + const inst = this._register(instantiationService.createInstance(discovery)); + inst.start(); + } + } +} diff --git a/src/vs/workbench/contrib/mcp/common/discovery/configMcpDiscovery.ts b/src/vs/workbench/contrib/mcp/common/discovery/configMcpDiscovery.ts new file mode 100644 index 000000000000..04931acfe3d7 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/discovery/configMcpDiscovery.ts @@ -0,0 +1,145 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { equals as arrayEquals } from '../../../../../base/common/arrays.js'; +import { Disposable, IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; +import { Schemas } from '../../../../../base/common/network.js'; +import { ISettableObservable, observableValue } from '../../../../../base/common/observable.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { localize } from '../../../../../nls.js'; +import { ConfigurationTarget, IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { ILabelService } from '../../../../../platform/label/common/label.js'; +import { IProductService } from '../../../../../platform/product/common/productService.js'; +import { StorageScope } from '../../../../../platform/storage/common/storage.js'; +import { IWorkbenchEnvironmentService } from '../../../../services/environment/common/environmentService.js'; +import { IMcpConfiguration, mcpConfigurationSection } from '../mcpConfiguration.js'; +import { IMcpRegistry } from '../mcpRegistryTypes.js'; +import { McpCollectionSortOrder, McpServerDefinition, McpServerTransportType } from '../mcpTypes.js'; +import { IMcpDiscovery } from './mcpDiscovery.js'; + + +/** + * Discovers MCP servers based on various config sources. + */ +export class ConfigMcpDiscovery extends Disposable implements IMcpDiscovery { + private readonly configSources: { + key: 'userLocalValue' | 'userRemoteValue' | 'workspaceValue'; + label: string; + serverDefinitions: ISettableObservable; + scope: StorageScope; + target: ConfigurationTarget; + disposable: MutableDisposable; + order: number; + remoteAuthority?: string; + }[]; + + constructor( + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IMcpRegistry private readonly _mcpRegistry: IMcpRegistry, + @IProductService productService: IProductService, + @ILabelService labelService: ILabelService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + ) { + super(); + const remoteLabel = environmentService.remoteAuthority ? labelService.getHostLabel(Schemas.vscodeRemote, environmentService.remoteAuthority) : 'Remote'; + this.configSources = [ + { + key: 'userLocalValue', + target: ConfigurationTarget.USER_LOCAL, + label: localize('mcp.configuration.userLocalValue', 'Global in {0}', productService.nameShort), + serverDefinitions: observableValue(this, []), + scope: StorageScope.PROFILE, + disposable: this._register(new MutableDisposable()), + order: McpCollectionSortOrder.User, + }, + { + key: 'userRemoteValue', + target: ConfigurationTarget.USER_REMOTE, + label: localize('mcp.configuration.userRemoteValue', 'From {0}', remoteLabel), + serverDefinitions: observableValue(this, []), + scope: StorageScope.PROFILE, + disposable: this._register(new MutableDisposable()), + remoteAuthority: environmentService.remoteAuthority, + order: McpCollectionSortOrder.User + McpCollectionSortOrder.RemotePenalty, + }, + { + key: 'workspaceValue', + target: ConfigurationTarget.WORKSPACE, + label: localize('mcp.configuration.workspaceValue', 'From your workspace'), + serverDefinitions: observableValue(this, []), + scope: StorageScope.WORKSPACE, + disposable: this._register(new MutableDisposable()), + order: McpCollectionSortOrder.Workspace, + + }, + ]; + } + + public start() { + this._register(this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(mcpConfigurationSection)) { + this.sync(); + } + })); + + this.sync(); + } + + private sync() { + const configurationKey = this._configurationService.inspect(mcpConfigurationSection); + + for (const src of this.configSources) { + const collectionId = `mcp.config.${src.key}`; + let value = configurationKey[src.key]; + + // If we see there are MCP servers, migrate them automatically + if (value?.mcpServers) { + value = { ...value, servers: { ...value.servers, ...value.mcpServers }, mcpServers: undefined }; + this._configurationService.updateValue(mcpConfigurationSection, value, {}, src.target, { donotNotifyError: true }); + } + + const nextDefinitions = Object.entries(value?.servers || {}).map(([name, value]): McpServerDefinition => ({ + id: `${collectionId}.${name}`, + label: name, + launch: 'type' in value && value.type === 'sse' ? { + type: McpServerTransportType.SSE, + uri: URI.parse(value.url), + headers: Object.entries(value.headers || {}), + } : { + type: McpServerTransportType.Stdio, + args: value.args || [], + command: value.command, + env: value.env || {}, + cwd: undefined, + }, + variableReplacement: { + section: mcpConfigurationSection, + target: src.target, + } + })); + + if (arrayEquals(nextDefinitions, src.serverDefinitions.get(), McpServerDefinition.equals)) { + continue; + } + + + if (!nextDefinitions.length) { + src.disposable.clear(); + src.serverDefinitions.set(nextDefinitions, undefined); + } else { + src.serverDefinitions.set(nextDefinitions, undefined); + src.disposable.value ??= this._mcpRegistry.registerCollection({ + id: collectionId, + label: src.label, + presentation: { order: src.order }, + remoteAuthority: src.remoteAuthority || null, + serverDefinitions: src.serverDefinitions, + isTrustedByDefault: true, + scope: src.scope, + }); + } + } + } +} diff --git a/src/vs/workbench/contrib/mcp/common/discovery/extensionMcpDiscovery.ts b/src/vs/workbench/contrib/mcp/common/discovery/extensionMcpDiscovery.ts new file mode 100644 index 000000000000..75cbd2081f6d --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/discovery/extensionMcpDiscovery.ts @@ -0,0 +1,99 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, DisposableMap } from '../../../../../base/common/lifecycle.js'; +import { observableValue } from '../../../../../base/common/observable.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js'; +import { IExtensionService } from '../../../../services/extensions/common/extensions.js'; +import * as extensionsRegistry from '../../../../services/extensions/common/extensionsRegistry.js'; +import { mcpActivationEvent, mcpContributionPoint } from '../mcpConfiguration.js'; +import { IMcpRegistry } from '../mcpRegistryTypes.js'; +import { extensionPrefixedIdentifier, McpServerDefinition } from '../mcpTypes.js'; +import { IMcpDiscovery } from './mcpDiscovery.js'; + +const cacheKey = 'mcp.extCachedServers'; + +interface IServerCacheEntry { + readonly servers: readonly McpServerDefinition.Serialized[]; +} + +const _mcpExtensionPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint(mcpContributionPoint); + +export class ExtensionMcpDiscovery extends Disposable implements IMcpDiscovery { + private readonly _extensionCollectionIdsToPersist = new Set(); + private readonly cachedServers: { [collcetionId: string]: IServerCacheEntry }; + + constructor( + @IMcpRegistry private readonly _mcpRegistry: IMcpRegistry, + @IStorageService storageService: IStorageService, + @IExtensionService private readonly _extensionService: IExtensionService, + ) { + super(); + this.cachedServers = storageService.getObject(cacheKey, StorageScope.WORKSPACE, {}); + + this._register(storageService.onWillSaveState(() => { + let updated = false; + for (const collectionId of this._extensionCollectionIdsToPersist) { + const collection = this._mcpRegistry.collections.get().find(c => c.id === collectionId); + if (!collection || collection.lazy) { + continue; + } + + const defs = collection.serverDefinitions.get(); + if (defs) { + updated = true; + this.cachedServers[collectionId] = { servers: defs.map(McpServerDefinition.toSerialized) }; + } + } + + if (updated) { + storageService.store(cacheKey, this.cachedServers, StorageScope.WORKSPACE, StorageTarget.MACHINE); + } + })); + } + + public start(): void { + const extensionCollections = this._register(new DisposableMap()); + this._register(_mcpExtensionPoint.setHandler((_extensions, delta) => { + const { added, removed } = delta; + + for (const collections of removed) { + for (const coll of collections.value) { + extensionCollections.deleteAndDispose(extensionPrefixedIdentifier(collections.description.identifier, coll.id)); + } + } + + for (const collections of added) { + for (const coll of collections.value) { + const id = extensionPrefixedIdentifier(collections.description.identifier, coll.id); + this._extensionCollectionIdsToPersist.add(id); + + const serverDefs = this.cachedServers.hasOwnProperty(id) ? this.cachedServers[id].servers : undefined; + const dispo = this._mcpRegistry.registerCollection({ + id, + label: coll.label, + remoteAuthority: null, + isTrustedByDefault: true, + scope: StorageScope.WORKSPACE, + serverDefinitions: observableValue(this, serverDefs?.map(McpServerDefinition.fromSerialized) || []), + lazy: { + isCached: !!serverDefs, + load: () => this._activateExtensionServers(coll.id), + removed: () => extensionCollections.deleteAndDispose(id), + } + }); + + extensionCollections.set(id, dispo); + } + } + })); + } + + private async _activateExtensionServers(collectionId: string): Promise { + await this._extensionService.activateByEvent(mcpActivationEvent(collectionId)); + await Promise.all(this._mcpRegistry.delegates + .map(r => r.waitForInitialProviderPromises())); + } +} diff --git a/src/vs/workbench/contrib/mcp/common/discovery/mcpDiscovery.ts b/src/vs/workbench/contrib/mcp/common/discovery/mcpDiscovery.ts new file mode 100644 index 000000000000..a6a091cfaa7d --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/discovery/mcpDiscovery.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IDisposable } from '../../../../../base/common/lifecycle.js'; +import { SyncDescriptor0 } from '../../../../../platform/instantiation/common/descriptors.js'; + + +export interface IMcpDiscovery extends IDisposable { + start(): void; +} + +class McpDiscoveryRegistry { + private readonly _discovery: SyncDescriptor0[] = []; + + register(discovery: SyncDescriptor0): void { + this._discovery.push(discovery); + } + + getAll(): readonly SyncDescriptor0[] { + return this._discovery; + } +} + +export const mcpDiscoveryRegistry = new McpDiscoveryRegistry(); + + + diff --git a/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAbstract.ts b/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAbstract.ts new file mode 100644 index 000000000000..803439a8b83d --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAbstract.ts @@ -0,0 +1,120 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { RunOnceScheduler } from '../../../../../base/common/async.js'; +import { Disposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; +import { Schemas } from '../../../../../base/common/network.js'; +import { autorunWithStore, IObservable, observableValue } from '../../../../../base/common/observable.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { localize } from '../../../../../nls.js'; +import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { IFileService } from '../../../../../platform/files/common/files.js'; +import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; +import { ILabelService } from '../../../../../platform/label/common/label.js'; +import { INativeMcpDiscoveryData } from '../../../../../platform/mcp/common/nativeMcpDiscoveryHelper.js'; +import { observableConfigValue } from '../../../../../platform/observable/common/platformObservableUtils.js'; +import { StorageScope } from '../../../../../platform/storage/common/storage.js'; +import { Dto } from '../../../../services/extensions/common/proxyIdentifier.js'; +import { mcpDiscoverySection } from '../mcpConfiguration.js'; +import { IMcpRegistry } from '../mcpRegistryTypes.js'; +import { McpCollectionDefinition, McpCollectionSortOrder, McpServerDefinition } from '../mcpTypes.js'; +import { IMcpDiscovery } from './mcpDiscovery.js'; +import { ClaudeDesktopMpcDiscoveryAdapter, NativeMpcDiscoveryAdapter } from './nativeMcpDiscoveryAdapters.js'; + +/** + * Base class that discovers MCP servers on a filesystem, outside of the ones + * defined in VS Code settings. + */ +export abstract class FilesystemMpcDiscovery extends Disposable implements IMcpDiscovery { + private readonly adapters: readonly NativeMpcDiscoveryAdapter[]; + private _fsDiscoveryEnabled: IObservable; + private suffix = ''; + + constructor( + remoteAuthority: string | null, + @ILabelService labelService: ILabelService, + @IFileService private readonly fileService: IFileService, + @IInstantiationService instantiationService: IInstantiationService, + @IMcpRegistry private readonly mcpRegistry: IMcpRegistry, + @IConfigurationService configurationService: IConfigurationService, + ) { + super(); + if (remoteAuthority) { + this.suffix = ' ' + localize('onRemoteLabel', ' on {0}', labelService.getHostLabel(Schemas.vscodeRemote, remoteAuthority)); + } + + this._fsDiscoveryEnabled = observableConfigValue(mcpDiscoverySection, false, configurationService); + + this.adapters = [ + instantiationService.createInstance(ClaudeDesktopMpcDiscoveryAdapter, remoteAuthority) + ]; + } + + public abstract start(): void; + + protected setDetails(detailsDto: Dto | undefined) { + if (!detailsDto) { + return; + } + + const details: INativeMcpDiscoveryData = { + ...detailsDto, + homedir: URI.revive(detailsDto.homedir), + xdgHome: detailsDto.xdgHome ? URI.revive(detailsDto.xdgHome) : undefined, + winAppData: detailsDto.winAppData ? URI.revive(detailsDto.winAppData) : undefined, + }; + + for (const adapter of this.adapters) { + const file = adapter.getFilePath(details); + if (!file) { + continue; + } + + const collection = { + id: adapter.id, + label: adapter.label + this.suffix, + remoteAuthority: adapter.remoteAuthority, + scope: StorageScope.PROFILE, + isTrustedByDefault: false, + serverDefinitions: observableValue(this, []), + presentation: { + origin: file, + order: adapter.order + (adapter.remoteAuthority ? McpCollectionSortOrder.RemotePenalty : 0), + }, + } satisfies McpCollectionDefinition; + + const collectionRegistration = this._register(new MutableDisposable()); + const updateFile = async () => { + let definitions: McpServerDefinition[] = []; + try { + const contents = await this.fileService.readFile(file); + definitions = adapter.adaptFile(contents.value, details) || []; + } catch { + // ignored + } + if (!definitions.length) { + collectionRegistration.clear(); + } else { + collection.serverDefinitions.set(definitions, undefined); + if (!collectionRegistration.value) { + collectionRegistration.value = this.mcpRegistry.registerCollection(collection); + } + } + }; + + this._register(autorunWithStore((reader, store) => { + if (!this._fsDiscoveryEnabled.read(reader)) { + collectionRegistration.clear(); + return; + } + + const throttler = store.add(new RunOnceScheduler(updateFile, 500)); + const watcher = store.add(this.fileService.createWatcher(file, { recursive: false, excludes: [] })); + store.add(watcher.onDidChange(() => throttler.schedule())); + updateFile(); + })); + } + } +} diff --git a/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAdapters.ts b/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAdapters.ts new file mode 100644 index 000000000000..8eb01b9c7227 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAdapters.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { VSBuffer } from '../../../../../base/common/buffer.js'; +import { Platform } from '../../../../../base/common/platform.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { INativeMcpDiscoveryData } from '../../../../../platform/mcp/common/nativeMcpDiscoveryHelper.js'; +import { McpCollectionSortOrder, McpServerDefinition, McpServerTransportType } from '../mcpTypes.js'; + +export interface NativeMpcDiscoveryAdapter { + readonly remoteAuthority: string | null; + readonly id: string; + readonly label: string; + readonly order: number; + + getFilePath(details: INativeMcpDiscoveryData): URI | undefined; + adaptFile(contents: VSBuffer, details: INativeMcpDiscoveryData): McpServerDefinition[] | undefined; +} + +export class ClaudeDesktopMpcDiscoveryAdapter implements NativeMpcDiscoveryAdapter { + public readonly id: string; + public readonly label: string = 'Claude Desktop'; + public readonly order = McpCollectionSortOrder.Filesystem; + + constructor(public readonly remoteAuthority: string | null) { + this.id = `claude-desktop.${this.remoteAuthority}`; + } + + getFilePath({ platform, winAppData, xdgHome, homedir }: INativeMcpDiscoveryData): URI | undefined { + if (platform === Platform.Windows) { + const appData = winAppData || URI.joinPath(homedir, 'AppData', 'Roaming'); + return URI.joinPath(appData, 'Claude', 'claude_desktop_config.json'); + } else if (platform === Platform.Mac) { + return URI.joinPath(homedir, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'); + } else { + const configDir = xdgHome || URI.joinPath(homedir, '.config'); + return URI.joinPath(configDir, 'Claude', 'claude_desktop_config.json'); + } + } + + adaptFile(contents: VSBuffer, { homedir }: INativeMcpDiscoveryData): McpServerDefinition[] | undefined { + let parsed: { + mcpServers: Record; + }>; + }; + + try { + parsed = JSON.parse(contents.toString()); + } catch { + return; + } + return Object.entries(parsed.mcpServers).map(([name, server]): McpServerDefinition => { + return { + id: `claude_desktop_config.${name}`, + label: name, + launch: { + type: McpServerTransportType.Stdio, + args: server.args || [], + command: server.command, + env: server.env || {}, + cwd: homedir, + } + }; + }); + } +} diff --git a/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpRemoteDiscovery.ts b/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpRemoteDiscovery.ts new file mode 100644 index 000000000000..4f525561f6f6 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpRemoteDiscovery.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ProxyChannel } from '../../../../../base/parts/ipc/common/ipc.js'; +import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { IFileService } from '../../../../../platform/files/common/files.js'; +import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; +import { ILabelService } from '../../../../../platform/label/common/label.js'; +import { ILogService } from '../../../../../platform/log/common/log.js'; +import { INativeMcpDiscoveryHelperService, NativeMcpDiscoveryHelperChannelName } from '../../../../../platform/mcp/common/nativeMcpDiscoveryHelper.js'; +import { IRemoteAgentService } from '../../../../services/remote/common/remoteAgentService.js'; +import { IMcpRegistry } from '../mcpRegistryTypes.js'; +import { FilesystemMpcDiscovery } from './nativeMcpDiscoveryAbstract.js'; + +/** + * Discovers MCP servers on the remote filesystem, if any. + */ +export class RemoteNativeMpcDiscovery extends FilesystemMpcDiscovery { + constructor( + @IRemoteAgentService private readonly remoteAgent: IRemoteAgentService, + @ILogService private readonly logService: ILogService, + @ILabelService labelService: ILabelService, + @IFileService fileService: IFileService, + @IInstantiationService instantiationService: IInstantiationService, + @IMcpRegistry mcpRegistry: IMcpRegistry, + @IConfigurationService configurationService: IConfigurationService, + ) { + super(remoteAgent.getConnection()?.remoteAuthority || null, labelService, fileService, instantiationService, mcpRegistry, configurationService); + } + + public override async start() { + const connection = this.remoteAgent.getConnection(); + if (!connection) { + return this.setDetails(undefined); + } + + await connection.withChannel(NativeMcpDiscoveryHelperChannelName, async channel => { + const service = ProxyChannel.toService(channel); + + service.load().then( + data => this.setDetails(data), + err => { + this.logService.warn('Error getting remote process MCP environment', err); + this.setDetails(undefined); + } + ); + }); + } +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts b/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts new file mode 100644 index 000000000000..46864f954c10 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts @@ -0,0 +1,142 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IJSONSchema } from '../../../../base/common/jsonSchema.js'; +import { localize } from '../../../../nls.js'; +import { IMcpCollectionContribution } from '../../../../platform/extensions/common/extensions.js'; +import { mcpSchemaId } from '../../../services/configuration/common/configuration.js'; +import { inputsSchema } from '../../../services/configurationResolver/common/configurationResolverSchema.js'; +import { IExtensionPointDescriptor } from '../../../services/extensions/common/extensionsRegistry.js'; + +export type { McpConfigurationServer, IMcpConfigurationStdio, IMcpConfiguration } from '../../../../platform/mcp/common/mcpPlatformTypes.js'; + +const mcpActivationEventPrefix = 'onMcpCollection:'; + +export const mcpActivationEvent = (collectionId: string) => mcpActivationEventPrefix + collectionId; + +const mcpSchemaExampleServer = { + command: 'node', + args: ['my-mcp-server.js'], + env: {}, +}; + +export const mcpConfigurationSection = 'mcp'; +export const mcpDiscoverySection = 'chat.mcp.discovery.enabled'; + +export const mcpSchemaExampleServers = { + 'mcp-server-time': { + command: 'python', + args: ['-m', 'mcp_server_time', '--local-timezone=America/Los_Angeles'], + env: {}, + } +}; + +export const mcpStdioServerSchema: IJSONSchema = { + type: 'object', + additionalProperties: false, + examples: [mcpSchemaExampleServer], + properties: { + type: { + type: 'string', + enum: ['stdio'], + description: localize('app.mcp.json.type', "The type of the server.") + }, + command: { + type: 'string', + description: localize('app.mcp.json.command', "The command to run the server.") + }, + args: { + type: 'array', + description: localize('app.mcp.args.command', "Arguments passed to the server."), + items: { + type: 'string' + }, + }, + env: { + description: localize('app.mcp.env.command', "Environment variables passed to the server."), + additionalProperties: { + anyOf: [ + { type: 'null' }, + { type: 'string' }, + { type: 'number' }, + ] + } + }, + } +}; + +export const mcpServerSchema: IJSONSchema = { + id: mcpSchemaId, + type: 'object', + title: localize('app.mcp.json.title', "Model Context Protocol Servers"), + allowTrailingCommas: true, + allowComments: true, + additionalProperties: false, + properties: { + servers: { + examples: [mcpSchemaExampleServers], + additionalProperties: { + oneOf: [mcpStdioServerSchema, { + type: 'object', + additionalProperties: false, + required: ['url', 'type'], + examples: [{ + type: 'sse', + url: 'http://localhost:3001', + headers: {}, + }], + properties: { + type: { + type: 'string', + enum: ['sse'], + description: localize('app.mcp.json.type', "The type of the server.") + }, + url: { + type: 'string', + format: 'uri', + description: localize('app.mcp.json.url', "The URL of the server-sent-event (SSE) server.") + }, + env: { + description: localize('app.mcp.json.headers', "Additional headers sent to the server."), + additionalProperties: { type: 'string' }, + }, + } + }] + } + }, + inputs: inputsSchema.definitions!.inputs + } +}; + +export const mcpContributionPoint: IExtensionPointDescriptor = { + extensionPoint: 'modelContextServerCollections', + activationEventsGenerator(contribs, result) { + for (const contrib of contribs) { + if (contrib.id) { + result.push(mcpActivationEvent(contrib.id)); + } + } + }, + jsonSchema: { + description: localize('vscode.extension.contributes.mcp', 'Contributes Model Context Protocol servers. Users of this should also use `vscode.lm.registerMcpConfigurationProvider`.'), + type: 'array', + defaultSnippets: [{ body: [{ id: '', label: '' }] }], + items: { + additionalProperties: false, + type: 'object', + defaultSnippets: [{ body: { id: '', label: '' } }], + properties: { + id: { + description: localize('vscode.extension.contributes.mcp.id', "Unique ID for the collection."), + type: 'string' + }, + label: { + description: localize('vscode.extension.contributes.mcp.label', "Display name for the collection."), + type: 'string' + } + } + } + } +}; diff --git a/src/vs/workbench/contrib/mcp/common/mcpContextKeys.ts b/src/vs/workbench/contrib/mcp/common/mcpContextKeys.ts new file mode 100644 index 000000000000..0d0aff9aa6d3 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpContextKeys.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { autorun } from '../../../../base/common/observable.js'; +import { localize } from '../../../../nls.js'; +import { IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; +import { bindContextKey } from '../../../../platform/observable/common/platformObservableUtils.js'; +import { IWorkbenchContribution } from '../../../common/contributions.js'; +import { LazyCollectionState, IMcpService, McpServerToolsState, McpConnectionState } from './mcpTypes.js'; + + +export namespace McpContextKeys { + + export const serverCount = new RawContextKey('mcp.serverCount', undefined, { type: 'number', description: localize('mcp.serverCount.description', "Context key that has the number of registered MCP servers") }); + export const hasUnknownTools = new RawContextKey('mcp.hasUnknownTools', undefined, { type: 'boolean', description: localize('mcp.hasUnknownTools.description', "Indicates whether there are MCP servers with unknown tools.") }); + /** + * A context key that indicates whether there are any servers with errors. + * + * @type {boolean} + * @default undefined + * @description This key is used to track the presence of servers with errors in the MCP context. + */ + export const hasServersWithErrors = new RawContextKey('mcp.hasServersWithErrors', undefined, { type: 'boolean', description: localize('mcp.hasServersWithErrors.description', "Indicates whether there are any MCP servers with errors.") }); + export const toolsCount = new RawContextKey('mcp.toolsCount', undefined, { type: 'number', description: localize('mcp.toolsCount.description', "Context key that has the number of registered MCP tools") }); +} + + +export class McpContextKeysController extends Disposable implements IWorkbenchContribution { + + static readonly ID = 'workbench.contrib.mcp.contextKey'; + + constructor( + @IMcpService mcpService: IMcpService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super(); + + const ctxServerCount = McpContextKeys.serverCount.bindTo(contextKeyService); + const ctxToolsCount = McpContextKeys.toolsCount.bindTo(contextKeyService); + const ctxHasUnknownTools = McpContextKeys.hasUnknownTools.bindTo(contextKeyService); + + this._store.add(bindContextKey(McpContextKeys.hasServersWithErrors, contextKeyService, r => mcpService.servers.read(r).some(c => c.connectionState.read(r).state === McpConnectionState.Kind.Error))); + + this._store.add(autorun(r => { + const servers = mcpService.servers.read(r); + const serverTools = servers.map(s => s.tools.read(r)); + ctxServerCount.set(servers.length); + ctxToolsCount.set(serverTools.reduce((count, tools) => count + tools.length, 0)); + ctxHasUnknownTools.set(mcpService.lazyCollectionState.read(r) !== LazyCollectionState.AllKnown || servers.some(s => { + if (s.trusted.read(r) === false) { + return false; + } + + const toolState = s.toolsState.read(r); + return toolState === McpServerToolsState.Unknown || toolState === McpServerToolsState.RefreshingFromUnknown; + })); + })); + } +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpRegistry.ts b/src/vs/workbench/contrib/mcp/common/mcpRegistry.ts new file mode 100644 index 000000000000..6ae03b715b69 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpRegistry.ts @@ -0,0 +1,278 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { StringSHA1 } from '../../../../base/common/hash.js'; +import { MarkdownString } from '../../../../base/common/htmlContent.js'; +import { Lazy } from '../../../../base/common/lazy.js'; +import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; +import { derived, IObservable, observableValue } from '../../../../base/common/observable.js'; +import { basename } from '../../../../base/common/resources.js'; +import { localize } from '../../../../nls.js'; +import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { observableMemento } from '../../../../platform/observable/common/observableMemento.js'; +import { IProductService } from '../../../../platform/product/common/productService.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +import { IConfigurationResolverService } from '../../../services/configurationResolver/common/configurationResolver.js'; +import { McpRegistryInputStorage } from './mcpRegistryInputStorage.js'; +import { IMcpHostDelegate, IMcpRegistry, IMcpResolveConnectionOptions } from './mcpRegistryTypes.js'; +import { McpServerConnection } from './mcpServerConnection.js'; +import { IMcpServerConnection, LazyCollectionState, McpCollectionDefinition, McpCollectionReference } from './mcpTypes.js'; + +const createTrustMemento = observableMemento>>({ + defaultValue: {}, + key: 'mcp.trustedCollections' +}); + +const collectionPrefixLen = 3; + +export class McpRegistry extends Disposable implements IMcpRegistry { + declare public readonly _serviceBrand: undefined; + + private readonly _trustPrompts = new Map>(); + + private readonly _collections = observableValue('collections', []); + private readonly _delegates: IMcpHostDelegate[] = []; + public readonly collections: IObservable = this._collections; + + private readonly _collectionToPrefixes = this._collections.map(c => { + // This creates tool prefixes based on a hash of the collection ID. This is + // a short prefix because tool names that are too long can cause errors (#243602). + // So we take a hash (in order for tools to be stable, because randomized + // names can cause hallicinations if present in history) and then adjust + // them if there are any collisions. + type CollectionHash = { view: number; hash: string; collection: McpCollectionDefinition }; + + const hashes = c.map((collection): CollectionHash => { + const sha = new StringSHA1(); + sha.update(collection.id); + return { view: 0, hash: sha.digest(), collection }; + }); + + const view = (h: CollectionHash) => h.hash.slice(h.view, h.view + collectionPrefixLen); + + let collided = false; + do { + hashes.sort((a, b) => view(a).localeCompare(view(b)) || a.collection.id.localeCompare(b.collection.id)); + collided = false; + for (let i = 1; i < hashes.length; i++) { + const prev = hashes[i - 1]; + const curr = hashes[i]; + if (view(prev) === view(curr) && curr.view + collectionPrefixLen < curr.hash.length) { + curr.view++; + collided = true; + } + } + } while (collided); + + return Object.fromEntries(hashes.map(h => [h.collection.id, view(h) + '.'])); + }); + + private readonly _workspaceStorage = new Lazy(() => this._register(this._instantiationService.createInstance(McpRegistryInputStorage, StorageScope.WORKSPACE, StorageTarget.USER))); + private readonly _profileStorage = new Lazy(() => this._register(this._instantiationService.createInstance(McpRegistryInputStorage, StorageScope.PROFILE, StorageTarget.USER))); + + private readonly _trustMemento = new Lazy(() => this._register(createTrustMemento(StorageScope.APPLICATION, StorageTarget.MACHINE, this._storageService))); + private readonly _lazyCollectionsToUpdate = new Set(); + private readonly _ongoingLazyActivations = observableValue(this, 0); + + public readonly lazyCollectionState = derived(reader => { + if (this._ongoingLazyActivations.read(reader) > 0) { + return LazyCollectionState.LoadingUnknown; + } + const collections = this._collections.read(reader); + return collections.some(c => c.lazy && c.lazy.isCached === false) ? LazyCollectionState.HasUnknown : LazyCollectionState.AllKnown; + }); + + public get delegates(): readonly IMcpHostDelegate[] { + return this._delegates; + } + + constructor( + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IConfigurationResolverService private readonly _configurationResolverService: IConfigurationResolverService, + @IDialogService private readonly _dialogService: IDialogService, + @IStorageService private readonly _storageService: IStorageService, + @IProductService private readonly _productService: IProductService, + ) { + super(); + } + + public registerDelegate(delegate: IMcpHostDelegate): IDisposable { + this._delegates.push(delegate); + return { + dispose: () => { + const index = this._delegates.indexOf(delegate); + if (index !== -1) { + this._delegates.splice(index, 1); + } + } + }; + } + + public registerCollection(collection: McpCollectionDefinition): IDisposable { + const currentCollections = this._collections.get(); + const toReplace = currentCollections.find(c => c.lazy && c.id === collection.id); + + // Incoming collections replace the "lazy" versions. See `ExtensionMcpDiscovery` for an example. + if (toReplace) { + this._lazyCollectionsToUpdate.add(collection.id); + this._collections.set(currentCollections.map(c => c === toReplace ? collection : c), undefined); + } else { + this._collections.set([...currentCollections, collection], undefined); + } + + return { + dispose: () => { + const currentCollections = this._collections.get(); + this._collections.set(currentCollections.filter(c => c !== collection), undefined); + } + }; + } + + public collectionToolPrefix(collection: McpCollectionReference): IObservable { + return this._collectionToPrefixes.map(p => p[collection.id] ?? ''); + } + + public async discoverCollections(): Promise { + const toDiscover = this._collections.get().filter(c => c.lazy && !c.lazy.isCached); + + this._ongoingLazyActivations.set(this._ongoingLazyActivations.get() + 1, undefined); + await Promise.all(toDiscover.map(c => c.lazy?.load())).finally(() => { + this._ongoingLazyActivations.set(this._ongoingLazyActivations.get() - 1, undefined); + }); + + const found: McpCollectionDefinition[] = []; + const current = this._collections.get(); + for (const collection of toDiscover) { + const rec = current.find(c => c.id === collection.id); + if (!rec) { + // ignored + } else if (rec.lazy) { + rec.lazy.removed?.(); // did not get replaced by the non-lazy version + } else { + found.push(rec); + } + } + + + return found; + } + + public clearSavedInputs() { + this._profileStorage.value.clearAll(); + this._workspaceStorage.value.clearAll(); + } + + public resetTrust(): void { + this._trustMemento.value.set({}, undefined); + } + + public getTrust(collectionRef: McpCollectionReference): IObservable { + return derived(reader => { + const collection = this._collections.read(reader).find(c => c.id === collectionRef.id); + if (!collection || collection.isTrustedByDefault) { + return true; + } + + const memento = this._trustMemento.value.read(reader); + return memento.hasOwnProperty(collection.id) ? memento[collection.id] : undefined; + }); + } + + private _promptForTrust(collection: McpCollectionDefinition): Promise { + // Collect all trust prompts for a single config so that concurrently trying to start N + // servers in a config don't result in N different dialogs + let resultPromise = this._trustPrompts.get(collection.id); + resultPromise ??= this._promptForTrustOpenDialog(collection).finally(() => { + this._trustPrompts.delete(collection.id); + }); + this._trustPrompts.set(collection.id, resultPromise); + + return resultPromise; + } + + private async _promptForTrustOpenDialog(collection: McpCollectionDefinition): Promise { + const labelWithOrigin = collection.presentation?.origin + ? `[\`${basename(collection.presentation.origin)}\`](${collection.presentation.origin})` + : collection.label; + const result = await this._dialogService.prompt( + { + message: localize('trustTitleWithOrigin', 'Trust MCP servers from {0}?', collection.label), + custom: { + markdownDetails: [{ + markdown: new MarkdownString(localize('mcp.trust.details', '{0} discovered Model Context Protocol servers from {1} (`{2}`). {0} can use their capabilities in Chat.\n\nDo you want to allow running MCP servers from {3}?', this._productService.nameShort, collection.label, collection.serverDefinitions.get().map(s => s.label).join('`, `'), labelWithOrigin)), + dismissOnLinkClick: true, + }] + }, + buttons: [ + { label: localize('mcp.trust.yes', 'Trust'), run: () => true }, + { label: localize('mcp.trust.no', 'Do not trust'), run: () => false } + ], + }, + ); + + return result.result; + } + + public async resolveConnection({ collectionRef, definitionRef, forceTrust }: IMcpResolveConnectionOptions): Promise { + const collection = this._collections.get().find(c => c.id === collectionRef.id); + const definition = collection?.serverDefinitions.get().find(s => s.id === definitionRef.id); + if (!collection || !definition) { + throw new Error(`Collection or definition not found for ${collectionRef.id} and ${definitionRef.id}`); + } + + const delegate = this._delegates.find(d => d.canStart(collection, definition)); + if (!delegate) { + throw new Error('No delegate found that can handle the connection'); + } + + if (!collection.isTrustedByDefault) { + const memento = this._trustMemento.value.get(); + const trusted = memento.hasOwnProperty(collection.id) ? memento[collection.id] : undefined; + + if (trusted) { + // continue + } else if (trusted === undefined || forceTrust) { + const trustValue = await this._promptForTrust(collection); + if (trustValue !== undefined) { + this._trustMemento.value.set({ ...memento, [collection.id]: trustValue }, undefined); + } + if (!trustValue) { + return; + } + } else /** trusted === false && !forceTrust */ { + return undefined; + } + } + + let launch = definition.launch; + + if (definition.variableReplacement) { + const inputStorage = definition.variableReplacement.folder ? this._workspaceStorage.value : this._profileStorage.value; + const previouslyStored = await inputStorage.getMap(); + + const { folder, section, target } = definition.variableReplacement; + + // based on _configurationResolverService.resolveWithInteractionReplace + launch = await this._configurationResolverService.resolveAnyAsync(folder, launch); + + const newVariables = await this._configurationResolverService.resolveWithInteraction(folder, launch, section, previouslyStored, target); + + if (newVariables?.size) { + const completeVariables = { ...previouslyStored, ...Object.fromEntries(newVariables) }; + launch = await this._configurationResolverService.resolveAnyAsync(folder, launch, completeVariables); + await inputStorage.setSecrets(completeVariables); + } + } + + return this._instantiationService.createInstance( + McpServerConnection, + collection, + definition, + delegate, + launch, + ); + } +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpRegistryInputStorage.ts b/src/vs/workbench/contrib/mcp/common/mcpRegistryInputStorage.ts new file mode 100644 index 000000000000..6e23a0599988 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpRegistryInputStorage.ts @@ -0,0 +1,192 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Sequencer } from '../../../../base/common/async.js'; +import { decodeBase64, encodeBase64, VSBuffer } from '../../../../base/common/buffer.js'; +import { Lazy } from '../../../../base/common/lazy.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { isEmptyObject } from '../../../../base/common/types.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +import { ISecretStorageService } from '../../../../platform/secrets/common/secrets.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; + +const MCP_ENCRYPTION_KEY_NAME = 'mcpEncryptionKey'; +const MCP_ENCRYPTION_KEY_ALGORITHM = 'AES-GCM'; +const MCP_ENCRYPTION_KEY_LEN = 256; +const MCP_ENCRYPTION_IV_LENGTH = 12; // 96 bits +const MCP_DATA_STORED_VERSION = 1; +const MCP_DATA_STORED_KEY = 'mcpInputs'; + +interface IStoredData { + version: number; + values: Record; + secrets?: { value: string; iv: string }; // base64, encrypted +} + +interface IHydratedData extends IStoredData { + unsealedSecrets?: Record; +} + +export class McpRegistryInputStorage extends Disposable { + private static secretSequencer = new Sequencer(); + private readonly _secretsSealerSequencer = new Sequencer(); + + private readonly _getEncryptionKey = new Lazy(() => { + return McpRegistryInputStorage.secretSequencer.queue(async () => { + const existing = await this._secretStorageService.get(MCP_ENCRYPTION_KEY_NAME); + if (existing) { + try { + const parsed: JsonWebKey = JSON.parse(existing); + return await crypto.subtle.importKey('jwk', parsed, MCP_ENCRYPTION_KEY_ALGORITHM, false, ['encrypt', 'decrypt']); + } catch { + // fall through + } + } + + const key = await crypto.subtle.generateKey( + { name: MCP_ENCRYPTION_KEY_ALGORITHM, length: MCP_ENCRYPTION_KEY_LEN }, + true, + ['encrypt', 'decrypt'], + ); + + const exported = await crypto.subtle.exportKey('jwk', key); + await this._secretStorageService.set(MCP_ENCRYPTION_KEY_NAME, JSON.stringify(exported)); + return key; + }); + }); + + private _didChange = false; + + private _record = new Lazy(() => { + const stored = this._storageService.getObject(MCP_DATA_STORED_KEY, this._scope); + return stored?.version === MCP_DATA_STORED_VERSION ? { ...stored } : { version: MCP_DATA_STORED_VERSION, values: {} }; + }); + + + constructor( + private readonly _scope: StorageScope, + _target: StorageTarget, + @IStorageService private readonly _storageService: IStorageService, + @ISecretStorageService private readonly _secretStorageService: ISecretStorageService, + @ILogService private readonly _logService: ILogService, + ) { + super(); + + this._register(_storageService.onWillSaveState(() => { + if (this._didChange) { + this._storageService.store(MCP_DATA_STORED_KEY, { + version: MCP_DATA_STORED_VERSION, + values: this._record.value.values, + secrets: this._record.value.secrets, + } satisfies IStoredData, this._scope, _target); + this._didChange = false; + } + })); + } + + /** Deletes all collection data from storage. */ + public clearAll() { + this._record.value.values = {}; + this._record.value.secrets = undefined; + this._record.value.unsealedSecrets = undefined; + this._didChange = true; + } + + /** Delete a single collection data from the storage. */ + public async clear(inputKey: string) { + const secrets = await this._unsealSecrets(); + delete this._record.value.values[inputKey]; + this._didChange = true; + + if (secrets.hasOwnProperty(inputKey)) { + delete secrets[inputKey]; + await this._sealSecrets(); + } + } + + /** Gets a mapping of saved input data. */ + public async getMap() { + const secrets = await this._unsealSecrets(); + return { ...this._record.value.values, ...secrets }; + } + + /** Updates the input data mapping. */ + public async setPlainText(values: Record) { + Object.assign(this._record.value.values, values); + this._didChange = true; + } + + /** Updates the input secrets mapping. */ + public async setSecrets(values: Record) { + const unsealed = await this._unsealSecrets(); + Object.assign(unsealed, values); + await this._sealSecrets(); + } + + private async _sealSecrets() { + return this._secretsSealerSequencer.queue(async () => { + if (!this._record.value.unsealedSecrets || isEmptyObject(this._record.value.unsealedSecrets)) { + this._record.value.secrets = undefined; + return; + } + + if (!this._record.value.secrets) { + const iv = crypto.getRandomValues(new Uint8Array(MCP_ENCRYPTION_IV_LENGTH)); + this._record.value.secrets = { + value: '', + iv: encodeBase64(VSBuffer.wrap(iv)), + }; + } + + const toSeal = JSON.stringify(this._record.value.unsealedSecrets); + const iv = decodeBase64(this._record.value.secrets.iv); + const key = await this._getEncryptionKey.value; + const encrypted = await crypto.subtle.encrypt( + { name: MCP_ENCRYPTION_KEY_ALGORITHM, iv: iv.buffer }, + key, + new TextEncoder().encode(toSeal).buffer, + ); + + const enc = encodeBase64(VSBuffer.wrap(new Uint8Array(encrypted))); + if (this._record.value.secrets.value === enc) { + return; + } + + this._record.value.secrets.value = enc; + this._didChange = true; + }); + } + + private async _unsealSecrets(): Promise> { + if (!this._record.value.secrets) { + return this._record.value.unsealedSecrets ??= {}; + } + + if (this._record.value.unsealedSecrets) { + return this._record.value.unsealedSecrets; + } + + try { + const key = await this._getEncryptionKey.value; + const iv = decodeBase64(this._record.value.secrets.iv); + const encrypted = decodeBase64(this._record.value.secrets.value); + + const decrypted = await crypto.subtle.decrypt( + { name: MCP_ENCRYPTION_KEY_ALGORITHM, iv: iv.buffer }, + key, + encrypted.buffer, + ); + + const unsealedSecrets = JSON.parse(new TextDecoder().decode(decrypted)); + this._record.value.unsealedSecrets = unsealedSecrets; + return unsealedSecrets; + } catch (e) { + this._logService.warn('Error unsealing MCP secrets', e); + this._record.value.secrets = undefined; + } + + return {}; + } +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpRegistryTypes.ts b/src/vs/workbench/contrib/mcp/common/mcpRegistryTypes.ts new file mode 100644 index 000000000000..e835be770b88 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpRegistryTypes.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from '../../../../base/common/event.js'; +import { IDisposable } from '../../../../base/common/lifecycle.js'; +import { IObservable } from '../../../../base/common/observable.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { McpCollectionDefinition, McpServerDefinition, McpServerLaunch, McpConnectionState, IMcpServerConnection, McpCollectionReference, McpDefinitionReference, LazyCollectionState } from './mcpTypes.js'; +import { MCP } from './modelContextProtocol.js'; + +export const IMcpRegistry = createDecorator('mcpRegistry'); + +/** Message transport to a single MCP server. */ +export interface IMcpMessageTransport extends IDisposable { + readonly state: IObservable; + readonly onDidLog: Event; + readonly onDidReceiveMessage: Event; + send(message: MCP.JSONRPCMessage): void; + stop(): void; +} + +export interface IMcpHostDelegate { + waitForInitialProviderPromises(): Promise; + canStart(collectionDefinition: McpCollectionDefinition, serverDefinition: McpServerDefinition): boolean; + start(collectionDefinition: McpCollectionDefinition, serverDefinition: McpServerDefinition, resolvedLaunch: McpServerLaunch): IMcpMessageTransport; +} + +export interface IMcpResolveConnectionOptions { + collectionRef: McpCollectionReference; + definitionRef: McpDefinitionReference; + /** If set, the user will be asked to trust the collection even if they untrusted it previously */ + forceTrust?: boolean; +} + +export interface IMcpRegistry { + readonly _serviceBrand: undefined; + + readonly collections: IObservable; + readonly delegates: readonly IMcpHostDelegate[]; + + /** Gets the prefix that should be applied to a collection's tools in order to avoid ID conflicts */ + collectionToolPrefix(collection: McpCollectionReference): IObservable; + + /** Whether there are new collections that can be resolved with a discover() call */ + readonly lazyCollectionState: IObservable; + /** Discover new collections, returning newly-discovered ones. */ + discoverCollections(): Promise; + + registerDelegate(delegate: IMcpHostDelegate): IDisposable; + registerCollection(collection: McpCollectionDefinition): IDisposable; + + /** Resets the trust state of all collections. */ + resetTrust(): void; + + /** Gets whether the collection is trusted. */ + getTrust(collection: McpCollectionReference): IObservable; + + /** Resets any saved inputs for the connection. */ + clearSavedInputs(collection: McpCollectionReference, definition: McpServerDefinition): void; + /** Creates a connection for the collection and definition. */ + resolveConnection(options: IMcpResolveConnectionOptions): Promise; +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpServer.ts b/src/vs/workbench/contrib/mcp/common/mcpServer.ts new file mode 100644 index 000000000000..640f98da8ca3 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpServer.ts @@ -0,0 +1,325 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { raceCancellationError, Sequencer } from '../../../../base/common/async.js'; +import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { LRUCache } from '../../../../base/common/map.js'; +import { autorun, autorunWithStore, derived, disposableObservableValue, IObservable, ITransaction, observableFromEvent, ObservablePromise, observableValue, transaction } from '../../../../base/common/observable.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { IExtensionService } from '../../../services/extensions/common/extensions.js'; +import { mcpActivationEvent } from './mcpConfiguration.js'; +import { IMcpRegistry } from './mcpRegistryTypes.js'; +import { McpServerRequestHandler } from './mcpServerRequestHandler.js'; +import { extensionMcpCollectionPrefix, IMcpServer, IMcpServerConnection, IMcpTool, McpCollectionReference, McpConnectionFailedError, McpConnectionState, McpDefinitionReference, McpServerDefinition, McpServerToolsState } from './mcpTypes.js'; +import { MCP } from './modelContextProtocol.js'; + + +interface IToolCacheEntry { + /** Cached tools so we can show what's available before it's started */ + readonly tools: readonly MCP.Tool[]; +} + +interface IServerCacheEntry { + readonly servers: readonly McpServerDefinition.Serialized[]; +} + +export class McpServerMetadataCache extends Disposable { + private didChange = false; + private readonly cache = new LRUCache(128); + private readonly extensionServers = new Map(); + + constructor( + scope: StorageScope, + @IStorageService storageService: IStorageService, + ) { + super(); + + type StoredType = { + extensionServers: [string, IServerCacheEntry][]; + serverTools: [string, IToolCacheEntry][]; + }; + + const storageKey = 'mcpToolCache'; + this._register(storageService.onWillSaveState(() => { + if (this.didChange) { + storageService.store(storageKey, { + extensionServers: [...this.extensionServers], + serverTools: this.cache.toJSON(), + } satisfies StoredType, scope, StorageTarget.MACHINE); + this.didChange = false; + } + })); + + try { + const cached: StoredType | undefined = storageService.getObject(storageKey, scope); + this.extensionServers = new Map(cached?.extensionServers ?? []); + cached?.serverTools?.forEach(([k, v]) => this.cache.set(k, v)); + } catch { + // ignored + } + } + + /** Resets the cache for tools and extension servers */ + reset() { + this.cache.clear(); + this.extensionServers.clear(); + this.didChange = true; + } + + /** Gets cached tools for a server (used before a server is running) */ + getTools(definitionId: string): readonly MCP.Tool[] | undefined { + return this.cache.get(definitionId)?.tools; + } + + /** Sets cached tools for a server */ + storeTools(definitionId: string, tools: readonly MCP.Tool[]): void { + this.cache.set(definitionId, { ...this.cache.get(definitionId), tools }); + this.didChange = true; + } + + /** Gets cached servers for a collection (used for extensions, before the extension activates) */ + getServers(collectionId: string) { + return this.extensionServers.get(collectionId); + } + + /** Sets cached servers for a collection */ + storeServers(collectionId: string, entry: IServerCacheEntry | undefined): void { + if (entry) { + this.extensionServers.set(collectionId, entry); + } else { + this.extensionServers.delete(collectionId); + } + this.didChange = true; + } +} + +export class McpServer extends Disposable implements IMcpServer { + private readonly _connectionSequencer = new Sequencer(); + private readonly _connection = this._register(disposableObservableValue(this, undefined)); + + public readonly connection = this._connection; + public readonly connectionState: IObservable = derived(reader => this._connection.read(reader)?.state.read(reader) ?? { state: McpConnectionState.Kind.Stopped }); + + private get toolsFromCache() { + return this._toolCache.getTools(this.definition.id); + } + private readonly toolsFromServerPromise = observableValue | undefined>(this, undefined); + private readonly toolsFromServer = derived(reader => this.toolsFromServerPromise.read(reader)?.promiseResult.read(reader)?.data); + + public readonly tools: IObservable; + + public readonly toolsState = derived(reader => { + const fromServer = this.toolsFromServerPromise.read(reader); + const connectionState = this.connectionState.read(reader); + const isIdle = McpConnectionState.canBeStarted(connectionState.state) && !fromServer; + if (isIdle) { + return this.toolsFromCache ? McpServerToolsState.Cached : McpServerToolsState.Unknown; + } + + const fromServerResult = fromServer?.promiseResult.read(reader); + if (!fromServerResult) { + return this.toolsFromCache ? McpServerToolsState.RefreshingFromCached : McpServerToolsState.RefreshingFromUnknown; + } + + return fromServerResult.error ? (this.toolsFromCache ? McpServerToolsState.Cached : McpServerToolsState.Unknown) : McpServerToolsState.Live; + }); + + public get trusted() { + return this._mcpRegistry.getTrust(this.collection); + } + + constructor( + public readonly collection: McpCollectionReference, + public readonly definition: McpDefinitionReference, + private readonly _requiresExtensionActivation: boolean | undefined, + private readonly _toolCache: McpServerMetadataCache, + @IMcpRegistry private readonly _mcpRegistry: IMcpRegistry, + @IWorkspaceContextService workspacesService: IWorkspaceContextService, + @IExtensionService private readonly _extensionService: IExtensionService, + ) { + super(); + + // 1. Reflect workspaces into the MCP roots + const workspaces = observableFromEvent( + this, + workspacesService.onDidChangeWorkspaceFolders, + () => workspacesService.getWorkspace().folders, + ); + + this._register(autorunWithStore(reader => { + const cnx = this._connection.read(reader)?.handler.read(reader); + if (!cnx) { + return; + } + + cnx.roots = workspaces.read(reader).map(wf => ({ + uri: wf.uri.toString(), + name: wf.name, + })); + })); + + // 2. Populate this.tools when we connect to a server. + this._register(autorunWithStore((reader, store) => { + const cnx = this._connection.read(reader)?.handler.read(reader); + if (cnx) { + this.populateLiveData(cnx, store); + } else { + this.resetLiveData(); + } + })); + + // 3. Update the cache when tools update + this._register(autorun(reader => { + const tools = this.toolsFromServer.read(reader); + if (tools) { + this._toolCache.storeTools(definition.id, tools); + } + })); + + // 4. Publish tools + const toolPrefix = this._mcpRegistry.collectionToolPrefix(this.collection); + this.tools = derived(reader => { + const serverTools = this.toolsFromServer.read(reader); + const definitions = serverTools ?? this.toolsFromCache ?? []; + const prefix = toolPrefix.read(reader); + return definitions.map(def => new McpTool(this, prefix, def)); + }); + } + + public showOutput(): void { + this._connection.get()?.showOutput(); + } + + public start(isFromInteraction?: boolean): Promise { + return this._connectionSequencer.queue(async () => { + const activationEvent = mcpActivationEvent(this.collection.id.slice(extensionMcpCollectionPrefix.length)); + if (this._requiresExtensionActivation && !this._extensionService.activationEventIsDone(activationEvent)) { + await this._extensionService.activateByEvent(activationEvent); + await Promise.all(this._mcpRegistry.delegates + .map(r => r.waitForInitialProviderPromises())); + // This can happen if the server was created from a cached MCP server seen + // from an extension, but then it wasn't registered when the extension activated. + if (this._store.isDisposed) { + return { state: McpConnectionState.Kind.Stopped }; + } + } + + let connection = this._connection.get(); + if (connection && McpConnectionState.canBeStarted(connection.state.get().state)) { + connection.dispose(); + connection = undefined; + this._connection.set(connection, undefined); + } + + if (!connection) { + connection = await this._mcpRegistry.resolveConnection({ + collectionRef: this.collection, + definitionRef: this.definition, + forceTrust: isFromInteraction, + }); + if (!connection) { + return { state: McpConnectionState.Kind.Stopped }; + } + + if (this._store.isDisposed) { + connection.dispose(); + return { state: McpConnectionState.Kind.Stopped }; + } + + this._connection.set(connection, undefined); + } + + return connection.start(); + }); + } + + public stop(): Promise { + return this._connection.get()?.stop() || Promise.resolve(); + } + + private resetLiveData() { + transaction(tx => { + this.toolsFromServerPromise.set(undefined, tx); + }); + } + + private populateLiveData(handler: McpServerRequestHandler, store: DisposableStore) { + const cts = new CancellationTokenSource(); + store.add(toDisposable(() => cts.dispose(true))); + + // todo: add more than just tools here + + const updateTools = (tx: ITransaction | undefined) => { + this.toolsFromServerPromise.set(new ObservablePromise(handler.listTools({}, cts.token)), tx); + }; + + store.add(handler.onDidChangeToolList(() => updateTools(undefined))); + + transaction(tx => { + updateTools(tx); + }); + } + + /** + * Helper function to call the function on the handler once it's online. The + * connection started if it is not already. + */ + public async callOn(fn: (handler: McpServerRequestHandler) => Promise, token: CancellationToken = CancellationToken.None): Promise { + + await this.start(); // idempotent + + let ranOnce = false; + let d: IDisposable; + + const callPromise = new Promise((resolve, reject) => { + + d = autorun(reader => { + const connection = this._connection.read(reader); + if (!connection || ranOnce) { + return; + } + + const handler = connection.handler.read(reader); + if (!handler) { + const state = connection.state.read(reader); + if (state.state === McpConnectionState.Kind.Error) { + reject(new McpConnectionFailedError(`MCP server could not be started: ${state.message}`)); + return; + } else if (state.state === McpConnectionState.Kind.Stopped) { + reject(new McpConnectionFailedError('MCP server has stopped')); + return; + } else { + // keep waiting for handler + return; + } + } + + resolve(fn(handler)); + ranOnce = true; // aggressive prevent multiple racey calls, don't dispose because autorun is sync + }); + }); + + return raceCancellationError(callPromise, token).finally(() => d.dispose()); + } +} + +export class McpTool implements IMcpTool { + + readonly id: string; + + constructor( + private readonly _server: McpServer, + idPrefix: string, + public readonly definition: MCP.Tool, + ) { + this.id = (idPrefix + definition.name).replaceAll('.', '_'); + } + + call(params: Record, token?: CancellationToken): Promise { + return this._server.callOn(h => h.callTool({ name: this.definition.name, arguments: params }), token); + } +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpServerConnection.ts b/src/vs/workbench/contrib/mcp/common/mcpServerConnection.ts new file mode 100644 index 000000000000..5016ebe7f531 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpServerConnection.ts @@ -0,0 +1,145 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationTokenSource } from '../../../../base/common/cancellation.js'; +import { Disposable, DisposableStore, IReference, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { autorun, IObservable, observableValue } from '../../../../base/common/observable.js'; +import { localize } from '../../../../nls.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { ILogger, ILoggerService } from '../../../../platform/log/common/log.js'; +import { IOutputService } from '../../../services/output/common/output.js'; +import { IMcpHostDelegate, IMcpMessageTransport } from './mcpRegistryTypes.js'; +import { McpServerRequestHandler } from './mcpServerRequestHandler.js'; +import { McpCollectionDefinition, IMcpServerConnection, McpServerDefinition, McpConnectionState, McpServerLaunch } from './mcpTypes.js'; + +export class McpServerConnection extends Disposable implements IMcpServerConnection { + private readonly _launch = this._register(new MutableDisposable>()); + private readonly _state = observableValue('mcpServerState', { state: McpConnectionState.Kind.Stopped }); + private readonly _requestHandler = observableValue('mcpServerRequestHandler', undefined); + + public readonly state: IObservable = this._state; + public readonly handler: IObservable = this._requestHandler; + + private readonly _loggerId: string; + private readonly _logger: ILogger; + private _launchId = 0; + + constructor( + private readonly _collection: McpCollectionDefinition, + public readonly definition: McpServerDefinition, + private readonly _delegate: IMcpHostDelegate, + public readonly launchDefinition: McpServerLaunch, + @ILoggerService private readonly _loggerService: ILoggerService, + @IOutputService private readonly _outputService: IOutputService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + ) { + super(); + this._loggerId = `mcpServer/${definition.id}`; + this._logger = this._register(_loggerService.createLogger(this._loggerId, { hidden: true, name: `MCP: ${definition.label}` })); + } + + /** @inheritdoc */ + public showOutput(): void { + this._loggerService.setVisibility(this._loggerId, true); + this._outputService.showChannel(this._loggerId); + } + + /** @inheritdoc */ + public async start(): Promise { + const currentState = this._state.get(); + if (!McpConnectionState.canBeStarted(currentState.state)) { + return this._waitForState(McpConnectionState.Kind.Running, McpConnectionState.Kind.Error); + } + + const launchId = ++this._launchId; + this._launch.value = undefined; + this._state.set({ state: McpConnectionState.Kind.Starting }, undefined); + this._logger.info(localize('mcpServer.starting', 'Starting server {0}', this.definition.label)); + + try { + const launch = this._delegate.start(this._collection, this.definition, this.launchDefinition); + this._launch.value = this.adoptLaunch(launch, launchId); + return this._waitForState(McpConnectionState.Kind.Running, McpConnectionState.Kind.Error); + } catch (e) { + const errorState: McpConnectionState = { + state: McpConnectionState.Kind.Error, + message: e instanceof Error ? e.message : String(e) + }; + this._state.set(errorState, undefined); + return errorState; + } + } + + private adoptLaunch(launch: IMcpMessageTransport, launchId: number): IReference { + const store = new DisposableStore(); + const cts = new CancellationTokenSource(); + + store.add(toDisposable(() => cts.dispose(true))); + store.add(launch); + store.add(launch.onDidLog(msg => { + this._logger.info(msg); + })); + + let didStart = false; + store.add(autorun(reader => { + const state = launch.state.read(reader); + this._state.set(state, undefined); + this._logger.info(localize('mcpServer.state', 'Connection state: {0}', McpConnectionState.toString(state))); + + if (state.state === McpConnectionState.Kind.Running && !didStart) { + didStart = true; + McpServerRequestHandler.create(this._instantiationService, launch, this._logger, cts.token).then( + handler => { + if (this._launchId === launchId) { + this._requestHandler.set(handler, undefined); + } else { + handler.dispose(); + } + }, + err => { + store.dispose(); + if (this._launchId === launchId) { + this._logger.error(err); + this._state.set({ state: McpConnectionState.Kind.Error, message: `Could not initialize MCP server: ${err.message}` }, undefined); + } + }, + ); + } + })); + + return { dispose: () => store.dispose(), object: launch }; + } + + public async stop(): Promise { + this._launchId = -1; + this._logger.info(localize('mcpServer.stopping', 'Stopping server {0}', this.definition.label)); + this._launch.value?.object.stop(); + await this._waitForState(McpConnectionState.Kind.Stopped, McpConnectionState.Kind.Error); + } + + public override dispose(): void { + this._launchId = -1; + this._requestHandler.get()?.dispose(); + super.dispose(); + this._state.set({ state: McpConnectionState.Kind.Stopped }, undefined); + } + + private _waitForState(...kinds: McpConnectionState.Kind[]): Promise { + const current = this._state.get(); + if (kinds.includes(current.state)) { + return Promise.resolve(current); + } + + return new Promise(resolve => { + const disposable = autorun(reader => { + const state = this._state.read(reader); + if (kinds.includes(state.state)) { + disposable.dispose(); + resolve(state); + } + }); + }); + } +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpServerRequestHandler.ts b/src/vs/workbench/contrib/mcp/common/mcpServerRequestHandler.ts new file mode 100644 index 000000000000..651cb01d8cdc --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpServerRequestHandler.ts @@ -0,0 +1,461 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { equals } from '../../../../base/common/arrays.js'; +import { DeferredPromise } from '../../../../base/common/async.js'; +import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { Emitter } from '../../../../base/common/event.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { autorun } from '../../../../base/common/observable.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { ILogger } from '../../../../platform/log/common/log.js'; +import { IProductService } from '../../../../platform/product/common/productService.js'; +import { IMcpMessageTransport } from './mcpRegistryTypes.js'; +import { McpConnectionState, MpcResponseError } from './mcpTypes.js'; +import { MCP } from './modelContextProtocol.js'; + +/** + * Maps request IDs to handlers. + */ +interface PendingRequest { + promise: DeferredPromise; +} + +export interface McpRoot { + uri: string; + name?: string; +} + +/** + * Request handler for communicating with an MCP server. + * + * Handles sending requests and receiving responses, with automatic + * handling of ping requests and typed client request methods. + */ +export class McpServerRequestHandler extends Disposable { + private _nextRequestId = 1; + private readonly _pendingRequests = new Map(); + + private _hasAnnouncedRoots = false; + private _roots: MCP.Root[] = []; + + public set roots(roots: MCP.Root[]) { + if (!equals(this._roots, roots)) { + this._roots = roots; + if (this._hasAnnouncedRoots) { + this.sendNotification({ method: 'notifications/roots/list_changed' }); + this._hasAnnouncedRoots = false; + } + } + } + + private _serverInit!: MCP.InitializeResult; + public get capabilities(): MCP.ServerCapabilities { + return this._serverInit.capabilities; + } + + // Event emitters for server notifications + private readonly _onDidReceiveCancelledNotification = this._register(new Emitter()); + readonly onDidReceiveCancelledNotification = this._onDidReceiveCancelledNotification.event; + + private readonly _onDidReceiveProgressNotification = this._register(new Emitter()); + readonly onDidReceiveProgressNotification = this._onDidReceiveProgressNotification.event; + + private readonly _onDidChangeResourceList = this._register(new Emitter()); + readonly onDidChangeResourceList = this._onDidChangeResourceList.event; + + private readonly _onDidUpdateResource = this._register(new Emitter()); + readonly onDidUpdateResource = this._onDidUpdateResource.event; + + private readonly _onDidChangeToolList = this._register(new Emitter()); + readonly onDidChangeToolList = this._onDidChangeToolList.event; + + private readonly _onDidChangePromptList = this._register(new Emitter()); + readonly onDidChangePromptList = this._onDidChangePromptList.event; + + /** + * Connects to the MCP server and does the initialization handshake. + * @throws MpcResponseError if the server fails to initialize. + */ + public static async create(instaService: IInstantiationService, launch: IMcpMessageTransport, logger: ILogger, token?: CancellationToken) { + const mcp = new McpServerRequestHandler(launch, logger); + try { + await instaService.invokeFunction(async accessor => { + const productService = accessor.get(IProductService); + const initialized = await mcp.sendRequest({ + method: 'initialize', + params: { + protocolVersion: MCP.LATEST_PROTOCOL_VERSION, + capabilities: { + roots: { listChanged: true }, + }, + clientInfo: { + name: productService.nameLong, + version: productService.version, + } + } + }, token); + + mcp._serverInit = initialized; + + mcp.sendNotification({ + method: 'notifications/initialized' + }); + }); + + return mcp; + } catch (e) { + mcp.dispose(); + throw e; + } + } + + protected constructor( + private readonly launch: IMcpMessageTransport, + private readonly logger: ILogger, + ) { + super(); + this._register(launch.onDidReceiveMessage(message => this.handleMessage(message))); + this._register(autorun(reader => { + const state = launch.state.read(reader).state; + // the handler will get disposed when the launch stops, but if we're still + // create()'ing we need to make sure to cancel the initialize request. + if (state === McpConnectionState.Kind.Error || state === McpConnectionState.Kind.Stopped) { + this.cancelAllRequests(); + } + })); + } + + /** + * Send a client request to the server and return the response. + * + * @param request The request to send + * @param token Cancellation token + * @param timeoutMs Optional timeout in milliseconds + * @returns A promise that resolves with the response + */ + private async sendRequest( + request: Pick, + token: CancellationToken = CancellationToken.None + ): Promise { + const id = this._nextRequestId++; + + // Create the full JSON-RPC request + const jsonRpcRequest: MCP.JSONRPCRequest = { + jsonrpc: MCP.JSONRPC_VERSION, + id, + ...request + }; + + const promise = new DeferredPromise(); + // Store the pending request + this._pendingRequests.set(id, { promise }); + + // Set up cancellation + const cancelListener = token.onCancellationRequested(() => { + if (!promise.isSettled) { + this._pendingRequests.delete(id); + this.sendNotification({ method: 'notifications/cancelled', params: { requestId: id } }); + promise.cancel(); + } + }); + + // Send the request + this.launch.send(jsonRpcRequest); + const ret = promise.p.finally(() => { + cancelListener.dispose(); + this._pendingRequests.delete(id); + }); + + return ret as Promise; + } + + /** + * Handles paginated requests by making multiple requests until all items are retrieved. + * + * @param method The method name to call + * @param getItems Function to extract the array of items from a result + * @param initialParams Initial parameters + * @param token Cancellation token + * @returns Promise with all items combined + */ + private async sendRequestPaginated(method: T['method'], getItems: (result: R) => I[], initialParams?: Omit, token: CancellationToken = CancellationToken.None): Promise { + let allItems: I[] = []; + let nextCursor: MCP.Cursor | undefined = undefined; + + do { + const params: T['params'] = { + ...initialParams, + cursor: nextCursor + }; + + const result: R = await this.sendRequest({ method, params }, token); + allItems = allItems.concat(getItems(result)); + nextCursor = result.nextCursor; + } while (nextCursor !== undefined && !token.isCancellationRequested); + + return allItems; + } + + private sendNotification(notification: N): void { + this.launch.send({ ...notification, jsonrpc: MCP.JSONRPC_VERSION }); + } + + /** + * Handle incoming messages from the server + */ + private handleMessage(message: MCP.JSONRPCMessage): void { + // Handle responses to our requests + if ('id' in message) { + if ('result' in message) { + this.handleResult(message); + } else if ('error' in message) { + this.handleError(message); + } + } + + // Handle requests from the server + if ('method' in message) { + if ('id' in message) { + this.handleServerRequest(message as MCP.JSONRPCRequest & MCP.ServerRequest); + } else { + this.handleServerNotification(message as MCP.JSONRPCNotification & MCP.ServerNotification); + + } + } + } + + /** + * Handle successful responses + */ + private handleResult(response: MCP.JSONRPCResponse): void { + const request = this._pendingRequests.get(response.id); + if (request) { + this._pendingRequests.delete(response.id); + request.promise.complete(response.result); + } + } + + /** + * Handle error responses + */ + private handleError(response: MCP.JSONRPCError): void { + const request = this._pendingRequests.get(response.id); + if (request) { + this._pendingRequests.delete(response.id); + request.promise.error(new MpcResponseError(response.error.message, response.error.code, response.error.data)); + } + } + + /** + * Handle incoming server requests + */ + private handleServerRequest(request: MCP.JSONRPCRequest & MCP.ServerRequest): void { + switch (request.method) { + case 'ping': + return this.respondToRequest(request, this.handlePing(request)); + case 'roots/list': + return this.respondToRequest(request, this.handleRootsList(request)); + + default: { + const errorResponse: MCP.JSONRPCError = { + jsonrpc: MCP.JSONRPC_VERSION, + id: request.id, + error: { + code: MCP.METHOD_NOT_FOUND, + message: `Method not found: ${request.method}` + } + }; + this.launch.send(errorResponse); + break; + } + } + } + /** + * Handle incoming server notifications + */ + private handleServerNotification(request: MCP.JSONRPCNotification & MCP.ServerNotification): void { + switch (request.method) { + case 'notifications/message': + return this.handleLoggingNotification(request); + case 'notifications/cancelled': + this._onDidReceiveCancelledNotification.fire(request); + return this.handleCancelledNotification(request); + case 'notifications/progress': + this._onDidReceiveProgressNotification.fire(request); + return; + case 'notifications/resources/list_changed': + this._onDidChangeResourceList.fire(); + return; + case 'notifications/resources/updated': + this._onDidUpdateResource.fire(request); + return; + case 'notifications/tools/list_changed': + this._onDidChangeToolList.fire(); + return; + case 'notifications/prompts/list_changed': + this._onDidChangePromptList.fire(); + return; + } + } + + private handleCancelledNotification(request: MCP.CancelledNotification): void { + const pendingRequest = this._pendingRequests.get(request.params.requestId); + if (pendingRequest) { + this._pendingRequests.delete(request.params.requestId); + pendingRequest.promise.cancel(); + } + } + + private handleLoggingNotification(request: MCP.LoggingMessageNotification): void { + let contents = typeof request.params.data === 'string' ? request.params.data : JSON.stringify(request.params.data); + if (request.params.logger) { + contents = `${request.params.logger}: ${contents}`; + } + + switch (request.params?.level) { + case 'debug': + this.logger.debug(contents); + break; + case 'info': + case 'notice': + this.logger.info(contents); + break; + case 'warning': + this.logger.warn(contents); + break; + case 'error': + case 'critical': + case 'alert': + case 'emergency': + this.logger.error(contents); + break; + default: + this.logger.info(contents); + break; + } + } + + /** + * Send a generic response to a request + */ + private respondToRequest(request: MCP.JSONRPCRequest, result: MCP.Result): void { + const response: MCP.JSONRPCResponse = { + jsonrpc: MCP.JSONRPC_VERSION, + id: request.id, + result + }; + this.launch.send(response); + } + + /** + * Send a response to a ping request + */ + private handlePing(_request: MCP.PingRequest): {} { + return {}; + } + + /** + * Send a response to a roots/list request + */ + private handleRootsList(_request: MCP.ListRootsRequest): MCP.ListRootsResult { + this._hasAnnouncedRoots = true; + return { roots: this._roots }; + } + + private cancelAllRequests() { + this._pendingRequests.forEach(pending => pending.promise.cancel()); + this._pendingRequests.clear(); + } + + public override dispose(): void { + this.cancelAllRequests(); + super.dispose(); + } + + /** + * Send an initialize request + */ + initialize(params: MCP.InitializeRequest['params'], token?: CancellationToken): Promise { + return this.sendRequest({ method: 'initialize', params }, token); + } + + /** + * List available resources + */ + listResources(params?: MCP.ListResourcesRequest['params'], token?: CancellationToken): Promise { + return this.sendRequestPaginated('resources/list', result => result.resources, params, token); + } + + /** + * Read a specific resource + */ + readResource(params: MCP.ReadResourceRequest['params'], token?: CancellationToken): Promise { + return this.sendRequest({ method: 'resources/read', params }, token); + } + + /** + * List available resource templates + */ + listResourceTemplates(params?: MCP.ListResourceTemplatesRequest['params'], token?: CancellationToken): Promise { + return this.sendRequestPaginated('resources/templates/list', result => result.resourceTemplates, params, token); + } + + /** + * Subscribe to resource updates + */ + subscribe(params: MCP.SubscribeRequest['params'], token?: CancellationToken): Promise { + return this.sendRequest({ method: 'resources/subscribe', params }, token); + } + + /** + * Unsubscribe from resource updates + */ + unsubscribe(params: MCP.UnsubscribeRequest['params'], token?: CancellationToken): Promise { + return this.sendRequest({ method: 'resources/unsubscribe', params }, token); + } + + /** + * List available prompts + */ + listPrompts(params?: MCP.ListPromptsRequest['params'], token?: CancellationToken): Promise { + return this.sendRequestPaginated('prompts/list', result => result.prompts, params, token); + } + + /** + * Get a specific prompt + */ + getPrompt(params: MCP.GetPromptRequest['params'], token?: CancellationToken): Promise { + return this.sendRequest({ method: 'prompts/get', params }, token); + } + + /** + * List available tools + */ + listTools(params?: MCP.ListToolsRequest['params'], token?: CancellationToken): Promise { + return this.sendRequestPaginated('tools/list', result => result.tools, params, token); + } + + /** + * Call a specific tool + */ + callTool(params: MCP.CallToolRequest['params'], token?: CancellationToken): Promise { + return this.sendRequest({ method: 'tools/call', params }, token); + } + + /** + * Set the logging level + */ + setLevel(params: MCP.SetLevelRequest['params'], token?: CancellationToken): Promise { + return this.sendRequest({ method: 'logging/setLevel', params }, token); + } + + /** + * Find completions for an argument + */ + complete(params: MCP.CompleteRequest['params'], token?: CancellationToken): Promise { + return this.sendRequest({ method: 'completion/complete', params }, token); + } +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpService.ts b/src/vs/workbench/contrib/mcp/common/mcpService.ts new file mode 100644 index 000000000000..8def1af9e27b --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpService.ts @@ -0,0 +1,250 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { RunOnceScheduler } from '../../../../base/common/async.js'; +import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { MarkdownString } from '../../../../base/common/htmlContent.js'; +import { Disposable, DisposableStore, IDisposable, IReference, toDisposable } from '../../../../base/common/lifecycle.js'; +import { equals } from '../../../../base/common/objects.js'; +import { autorun, IObservable, observableValue, transaction } from '../../../../base/common/observable.js'; +import { localize } from '../../../../nls.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +import { IProductService } from '../../../../platform/product/common/productService.js'; +import { StorageScope } from '../../../../platform/storage/common/storage.js'; +import { CountTokensCallback, ILanguageModelToolsService, IPreparedToolInvocation, IToolData, IToolImpl, IToolInvocation, IToolResult } from '../../chat/common/languageModelToolsService.js'; +import { IMcpRegistry } from './mcpRegistryTypes.js'; +import { McpServer, McpServerMetadataCache } from './mcpServer.js'; +import { IMcpServer, IMcpService, IMcpTool, McpCollectionDefinition, McpServerDefinition, McpServerToolsState } from './mcpTypes.js'; + +interface ISyncedToolData { + toolData: IToolData; + toolDispose: IDisposable; + implDispose: IDisposable; +} + +type IMcpServerRec = IReference; + +export class McpService extends Disposable implements IMcpService { + + declare _serviceBrand: undefined; + + private readonly _servers = observableValue(this, []); + public readonly servers: IObservable = this._servers.map(servers => servers.map(s => s.object)); + + public get lazyCollectionState() { return this._mcpRegistry.lazyCollectionState; } + + protected readonly userCache: McpServerMetadataCache; + protected readonly workspaceCache: McpServerMetadataCache; + + constructor( + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IMcpRegistry private readonly _mcpRegistry: IMcpRegistry, + @ILanguageModelToolsService private readonly _toolsService: ILanguageModelToolsService, + @IProductService productService: IProductService, + @ILogService private readonly _logService: ILogService, + ) { + super(); + + this.userCache = this._register(_instantiationService.createInstance(McpServerMetadataCache, StorageScope.PROFILE)); + this.workspaceCache = this._register(_instantiationService.createInstance(McpServerMetadataCache, StorageScope.WORKSPACE)); + + const updateThrottle = this._store.add(new RunOnceScheduler(() => this._updateCollectedServers(), 500)); + + // Throttle changes so that if a collection is changed, or a server is + // unregistered/registered, we don't stop servers unnecessarily. + this._register(autorun(reader => { + for (const collection of this._mcpRegistry.collections.read(reader)) { + collection.serverDefinitions.read(reader); + } + updateThrottle.schedule(500); + })); + } + + public resetCaches(): void { + this.userCache.reset(); + this.workspaceCache.reset(); + } + + public async activateCollections(): Promise { + const collections = await this._mcpRegistry.discoverCollections(); + const collectionIds = new Set(collections.map(c => c.id)); + + this._updateCollectedServers(); + + // Discover any newly-collected servers with unknown tools + const todo: Promise[] = []; + for (const { object: server } of this._servers.get()) { + if (collectionIds.has(server.collection.id)) { + const state = server.toolsState.get(); + if (state === McpServerToolsState.Unknown) { + todo.push(server.start()); + } + } + } + + await Promise.all(todo); + } + + private _syncTools(server: McpServer, store: DisposableStore) { + const tools = new Map(); + + store.add(autorun(reader => { + const toDelete = new Set(tools.keys()); + for (const tool of server.tools.read(reader)) { + const existing = tools.get(tool.id); + const toolData: IToolData = { + id: tool.id, + displayName: tool.definition.name, + toolReferenceName: tool.definition.name, + modelDescription: tool.definition.description ?? '', + userDescription: tool.definition.description ?? '', + inputSchema: tool.definition.inputSchema, + canBeReferencedInPrompt: true, + tags: ['mcp', 'vscode_editing'], // TODO@jrieken remove this tag + }; + + if (existing) { + if (!equals(existing.toolData, toolData)) { + existing.toolData = toolData; + existing.toolDispose.dispose(); + existing.toolDispose = this._toolsService.registerToolData(toolData); + } + toDelete.delete(tool.id); + } else { + tools.set(tool.id, { + toolData, + toolDispose: this._toolsService.registerToolData(toolData), + implDispose: this._toolsService.registerToolImplementation(tool.id, this._instantiationService.createInstance(McpToolImplementation, tool, server)), + }); + } + } + + for (const id of toDelete) { + const tool = tools.get(id); + if (tool) { + tool.toolDispose.dispose(); + tool.implDispose.dispose(); + tools.delete(id); + } + } + })); + + store.add(toDisposable(() => { + for (const tool of tools.values()) { + tool.toolDispose.dispose(); + tool.implDispose.dispose(); + } + })); + } + + private _updateCollectedServers() { + const definitions = this._mcpRegistry.collections.get().flatMap(collectionDefinition => + collectionDefinition.serverDefinitions.get().map(serverDefinition => ({ + serverDefinition, + collectionDefinition, + })) + ); + + const nextDefinitions = new Set(definitions); + const currentServers = this._servers.get(); + const nextServers: IMcpServerRec[] = []; + const pushMatch = (match: (typeof definitions)[0], rec: IMcpServerRec) => { + nextDefinitions.delete(match); + nextServers.push(rec); + const connection = rec.object.connection.get(); + // if the definition was modified, stop the server; it'll be restarted again on-demand + if (connection && !McpServerDefinition.equals(connection.definition, match.serverDefinition)) { + rec.object.stop(); + this._logService.debug(`MCP server ${rec.object.definition.id} stopped because the definition changed`); + } + }; + + // Transfer over any servers that are still valid. + for (const server of currentServers) { + const match = definitions.find(d => defsEqual(server.object, d)); + if (match) { + pushMatch(match, server); + } else { + server.dispose(); + } + } + + // Create any new servers that are needed. + for (const def of nextDefinitions) { + const store = new DisposableStore(); + const object = this._instantiationService.createInstance(McpServer, def.collectionDefinition, def.serverDefinition, false, def.collectionDefinition.scope === StorageScope.WORKSPACE ? this.workspaceCache : this.userCache); + store.add(object); + this._syncTools(object, store); + + nextServers.push({ object, dispose: () => store.dispose() }); + } + + transaction(tx => { + this._servers.set(nextServers, tx); + }); + } + + public override dispose(): void { + this._servers.get().forEach(s => s.dispose()); + super.dispose(); + } +} + +function defsEqual(server: IMcpServer, def: { serverDefinition: McpServerDefinition; collectionDefinition: McpCollectionDefinition }) { + return server.collection.id === def.collectionDefinition.id && server.definition.id === def.serverDefinition.id; +} + +class McpToolImplementation implements IToolImpl { + constructor( + private readonly _tool: IMcpTool, + private readonly _server: IMcpServer, + @IProductService private readonly _productService: IProductService, + ) { } + + async prepareToolInvocation(parameters: any, token: CancellationToken) { + const tool = this._tool; + const server = this._server; + + const mcpToolWarning = localize( + 'mcp.tool.warning', + "MCP servers or malicious conversation content may attempt to misuse '{0}' through the installed tools. Please carefully review any requested actions.", + this._productService.nameShort + ); + + return { + confirmationMessages: { + title: localize('msg.title', "Run `{0}` from $(server) `{1}` (MCP server)", tool.definition.name, server.definition.label), + message: new MarkdownString(localize('msg.msg', "{0}\n\n$(warning) {1}", tool.definition.description, mcpToolWarning), { supportThemeIcons: true }), + toolInput: parameters + }, + invocationMessage: new MarkdownString(localize('msg.run', "Running `{0}`", tool.definition.name, server.definition.label)), + pastTenseMessage: new MarkdownString(localize('msg.ran', "Ran `{0}` ", tool.definition.name, server.definition.label)) + } satisfies IPreparedToolInvocation; + } + + async invoke(invocation: IToolInvocation, _countTokens: CountTokensCallback, token: CancellationToken) { + + const result: IToolResult = { + content: [] + }; + + const callResult = await this._tool.call(invocation.parameters as Record, token); + for (const item of callResult.content) { + if (item.type === 'text') { + result.content.push({ + kind: 'text', + value: item.text + }); + } else { + // TODO@jrieken handle different item types + } + } + + // result.toolResultMessage = new MarkdownString(localize('reuslt.pattern', "```json\n{0}\n```", JSON.stringify(callResult, undefined, 2))); + + return result; + } +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpTypes.ts b/src/vs/workbench/contrib/mcp/common/mcpTypes.ts new file mode 100644 index 000000000000..db12df70ab62 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpTypes.ts @@ -0,0 +1,384 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { assertNever } from '../../../../base/common/assert.js'; +import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { IDisposable } from '../../../../base/common/lifecycle.js'; +import { equals as objectsEqual } from '../../../../base/common/objects.js'; +import { IObservable } from '../../../../base/common/observable.js'; +import { URI, UriComponents } from '../../../../base/common/uri.js'; +import { localize } from '../../../../nls.js'; +import { ConfigurationTarget } from '../../../../platform/configuration/common/configuration.js'; +import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { StorageScope } from '../../../../platform/storage/common/storage.js'; +import { IWorkspaceFolderData } from '../../../../platform/workspace/common/workspace.js'; +import { McpServerRequestHandler } from './mcpServerRequestHandler.js'; +import { MCP } from './modelContextProtocol.js'; + +export const extensionMcpCollectionPrefix = 'ext.'; + +export function extensionPrefixedIdentifier(identifier: ExtensionIdentifier, id: string): string { + return ExtensionIdentifier.toKey(identifier) + '/' + id; +} + +/** + * An McpCollection contains McpServers. There may be multiple collections for + * different locations servers are discovered. + */ +export interface McpCollectionDefinition { + /** Origin authority from which this collection was discovered. */ + readonly remoteAuthority: string | null; + /** Globally-unique, stable ID for this definition */ + readonly id: string; + /** Human-readable label for the definition */ + readonly label: string; + /** Definitions this collection contains. */ + readonly serverDefinitions: IObservable; + /** If 'false', consent is required before any MCP servers in this collection are automatically launched. */ + readonly isTrustedByDefault: boolean; + /** Scope where associated collection info should be stored. */ + readonly scope: StorageScope; + + /** For lazy-loaded collections only: */ + readonly lazy?: { + /** True if `serverDefinitions` were loaded from the cache */ + isCached: boolean; + /** Triggers a load of the real server definition, which should be pushed to the IMcpRegistry. If not this definition will be removed. */ + load(): Promise; + /** Called after `load()` if the extension is not found. */ + removed?(): void; + }; + + readonly presentation?: { + /** Sort order of the collection. */ + readonly order?: number; + /** Place where this server is configured, used in workspac trust prompts */ + readonly origin?: URI; + }; +} + +export const enum McpCollectionSortOrder { + Workspace = 0, + User = 100, + Extension = 200, + Filesystem = 300, + + RemotePenalty = 50, +} + +export namespace McpCollectionDefinition { + export interface FromExtHost { + readonly id: string; + readonly label: string; + readonly isTrustedByDefault: boolean; + readonly scope: StorageScope; + } + + export function equals(a: McpCollectionDefinition, b: McpCollectionDefinition): boolean { + return a.id === b.id + && a.remoteAuthority === b.remoteAuthority + && a.label === b.label + && a.isTrustedByDefault === b.isTrustedByDefault; + } +} + +export interface McpServerDefinition { + /** Globally-unique, stable ID for this definition */ + readonly id: string; + /** Human-readable label for the definition */ + readonly label: string; + /** Descriptor defining how the configuration should be launched. */ + readonly launch: McpServerLaunch; + /** If set, allows configuration variables to be resolved in the {@link launch} with the given context */ + readonly variableReplacement?: McpServerDefinitionVariableReplacement; +} + +export namespace McpServerDefinition { + export interface Serialized { + readonly id: string; + readonly label: string; + readonly launch: McpServerLaunch.Serialized; + readonly variableReplacement?: McpServerDefinitionVariableReplacement.Serialized; + } + + export function toSerialized(def: McpServerDefinition): McpServerDefinition.Serialized { + return def; + } + + export function fromSerialized(def: McpServerDefinition.Serialized): McpServerDefinition { + return { + id: def.id, + label: def.label, + launch: McpServerLaunch.fromSerialized(def.launch), + variableReplacement: def.variableReplacement ? McpServerDefinitionVariableReplacement.fromSerialized(def.variableReplacement) : undefined, + }; + } + + export function equals(a: McpServerDefinition, b: McpServerDefinition): boolean { + return a.id === b.id + && a.label === b.label + && objectsEqual(a.launch, b.launch) + && objectsEqual(a.variableReplacement, b.variableReplacement); + } +} + + +export interface McpServerDefinitionVariableReplacement { + section?: string; // e.g. 'mcp' + folder?: IWorkspaceFolderData; + target?: ConfigurationTarget; +} + +export namespace McpServerDefinitionVariableReplacement { + export interface Serialized { + section?: string; + folder?: { name: string; index: number; uri: UriComponents }; + target?: ConfigurationTarget; + } + + export function toSerialized(def: McpServerDefinitionVariableReplacement): McpServerDefinitionVariableReplacement.Serialized { + return def; + } + + export function fromSerialized(def: McpServerDefinitionVariableReplacement.Serialized): McpServerDefinitionVariableReplacement { + return { + section: def.section, + folder: def.folder ? { ...def.folder, uri: URI.revive(def.folder.uri) } : undefined, + target: def.target, + }; + } +} + +export interface IMcpService { + _serviceBrand: undefined; + readonly servers: IObservable; + + /** Resets the cached tools. */ + resetCaches(): void; + + /** Set if there are extensions that register MCP servers that have never been activated. */ + readonly lazyCollectionState: IObservable; + /** Activatese extensions and runs their MCP servers. */ + activateCollections(): Promise; +} + +export const enum LazyCollectionState { + HasUnknown, + LoadingUnknown, + AllKnown, +} + +export const IMcpService = createDecorator('IMcpService'); + +export interface McpCollectionReference { + id: string; + label: string; + presentation?: McpCollectionDefinition['presentation']; +} + +export interface McpDefinitionReference { + id: string; + label: string; +} + +export interface IMcpServer extends IDisposable { + readonly collection: McpCollectionReference; + readonly definition: McpDefinitionReference; + readonly connection: IObservable; + readonly connectionState: IObservable; + /** + * Reflects the MCP server trust state. True if trusted, false if untrusted, + * undefined if consent is required but not indicated. + */ + readonly trusted: IObservable; + + showOutput(): void; + /** + * Starts the server and returns its resulting state. One of: + * - Running, if all good + * - Error, if the server failed to start + * - Stopped, if the server was disposed or the user cancelled the launch + */ + start(isFromInteraction?: boolean): Promise; + stop(): Promise; + + readonly toolsState: IObservable; + readonly tools: IObservable; +} + +export const enum McpServerToolsState { + /** Tools have not been read before */ + Unknown, + /** Tools were read from the cache */ + Cached, + /** Tools are refreshing for the first time */ + RefreshingFromUnknown, + /** Tools are refreshing and the current tools are cached */ + RefreshingFromCached, + /** Tool state is live, server is connected */ + Live, +} + +export interface IMcpTool { + + readonly id: string; + + readonly definition: MCP.Tool; + + /** + * Calls a tool + * @throws {@link MpcResponseError} if the tool fails to execute + * @throws {@link McpConnectionFailedError} if the connection to the server fails + */ + call(params: Record, token?: CancellationToken): Promise; +} + +export const enum McpServerTransportType { + /** A command-line MCP server communicating over standard in/out */ + Stdio = 1 << 0, + /** An MCP server that uses Server-Sent Events */ + SSE = 1 << 1, +} + +/** + * MCP server launched on the command line which communicated over stdio. + * https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/transports/#stdio + */ +export interface McpServerTransportStdio { + readonly type: McpServerTransportType.Stdio; + readonly cwd: URI | undefined; + readonly command: string; + readonly args: readonly string[]; + readonly env: Record; +} + +/** + * MCP server launched on the command line which communicated over server-sent-events. + * https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/transports/#http-with-sse + */ +export interface McpServerTransportSSE { + readonly type: McpServerTransportType.SSE; + readonly uri: URI; + readonly headers: [string, string][]; +} + +export type McpServerLaunch = + | McpServerTransportStdio + | McpServerTransportSSE; + +export namespace McpServerLaunch { + export type Serialized = + | { type: McpServerTransportType.SSE; uri: UriComponents; headers: [string, string][] } + | { type: McpServerTransportType.Stdio; cwd: UriComponents | undefined; command: string; args: readonly string[]; env: Record }; + + export function toSerialized(launch: McpServerLaunch): McpServerLaunch.Serialized { + return launch; + } + + export function fromSerialized(launch: McpServerLaunch.Serialized): McpServerLaunch { + switch (launch.type) { + case McpServerTransportType.SSE: + return { type: launch.type, uri: URI.revive(launch.uri), headers: launch.headers }; + case McpServerTransportType.Stdio: + return { + type: launch.type, + cwd: launch.cwd ? URI.revive(launch.cwd) : undefined, + command: launch.command, + args: launch.args, + env: launch.env, + }; + } + } +} + +/** + * An instance that manages a connection to an MCP server. It can be started, + * stopped, and restarted. Once started and in a running state, it will + * eventually build a {@link IMcpServerConnection.handler}. + */ +export interface IMcpServerConnection extends IDisposable { + readonly definition: McpServerDefinition; + readonly state: IObservable; + readonly handler: IObservable; + + /** + * Shows the current server output. + */ + showOutput(): void; + + /** + * Starts the server if it's stopped. Returns a promise that resolves once + * server exits a 'starting' state. + */ + start(): Promise; + + /** + * Stops the server. + */ + stop(): Promise; +} + +/** + * McpConnectionState is the state of the underlying connection and is + * communicated e.g. from the extension host to the renderer. + */ +export namespace McpConnectionState { + export const enum Kind { + Stopped, + Starting, + Running, + Error, + } + + export const toString = (s: McpConnectionState): string => { + switch (s.state) { + case Kind.Stopped: + return localize('mcpstate.stopped', 'Stopped'); + case Kind.Starting: + return localize('mcpstate.starting', 'Starting'); + case Kind.Running: + return localize('mcpstate.running', 'Running'); + case Kind.Error: + return localize('mcpstate.error', 'Error {0}', s.message); + default: + assertNever(s); + } + }; + + /** Returns if the MCP state is one where starting a new server is valid */ + export const canBeStarted = (s: Kind) => s === Kind.Error || s === Kind.Stopped; + + export interface Stopped { + readonly state: Kind.Stopped; + } + + export interface Starting { + readonly state: Kind.Starting; + } + + export interface Running { + readonly state: Kind.Running; + } + + export interface Error { + readonly state: Kind.Error; + readonly message: string; + } +} + +export type McpConnectionState = + | McpConnectionState.Stopped + | McpConnectionState.Starting + | McpConnectionState.Running + | McpConnectionState.Error; + +export class MpcResponseError extends Error { + constructor(message: string, public readonly code: number, public readonly data: unknown) { + super(`MPC ${code}: ${message}`); + } +} + +export class McpConnectionFailedError extends Error { } diff --git a/src/vs/workbench/contrib/mcp/common/modelContextProtocol.ts b/src/vs/workbench/contrib/mcp/common/modelContextProtocol.ts new file mode 100644 index 000000000000..582e69eaf930 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/modelContextProtocol.ts @@ -0,0 +1,1138 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* eslint-disable @stylistic/ts/member-delimiter-style */ +/* eslint-disable local/code-no-unexternalized-strings */ + +/** + * Schema updated from the Model Context Protocol repository at + * https://github.com/modelcontextprotocol/specification/tree/main/schema + * + * ⚠️ Do not edit within `namespace` manually except to update schema versions ⚠️ + */ +export namespace MCP { + /* JSON-RPC types */ + export type JSONRPCMessage = + | JSONRPCRequest + | JSONRPCNotification + | JSONRPCResponse + | JSONRPCError; + + export const LATEST_PROTOCOL_VERSION = "2024-11-05"; + export const JSONRPC_VERSION = "2.0"; + + /** + * A progress token, used to associate progress notifications with the original request. + */ + export type ProgressToken = string | number; + + /** + * An opaque token used to represent a cursor for pagination. + */ + export type Cursor = string; + + export interface Request { + method: string; + params?: { + _meta?: { + /** + * If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications. + */ + progressToken?: ProgressToken; + }; + [key: string]: unknown; + }; + } + + export interface Notification { + method: string; + params?: { + /** + * This parameter name is reserved by MCP to allow clients and servers to attach additional metadata to their notifications. + */ + _meta?: { [key: string]: unknown }; + [key: string]: unknown; + }; + } + + export interface Result { + /** + * This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses. + */ + _meta?: { [key: string]: unknown }; + [key: string]: unknown; + } + + /** + * A uniquely identifying ID for a request in JSON-RPC. + */ + export type RequestId = string | number; + + /** + * A request that expects a response. + */ + export interface JSONRPCRequest extends Request { + jsonrpc: typeof JSONRPC_VERSION; + id: RequestId; + } + + /** + * A notification which does not expect a response. + */ + export interface JSONRPCNotification extends Notification { + jsonrpc: typeof JSONRPC_VERSION; + } + + /** + * A successful (non-error) response to a request. + */ + export interface JSONRPCResponse { + jsonrpc: typeof JSONRPC_VERSION; + id: RequestId; + result: Result; + } + + // Standard JSON-RPC error codes + export const PARSE_ERROR = -32700; + export const INVALID_REQUEST = -32600; + export const METHOD_NOT_FOUND = -32601; + export const INVALID_PARAMS = -32602; + export const INTERNAL_ERROR = -32603; + + /** + * A response to a request that indicates an error occurred. + */ + export interface JSONRPCError { + jsonrpc: typeof JSONRPC_VERSION; + id: RequestId; + error: { + /** + * The error type that occurred. + */ + code: number; + /** + * A short description of the error. The message SHOULD be limited to a concise single sentence. + */ + message: string; + /** + * Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). + */ + data?: unknown; + }; + } + + /* Empty result */ + /** + * A response that indicates success but carries no data. + */ + export type EmptyResult = Result; + + /* Cancellation */ + /** + * This notification can be sent by either side to indicate that it is cancelling a previously-issued request. + * + * The request SHOULD still be in-flight, but due to communication latency, it is always possible that this notification MAY arrive after the request has already finished. + * + * This notification indicates that the result will be unused, so any associated processing SHOULD cease. + * + * A client MUST NOT attempt to cancel its `initialize` request. + */ + export interface CancelledNotification extends Notification { + method: "notifications/cancelled"; + params: { + /** + * The ID of the request to cancel. + * + * This MUST correspond to the ID of a request previously issued in the same direction. + */ + requestId: RequestId; + + /** + * An optional string describing the reason for the cancellation. This MAY be logged or presented to the user. + */ + reason?: string; + }; + } + + /* Initialization */ + /** + * This request is sent from the client to the server when it first connects, asking it to begin initialization. + */ + export interface InitializeRequest extends Request { + method: "initialize"; + params: { + /** + * The latest version of the Model Context Protocol that the client supports. The client MAY decide to support older versions as well. + */ + protocolVersion: string; + capabilities: ClientCapabilities; + clientInfo: Implementation; + }; + } + + /** + * After receiving an initialize request from the client, the server sends this response. + */ + export interface InitializeResult extends Result { + /** + * The version of the Model Context Protocol that the server wants to use. This may not match the version that the client requested. If the client cannot support this version, it MUST disconnect. + */ + protocolVersion: string; + capabilities: ServerCapabilities; + serverInfo: Implementation; + /** + * Instructions describing how to use the server and its features. + * + * This can be used by clients to improve the LLM's understanding of available tools, resources, etc. It can be thought of like a "hint" to the model. For example, this information MAY be added to the system prompt. + */ + instructions?: string; + } + + /** + * This notification is sent from the client to the server after initialization has finished. + */ + export interface InitializedNotification extends Notification { + method: "notifications/initialized"; + } + + /** + * Capabilities a client may support. Known capabilities are defined here, in this schema, but this is not a closed set: any client can define its own, additional capabilities. + */ + export interface ClientCapabilities { + /** + * Experimental, non-standard capabilities that the client supports. + */ + experimental?: { [key: string]: object }; + /** + * Present if the client supports listing roots. + */ + roots?: { + /** + * Whether the client supports notifications for changes to the roots list. + */ + listChanged?: boolean; + }; + /** + * Present if the client supports sampling from an LLM. + */ + sampling?: object; + } + + /** + * Capabilities that a server may support. Known capabilities are defined here, in this schema, but this is not a closed set: any server can define its own, additional capabilities. + */ + export interface ServerCapabilities { + /** + * Experimental, non-standard capabilities that the server supports. + */ + experimental?: { [key: string]: object }; + /** + * Present if the server supports sending log messages to the client. + */ + logging?: object; + /** + * Present if the server offers any prompt templates. + */ + prompts?: { + /** + * Whether this server supports notifications for changes to the prompt list. + */ + listChanged?: boolean; + }; + /** + * Present if the server offers any resources to read. + */ + resources?: { + /** + * Whether this server supports subscribing to resource updates. + */ + subscribe?: boolean; + /** + * Whether this server supports notifications for changes to the resource list. + */ + listChanged?: boolean; + }; + /** + * Present if the server offers any tools to call. + */ + tools?: { + /** + * Whether this server supports notifications for changes to the tool list. + */ + listChanged?: boolean; + }; + } + + /** + * Describes the name and version of an MCP implementation. + */ + export interface Implementation { + name: string; + version: string; + } + + /* Ping */ + /** + * A ping, issued by either the server or the client, to check that the other party is still alive. The receiver must promptly respond, or else may be disconnected. + */ + export interface PingRequest extends Request { + method: "ping"; + } + + /* Progress notifications */ + /** + * An out-of-band notification used to inform the receiver of a progress update for a long-running request. + */ + export interface ProgressNotification extends Notification { + method: "notifications/progress"; + params: { + /** + * The progress token which was given in the initial request, used to associate this notification with the request that is proceeding. + */ + progressToken: ProgressToken; + /** + * The progress thus far. This should increase every time progress is made, even if the total is unknown. + * + * @TJS-type number + */ + progress: number; + /** + * Total number of items to process (or total progress required), if known. + * + * @TJS-type number + */ + total?: number; + }; + } + + /* Pagination */ + export interface PaginatedRequest extends Request { + params?: { + /** + * An opaque token representing the current pagination position. + * If provided, the server should return results starting after this cursor. + */ + cursor?: Cursor; + }; + } + + export interface PaginatedResult extends Result { + /** + * An opaque token representing the pagination position after the last returned result. + * If present, there may be more results available. + */ + nextCursor?: Cursor; + } + + /* Resources */ + /** + * Sent from the client to request a list of resources the server has. + */ + export interface ListResourcesRequest extends PaginatedRequest { + method: "resources/list"; + } + + /** + * The server's response to a resources/list request from the client. + */ + export interface ListResourcesResult extends PaginatedResult { + resources: Resource[]; + } + + /** + * Sent from the client to request a list of resource templates the server has. + */ + export interface ListResourceTemplatesRequest extends PaginatedRequest { + method: "resources/templates/list"; + } + + /** + * The server's response to a resources/templates/list request from the client. + */ + export interface ListResourceTemplatesResult extends PaginatedResult { + resourceTemplates: ResourceTemplate[]; + } + + /** + * Sent from the client to the server, to read a specific resource URI. + */ + export interface ReadResourceRequest extends Request { + method: "resources/read"; + params: { + /** + * The URI of the resource to read. The URI can use any protocol; it is up to the server how to interpret it. + * + * @format uri + */ + uri: string; + }; + } + + /** + * The server's response to a resources/read request from the client. + */ + export interface ReadResourceResult extends Result { + contents: (TextResourceContents | BlobResourceContents)[]; + } + + /** + * An optional notification from the server to the client, informing it that the list of resources it can read from has changed. This may be issued by servers without any previous subscription from the client. + */ + export interface ResourceListChangedNotification extends Notification { + method: "notifications/resources/list_changed"; + } + + /** + * Sent from the client to request resources/updated notifications from the server whenever a particular resource changes. + */ + export interface SubscribeRequest extends Request { + method: "resources/subscribe"; + params: { + /** + * The URI of the resource to subscribe to. The URI can use any protocol; it is up to the server how to interpret it. + * + * @format uri + */ + uri: string; + }; + } + + /** + * Sent from the client to request cancellation of resources/updated notifications from the server. This should follow a previous resources/subscribe request. + */ + export interface UnsubscribeRequest extends Request { + method: "resources/unsubscribe"; + params: { + /** + * The URI of the resource to unsubscribe from. + * + * @format uri + */ + uri: string; + }; + } + + /** + * A notification from the server to the client, informing it that a resource has changed and may need to be read again. This should only be sent if the client previously sent a resources/subscribe request. + */ + export interface ResourceUpdatedNotification extends Notification { + method: "notifications/resources/updated"; + params: { + /** + * The URI of the resource that has been updated. This might be a sub-resource of the one that the client actually subscribed to. + * + * @format uri + */ + uri: string; + }; + } + + /** + * A known resource that the server is capable of reading. + */ + export interface Resource extends Annotated { + /** + * The URI of this resource. + * + * @format uri + */ + uri: string; + + /** + * A human-readable name for this resource. + * + * This can be used by clients to populate UI elements. + */ + name: string; + + /** + * A description of what this resource represents. + * + * This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model. + */ + description?: string; + + /** + * The MIME type of this resource, if known. + */ + mimeType?: string; + + /** + * The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known. + * + * This can be used by Hosts to display file sizes and estimate context window usage. + */ + size?: number; + } + + /** + * A template description for resources available on the server. + */ + export interface ResourceTemplate extends Annotated { + /** + * A URI template (according to RFC 6570) that can be used to construct resource URIs. + * + * @format uri-template + */ + uriTemplate: string; + + /** + * A human-readable name for the type of resource this template refers to. + * + * This can be used by clients to populate UI elements. + */ + name: string; + + /** + * A description of what this template is for. + * + * This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model. + */ + description?: string; + + /** + * The MIME type for all resources that match this template. This should only be included if all resources matching this template have the same type. + */ + mimeType?: string; + } + + /** + * The contents of a specific resource or sub-resource. + */ + export interface ResourceContents { + /** + * The URI of this resource. + * + * @format uri + */ + uri: string; + /** + * The MIME type of this resource, if known. + */ + mimeType?: string; + } + + export interface TextResourceContents extends ResourceContents { + /** + * The text of the item. This must only be set if the item can actually be represented as text (not binary data). + */ + text: string; + } + + export interface BlobResourceContents extends ResourceContents { + /** + * A base64-encoded string representing the binary data of the item. + * + * @format byte + */ + blob: string; + } + + /* Prompts */ + /** + * Sent from the client to request a list of prompts and prompt templates the server has. + */ + export interface ListPromptsRequest extends PaginatedRequest { + method: "prompts/list"; + } + + /** + * The server's response to a prompts/list request from the client. + */ + export interface ListPromptsResult extends PaginatedResult { + prompts: Prompt[]; + } + + /** + * Used by the client to get a prompt provided by the server. + */ + export interface GetPromptRequest extends Request { + method: "prompts/get"; + params: { + /** + * The name of the prompt or prompt template. + */ + name: string; + /** + * Arguments to use for templating the prompt. + */ + arguments?: { [key: string]: string }; + }; + } + + /** + * The server's response to a prompts/get request from the client. + */ + export interface GetPromptResult extends Result { + /** + * An optional description for the prompt. + */ + description?: string; + messages: PromptMessage[]; + } + + /** + * A prompt or prompt template that the server offers. + */ + export interface Prompt { + /** + * The name of the prompt or prompt template. + */ + name: string; + /** + * An optional description of what this prompt provides + */ + description?: string; + /** + * A list of arguments to use for templating the prompt. + */ + arguments?: PromptArgument[]; + } + + /** + * Describes an argument that a prompt can accept. + */ + export interface PromptArgument { + /** + * The name of the argument. + */ + name: string; + /** + * A human-readable description of the argument. + */ + description?: string; + /** + * Whether this argument must be provided. + */ + required?: boolean; + } + + /** + * The sender or recipient of messages and data in a conversation. + */ + export type Role = "user" | "assistant"; + + /** + * Describes a message returned as part of a prompt. + * + * This is similar to `SamplingMessage`, but also supports the embedding of + * resources from the MCP server. + */ + export interface PromptMessage { + role: Role; + content: TextContent | ImageContent | EmbeddedResource; + } + + /** + * The contents of a resource, embedded into a prompt or tool call result. + * + * It is up to the client how best to render embedded resources for the benefit + * of the LLM and/or the user. + */ + export interface EmbeddedResource extends Annotated { + type: "resource"; + resource: TextResourceContents | BlobResourceContents; + } + + /** + * An optional notification from the server to the client, informing it that the list of prompts it offers has changed. This may be issued by servers without any previous subscription from the client. + */ + export interface PromptListChangedNotification extends Notification { + method: "notifications/prompts/list_changed"; + } + + /* Tools */ + /** + * Sent from the client to request a list of tools the server has. + */ + export interface ListToolsRequest extends PaginatedRequest { + method: "tools/list"; + } + + /** + * The server's response to a tools/list request from the client. + */ + export interface ListToolsResult extends PaginatedResult { + tools: Tool[]; + } + + /** + * The server's response to a tool call. + * + * Any errors that originate from the tool SHOULD be reported inside the result + * object, with `isError` set to true, _not_ as an MCP protocol-level error + * response. Otherwise, the LLM would not be able to see that an error occurred + * and self-correct. + * + * However, any errors in _finding_ the tool, an error indicating that the + * server does not support tool calls, or any other exceptional conditions, + * should be reported as an MCP error response. + */ + export interface CallToolResult extends Result { + content: (TextContent | ImageContent | EmbeddedResource)[]; + + /** + * Whether the tool call ended in an error. + * + * If not set, this is assumed to be false (the call was successful). + */ + isError?: boolean; + } + + /** + * Used by the client to invoke a tool provided by the server. + */ + export interface CallToolRequest extends Request { + method: "tools/call"; + params: { + name: string; + arguments?: { [key: string]: unknown }; + }; + } + + /** + * An optional notification from the server to the client, informing it that the list of tools it offers has changed. This may be issued by servers without any previous subscription from the client. + */ + export interface ToolListChangedNotification extends Notification { + method: "notifications/tools/list_changed"; + } + + /** + * Definition for a tool the client can call. + */ + export interface Tool { + /** + * The name of the tool. + */ + name: string; + /** + * A human-readable description of the tool. + */ + description?: string; + /** + * A JSON Schema object defining the expected parameters for the tool. + */ + inputSchema: { + type: "object"; + properties?: { [key: string]: object }; + required?: string[]; + }; + } + + /* Logging */ + /** + * A request from the client to the server, to enable or adjust logging. + */ + export interface SetLevelRequest extends Request { + method: "logging/setLevel"; + params: { + /** + * The level of logging that the client wants to receive from the server. The server should send all logs at this level and higher (i.e., more severe) to the client as notifications/message. + */ + level: LoggingLevel; + }; + } + + /** + * Notification of a log message passed from server to client. If no logging/setLevel request has been sent from the client, the server MAY decide which messages to send automatically. + */ + export interface LoggingMessageNotification extends Notification { + method: "notifications/message"; + params: { + /** + * The severity of this log message. + */ + level: LoggingLevel; + /** + * An optional name of the logger issuing this message. + */ + logger?: string; + /** + * The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here. + */ + data: unknown; + }; + } + + /** + * The severity of a log message. + * + * These map to syslog message severities, as specified in RFC-5424: + * https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1 + */ + export type LoggingLevel = + | "debug" + | "info" + | "notice" + | "warning" + | "error" + | "critical" + | "alert" + | "emergency"; + + /* Sampling */ + /** + * A request from the server to sample an LLM via the client. The client has full discretion over which model to select. The client should also inform the user before beginning sampling, to allow them to inspect the request (human in the loop) and decide whether to approve it. + */ + export interface CreateMessageRequest extends Request { + method: "sampling/createMessage"; + params: { + messages: SamplingMessage[]; + /** + * The server's preferences for which model to select. The client MAY ignore these preferences. + */ + modelPreferences?: ModelPreferences; + /** + * An optional system prompt the server wants to use for sampling. The client MAY modify or omit this prompt. + */ + systemPrompt?: string; + /** + * A request to include context from one or more MCP servers (including the caller), to be attached to the prompt. The client MAY ignore this request. + */ + includeContext?: "none" | "thisServer" | "allServers"; + /** + * @TJS-type number + */ + temperature?: number; + /** + * The maximum number of tokens to sample, as requested by the server. The client MAY choose to sample fewer tokens than requested. + */ + maxTokens: number; + stopSequences?: string[]; + /** + * Optional metadata to pass through to the LLM provider. The format of this metadata is provider-specific. + */ + metadata?: object; + }; + } + + /** + * The client's response to a sampling/create_message request from the server. The client should inform the user before returning the sampled message, to allow them to inspect the response (human in the loop) and decide whether to allow the server to see it. + */ + export interface CreateMessageResult extends Result, SamplingMessage { + /** + * The name of the model that generated the message. + */ + model: string; + /** + * The reason why sampling stopped, if known. + */ + stopReason?: "endTurn" | "stopSequence" | "maxTokens" | string; + } + + /** + * Describes a message issued to or received from an LLM API. + */ + export interface SamplingMessage { + role: Role; + content: TextContent | ImageContent; + } + + /** + * Base for objects that include optional annotations for the client. The client can use annotations to inform how objects are used or displayed + */ + export interface Annotated { + annotations?: { + /** + * Describes who the intended customer of this object or data is. + * + * It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`). + */ + audience?: Role[]; + + /** + * Describes how important this data is for operating the server. + * + * A value of 1 means "most important," and indicates that the data is + * effectively required, while 0 means "least important," and indicates that + * the data is entirely optional. + * + * @TJS-type number + * @minimum 0 + * @maximum 1 + */ + priority?: number; + } + } + + /** + * Text provided to or from an LLM. + */ + export interface TextContent extends Annotated { + type: "text"; + /** + * The text content of the message. + */ + text: string; + } + + /** + * An image provided to or from an LLM. + */ + export interface ImageContent extends Annotated { + type: "image"; + /** + * The base64-encoded image data. + * + * @format byte + */ + data: string; + /** + * The MIME type of the image. Different providers may support different image types. + */ + mimeType: string; + } + + /** + * The server's preferences for model selection, requested of the client during sampling. + * + * Because LLMs can vary along multiple dimensions, choosing the "best" model is + * rarely straightforward. Different models excel in different areas-some are + * faster but less capable, others are more capable but more expensive, and so + * on. This interface allows servers to express their priorities across multiple + * dimensions to help clients make an appropriate selection for their use case. + * + * These preferences are always advisory. The client MAY ignore them. It is also + * up to the client to decide how to interpret these preferences and how to + * balance them against other considerations. + */ + export interface ModelPreferences { + /** + * Optional hints to use for model selection. + * + * If multiple hints are specified, the client MUST evaluate them in order + * (such that the first match is taken). + * + * The client SHOULD prioritize these hints over the numeric priorities, but + * MAY still use the priorities to select from ambiguous matches. + */ + hints?: ModelHint[]; + + /** + * How much to prioritize cost when selecting a model. A value of 0 means cost + * is not important, while a value of 1 means cost is the most important + * factor. + * + * @TJS-type number + * @minimum 0 + * @maximum 1 + */ + costPriority?: number; + + /** + * How much to prioritize sampling speed (latency) when selecting a model. A + * value of 0 means speed is not important, while a value of 1 means speed is + * the most important factor. + * + * @TJS-type number + * @minimum 0 + * @maximum 1 + */ + speedPriority?: number; + + /** + * How much to prioritize intelligence and capabilities when selecting a + * model. A value of 0 means intelligence is not important, while a value of 1 + * means intelligence is the most important factor. + * + * @TJS-type number + * @minimum 0 + * @maximum 1 + */ + intelligencePriority?: number; + } + + /** + * Hints to use for model selection. + * + * Keys not declared here are currently left unspecified by the spec and are up + * to the client to interpret. + */ + export interface ModelHint { + /** + * A hint for a model name. + * + * The client SHOULD treat this as a substring of a model name; for example: + * - `claude-3-5-sonnet` should match `claude-3-5-sonnet-20241022` + * - `sonnet` should match `claude-3-5-sonnet-20241022`, `claude-3-sonnet-20240229`, etc. + * - `claude` should match any Claude model + * + * The client MAY also map the string to a different provider's model name or a different model family, as long as it fills a similar niche; for example: + * - `gemini-1.5-flash` could match `claude-3-haiku-20240307` + */ + name?: string; + } + + /* Autocomplete */ + /** + * A request from the client to the server, to ask for completion options. + */ + export interface CompleteRequest extends Request { + method: "completion/complete"; + params: { + ref: PromptReference | ResourceReference; + /** + * The argument's information + */ + argument: { + /** + * The name of the argument + */ + name: string; + /** + * The value of the argument to use for completion matching. + */ + value: string; + }; + }; + } + + /** + * The server's response to a completion/complete request + */ + export interface CompleteResult extends Result { + completion: { + /** + * An array of completion values. Must not exceed 100 items. + */ + values: string[]; + /** + * The total number of completion options available. This can exceed the number of values actually sent in the response. + */ + total?: number; + /** + * Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown. + */ + hasMore?: boolean; + }; + } + + /** + * A reference to a resource or resource template definition. + */ + export interface ResourceReference { + type: "ref/resource"; + /** + * The URI or URI template of the resource. + * + * @format uri-template + */ + uri: string; + } + + /** + * Identifies a prompt. + */ + export interface PromptReference { + type: "ref/prompt"; + /** + * The name of the prompt or prompt template + */ + name: string; + } + + /* Roots */ + /** + * Sent from the server to request a list of root URIs from the client. Roots allow + * servers to ask for specific directories or files to operate on. A common example + * for roots is providing a set of repositories or directories a server should operate + * on. + * + * This request is typically used when the server needs to understand the file system + * structure or access specific locations that the client has permission to read from. + */ + export interface ListRootsRequest extends Request { + method: "roots/list"; + } + + /** + * The client's response to a roots/list request from the server. + * This result contains an array of Root objects, each representing a root directory + * or file that the server can operate on. + */ + export interface ListRootsResult extends Result { + roots: Root[]; + } + + /** + * Represents a root directory or file that the server can operate on. + */ + export interface Root { + /** + * The URI identifying the root. This *must* start with file:// for now. + * This restriction may be relaxed in future versions of the protocol to allow + * other URI schemes. + * + * @format uri + */ + uri: string; + /** + * An optional name for the root. This can be used to provide a human-readable + * identifier for the root, which may be useful for display purposes or for + * referencing the root in other parts of the application. + */ + name?: string; + } + + /** + * A notification from the client to the server, informing it that the list of roots has changed. + * This notification should be sent whenever the client adds, removes, or modifies any root. + * The server should then request an updated list of roots using the ListRootsRequest. + */ + export interface RootsListChangedNotification extends Notification { + method: "notifications/roots/list_changed"; + } + + /* Client messages */ + export type ClientRequest = + | PingRequest + | InitializeRequest + | CompleteRequest + | SetLevelRequest + | GetPromptRequest + | ListPromptsRequest + | ListResourcesRequest + | ListResourceTemplatesRequest + | ReadResourceRequest + | SubscribeRequest + | UnsubscribeRequest + | CallToolRequest + | ListToolsRequest; + + export type ClientNotification = + | CancelledNotification + | ProgressNotification + | InitializedNotification + | RootsListChangedNotification; + + export type ClientResult = EmptyResult | CreateMessageResult | ListRootsResult; + + /* Server messages */ + export type ServerRequest = + | PingRequest + | CreateMessageRequest + | ListRootsRequest; + + export type ServerNotification = + | CancelledNotification + | ProgressNotification + | LoggingMessageNotification + | ResourceUpdatedNotification + | ResourceListChangedNotification + | ToolListChangedNotification + | PromptListChangedNotification; + + export type ServerResult = + | EmptyResult + | InitializeResult + | CompleteResult + | GetPromptResult + | ListPromptsResult + | ListResourcesResult + | ListResourceTemplatesResult + | ReadResourceResult + | CallToolResult + | ListToolsResult; +} diff --git a/src/vs/workbench/contrib/mcp/electron-sandbox/mcp.contribution.ts b/src/vs/workbench/contrib/mcp/electron-sandbox/mcp.contribution.ts new file mode 100644 index 000000000000..efdf3ee192d4 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/electron-sandbox/mcp.contribution.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; +import { mcpDiscoveryRegistry } from '../common/discovery/mcpDiscovery.js'; +import { NativeMcpDiscovery } from './nativeMpcDiscovery.js'; + +mcpDiscoveryRegistry.register(new SyncDescriptor(NativeMcpDiscovery)); diff --git a/src/vs/workbench/contrib/mcp/electron-sandbox/nativeMpcDiscovery.ts b/src/vs/workbench/contrib/mcp/electron-sandbox/nativeMpcDiscovery.ts new file mode 100644 index 000000000000..11da0b14991f --- /dev/null +++ b/src/vs/workbench/contrib/mcp/electron-sandbox/nativeMpcDiscovery.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ProxyChannel } from '../../../../base/parts/ipc/common/ipc.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { IMainProcessService } from '../../../../platform/ipc/common/mainProcessService.js'; +import { ILabelService } from '../../../../platform/label/common/label.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +import { INativeMcpDiscoveryHelperService, NativeMcpDiscoveryHelperChannelName } from '../../../../platform/mcp/common/nativeMcpDiscoveryHelper.js'; +import { FilesystemMpcDiscovery } from '../common/discovery/nativeMcpDiscoveryAbstract.js'; +import { IMcpRegistry } from '../common/mcpRegistryTypes.js'; + +export class NativeMcpDiscovery extends FilesystemMpcDiscovery { + constructor( + @IMainProcessService private readonly mainProcess: IMainProcessService, + @ILogService private readonly logService: ILogService, + @ILabelService labelService: ILabelService, + @IFileService fileService: IFileService, + @IInstantiationService instantiationService: IInstantiationService, + @IMcpRegistry mcpRegistry: IMcpRegistry, + @IConfigurationService configurationService: IConfigurationService, + ) { + super(null, labelService, fileService, instantiationService, mcpRegistry, configurationService); + } + + public override start(): void { + const service = ProxyChannel.toService( + this.mainProcess.getChannel(NativeMcpDiscoveryHelperChannelName)); + + service.load().then( + data => this.setDetails(data), + err => { + this.logService.warn('Error getting main process MCP environment', err); + this.setDetails(undefined); + } + ); + } +} diff --git a/src/vs/workbench/contrib/mcp/test/common/mcpRegistry.test.ts b/src/vs/workbench/contrib/mcp/test/common/mcpRegistry.test.ts new file mode 100644 index 000000000000..774878b19d28 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/test/common/mcpRegistry.test.ts @@ -0,0 +1,591 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import { cloneAndChange } from '../../../../../base/common/objects.js'; +import { ISettableObservable, observableValue } from '../../../../../base/common/observable.js'; +import { upcast } from '../../../../../base/common/types.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { ConfigurationTarget } from '../../../../../platform/configuration/common/configuration.js'; +import { IDialogService } from '../../../../../platform/dialogs/common/dialogs.js'; +import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js'; +import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; +import { ILoggerService } from '../../../../../platform/log/common/log.js'; +import { ISecretStorageService } from '../../../../../platform/secrets/common/secrets.js'; +import { TestSecretStorageService } from '../../../../../platform/secrets/test/common/testSecretStorageService.js'; +import { IStorageService, StorageScope } from '../../../../../platform/storage/common/storage.js'; +import { IConfigurationResolverService } from '../../../../services/configurationResolver/common/configurationResolver.js'; +import { IOutputService } from '../../../../services/output/common/output.js'; +import { TestLoggerService, TestStorageService } from '../../../../test/common/workbenchTestServices.js'; +import { McpRegistry } from '../../common/mcpRegistry.js'; +import { IMcpHostDelegate, IMcpMessageTransport } from '../../common/mcpRegistryTypes.js'; +import { McpServerConnection } from '../../common/mcpServerConnection.js'; +import { LazyCollectionState, McpCollectionDefinition, McpCollectionReference, McpServerDefinition, McpServerTransportType } from '../../common/mcpTypes.js'; +import { TestMcpMessageTransport } from './mcpRegistryTypes.js'; +import { timeout } from '../../../../../base/common/async.js'; +import { IProductService } from '../../../../../platform/product/common/productService.js'; + +class TestConfigurationResolverService implements Partial { + declare readonly _serviceBrand: undefined; + + private interactiveCounter = 0; + + // Used to simulate stored/resolved variables + private readonly resolvedVariables = new Map(); + + constructor() { + // Add some test variables + this.resolvedVariables.set('workspaceFolder', '/test/workspace'); + this.resolvedVariables.set('fileBasename', 'test.txt'); + } + + resolveAsync(folder: any, value: any): Promise { + if (typeof value === 'string') { + return Promise.resolve(this.replaceVariables(value)); + } else if (Array.isArray(value)) { + return Promise.resolve(value.map(v => typeof v === 'string' ? this.replaceVariables(v) : v)); + } else { + const result: Record = {}; + for (const key in value) { + if (typeof value[key] === 'string') { + result[key] = this.replaceVariables(value[key]); + } else { + result[key] = value[key]; + } + } + return Promise.resolve(result); + } + } + + private replaceVariables(value: string): string { + let result = value; + for (const [key, val] of this.resolvedVariables.entries()) { + result = result.replace(`\${${key}}`, val); + } + return result; + } + + resolveAnyAsync(folder: any, config: any, commandValueMapping?: Record): Promise { + // Use cloneAndChange to recursively replace variables in the config + const newConfig = cloneAndChange(config, (value) => { + if (typeof value === 'string') { + // Replace any ${variable} with its value + let result = value; + for (const [key, val] of this.resolvedVariables.entries()) { + result = result.replace(`\${${key}}`, val); + } + + // If a commandValueMapping is provided, use it for additional replacements + if (commandValueMapping) { + for (const [key, val] of Object.entries(commandValueMapping)) { + result = result.replace(`\${${key}}`, val); + } + } + + return result === value ? undefined : result; + } + return undefined; + }); + + return Promise.resolve(newConfig); + } + + resolveWithInteraction(folder: any, config: any, section?: string, variables?: Record, target?: ConfigurationTarget): Promise | undefined> { + // For testing, we simulate interaction by returning a map with some variables + const result = new Map(); + result.set('input:testInteractive', `interactiveValue${this.interactiveCounter++}`); + result.set('command:testCommand', `commandOutput${this.interactiveCounter++}}`); + + // If variables are provided, include those too + if (variables) { + Object.entries(variables).forEach(([key, value]) => { + result.set(key, value); + }); + } + + return Promise.resolve(result); + } +} + +class TestMcpHostDelegate implements IMcpHostDelegate { + canStart(): boolean { + return true; + } + + start(): IMcpMessageTransport { + return new TestMcpMessageTransport(); + } + + waitForInitialProviderPromises(): Promise { + return Promise.resolve(); + } +} + +class TestDialogService implements Partial { + declare readonly _serviceBrand: undefined; + + private _promptResult: boolean | undefined; + private _promptSpy: sinon.SinonStub; + + constructor() { + this._promptSpy = sinon.stub(); + this._promptSpy.callsFake(() => { + return Promise.resolve({ result: this._promptResult }); + }); + } + + setPromptResult(result: boolean | undefined): void { + this._promptResult = result; + } + + get promptSpy(): sinon.SinonStub { + return this._promptSpy; + } + + prompt(options: any): Promise { + return this._promptSpy(options); + } +} + +suite('Workbench - MCP - Registry', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + + let registry: McpRegistry; + let testStorageService: TestStorageService; + let testConfigResolverService: TestConfigurationResolverService; + let testDialogService: TestDialogService; + let testCollection: McpCollectionDefinition & { serverDefinitions: ISettableObservable }; + let baseDefinition: McpServerDefinition; + + setup(() => { + testConfigResolverService = new TestConfigurationResolverService(); + testStorageService = store.add(new TestStorageService()); + testDialogService = new TestDialogService(); + + const services = new ServiceCollection( + [IConfigurationResolverService, testConfigResolverService], + [IStorageService, testStorageService], + [ISecretStorageService, new TestSecretStorageService()], + [ILoggerService, store.add(new TestLoggerService())], + [IOutputService, upcast({ showChannel: () => { } })], + [IDialogService, testDialogService], + [IProductService, {}], + ); + + const instaService = store.add(new TestInstantiationService(services)); + registry = store.add(instaService.createInstance(McpRegistry)); + + // Create test collection that can be reused + testCollection = { + id: 'test-collection', + label: 'Test Collection', + remoteAuthority: null, + serverDefinitions: observableValue('serverDefs', []), + isTrustedByDefault: true, + scope: StorageScope.APPLICATION + }; + + // Create base definition that can be reused + baseDefinition = { + id: 'test-server', + label: 'Test Server', + launch: { + type: McpServerTransportType.Stdio, + command: 'test-command', + args: [], + env: {}, + cwd: URI.parse('file:///test') + } + }; + }); + + test('registerCollection adds collection to registry', () => { + const disposable = registry.registerCollection(testCollection); + store.add(disposable); + + assert.strictEqual(registry.collections.get().length, 1); + assert.strictEqual(registry.collections.get()[0], testCollection); + + disposable.dispose(); + assert.strictEqual(registry.collections.get().length, 0); + }); + + test('registerDelegate adds delegate to registry', () => { + const delegate = new TestMcpHostDelegate(); + const disposable = registry.registerDelegate(delegate); + store.add(disposable); + + assert.strictEqual(registry.delegates.length, 1); + assert.strictEqual(registry.delegates[0], delegate); + + disposable.dispose(); + assert.strictEqual(registry.delegates.length, 0); + }); + + test('resolveConnection creates connection with resolved variables and memorizes them until cleared', async () => { + const definition: McpServerDefinition = { + ...baseDefinition, + launch: { + type: McpServerTransportType.Stdio, + command: '${workspaceFolder}/cmd', + args: ['--file', '${fileBasename}'], + env: { + PATH: '${input:testInteractive}' + }, + cwd: URI.parse('file:///test') + }, + variableReplacement: { + section: 'mcp' + } + }; + + const delegate = new TestMcpHostDelegate(); + store.add(registry.registerDelegate(delegate)); + testCollection.serverDefinitions.set([definition], undefined); + store.add(registry.registerCollection(testCollection)); + + const connection = await registry.resolveConnection({ collectionRef: testCollection, definitionRef: definition }) as McpServerConnection; + + assert.ok(connection); + assert.strictEqual(connection.definition, definition); + assert.strictEqual((connection.launchDefinition as any).command, '/test/workspace/cmd'); + assert.strictEqual((connection.launchDefinition as any).env.PATH, 'interactiveValue0'); + connection.dispose(); + + const connection2 = await registry.resolveConnection({ collectionRef: testCollection, definitionRef: definition }) as McpServerConnection; + + assert.ok(connection2); + assert.strictEqual((connection2.launchDefinition as any).env.PATH, 'interactiveValue0'); + connection2.dispose(); + + registry.clearSavedInputs(); + + const connection3 = await registry.resolveConnection({ collectionRef: testCollection, definitionRef: definition }) as McpServerConnection; + + assert.ok(connection3); + assert.strictEqual((connection3.launchDefinition as any).env.PATH, 'interactiveValue4'); + connection3.dispose(); + }); + + suite('Trust Management', () => { + setup(() => { + const delegate = new TestMcpHostDelegate(); + store.add(registry.registerDelegate(delegate)); + }); + + test('resolveConnection connects to server when trusted by default', async () => { + const definition = { ...baseDefinition }; + store.add(registry.registerCollection(testCollection)); + testCollection.serverDefinitions.set([definition], undefined); + + const connection = await registry.resolveConnection({ collectionRef: testCollection, definitionRef: definition }); + + assert.ok(connection); + assert.strictEqual(testDialogService.promptSpy.called, false); + connection?.dispose(); + }); + + test('resolveConnection prompts for confirmation when not trusted by default', async () => { + const untrustedCollection: McpCollectionDefinition = { + ...testCollection, + isTrustedByDefault: false + }; + + const definition = { ...baseDefinition }; + store.add(registry.registerCollection(untrustedCollection)); + testCollection.serverDefinitions.set([definition], undefined); + + testDialogService.setPromptResult(true); + + const connection = await registry.resolveConnection({ + collectionRef: untrustedCollection, + definitionRef: definition + }); + + assert.ok(connection); + assert.strictEqual(testDialogService.promptSpy.called, true); + connection?.dispose(); + + testDialogService.promptSpy.resetHistory(); + const connection2 = await registry.resolveConnection({ + collectionRef: untrustedCollection, + definitionRef: definition + }); + + assert.ok(connection2); + assert.strictEqual(testDialogService.promptSpy.called, false); + connection2?.dispose(); + }); + + test('resolveConnection returns undefined when user does not trust the server', async () => { + const untrustedCollection: McpCollectionDefinition = { + ...testCollection, + isTrustedByDefault: false + }; + + const definition = { ...baseDefinition }; + store.add(registry.registerCollection(untrustedCollection)); + testCollection.serverDefinitions.set([definition], undefined); + + testDialogService.setPromptResult(false); + + const connection = await registry.resolveConnection({ + collectionRef: untrustedCollection, + definitionRef: definition + }); + + assert.strictEqual(connection, undefined); + assert.strictEqual(testDialogService.promptSpy.called, true); + + testDialogService.promptSpy.resetHistory(); + const connection2 = await registry.resolveConnection({ + collectionRef: untrustedCollection, + definitionRef: definition + }); + + assert.strictEqual(connection2, undefined); + assert.strictEqual(testDialogService.promptSpy.called, false); + }); + + test('resolveConnection honors forceTrust parameter', async () => { + const untrustedCollection: McpCollectionDefinition = { + ...testCollection, + isTrustedByDefault: false + }; + + const definition = { ...baseDefinition }; + store.add(registry.registerCollection(untrustedCollection)); + testCollection.serverDefinitions.set([definition], undefined); + + testDialogService.setPromptResult(false); + + const connection1 = await registry.resolveConnection({ + collectionRef: untrustedCollection, + definitionRef: definition + }); + + assert.strictEqual(connection1, undefined); + + testDialogService.promptSpy.resetHistory(); + testDialogService.setPromptResult(true); + + const connection2 = await registry.resolveConnection({ + collectionRef: untrustedCollection, + definitionRef: definition, + forceTrust: true + }); + + assert.ok(connection2); + assert.strictEqual(testDialogService.promptSpy.called, true); + connection2?.dispose(); + + testDialogService.promptSpy.resetHistory(); + const connection3 = await registry.resolveConnection({ + collectionRef: untrustedCollection, + definitionRef: definition + }); + + assert.ok(connection3); + assert.strictEqual(testDialogService.promptSpy.called, false); + connection3?.dispose(); + }); + }); + + suite('Lazy Collections', () => { + let lazyCollection: McpCollectionDefinition; + let normalCollection: McpCollectionDefinition; + let removedCalled: boolean; + + setup(() => { + removedCalled = false; + lazyCollection = { + ...testCollection, + id: 'lazy-collection', + lazy: { + isCached: false, + load: () => Promise.resolve(), + removed: () => { removedCalled = true; } + } + }; + normalCollection = { + ...testCollection, + id: 'lazy-collection', + serverDefinitions: observableValue('serverDefs', [baseDefinition]) + }; + }); + + test('registers lazy collection', () => { + const disposable = registry.registerCollection(lazyCollection); + store.add(disposable); + + assert.strictEqual(registry.collections.get().length, 1); + assert.strictEqual(registry.collections.get()[0], lazyCollection); + assert.strictEqual(registry.lazyCollectionState.get(), LazyCollectionState.HasUnknown); + }); + + test('lazy collection is replaced by normal collection', () => { + store.add(registry.registerCollection(lazyCollection)); + store.add(registry.registerCollection(normalCollection)); + + const collections = registry.collections.get(); + assert.strictEqual(collections.length, 1); + assert.strictEqual(collections[0], normalCollection); + assert.strictEqual(collections[0].lazy, undefined); + assert.strictEqual(registry.lazyCollectionState.get(), LazyCollectionState.AllKnown); + }); + + test('lazyCollectionState updates correctly during loading', async () => { + lazyCollection = { + ...lazyCollection, + lazy: { + ...lazyCollection.lazy!, + load: async () => { + await timeout(0); + store.add(registry.registerCollection(normalCollection)); + return Promise.resolve(); + } + } + }; + + store.add(registry.registerCollection(lazyCollection)); + assert.strictEqual(registry.lazyCollectionState.get(), LazyCollectionState.HasUnknown); + + const loadingPromise = registry.discoverCollections(); + assert.strictEqual(registry.lazyCollectionState.get(), LazyCollectionState.LoadingUnknown); + + await loadingPromise; + + // The collection wasn't replaced, so it should be removed + assert.strictEqual(registry.collections.get().length, 1); + assert.strictEqual(registry.lazyCollectionState.get(), LazyCollectionState.AllKnown); + assert.strictEqual(removedCalled, false); + }); + + test('removed callback is called when lazy collection is not replaced', async () => { + store.add(registry.registerCollection(lazyCollection)); + await registry.discoverCollections(); + + assert.strictEqual(removedCalled, true); + }); + + test('cached lazy collections are tracked correctly', () => { + lazyCollection.lazy!.isCached = true; + store.add(registry.registerCollection(lazyCollection)); + + assert.strictEqual(registry.lazyCollectionState.get(), LazyCollectionState.AllKnown); + + // Adding an uncached lazy collection changes the state + const uncachedLazy = { + ...lazyCollection, + id: 'uncached-lazy', + lazy: { + ...lazyCollection.lazy!, + isCached: false + } + }; + store.add(registry.registerCollection(uncachedLazy)); + + assert.strictEqual(registry.lazyCollectionState.get(), LazyCollectionState.HasUnknown); + }); + }); + + suite('Collection Tool Prefixes', () => { + test('assigns unique prefixes to collections', () => { + const collection1: McpCollectionDefinition = { + id: 'collection1', + label: 'Collection 1', + remoteAuthority: null, + serverDefinitions: observableValue('serverDefs', []), + isTrustedByDefault: true, + scope: StorageScope.APPLICATION + }; + + const collection2: McpCollectionDefinition = { + id: 'collection2', + label: 'Collection 2', + remoteAuthority: null, + serverDefinitions: observableValue('serverDefs', []), + isTrustedByDefault: true, + scope: StorageScope.APPLICATION + }; + + store.add(registry.registerCollection(collection1)); + store.add(registry.registerCollection(collection2)); + + const prefix1 = registry.collectionToolPrefix(collection1).get(); + const prefix2 = registry.collectionToolPrefix(collection2).get(); + + assert.notStrictEqual(prefix1, prefix2); + assert.ok(/^[a-f0-9]{3}\.$/.test(prefix1)); + assert.ok(/^[a-f0-9]{3}\.$/.test(prefix2)); + }); + + test('handles hash collisions by incrementing view', () => { + // These strings are known to have SHA1 hash collisions in their first 3 characters + const collection1: McpCollectionDefinition = { + id: 'potato', + label: 'Collection 1', + remoteAuthority: null, + serverDefinitions: observableValue('serverDefs', []), + isTrustedByDefault: true, + scope: StorageScope.APPLICATION + }; + + const collection2: McpCollectionDefinition = { + id: 'candidate_83048', + label: 'Collection 2', + remoteAuthority: null, + serverDefinitions: observableValue('serverDefs', []), + isTrustedByDefault: true, + scope: StorageScope.APPLICATION + }; + + store.add(registry.registerCollection(collection1)); + store.add(registry.registerCollection(collection2)); + + const prefix1 = registry.collectionToolPrefix(collection1).get(); + const prefix2 = registry.collectionToolPrefix(collection2).get(); + + assert.notStrictEqual(prefix1, prefix2); + assert.ok(/^[a-f0-9]{3}\.$/.test(prefix1)); + assert.ok(/^[a-f0-9]{3}\.$/.test(prefix2)); + }); + + test('prefix changes when collections change', () => { + const collection1: McpCollectionDefinition = { + id: 'collection1', + label: 'Collection 1', + remoteAuthority: null, + serverDefinitions: observableValue('serverDefs', []), + isTrustedByDefault: true, + scope: StorageScope.APPLICATION + }; + + const disposable = registry.registerCollection(collection1); + store.add(disposable); + + const prefix1 = registry.collectionToolPrefix(collection1).get(); + assert.ok(!!prefix1); + + disposable.dispose(); + + const prefix2 = registry.collectionToolPrefix(collection1).get(); + + assert.strictEqual(prefix2, ''); + }); + + test('prefix is empty for unknown collections', () => { + const unknownCollection: McpCollectionReference = { + id: 'unknown', + label: 'Unknown' + }; + + const prefix = registry.collectionToolPrefix(unknownCollection).get(); + assert.strictEqual(prefix, ''); + }); + }); +}); diff --git a/src/vs/workbench/contrib/mcp/test/common/mcpRegistryInputStorage.test.ts b/src/vs/workbench/contrib/mcp/test/common/mcpRegistryInputStorage.test.ts new file mode 100644 index 000000000000..f6f745d57512 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/test/common/mcpRegistryInputStorage.test.ts @@ -0,0 +1,178 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { ILogService, NullLogService } from '../../../../../platform/log/common/log.js'; +import { TestSecretStorageService } from '../../../../../platform/secrets/test/common/testSecretStorageService.js'; +import { StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js'; +import { TestStorageService } from '../../../../test/common/workbenchTestServices.js'; +import { McpRegistryInputStorage } from '../../common/mcpRegistryInputStorage.js'; + +suite('Workbench - MCP - RegistryInputStorage', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + + let testStorageService: TestStorageService; + let testSecretStorageService: TestSecretStorageService; + let testLogService: ILogService; + let mcpInputStorage: McpRegistryInputStorage; + + setup(() => { + testStorageService = store.add(new TestStorageService()); + testSecretStorageService = new TestSecretStorageService(); + testLogService = store.add(new NullLogService()); + + // Create the input storage with APPLICATION scope + mcpInputStorage = store.add(new McpRegistryInputStorage( + StorageScope.APPLICATION, + StorageTarget.MACHINE, + testStorageService, + testSecretStorageService, + testLogService + )); + }); + + test('setPlainText stores values that can be retrieved with getMap', async () => { + const values = { + 'key1': 'value1', + 'key2': 'value2' + }; + + await mcpInputStorage.setPlainText(values); + const result = await mcpInputStorage.getMap(); + + assert.strictEqual(result.key1, 'value1'); + assert.strictEqual(result.key2, 'value2'); + }); + + test('setSecrets stores encrypted values that can be retrieved with getMap', async () => { + const secrets = { + 'secretKey1': 'secretValue1', + 'secretKey2': 'secretValue2' + }; + + await mcpInputStorage.setSecrets(secrets); + const result = await mcpInputStorage.getMap(); + + assert.strictEqual(result.secretKey1, 'secretValue1'); + assert.strictEqual(result.secretKey2, 'secretValue2'); + }); + + test('getMap returns combined plain text and secret values', async () => { + await mcpInputStorage.setPlainText({ + 'plainKey': 'plainValue' + }); + + await mcpInputStorage.setSecrets({ + 'secretKey': 'secretValue' + }); + + const result = await mcpInputStorage.getMap(); + + assert.strictEqual(result.plainKey, 'plainValue'); + assert.strictEqual(result.secretKey, 'secretValue'); + }); + + test('clear removes specific values', async () => { + await mcpInputStorage.setPlainText({ + 'key1': 'value1', + 'key2': 'value2' + }); + + await mcpInputStorage.setSecrets({ + 'secretKey1': 'secretValue1', + 'secretKey2': 'secretValue2' + }); + + // Clear one plain and one secret value + await mcpInputStorage.clear('key1'); + await mcpInputStorage.clear('secretKey1'); + + const result = await mcpInputStorage.getMap(); + + assert.strictEqual(result.key1, undefined); + assert.strictEqual(result.key2, 'value2'); + assert.strictEqual(result.secretKey1, undefined); + assert.strictEqual(result.secretKey2, 'secretValue2'); + }); + + test('clearAll removes all values', async () => { + await mcpInputStorage.setPlainText({ + 'key1': 'value1' + }); + + await mcpInputStorage.setSecrets({ + 'secretKey1': 'secretValue1' + }); + + mcpInputStorage.clearAll(); + + const result = await mcpInputStorage.getMap(); + + assert.deepStrictEqual(result, {}); + }); + + test('updates to plain text values overwrite existing values', async () => { + await mcpInputStorage.setPlainText({ + 'key1': 'value1', + 'key2': 'value2' + }); + + await mcpInputStorage.setPlainText({ + 'key1': 'updatedValue1' + }); + + const result = await mcpInputStorage.getMap(); + + assert.strictEqual(result.key1, 'updatedValue1'); + assert.strictEqual(result.key2, 'value2'); + }); + + test('updates to secret values overwrite existing values', async () => { + await mcpInputStorage.setSecrets({ + 'secretKey1': 'secretValue1', + 'secretKey2': 'secretValue2' + }); + + await mcpInputStorage.setSecrets({ + 'secretKey1': 'updatedSecretValue1' + }); + + const result = await mcpInputStorage.getMap(); + + assert.strictEqual(result.secretKey1, 'updatedSecretValue1'); + assert.strictEqual(result.secretKey2, 'secretValue2'); + }); + + test('storage persists values across instances', async () => { + // Set values on first instance + await mcpInputStorage.setPlainText({ + 'key1': 'value1' + }); + + await mcpInputStorage.setSecrets({ + 'secretKey1': 'secretValue1' + }); + + await testStorageService.flush(); + + // Create a second instance that should have access to the same storage + const secondInstance = store.add(new McpRegistryInputStorage( + StorageScope.APPLICATION, + StorageTarget.MACHINE, + testStorageService, + testSecretStorageService, + testLogService + )); + + const result = await secondInstance.getMap(); + + assert.strictEqual(result.key1, 'value1'); + assert.strictEqual(result.secretKey1, 'secretValue1'); + + assert.ok(!testStorageService.get('mcpInputs', StorageScope.APPLICATION)?.includes('secretValue1')); + }); +}); + diff --git a/src/vs/workbench/contrib/mcp/test/common/mcpRegistryTypes.ts b/src/vs/workbench/contrib/mcp/test/common/mcpRegistryTypes.ts new file mode 100644 index 000000000000..567c613329da --- /dev/null +++ b/src/vs/workbench/contrib/mcp/test/common/mcpRegistryTypes.ts @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter } from '../../../../../base/common/event.js'; +import { Disposable } from '../../../../../base/common/lifecycle.js'; +import { observableValue } from '../../../../../base/common/observable.js'; +import { IMcpMessageTransport } from '../../common/mcpRegistryTypes.js'; +import { McpConnectionState } from '../../common/mcpTypes.js'; +import { MCP } from '../../common/modelContextProtocol.js'; + +/** + * Implementation of IMcpMessageTransport for testing purposes. + * Allows tests to easily send/receive messages and control the connection state. + */ +export class TestMcpMessageTransport extends Disposable implements IMcpMessageTransport { + private readonly _onDidLog = this._register(new Emitter()); + public readonly onDidLog = this._onDidLog.event; + + private readonly _onDidReceiveMessage = this._register(new Emitter()); + public readonly onDidReceiveMessage = this._onDidReceiveMessage.event; + + private readonly _stateValue = observableValue('testTransportState', { state: McpConnectionState.Kind.Starting }); + public readonly state = this._stateValue; + + private readonly _sentMessages: MCP.JSONRPCMessage[] = []; + + constructor() { + super(); + } + + /** + * Send a message through the transport. + */ + public send(message: MCP.JSONRPCMessage): void { + this._sentMessages.push(message); + } + + /** + * Stop the transport. + */ + public stop(): void { + this._stateValue.set({ state: McpConnectionState.Kind.Stopped }, undefined); + } + + // Test Helper Methods + + /** + * Simulate receiving a message from the server. + */ + public simulateReceiveMessage(message: MCP.JSONRPCMessage): void { + this._onDidReceiveMessage.fire(message); + } + + /** + * Simulates a reply to an 'initialized' request. + */ + public simulateInitialized() { + if (!this._sentMessages.length) { + throw new Error('initialize was not called yet'); + } + + this.simulateReceiveMessage({ + jsonrpc: MCP.JSONRPC_VERSION, + id: (this.getSentMessages()[0] as MCP.JSONRPCRequest).id, + result: { + protocolVersion: MCP.LATEST_PROTOCOL_VERSION, + capabilities: { + tools: {}, + }, + serverInfo: { + name: 'Test Server', + version: '1.0.0' + }, + } satisfies MCP.InitializeResult + }); + } + + /** + * Simulate a log event. + */ + public simulateLog(message: string): void { + this._onDidLog.fire(message); + } + + /** + * Set the connection state. + */ + public setConnectionState(state: McpConnectionState): void { + this._stateValue.set(state, undefined); + } + + /** + * Get all messages that have been sent. + */ + public getSentMessages(): readonly MCP.JSONRPCMessage[] { + return [...this._sentMessages]; + } + + /** + * Clear the sent messages history. + */ + public clearSentMessages(): void { + this._sentMessages.length = 0; + } +} diff --git a/src/vs/workbench/contrib/mcp/test/common/mcpServerConnection.test.ts b/src/vs/workbench/contrib/mcp/test/common/mcpServerConnection.test.ts new file mode 100644 index 000000000000..83b9933c3f25 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/test/common/mcpServerConnection.test.ts @@ -0,0 +1,429 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { timeout } from '../../../../../base/common/async.js'; +import { Disposable } from '../../../../../base/common/lifecycle.js'; +import { autorun, observableValue } from '../../../../../base/common/observable.js'; +import { upcast } from '../../../../../base/common/types.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js'; +import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; +import { ILogger, ILoggerService } from '../../../../../platform/log/common/log.js'; +import { IProductService } from '../../../../../platform/product/common/productService.js'; +import { IStorageService, StorageScope } from '../../../../../platform/storage/common/storage.js'; +import { IOutputService } from '../../../../services/output/common/output.js'; +import { TestLoggerService, TestProductService, TestStorageService } from '../../../../test/common/workbenchTestServices.js'; +import { IMcpHostDelegate, IMcpMessageTransport } from '../../common/mcpRegistryTypes.js'; +import { McpServerConnection } from '../../common/mcpServerConnection.js'; +import { McpCollectionDefinition, McpConnectionState, McpServerDefinition, McpServerTransportType } from '../../common/mcpTypes.js'; +import { TestMcpMessageTransport } from './mcpRegistryTypes.js'; + +class TestMcpHostDelegate extends Disposable implements IMcpHostDelegate { + private readonly _transport: TestMcpMessageTransport; + private _canStartValue = true; + + constructor() { + super(); + this._transport = this._register(new TestMcpMessageTransport()); + } + + canStart(): boolean { + return this._canStartValue; + } + + start(): IMcpMessageTransport { + if (!this._canStartValue) { + throw new Error('Cannot start server'); + } + return this._transport; + } + + getTransport(): TestMcpMessageTransport { + return this._transport; + } + + setCanStart(value: boolean): void { + this._canStartValue = value; + } + + waitForInitialProviderPromises(): Promise { + return Promise.resolve(); + } +} + +suite('Workbench - MCP - ServerConnection', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + + let instantiationService: TestInstantiationService; + let delegate: TestMcpHostDelegate; + let transport: TestMcpMessageTransport; + let collection: McpCollectionDefinition; + let serverDefinition: McpServerDefinition; + + setup(() => { + delegate = store.add(new TestMcpHostDelegate()); + transport = delegate.getTransport(); + + // Setup test services + const services = new ServiceCollection( + [ILoggerService, store.add(new TestLoggerService())], + [IOutputService, upcast({ showChannel: () => { } })], + [IStorageService, store.add(new TestStorageService())], + [IProductService, TestProductService], + ); + + instantiationService = store.add(new TestInstantiationService(services)); + + // Create test collection + collection = { + id: 'test-collection', + label: 'Test Collection', + remoteAuthority: null, + serverDefinitions: observableValue('serverDefs', []), + isTrustedByDefault: true, + scope: StorageScope.APPLICATION + }; + + // Create server definition + serverDefinition = { + id: 'test-server', + label: 'Test Server', + launch: { + type: McpServerTransportType.Stdio, + command: 'test-command', + args: [], + env: {}, + cwd: URI.parse('file:///test') + } + }; + }); + + function waitForHandler(cnx: McpServerConnection) { + const handler = cnx.handler.get(); + if (handler) { + return Promise.resolve(handler); + } + + return new Promise(resolve => { + const disposable = autorun(reader => { + const handler = cnx.handler.read(reader); + if (handler) { + disposable.dispose(); + resolve(handler); + } + }); + }); + } + + test('should start and set state to Running when transport succeeds', async () => { + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // Start the connection + const startPromise = connection.start(); + + // Simulate successful connection + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + + const state = await startPromise; + assert.strictEqual(state.state, McpConnectionState.Kind.Running); + + transport.simulateInitialized(); + assert.ok(await waitForHandler(connection)); + }); + + test('should handle errors during start', async () => { + // Setup delegate to fail on start + delegate.setCanStart(false); + + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // Start the connection + const state = await connection.start(); + + assert.strictEqual(state.state, McpConnectionState.Kind.Error); + assert.ok(state.message); + }); + + test('should handle transport errors', async () => { + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // Start the connection + const startPromise = connection.start(); + + // Simulate error in transport + transport.setConnectionState({ + state: McpConnectionState.Kind.Error, + message: 'Test error message' + }); + + const state = await startPromise; + assert.strictEqual(state.state, McpConnectionState.Kind.Error); + assert.strictEqual(state.message, 'Test error message'); + }); + + test('should stop and set state to Stopped', async () => { + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // Start the connection + const startPromise = connection.start(); + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + await startPromise; + + // Stop the connection + const stopPromise = connection.stop(); + await stopPromise; + + assert.strictEqual(connection.state.get().state, McpConnectionState.Kind.Stopped); + }); + + test('should not restart if already starting', async () => { + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // Start the connection + const startPromise1 = connection.start(); + + // Try to start again while starting + const startPromise2 = connection.start(); + + // Simulate successful connection + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + + const state1 = await startPromise1; + const state2 = await startPromise2; + + // Both promises should resolve to the same state + assert.strictEqual(state1.state, McpConnectionState.Kind.Running); + assert.strictEqual(state2.state, McpConnectionState.Kind.Running); + + transport.simulateInitialized(); + assert.ok(await waitForHandler(connection)); + }); + + test('should clean up when disposed', async () => { + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + + // Start the connection + const startPromise = connection.start(); + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + await startPromise; + + // Dispose the connection + connection.dispose(); + + assert.strictEqual(connection.state.get().state, McpConnectionState.Kind.Stopped); + }); + + test('showOutput should call logger and output services', () => { + let channelShown = false; + const outputService = { + showChannel: (id: string) => { + assert.strictEqual(id, `mcpServer/${serverDefinition.id}`); + channelShown = true; + } + }; + + let loggerVisible = false; + const loggerService = new class extends TestLoggerService { + override setVisibility(id: string, visible: boolean): void { + assert.strictEqual(id, `mcpServer/${serverDefinition.id}`); + assert.strictEqual(visible, true); + loggerVisible = true; + } + }; + + // Override services + const services = new ServiceCollection( + [ILoggerService, store.add(loggerService)], + [IOutputService, upcast(outputService)], + [IStorageService, store.add(new TestStorageService())] + ); + + const localInstantiationService = store.add(new TestInstantiationService(services)); + + // Create server connection + const connection = localInstantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // Show output + connection.showOutput(); + + assert.strictEqual(channelShown, true); + assert.strictEqual(loggerVisible, true); + }); + + test('should log transport messages', async () => { + // Track logged messages + const loggedMessages: string[] = []; + const loggerService = new class extends TestLoggerService { + override createLogger(id: string) { + return { + info: (message: string) => { + loggedMessages.push(message); + }, + error: () => { }, + dispose: () => { } + } as Partial as ILogger; + } + }; + + // Override services + const services = new ServiceCollection( + [ILoggerService, store.add(loggerService)], + [IOutputService, upcast({ showChannel: () => { } })], + [IStorageService, store.add(new TestStorageService())] + ); + + const localInstantiationService = store.add(new TestInstantiationService(services)); + + // Create server connection + const connection = localInstantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // Start the connection + const startPromise = connection.start(); + + // Simulate log message from transport + transport.simulateLog('Test log message'); + + // Set connection to running + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + await startPromise; + + // Check that the message was logged + assert.ok(loggedMessages.some(msg => msg === 'Test log message')); + }); + + test('should correctly handle transitions to and from error state', async () => { + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // Start the connection + const startPromise = connection.start(); + + // Transition to error state + const errorState: McpConnectionState = { + state: McpConnectionState.Kind.Error, + message: 'Temporary error' + }; + transport.setConnectionState(errorState); + + let state = await startPromise; + assert.equal(state, errorState); + + + transport.setConnectionState({ state: McpConnectionState.Kind.Stopped }); + + // Transition back to running state + const startPromise2 = connection.start(); + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + state = await startPromise2; + assert.deepStrictEqual(state, { state: McpConnectionState.Kind.Running }); + + connection.dispose(); + await timeout(10); + }); + + test('should handle multiple start/stop cycles', async () => { + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // First cycle + let startPromise = connection.start(); + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + await startPromise; + + await connection.stop(); + assert.deepStrictEqual(connection.state.get(), { state: McpConnectionState.Kind.Stopped }); + + // Second cycle + startPromise = connection.start(); + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + await startPromise; + + assert.deepStrictEqual(connection.state.get(), { state: McpConnectionState.Kind.Running }); + + await connection.stop(); + + assert.deepStrictEqual(connection.state.get(), { state: McpConnectionState.Kind.Stopped }); + + connection.dispose(); + await timeout(10); + }); +}); diff --git a/src/vs/workbench/contrib/mcp/test/common/mcpServerRequestHandler.test.ts b/src/vs/workbench/contrib/mcp/test/common/mcpServerRequestHandler.test.ts new file mode 100644 index 000000000000..ebbfeb757981 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/test/common/mcpServerRequestHandler.test.ts @@ -0,0 +1,396 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { upcast } from '../../../../../base/common/types.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js'; +import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; +import { ILoggerService } from '../../../../../platform/log/common/log.js'; +import { IProductService } from '../../../../../platform/product/common/productService.js'; +import { IStorageService } from '../../../../../platform/storage/common/storage.js'; +import { TestLoggerService, TestProductService, TestStorageService } from '../../../../test/common/workbenchTestServices.js'; +import { IMcpHostDelegate } from '../../common/mcpRegistryTypes.js'; +import { McpServerRequestHandler } from '../../common/mcpServerRequestHandler.js'; +import { McpConnectionState } from '../../common/mcpTypes.js'; +import { MCP } from '../../common/modelContextProtocol.js'; +import { TestMcpMessageTransport } from './mcpRegistryTypes.js'; +import { IOutputService } from '../../../../services/output/common/output.js'; +import { Disposable } from '../../../../../base/common/lifecycle.js'; +import { CancellationTokenSource } from '../../../../../base/common/cancellation.js'; + +class TestMcpHostDelegate extends Disposable implements IMcpHostDelegate { + private readonly _transport: TestMcpMessageTransport; + + constructor() { + super(); + this._transport = this._register(new TestMcpMessageTransport()); + } + + canStart(): boolean { + return true; + } + + start(): TestMcpMessageTransport { + return this._transport; + } + + getTransport(): TestMcpMessageTransport { + return this._transport; + } + + waitForInitialProviderPromises(): Promise { + return Promise.resolve(); + } +} + +suite('Workbench - MCP - ServerRequestHandler', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + + let instantiationService: TestInstantiationService; + let delegate: TestMcpHostDelegate; + let transport: TestMcpMessageTransport; + let handler: McpServerRequestHandler; + let cts: CancellationTokenSource; + + setup(async () => { + delegate = store.add(new TestMcpHostDelegate()); + transport = delegate.getTransport(); + cts = store.add(new CancellationTokenSource()); + + // Setup test services + const services = new ServiceCollection( + [ILoggerService, store.add(new TestLoggerService())], + [IOutputService, upcast({ showChannel: () => { } })], + [IStorageService, store.add(new TestStorageService())], + [IProductService, TestProductService], + ); + + instantiationService = store.add(new TestInstantiationService(services)); + + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + + // Manually create the handler since we need the transport already set up + const logger = store.add((instantiationService.get(ILoggerService) as TestLoggerService) + .createLogger('mcpServerTest', { hidden: true, name: 'MCP Test' })); + + // Start the handler creation + const handlerPromise = McpServerRequestHandler.create(instantiationService, transport, logger, cts.token); + + // Simulate successful initialization + // We need to respond to the initialize request that the handler will make + transport.simulateReceiveMessage({ + jsonrpc: MCP.JSONRPC_VERSION, + id: 1, // The handler uses 1 for the first request + result: { + protocolVersion: MCP.LATEST_PROTOCOL_VERSION, + serverInfo: { + name: 'Test MCP Server', + version: '1.0.0', + }, + capabilities: { + resources: { + supportedTypes: ['text/plain'], + }, + tools: { + supportsCancellation: true, + } + } + } + }); + + handler = await handlerPromise; + store.add(handler); + }); + + test('should send and receive JSON-RPC requests', async () => { + // Setup request + const requestPromise = handler.listResources(); + + // Get the sent message and verify it + const sentMessages = transport.getSentMessages(); + assert.strictEqual(sentMessages.length, 3); // initialize + listResources + + // Verify listResources request format + const listResourcesRequest = sentMessages[2] as MCP.JSONRPCRequest; + assert.strictEqual(listResourcesRequest.method, 'resources/list'); + assert.strictEqual(listResourcesRequest.jsonrpc, MCP.JSONRPC_VERSION); + assert.ok(typeof listResourcesRequest.id === 'number'); + + // Simulate server response with mock resources that match the expected Resource interface + transport.simulateReceiveMessage({ + jsonrpc: MCP.JSONRPC_VERSION, + id: listResourcesRequest.id, + result: { + resources: [ + { uri: 'resource1', type: 'text/plain', name: 'Test Resource 1' }, + { uri: 'resource2', type: 'text/plain', name: 'Test Resource 2' } + ] + } + }); + + // Verify the result + const resources = await requestPromise; + assert.strictEqual(resources.length, 2); + assert.strictEqual(resources[0].uri, 'resource1'); + assert.strictEqual(resources[1].name, 'Test Resource 2'); + }); + + test('should handle paginated requests', async () => { + // Setup request + const requestPromise = handler.listResources(); + + // Get the first request and respond with pagination + const sentMessages = transport.getSentMessages(); + const listResourcesRequest = sentMessages[2] as MCP.JSONRPCRequest; + + // Send first page with nextCursor + transport.simulateReceiveMessage({ + jsonrpc: MCP.JSONRPC_VERSION, + id: listResourcesRequest.id, + result: { + resources: [ + { uri: 'resource1', type: 'text/plain', name: 'Test Resource 1' } + ], + nextCursor: 'page2' + } + }); + + // Clear the sent messages to only capture the next page request + transport.clearSentMessages(); + + // Wait a bit to allow the handler to process and send the next request + await new Promise(resolve => setTimeout(resolve, 0)); + + // Get the second request and verify cursor is included + const sentMessages2 = transport.getSentMessages(); + assert.strictEqual(sentMessages2.length, 1); + + const listResourcesRequest2 = sentMessages2[0] as MCP.JSONRPCRequest; + assert.strictEqual(listResourcesRequest2.method, 'resources/list'); + assert.deepStrictEqual(listResourcesRequest2.params, { cursor: 'page2' }); + + // Send final page with no nextCursor + transport.simulateReceiveMessage({ + jsonrpc: MCP.JSONRPC_VERSION, + id: listResourcesRequest2.id, + result: { + resources: [ + { uri: 'resource2', type: 'text/plain', name: 'Test Resource 2' } + ] + } + }); + + // Verify the combined result + const resources = await requestPromise; + assert.strictEqual(resources.length, 2); + assert.strictEqual(resources[0].uri, 'resource1'); + assert.strictEqual(resources[1].uri, 'resource2'); + }); + + test('should handle error responses', async () => { + // Setup request + const requestPromise = handler.readResource({ uri: 'non-existent' }); + + // Get the sent message + const sentMessages = transport.getSentMessages(); + const readResourceRequest = sentMessages[2] as MCP.JSONRPCRequest; // [0] is initialize + + // Simulate error response + transport.simulateReceiveMessage({ + jsonrpc: MCP.JSONRPC_VERSION, + id: readResourceRequest.id, + error: { + code: MCP.METHOD_NOT_FOUND, + message: 'Resource not found' + } + }); + + // Verify the error is thrown correctly + try { + await requestPromise; + assert.fail('Expected error was not thrown'); + } catch (e: any) { + assert.strictEqual(e.message, 'MPC -32601: Resource not found'); + assert.strictEqual(e.code, MCP.METHOD_NOT_FOUND); + } + }); + + test('should handle server requests', async () => { + // Simulate ping request from server + const pingRequest: MCP.JSONRPCRequest & MCP.PingRequest = { + jsonrpc: MCP.JSONRPC_VERSION, + id: 100, + method: 'ping' + }; + + transport.simulateReceiveMessage(pingRequest); + + // The handler should have sent a response + const sentMessages = transport.getSentMessages(); + const pingResponse = sentMessages.find(m => + 'id' in m && m.id === pingRequest.id && 'result' in m + ) as MCP.JSONRPCResponse; + + assert.ok(pingResponse, 'No ping response was sent'); + assert.deepStrictEqual(pingResponse.result, {}); + }); + + test('should handle roots list requests', async () => { + // Set roots + handler.roots = [ + { uri: 'file:///test/root1', name: 'Root 1' }, + { uri: 'file:///test/root2', name: 'Root 2' } + ]; + + // Simulate roots/list request from server + const rootsRequest: MCP.JSONRPCRequest & MCP.ListRootsRequest = { + jsonrpc: MCP.JSONRPC_VERSION, + id: 101, + method: 'roots/list' + }; + + transport.simulateReceiveMessage(rootsRequest); + + // The handler should have sent a response + const sentMessages = transport.getSentMessages(); + const rootsResponse = sentMessages.find(m => + 'id' in m && m.id === rootsRequest.id && 'result' in m + ) as MCP.JSONRPCResponse; + + assert.ok(rootsResponse, 'No roots/list response was sent'); + assert.strictEqual((rootsResponse.result as MCP.ListRootsResult).roots.length, 2); + assert.strictEqual((rootsResponse.result as MCP.ListRootsResult).roots[0].uri, 'file:///test/root1'); + }); + + test('should handle server notifications', async () => { + let progressNotificationReceived = false; + store.add(handler.onDidReceiveProgressNotification(notification => { + progressNotificationReceived = true; + assert.strictEqual(notification.method, 'notifications/progress'); + assert.strictEqual(notification.params.progressToken, 'token1'); + assert.strictEqual(notification.params.progress, 50); + })); + + // Simulate progress notification with correct format + const progressNotification: MCP.JSONRPCNotification & MCP.ProgressNotification = { + jsonrpc: MCP.JSONRPC_VERSION, + method: 'notifications/progress', + params: { + progressToken: 'token1', + progress: 50, + total: 100 + } + }; + + transport.simulateReceiveMessage(progressNotification); + assert.strictEqual(progressNotificationReceived, true); + }); + + test('should handle cancellation', async () => { + // Setup a new cancellation token source for this specific test + const testCts = store.add(new CancellationTokenSource()); + const requestPromise = handler.listResources(undefined, testCts.token); + + // Get the request ID + const sentMessages = transport.getSentMessages(); + const listResourcesRequest = sentMessages[2] as MCP.JSONRPCRequest; + const requestId = listResourcesRequest.id; + + // Cancel the request + testCts.cancel(); + + // Check that a cancellation notification was sent + const cancelNotification = transport.getSentMessages().find(m => + !('id' in m) && + 'method' in m && + m.method === 'notifications/cancelled' && + 'params' in m && + m.params && m.params.requestId === requestId + ); + + assert.ok(cancelNotification, 'No cancellation notification was sent'); + + // Verify the promise was cancelled + try { + await requestPromise; + assert.fail('Promise should have been cancelled'); + } catch (e) { + assert.strictEqual(e.name, 'Canceled'); + } + }); + + test('should handle cancelled notification from server', async () => { + // Setup request + const requestPromise = handler.listResources(); + + // Get the request ID + const sentMessages = transport.getSentMessages(); + const listResourcesRequest = sentMessages[2] as MCP.JSONRPCRequest; + const requestId = listResourcesRequest.id; + + // Simulate cancelled notification from server + const cancelledNotification: MCP.JSONRPCNotification & MCP.CancelledNotification = { + jsonrpc: MCP.JSONRPC_VERSION, + method: 'notifications/cancelled', + params: { + requestId + } + }; + + transport.simulateReceiveMessage(cancelledNotification); + + // Verify the promise was cancelled + try { + await requestPromise; + assert.fail('Promise should have been cancelled'); + } catch (e) { + assert.strictEqual(e.name, 'Canceled'); + } + }); + + test('should dispose properly and cancel pending requests', async () => { + // Setup multiple requests + const request1 = handler.listResources(); + const request2 = handler.listTools(); + + // Dispose the handler + handler.dispose(); + + // Verify all promises were cancelled + try { + await request1; + assert.fail('Promise 1 should have been cancelled'); + } catch (e) { + assert.strictEqual(e.name, 'Canceled'); + } + + try { + await request2; + assert.fail('Promise 2 should have been cancelled'); + } catch (e) { + assert.strictEqual(e.name, 'Canceled'); + } + }); + + test('should handle connection error by cancelling requests', async () => { + // Setup request + const requestPromise = handler.listResources(); + + // Simulate connection error + transport.setConnectionState({ + state: McpConnectionState.Kind.Error, + message: 'Connection lost' + }); + + // Verify the promise was cancelled + try { + await requestPromise; + assert.fail('Promise should have been cancelled'); + } catch (e) { + assert.strictEqual(e.name, 'Canceled'); + } + }); +}); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts index c0c4acc617b7..a110faf6a993 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { assertFn } from '../../../../base/common/assert.js'; -import { BugIndicatingError } from '../../../../base/common/errors.js'; +import { BugIndicatingError, onUnexpectedError } from '../../../../base/common/errors.js'; import { Event } from '../../../../base/common/event.js'; -import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; +import { DisposableStore, IDisposable, IReference } from '../../../../base/common/lifecycle.js'; import { derived, IObservable, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; import { basename, isEqual } from '../../../../base/common/resources.js'; import Severity from '../../../../base/common/severity.js'; @@ -27,6 +27,8 @@ import { MergeEditorTelemetry } from './telemetry.js'; import { StorageCloseWithConflicts } from '../common/mergeEditor.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { ITextFileEditorModel, ITextFileSaveOptions, ITextFileService } from '../../../services/textfile/common/textfiles.js'; +import { ITextModel } from '../../../../editor/common/model.js'; +import { ILanguageService } from '../../../../editor/common/languages/language.js'; export interface MergeEditorArgs { base: URI; @@ -273,6 +275,8 @@ export class WorkspaceMergeEditorModeFactory implements IMergeEditorInputModelFa @IInstantiationService private readonly _instantiationService: IInstantiationService, @ITextModelService private readonly _textModelService: ITextModelService, @ITextFileService private readonly textFileService: ITextFileService, + @IModelService private readonly _modelService: IModelService, + @ILanguageService private readonly _languageService: ILanguageService, ) { } @@ -292,18 +296,33 @@ export class WorkspaceMergeEditorModeFactory implements IMergeEditorInputModelFa modelListener.add(this.textFileService.files.onDidCreate(handleDidCreate)); this.textFileService.files.models.forEach(handleDidCreate); - const [ + let [ base, result, input1Data, input2Data, ] = await Promise.all([ - this._textModelService.createModelReference(args.base), + this._textModelService.createModelReference(args.base).then>(v => ({ + object: v.object.textEditorModel, + dispose: () => v.dispose(), + })).catch(e => { + onUnexpectedError(e); + console.error(e); // Only file not found error should be handled ideally + return undefined; + }), this._textModelService.createModelReference(args.result), toInputData(args.input1, this._textModelService, store), toInputData(args.input2, this._textModelService, store), ]); + if (base === undefined) { + const tm = this._modelService.createModel('', this._languageService.createById(result.object.getLanguageId())); + base = { + dispose: () => { tm.dispose(); }, + object: tm + }; + } + store.add(base); store.add(result); @@ -321,7 +340,7 @@ export class WorkspaceMergeEditorModeFactory implements IMergeEditorInputModelFa const model = this._instantiationService.createInstance( MergeEditorModel, - base.object.textEditorModel, + base.object, input1Data, input2Data, result.object.textEditorModel, diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts index 63512ae0e383..2741b6681d49 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts @@ -12,7 +12,7 @@ import { LineRangeEdit } from './editing.js'; import { LineRange } from './lineRange.js'; import { ReentrancyBarrier } from '../../../../../base/common/controlFlow.js'; import { IMergeDiffComputer } from './diffComputer.js'; -import { autorun, IObservable, IReader, ITransaction, observableSignal, observableValue, transaction } from '../../../../../base/common/observable.js'; +import { autorun, IObservableWithChange, IReader, ITransaction, observableSignal, observableValue, transaction } from '../../../../../base/common/observable.js'; import { UndoRedoGroup } from '../../../../../platform/undoRedo/common/undoRedo.js'; export class TextModelDiffs extends Disposable { @@ -61,14 +61,14 @@ export class TextModelDiffs extends Disposable { })); } - public get state(): IObservable { + public get state(): IObservableWithChange { return this._state; } /** * Diffs from base to input. */ - public get diffs(): IObservable { + public get diffs(): IObservableWithChange { return this._diffs; } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/utils.ts b/src/vs/workbench/contrib/mergeEditor/browser/utils.ts index d60d64bcb8d0..308db886d5b4 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/utils.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/utils.ts @@ -5,7 +5,7 @@ import { ArrayQueue, CompareResult } from '../../../../base/common/arrays.js'; import { onUnexpectedError } from '../../../../base/common/errors.js'; -import { DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; import { IObservable, autorunOpts } from '../../../../base/common/observable.js'; import { CodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; import { IModelDeltaDecoration } from '../../../../editor/common/model.js'; @@ -85,19 +85,6 @@ export function elementAtOrUndefined(arr: T[], index: number): T | undefined return arr[index]; } -export function thenIfNotDisposed(promise: Promise, then: () => void): IDisposable { - let disposed = false; - promise.then(() => { - if (disposed) { - return; - } - then(); - }); - return toDisposable(() => { - disposed = true; - }); -} - export function setFields(obj: T, fields: Partial): T { return Object.assign(obj, fields); } @@ -154,4 +141,3 @@ export class PersistentStore { ); } } - diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/conflictActions.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/conflictActions.ts index 8a1cd72be349..0a0c485c598b 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/conflictActions.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/conflictActions.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { $, createStyleSheet, h, isInShadowDOM, reset } from '../../../../../base/browser/dom.js'; +import { $, h, isInShadowDOM, reset } from '../../../../../base/browser/dom.js'; +import { createStyleSheet } from '../../../../../base/browser/domStylesheets.js'; import { renderLabelWithIcons } from '../../../../../base/browser/ui/iconLabel/iconLabels.js'; import { hash } from '../../../../../base/common/hash.js'; import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts index 32b12772bef7..666ad3f373ee 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts @@ -10,7 +10,7 @@ import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { Color } from '../../../../../base/common/color.js'; import { BugIndicatingError, onUnexpectedError } from '../../../../../base/common/errors.js'; import { Emitter, Event } from '../../../../../base/common/event.js'; -import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore, IDisposable, MutableDisposable, thenIfNotDisposed, toDisposable } from '../../../../../base/common/lifecycle.js'; import { autorun, autorunWithStore, IObservable, IReader, observableValue, transaction } from '../../../../../base/common/observable.js'; import { basename, isEqual } from '../../../../../base/common/resources.js'; import { isDefined } from '../../../../../base/common/types.js'; @@ -23,7 +23,6 @@ import { ICodeEditorViewState, ScrollType } from '../../../../../editor/common/e import { ITextModel } from '../../../../../editor/common/model.js'; import { ITextResourceConfigurationService } from '../../../../../editor/common/services/textResourceConfiguration.js'; import { localize } from '../../../../../nls.js'; -import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IContextKey, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IEditorOptions, ITextEditorOptions, ITextResourceEditorInput } from '../../../../../platform/editor/common/editor.js'; import { IFileService } from '../../../../../platform/files/common/files.js'; @@ -39,8 +38,7 @@ import { readTransientState, writeTransientState } from '../../../codeEditor/bro import { MergeEditorInput } from '../mergeEditorInput.js'; import { IMergeEditorInputModel } from '../mergeEditorInputModel.js'; import { MergeEditorModel } from '../model/mergeEditorModel.js'; -import { deepMerge, PersistentStore, thenIfNotDisposed } from '../utils.js'; -import { observableConfigValue } from '../../../../../platform/observable/common/platformObservableUtils.js'; +import { deepMerge, PersistentStore } from '../utils.js'; import { BaseCodeEditorView } from './editors/baseCodeEditorView.js'; import { ScrollSynchronizer } from './scrollSynchronizer.js'; import { MergeEditorViewModel } from './viewModel.js'; @@ -90,22 +88,12 @@ export class MergeEditor extends AbstractTextEditor { return this.inputModel.get()?.model; } - private get inputsWritable(): boolean { - return !!this._configurationService.getValue('mergeEditor.writableInputs'); - } - private readonly viewZoneComputer = new ViewZoneComputer( this.input1View.editor, this.input2View.editor, this.inputResultView.editor, ); - protected readonly codeLensesVisible = observableConfigValue( - 'mergeEditor.showCodeLenses', - true, - this.configurationService, - ); - private readonly scrollSynchronizer = this._register(new ScrollSynchronizer(this._viewModel, this.input1View, this.input2View, this.baseView, this.inputResultView, this._layoutModeObs)); constructor( @@ -116,12 +104,10 @@ export class MergeEditor extends AbstractTextEditor { @IStorageService storageService: IStorageService, @IThemeService themeService: IThemeService, @ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService, - @IConfigurationService private readonly _configurationService: IConfigurationService, @IEditorService editorService: IEditorService, @IEditorGroupsService editorGroupService: IEditorGroupsService, @IFileService fileService: IFileService, - @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, - @IConfigurationService private readonly configurationService: IConfigurationService + @ICodeEditorService private readonly _codeEditorService: ICodeEditorService ) { super(MergeEditor.ID, group, telemetryService, instantiation, storageService, textResourceConfigurationService, themeService, editorService, editorGroupService, fileService); } @@ -170,14 +156,18 @@ export class MergeEditor extends AbstractTextEditor { const inputOptions: ICodeEditorOptions = deepMerge(options, { minimap: { enabled: false }, glyphMargin: false, - lineNumbersMinChars: 2, - readOnly: !this.inputsWritable + lineNumbersMinChars: 2 }); - this.input1View.updateOptions(inputOptions); - this.input2View.updateOptions(inputOptions); + const readOnlyInputOptions: ICodeEditorOptions = deepMerge(inputOptions, { + readOnly: true, + readOnlyMessage: undefined + }); + + this.input1View.updateOptions(readOnlyInputOptions); + this.input2View.updateOptions(readOnlyInputOptions); this.baseViewOptions.set({ ...this.input2View.editor.getRawOptions() }, undefined); - this.inputResultView.updateOptions(options); + this.inputResultView.updateOptions(inputOptions); } protected getMainControl(): ICodeEditor | undefined { @@ -213,7 +203,6 @@ export class MergeEditor extends AbstractTextEditor { this.showNonConflictingChanges, ); - model.telemetry.reportMergeEditorOpened({ combinableConflictCount: model.combinableConflictCount, conflictCount: model.conflictCount, @@ -382,7 +371,7 @@ export class MergeEditor extends AbstractTextEditor { const resultViewZoneIds: string[] = []; const viewZones = this.viewZoneComputer.computeViewZones(reader, viewModel, { - codeLensesVisible: this.codeLensesVisible.read(reader), + codeLensesVisible: true, showNonConflictingChanges: this.showNonConflictingChanges.read(reader), shouldAlignBase, shouldAlignResult, diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts index 0ab8f25019bf..aa09311d4018 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts @@ -97,11 +97,10 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor /** @description Updates name */ const resources = this.resources.read(reader); const label = this.label ?? localize('name', "Multi Diff Editor"); - if (resources) { - this._name = label + localize({ - key: 'files', - comment: ['the number of files being shown'] - }, " ({0} files)", resources.length); + if (resources && resources.length === 1) { + this._name = localize({ key: 'nameWithOneFile', comment: ['{0} is the name of the editor'] }, "{0} (1 file)", label); + } else if (resources) { + this._name = localize({ key: 'nameWithFiles', comment: ['{0} is the name of the editor', '{1} is the number of files being shown'] }, "{0} ({1} files)", label, resources.length); } else { this._name = label; } @@ -280,9 +279,9 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor override readonly closeHandler: IEditorCloseHandler = { - // TODO@bpasero TODO@hediet this is a workaround for - // not having a better way to figure out if the - // editors this input wraps around are opened or not + // This is a workaround for not having a better way + // to figure out if the editors this input wraps + // around are opened or not async confirm() { return ConfirmResult.DONT_SAVE; diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts index cdd3aac6b293..ec198f8ad634 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts @@ -139,7 +139,7 @@ export class OpenScmGroupAction extends Action2 { constructor() { super({ id: '_workbench.openScmMultiDiffEditor', - title: localize2('viewChanges', 'View Changes'), + title: localize2('openChanges', 'Open Changes'), f1: false }); } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands.ts index 261c80215623..2abc28493e91 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellCommands/cellCommands.ts @@ -205,6 +205,8 @@ registerAction2(class extends NotebookCellAction { ], { quotableLabel: 'Split Notebook Cell' } ); + + context.notebookEditor.cellAt(index + 1)?.updateEditState(cell.getEditState(), 'splitCell'); } } } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticEditorContrib.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticEditorContrib.ts index d5af8a454949..584fc890a929 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticEditorContrib.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticEditorContrib.ts @@ -6,22 +6,15 @@ import { Disposable, IDisposable, toDisposable } from '../../../../../../base/common/lifecycle.js'; import { IMarkerData, IMarkerService } from '../../../../../../platform/markers/common/markers.js'; import { IRange } from '../../../../../../editor/common/core/range.js'; -import { ICellExecutionError, ICellExecutionStateChangedEvent, IExecutionStateChangedEvent, INotebookExecutionStateService, NotebookExecutionType } from '../../../common/notebookExecutionStateService.js'; +import { ICellExecutionStateChangedEvent, IExecutionStateChangedEvent, INotebookExecutionStateService, NotebookExecutionType } from '../../../common/notebookExecutionStateService.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; import { CellKind, NotebookSetting } from '../../../common/notebookCommon.js'; import { INotebookEditor, INotebookEditorContribution } from '../../notebookBrowser.js'; import { registerNotebookContribution } from '../../notebookEditorExtensions.js'; -import { Iterable } from '../../../../../../base/common/iterator.js'; import { CodeCellViewModel } from '../../viewModel/codeCellViewModel.js'; -import { URI } from '../../../../../../base/common/uri.js'; import { Event } from '../../../../../../base/common/event.js'; import { IChatAgentService } from '../../../../chat/common/chatAgents.js'; - -type CellDiagnostic = { - cellUri: URI; - error: ICellExecutionError; - disposables: IDisposable[]; -}; +import { ChatAgentLocation } from '../../../../chat/common/constants.js'; export class CellDiagnostics extends Disposable implements INotebookEditorContribution { @@ -29,7 +22,7 @@ export class CellDiagnostics extends Disposable implements INotebookEditorContri private enabled = false; private listening = false; - private diagnosticsByHandle: Map = new Map(); + private diagnosticsByHandle: Map = new Map(); constructor( private readonly notebookEditor: INotebookEditor, @@ -50,12 +43,17 @@ export class CellDiagnostics extends Disposable implements INotebookEditorContri })); } + private hasNotebookAgent(): boolean { + const agents = this.chatAgentService.getAgents(); + return !!agents.find(agent => agent.locations.includes(ChatAgentLocation.Notebook)); + } + private updateEnabled() { const settingEnabled = this.configurationService.getValue(NotebookSetting.cellFailureDiagnostics); - if (this.enabled && (!settingEnabled || Iterable.isEmpty(this.chatAgentService.getAgents()))) { + if (this.enabled && (!settingEnabled || !this.hasNotebookAgent())) { this.enabled = false; this.clearAll(); - } else if (!this.enabled && settingEnabled && !Iterable.isEmpty(this.chatAgentService.getAgents())) { + } else if (!this.enabled && settingEnabled && this.hasNotebookAgent()) { this.enabled = true; if (!this.listening) { this.listening = true; @@ -66,8 +64,6 @@ export class CellDiagnostics extends Disposable implements INotebookEditorContri } } - - private handleChangeExecutionState(changes: (ICellExecutionStateChangedEvent | IExecutionStateChangedEvent)[]) { if (!this.enabled) { return; @@ -96,9 +92,9 @@ export class CellDiagnostics extends Disposable implements INotebookEditorContri } public clear(cellHandle: number) { - const diagnostic = this.diagnosticsByHandle.get(cellHandle); - if (diagnostic) { - for (const disposable of diagnostic.disposables) { + const disposables = this.diagnosticsByHandle.get(cellHandle); + if (disposables) { + for (const disposable of disposables) { disposable.dispose(); } this.diagnosticsByHandle.delete(cellHandle); @@ -119,11 +115,12 @@ export class CellDiagnostics extends Disposable implements INotebookEditorContri const metadata = cell.model.internalMetadata; if (cell instanceof CodeCellViewModel && !metadata.lastRunSuccess && metadata?.error?.location) { const disposables: IDisposable[] = []; - const marker = this.createMarkerData(metadata.error.message, metadata.error.location); + const errorLabel = metadata.error.name ? `${metadata.error.name}: ${metadata.error.message}` : metadata.error.message; + const marker = this.createMarkerData(errorLabel, metadata.error.location); this.markerService.changeOne(CellDiagnostics.ID, cell.uri, [marker]); disposables.push(toDisposable(() => this.markerService.changeOne(CellDiagnostics.ID, cell.uri, []))); - cell.excecutionError.set(metadata.error, undefined); - disposables.push(toDisposable(() => cell.excecutionError.set(undefined, undefined))); + cell.executionErrorDiagnostic.set(metadata.error, undefined); + disposables.push(toDisposable(() => cell.executionErrorDiagnostic.set(undefined, undefined))); disposables.push(cell.model.onDidChangeOutputs(() => { if (cell.model.outputs.length === 0) { this.clear(cellHandle); @@ -132,7 +129,7 @@ export class CellDiagnostics extends Disposable implements INotebookEditorContri disposables.push(cell.model.onDidChangeContent(() => { this.clear(cellHandle); })); - this.diagnosticsByHandle.set(cellHandle, { cellUri: cell.uri, error: metadata.error, disposables }); + this.diagnosticsByHandle.set(cellHandle, disposables); } } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticsActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticsActions.ts index f619c126da2f..d41ae0fcfd41 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticsActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticsActions.ts @@ -15,8 +15,13 @@ import { KeybindingWeight } from '../../../../../../platform/keybinding/common/k import { INotebookCellActionContext, NotebookCellAction, findTargetCellEditor } from '../../controller/coreActions.js'; import { CodeCellViewModel } from '../../viewModel/codeCellViewModel.js'; import { NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_HAS_ERROR_DIAGNOSTICS } from '../../../common/notebookContextKeys.js'; +import { InlineChatController } from '../../../../inlineChat/browser/inlineChatController.js'; +import { showChatView } from '../../../../chat/browser/chat.js'; +import { IViewsService } from '../../../../../services/views/common/viewsService.js'; export const OPEN_CELL_FAILURE_ACTIONS_COMMAND_ID = 'notebook.cell.openFailureActions'; +export const FIX_CELL_ERROR_COMMAND_ID = 'notebook.cell.chat.fixError'; +export const EXPLAIN_CELL_ERROR_COMMAND_ID = 'notebook.cell.chat.explainError'; registerAction2(class extends NotebookCellAction { constructor() { @@ -35,7 +40,7 @@ registerAction2(class extends NotebookCellAction { async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { if (context.cell instanceof CodeCellViewModel) { - const error = context.cell.excecutionError.get(); + const error = context.cell.executionErrorDiagnostic.get(); if (error?.location) { const location = Range.lift({ startLineNumber: error.location.startLineNumber + 1, @@ -56,3 +61,61 @@ registerAction2(class extends NotebookCellAction { } } }); + +registerAction2(class extends NotebookCellAction { + constructor() { + super({ + id: FIX_CELL_ERROR_COMMAND_ID, + title: localize2('notebookActions.chatFixCellError', "Fix Cell Error"), + precondition: ContextKeyExpr.and(NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_HAS_ERROR_DIAGNOSTICS, NOTEBOOK_CELL_EDITOR_FOCUSED.toNegated()), + f1: true + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + if (context.cell instanceof CodeCellViewModel) { + const error = context.cell.executionErrorDiagnostic.get(); + if (error?.location) { + const location = Range.lift({ + startLineNumber: error.location.startLineNumber + 1, + startColumn: error.location.startColumn + 1, + endLineNumber: error.location.endLineNumber + 1, + endColumn: error.location.endColumn + 1 + }); + context.notebookEditor.setCellEditorSelection(context.cell, Range.lift(location)); + const editor = findTargetCellEditor(context, context.cell); + if (editor) { + const controller = InlineChatController.get(editor); + const message = error.name ? `${error.name}: ${error.message}` : error.message; + if (controller) { + await controller.run({ message: '/fix ' + message, initialRange: location, autoSend: true }); + } + } + } + } + } +}); + +registerAction2(class extends NotebookCellAction { + constructor() { + super({ + id: EXPLAIN_CELL_ERROR_COMMAND_ID, + title: localize2('notebookActions.chatExplainCellError', "Explain Cell Error"), + precondition: ContextKeyExpr.and(NOTEBOOK_CELL_FOCUSED, NOTEBOOK_CELL_HAS_ERROR_DIAGNOSTICS, NOTEBOOK_CELL_EDITOR_FOCUSED.toNegated()), + f1: true + }); + } + + async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { + if (context.cell instanceof CodeCellViewModel) { + const error = context.cell.executionErrorDiagnostic.get(); + if (error?.message) { + const viewsService = accessor.get(IViewsService); + const chatWidget = await showChatView(viewsService); + const message = error.name ? `${error.name}: ${error.message}` : error.message; + // TODO: can we add special prompt instructions? e.g. use "%pip install" + chatWidget?.acceptInput('@workspace /explain ' + message,); + } + } + } +}); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/diagnosticCellStatusBarContrib.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/diagnosticCellStatusBarContrib.ts index 561addf90df7..f10ea92275cc 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/diagnosticCellStatusBarContrib.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/diagnosticCellStatusBarContrib.ts @@ -15,6 +15,8 @@ import { registerNotebookContribution } from '../../notebookEditorExtensions.js' import { CodeCellViewModel } from '../../viewModel/codeCellViewModel.js'; import { INotebookCellStatusBarItem, CellStatusbarAlignment } from '../../../common/notebookCommon.js'; import { ICellExecutionError } from '../../../common/notebookExecutionStateService.js'; +import { IChatAgentService } from '../../../../chat/common/chatAgents.js'; +import { ChatAgentLocation } from '../../../../chat/common/constants.js'; export class DiagnosticCellStatusBarContrib extends Disposable implements INotebookEditorContribution { static id: string = 'workbench.notebook.statusBar.diagtnostic'; @@ -41,16 +43,21 @@ class DiagnosticCellStatusBarItem extends Disposable { private readonly _notebookViewModel: INotebookViewModel, private readonly cell: CodeCellViewModel, @IKeybindingService private readonly keybindingService: IKeybindingService, + @IChatAgentService private readonly chatAgentService: IChatAgentService, ) { super(); - this._register(autorun((reader) => this.updateSparkleItem(reader.readObservable(cell.excecutionError)))); + this._register(autorun((reader) => this.updateSparkleItem(reader.readObservable(cell.executionErrorDiagnostic)))); + } + private hasNotebookAgent(): boolean { + const agents = this.chatAgentService.getAgents(); + return !!agents.find(agent => agent.locations.includes(ChatAgentLocation.Notebook)); } private async updateSparkleItem(error: ICellExecutionError | undefined) { let item: INotebookCellStatusBarItem | undefined; - if (error?.location) { + if (error?.location && this.hasNotebookAgent()) { const keybinding = this.keybindingService.lookupKeybinding(OPEN_CELL_FAILURE_ACTIONS_COMMAND_ID)?.getLabel(); const tooltip = localize('notebook.cell.status.diagnostic', "Quick Actions {0}", `(${keybinding})`); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts index ece78349224b..184c94dbaac8 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts @@ -7,6 +7,7 @@ import { disposableTimeout, RunOnceScheduler } from '../../../../../../base/comm import { Disposable, dispose, IDisposable, MutableDisposable } from '../../../../../../base/common/lifecycle.js'; import { language } from '../../../../../../base/common/platform.js'; import { localize } from '../../../../../../nls.js'; +import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { themeColorFromId } from '../../../../../../platform/theme/common/themeService.js'; import { ThemeIcon } from '../../../../../../base/common/themables.js'; @@ -15,7 +16,7 @@ import { ICellViewModel, INotebookEditor, INotebookEditorContribution, INotebook import { registerNotebookContribution } from '../../notebookEditorExtensions.js'; import { cellStatusIconError, cellStatusIconSuccess } from '../../notebookEditorWidget.js'; import { errorStateIcon, executingStateIcon, pendingStateIcon, successStateIcon } from '../../notebookIcons.js'; -import { CellStatusbarAlignment, INotebookCellStatusBarItem, NotebookCellExecutionState, NotebookCellInternalMetadata } from '../../../common/notebookCommon.js'; +import { CellStatusbarAlignment, INotebookCellStatusBarItem, NotebookCellExecutionState, NotebookCellInternalMetadata, NotebookSetting } from '../../../common/notebookCommon.js'; import { INotebookCellExecution, INotebookExecutionStateService, NotebookExecutionType } from '../../../common/notebookExecutionStateService.js'; import { INotebookService } from '../../../common/notebookService.js'; import { IMarkdownString } from '../../../../../../base/common/htmlContent.js'; @@ -227,17 +228,28 @@ class TimerCellStatusBarItem extends Disposable { private _deferredUpdate: IDisposable | undefined; + private _isVerbose: boolean; + constructor( private readonly _notebookViewModel: INotebookViewModel, private readonly _cell: ICellViewModel, @INotebookExecutionStateService private readonly _executionStateService: INotebookExecutionStateService, @INotebookService private readonly _notebookService: INotebookService, + @IConfigurationService private readonly _configurationService: IConfigurationService, ) { super(); + this._isVerbose = this._configurationService.getValue(NotebookSetting.cellExecutionTimeVerbosity) === 'verbose'; this._scheduler = this._register(new RunOnceScheduler(() => this._update(), TimerCellStatusBarItem.UPDATE_INTERVAL)); this._update(); this._register(this._cell.model.onDidChangeInternalMetadata(() => this._update())); + + this._register(this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(NotebookSetting.cellExecutionTimeVerbosity)) { + this._isVerbose = this._configurationService.getValue(NotebookSetting.cellExecutionTimeVerbosity) === 'verbose'; + this._update(); + } + })); } private async _update() { @@ -276,7 +288,7 @@ class TimerCellStatusBarItem extends Disposable { this._deferredUpdate = disposableTimeout(() => { this._deferredUpdate = undefined; this._currentItemIds = this._notebookViewModel.deltaCellStatusBarItems(this._currentItemIds, [{ handle: this._cell.handle, items }]); - }, UPDATE_TIMER_GRACE_PERIOD); + }, UPDATE_TIMER_GRACE_PERIOD, this._store); } } else { this._deferredUpdate?.dispose(); @@ -290,8 +302,9 @@ class TimerCellStatusBarItem extends Disposable { let tooltip: IMarkdownString | undefined; + const lastExecution = new Date(endTime).toLocaleTimeString(language); + if (runtimeInformation) { - const lastExecution = new Date(endTime).toLocaleTimeString(language); const { renderDuration, executionDuration, timerDuration } = runtimeInformation; let renderTimes = ''; @@ -318,8 +331,12 @@ class TimerCellStatusBarItem extends Disposable { } + const executionText = this._isVerbose ? + localize('notebook.cell.statusBar.timerVerbose', "Last Execution: {0}, Duration: {1}", lastExecution, formatCellDuration(duration, false)) : + formatCellDuration(duration, false); + return { - text: formatCellDuration(duration, false), + text: executionText, alignment: CellStatusbarAlignment.Left, priority: Number.MAX_SAFE_INTEGER - 5, tooltip diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard.ts b/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard.ts deleted file mode 100644 index 9b877b758bf2..000000000000 --- a/src/vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard.ts +++ /dev/null @@ -1,67 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IClipboardService } from '../../../../../../platform/clipboard/common/clipboardService.js'; -import { ILogService } from '../../../../../../platform/log/common/log.js'; -import { ICellOutputViewModel, ICellViewModel } from '../../notebookBrowser.js'; -import { isTextStreamMime } from '../../../common/notebookCommon.js'; - -export async function copyCellOutput(mimeType: string | undefined, outputViewModel: ICellOutputViewModel, clipboardService: IClipboardService, logService: ILogService) { - const cellOutput = outputViewModel.model; - const output = mimeType && TEXT_BASED_MIMETYPES.includes(mimeType) ? - cellOutput.outputs.find(output => output.mime === mimeType) : - cellOutput.outputs.find(output => TEXT_BASED_MIMETYPES.includes(output.mime)); - - mimeType = output?.mime; - - if (!mimeType || !output) { - return; - } - - const decoder = new TextDecoder(); - let text = decoder.decode(output.data.buffer); - - // append adjacent text streams since they are concatenated in the renderer - if (isTextStreamMime(mimeType)) { - const cellViewModel = outputViewModel.cellViewModel as ICellViewModel; - let index = cellViewModel.outputsViewModels.indexOf(outputViewModel) + 1; - while (index < cellViewModel.model.outputs.length) { - const nextCellOutput = cellViewModel.model.outputs[index]; - const nextOutput = nextCellOutput.outputs.find(output => isTextStreamMime(output.mime)); - if (!nextOutput) { - break; - } - - text = text + decoder.decode(nextOutput.data.buffer); - index = index + 1; - } - } - - if (mimeType.endsWith('error')) { - text = text.replace(/\\u001b\[[0-9;]*m/gi, '').replaceAll('\\n', '\n'); - } - - - try { - await clipboardService.writeText(text); - - } catch (e) { - logService.error(`Failed to copy content: ${e}`); - } -} - -export const TEXT_BASED_MIMETYPES = [ - 'text/latex', - 'text/html', - 'application/vnd.code.notebook.error', - 'application/vnd.code.notebook.stdout', - 'application/x.notebook.stdout', - 'application/x.notebook.stream', - 'application/vnd.code.notebook.stderr', - 'application/x.notebook.stderr', - 'text/plain', - 'text/markdown', - 'application/json' -]; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorHint/emptyCellEditorHint.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorHint/emptyCellEditorHint.ts index 0beb76ab69db..7df058a69016 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorHint/emptyCellEditorHint.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorHint/emptyCellEditorHint.ts @@ -64,11 +64,6 @@ export class EmptyCellEditorHintContribution extends EmptyTextEditorHintContribu } protected override _shouldRenderHint(): boolean { - const shouldRenderHint = super._shouldRenderHint(); - if (!shouldRenderHint) { - return false; - } - const model = this.editor.getModel(); if (!model) { return false; @@ -80,7 +75,12 @@ export class EmptyCellEditorHintContribution extends EmptyTextEditorHintContribu } const activeEditor = getNotebookEditorFromEditorPane(this._editorService.activeEditorPane); - if (!activeEditor) { + if (!activeEditor || !activeEditor.isDisposed) { + return false; + } + + const shouldRenderHint = super._shouldRenderHint(); + if (!shouldRenderHint) { return false; } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index fd1ed5694fe6..db21d25ea27f 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -72,7 +72,7 @@ class ImplictKernelSelector implements IDisposable { } } -export class KernelStatus extends Disposable implements IWorkbenchContribution { +class KernelStatus extends Disposable implements IWorkbenchContribution { private readonly _editorDisposables = this._register(new DisposableStore()); private readonly _kernelInfoElement = this._register(new DisposableStore()); @@ -85,6 +85,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { ) { super(); this._register(this._editorService.onDidActiveEditorChange(() => this._updateStatusbar())); + this._updateStatusbar(); } private _updateStatusbar() { @@ -179,7 +180,7 @@ export class KernelStatus extends Disposable implements IWorkbenchContribution { } } -export class ActiveCellStatus extends Disposable implements IWorkbenchContribution { +class ActiveCellStatus extends Disposable implements IWorkbenchContribution { private readonly _itemDisposables = this._register(new DisposableStore()); private readonly _accessor = this._register(new MutableDisposable()); @@ -190,6 +191,7 @@ export class ActiveCellStatus extends Disposable implements IWorkbenchContributi ) { super(); this._register(this._editorService.onDidActiveEditorChange(() => this._update())); + this._update(); } private _update() { @@ -253,7 +255,7 @@ export class ActiveCellStatus extends Disposable implements IWorkbenchContributi } } -export class NotebookIndentationStatus extends Disposable { +class NotebookIndentationStatus extends Disposable { private readonly _itemDisposables = this._register(new DisposableStore()); private readonly _accessor = this._register(new MutableDisposable()); @@ -272,6 +274,7 @@ export class NotebookIndentationStatus extends Disposable { this._update(); } })); + this._update(); } private _update() { @@ -335,7 +338,7 @@ export class NotebookIndentationStatus extends Disposable { } } -export class NotebookEditorStatusContribution extends Disposable implements IWorkbenchContribution { +class NotebookEditorStatusContribution extends Disposable implements IWorkbenchContribution { static readonly ID = 'notebook.contrib.editorStatus'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts index ccf772fb9202..f2de65d5bdc1 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts @@ -20,6 +20,7 @@ import { Widget } from '../../../../../../base/browser/ui/widget.js'; import { Action, ActionRunner, IAction, IActionRunner, Separator } from '../../../../../../base/common/actions.js'; import { Delayer } from '../../../../../../base/common/async.js'; import { Codicon } from '../../../../../../base/common/codicons.js'; +import { Event } from '../../../../../../base/common/event.js'; import { KeyCode } from '../../../../../../base/common/keyCodes.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; import { isSafari } from '../../../../../../base/common/platform.js'; @@ -28,7 +29,7 @@ import { Range } from '../../../../../../editor/common/core/range.js'; import { FindReplaceState, FindReplaceStateChangedEvent } from '../../../../../../editor/contrib/find/browser/findState.js'; import { findNextMatchIcon, findPreviousMatchIcon, findReplaceAllIcon, findReplaceIcon, findSelectionIcon, SimpleButton } from '../../../../../../editor/contrib/find/browser/findWidget.js'; import { parseReplaceString, ReplacePattern } from '../../../../../../editor/contrib/find/browser/replacePattern.js'; -import { createAndFillInActionBarActions } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; +import { getActionBarActions } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IMenu } from '../../../../../../platform/actions/common/actions.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; import { IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; @@ -222,7 +223,7 @@ export class NotebookFindInputFilterButton extends Disposable { this._actionbar = this._register(new ActionBar(container, { actionViewItemProvider: (action, options) => { if (action.id === this._filtersAction.id) { - return this.instantiationService.createInstance(NotebookFindFilterActionViewItem, this.filters, action, options, new ActionRunner()); + return this.instantiationService.createInstance(NotebookFindFilterActionViewItem, this.filters, action, options, this._register(new ActionRunner())); } return undefined; } @@ -279,13 +280,7 @@ export class NotebookFindInput extends FindInput { } getCellToolbarActions(menu: IMenu): { primary: IAction[]; secondary: IAction[] } { - const primary: IAction[] = []; - const secondary: IAction[] = []; - const result = { primary, secondary }; - - createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); - - return result; + return getActionBarActions(menu.getActions({ shouldForwardArgs: true }), g => /^inline/.test(g)); } } @@ -335,6 +330,8 @@ export abstract class SimpleFindReplaceWidget extends Widget { ) { super(); + this._register(this._state); + const findFilters = this._configurationService.getValue<{ markupSource: boolean; markupPreview: boolean; @@ -351,6 +348,17 @@ export abstract class SimpleFindReplaceWidget extends Widget { this._domNode = document.createElement('div'); this._domNode.classList.add('simple-fr-find-part-wrapper'); + + this._register(Event.runAndSubscribe(this._configurationService.onDidChangeConfiguration, e => { + if (!e || e.affectsConfiguration(NotebookSetting.globalToolbar)) { + if (this._notebookEditor.notebookOptions.getLayoutConfiguration().globalToolbar) { + this._domNode.style.top = '26px'; + } else { + this._domNode.style.top = '0px'; + } + } + })); + this._register(this._state.onFindReplaceStateChange((e) => this._onStateChanged(e))); this._scopedContextKeyService = contextKeyService.createScoped(this._domNode); @@ -560,7 +568,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { this._replaceInput = this._register(new ContextScopedReplaceInput(null, undefined, { label: NLS_REPLACE_INPUT_LABEL, placeholder: NLS_REPLACE_INPUT_PLACEHOLDER, - history: [], + history: new Set([]), inputBoxStyles: defaultInputBoxStyles, toggleStyles: defaultToggleStyles }, contextKeyService, false)); @@ -651,13 +659,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { } getCellToolbarActions(menu: IMenu): { primary: IAction[]; secondary: IAction[] } { - const primary: IAction[] = []; - const secondary: IAction[] = []; - const result = { primary, secondary }; - - createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); - - return result; + return getActionBarActions(menu.getActions({ shouldForwardArgs: true }), g => /^inline/.test(g)); } protected abstract onInputChanged(): boolean; @@ -690,6 +692,10 @@ export abstract class SimpleFindReplaceWidget extends Widget { return this._focusTracker; } + public get isVisible(): boolean { + return this._isVisible; + } + private _onStateChanged(e: FindReplaceStateChangedEvent): void { this._updateButtons(); this._updateMatchesCount(); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts index 0a8a46342e71..6c57739806ff 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts @@ -47,7 +47,7 @@ export class NotebookFindContrib extends Disposable implements INotebookEditorCo static readonly id: string = 'workbench.notebook.find'; - private readonly widget: Lazy; + private readonly _widget: Lazy; constructor( private readonly notebookEditor: INotebookEditor, @@ -55,24 +55,29 @@ export class NotebookFindContrib extends Disposable implements INotebookEditorCo ) { super(); - this.widget = new Lazy(() => this._register(this.instantiationService.createInstance(NotebookFindWidget, this.notebookEditor))); + this._widget = new Lazy(() => this._register(this.instantiationService.createInstance(NotebookFindWidget, this.notebookEditor))); + } + + get widget(): NotebookFindWidget { + return this._widget.value; } show(initialInput?: string, options?: IShowNotebookFindWidgetOptions): Promise { - return this.widget.value.show(initialInput, options); + return this._widget.value.show(initialInput, options); } hide() { - this.widget.rawValue?.hide(); + this._widget.rawValue?.hide(); } replace(searchString: string | undefined) { - return this.widget.value.replace(searchString); + return this._widget.value.replace(searchString); } } class NotebookFindWidget extends SimpleFindReplaceWidget implements INotebookEditorContribution { protected _findWidgetFocused: IContextKey; + private _isFocused: boolean = false; private _showTimeout: number | null = null; private _hideTimeout: number | null = null; private _previousFocusElement?: HTMLElement; @@ -124,6 +129,13 @@ class NotebookFindWidget extends SimpleFindReplaceWidget implements INotebookEdi }, true)); } + get findModel(): FindModel { + return this._findModel; + } + + get isFocused(): boolean { + return this._isFocused; + } private _onFindInputKeyDown(e: IKeyboardEvent): void { if (e.equals(KeyCode.Enter)) { @@ -227,11 +239,13 @@ class NotebookFindWidget extends SimpleFindReplaceWidget implements INotebookEdi protected onFocusTrackerFocus() { this._findWidgetFocused.set(true); + this._isFocused = true; } protected onFocusTrackerBlur() { this._previousFocusElement = undefined; this._findWidgetFocused.reset(); + this._isFocused = false; } protected onReplaceInputFocusTrackerFocus(): void { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/format/formatting.ts b/src/vs/workbench/contrib/notebook/browser/contrib/format/formatting.ts index aeedbb5dbaa3..4b201578667e 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/format/formatting.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/format/formatting.ts @@ -118,8 +118,7 @@ registerEditorAction(class FormatCellAction extends EditorAction { constructor() { super({ id: 'notebook.formatCell', - label: localize('formatCell.label', "Format Cell"), - alias: 'Format Cell', + label: localize2('formatCell.label', "Format Cell"), precondition: ContextKeyExpr.and(NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_EDITOR_EDITABLE, EditorContextKeys.inCompositeEditor, EditorContextKeys.writable, EditorContextKeys.hasDocumentFormattingProvider), kbOpts: { kbExpr: ContextKeyExpr.and(EditorContextKeys.editorTextFocus), diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts b/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts index 9309d784fd73..116d5d3fab9d 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts @@ -31,7 +31,6 @@ import { indentOfLine } from '../../../../../../editor/common/model/textModel.js import { ITextModelService } from '../../../../../../editor/common/services/resolverService.js'; import { ICoordinatesConverter } from '../../../../../../editor/common/viewModel.js'; import { ViewModelEventsCollector } from '../../../../../../editor/common/viewModelEventDispatcher.js'; -import { WordHighlighterContribution } from '../../../../../../editor/contrib/wordHighlighter/browser/wordHighlighter.js'; import { IAccessibilityService } from '../../../../../../platform/accessibility/common/accessibility.js'; import { MenuId, registerAction2 } from '../../../../../../platform/actions/common/actions.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; @@ -41,13 +40,17 @@ import { KeybindingWeight } from '../../../../../../platform/keybinding/common/k import { IPastFutureElements, IUndoRedoElement, IUndoRedoService, UndoRedoElementType } from '../../../../../../platform/undoRedo/common/undoRedo.js'; import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../../../common/contributions.js'; import { IEditorService } from '../../../../../services/editor/common/editorService.js'; -import { NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR } from '../../../common/notebookContextKeys.js'; +import { KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR } from '../../../common/notebookContextKeys.js'; import { INotebookActionContext, NotebookAction } from '../../controller/coreActions.js'; -import { getNotebookEditorFromEditorPane, ICellViewModel, INotebookEditor, INotebookEditorContribution } from '../../notebookBrowser.js'; +import { CellFindMatchWithIndex, getNotebookEditorFromEditorPane, ICellViewModel, INotebookEditor, INotebookEditorContribution } from '../../notebookBrowser.js'; import { registerNotebookContribution } from '../../notebookEditorExtensions.js'; import { CellEditorOptions } from '../../view/cellParts/cellEditorOptions.js'; +import { NotebookFindContrib } from '../find/notebookFindWidget.js'; +import { NotebookTextModel } from '../../../common/model/notebookTextModel.js'; +import { NotebookCellTextModel } from '../../../common/model/notebookCellTextModel.js'; const NOTEBOOK_ADD_FIND_MATCH_TO_SELECTION_ID = 'notebook.addFindMatchToSelection'; +const NOTEBOOK_SELECT_ALL_FIND_MATCHES_ID = 'notebook.selectAllFindMatches'; export enum NotebookMultiCursorState { Idle, @@ -68,10 +71,10 @@ interface SelectionTranslation { deltaEndLine: number; } -interface TrackedMatch { +interface TrackedCell { cellViewModel: ICellViewModel; initialSelection: Selection; - wordSelections: Selection[]; + matchSelections: Selection[]; editorConfig: IEditorConfiguration; cursorConfig: NotebookCursorConfig; decorationIds: string[]; @@ -88,7 +91,11 @@ export class NotebookMultiCursorController extends Disposable implements INotebo static readonly id: string = 'notebook.multiCursorController'; private word: string = ''; - private trackedMatches: TrackedMatch[] = []; + private startPosition: { + cellIndex: number; + position: Position; + } | undefined; + private trackedCells: TrackedCell[] = []; private readonly _onDidChangeAnchorCell = this._register(new Emitter()); readonly onDidChangeAnchorCell: Event = this._onDidChangeAnchorCell.event; @@ -122,99 +129,13 @@ export class NotebookMultiCursorController extends Disposable implements INotebo // anchor cell will catch and relay all type, cut, paste events to the cursors controllers // need to create new controllers when the anchor cell changes, then update their listeners // ** cursor controllers need to happen first, because anchor listeners relay to them - this._register(this.onDidChangeAnchorCell(() => { - this.updateCursorsControllers(); - this.updateAnchorListeners(); + this._register(this.onDidChangeAnchorCell(async () => { + await this.syncCursorsControllers(); + this.syncAnchorListeners(); })); } - private updateCursorsControllers() { - this.cursorsDisposables.clear(); // TODO: dial this back for perf and just update the relevant controllers - this.trackedMatches.forEach(async match => { - const textModelRef = await this.textModelService.createModelReference(match.cellViewModel.uri); - const textModel = textModelRef.object.textEditorModel; - if (!textModel) { - return; - } - - const cursorSimpleModel = this.constructCursorSimpleModel(match.cellViewModel); - const converter = this.constructCoordinatesConverter(); - const editorConfig = match.editorConfig; - - const controller = this.cursorsDisposables.add(new CursorsController( - textModel, - cursorSimpleModel, - converter, - new CursorConfiguration(textModel.getLanguageId(), textModel.getOptions(), editorConfig, this.languageConfigurationService) - )); - - controller.setSelections(new ViewModelEventsCollector(), undefined, match.wordSelections, CursorChangeReason.Explicit); - this.cursorsControllers.set(match.cellViewModel.uri, controller); - }); - } - - private constructCoordinatesConverter(): ICoordinatesConverter { - return { - convertViewPositionToModelPosition(viewPosition: Position): Position { - return viewPosition; - }, - convertViewRangeToModelRange(viewRange: Range): Range { - return viewRange; - }, - validateViewPosition(viewPosition: Position, expectedModelPosition: Position): Position { - return viewPosition; - }, - validateViewRange(viewRange: Range, expectedModelRange: Range): Range { - return viewRange; - }, - convertModelPositionToViewPosition(modelPosition: Position, affinity?: PositionAffinity, allowZeroLineNumber?: boolean, belowHiddenRanges?: boolean): Position { - return modelPosition; - }, - convertModelRangeToViewRange(modelRange: Range, affinity?: PositionAffinity): Range { - return modelRange; - }, - modelPositionIsVisible(modelPosition: Position): boolean { - return true; - }, - getModelLineViewLineCount(modelLineNumber: number): number { - return 1; - }, - getViewLineNumberOfModelPosition(modelLineNumber: number, modelColumn: number): number { - return modelLineNumber; - } - }; - } - - private constructCursorSimpleModel(cell: ICellViewModel): ICursorSimpleModel { - return { - getLineCount(): number { - return cell.textBuffer.getLineCount(); - }, - getLineContent(lineNumber: number): string { - return cell.textBuffer.getLineContent(lineNumber); - }, - getLineMinColumn(lineNumber: number): number { - return cell.textBuffer.getLineMinColumn(lineNumber); - }, - getLineMaxColumn(lineNumber: number): number { - return cell.textBuffer.getLineMaxColumn(lineNumber); - }, - getLineFirstNonWhitespaceColumn(lineNumber: number): number { - return cell.textBuffer.getLineFirstNonWhitespaceColumn(lineNumber); - }, - getLineLastNonWhitespaceColumn(lineNumber: number): number { - return cell.textBuffer.getLineLastNonWhitespaceColumn(lineNumber); - }, - normalizePosition(position: Position, affinity: PositionAffinity): Position { - return position; - }, - getLineIndentColumn(lineNumber: number): number { - return indentOfLine(cell.textBuffer.getLineContent(lineNumber)) + 1; - } - }; - } - - private updateAnchorListeners() { + private syncAnchorListeners() { this.anchorDisposables.clear(); if (!this.anchorCell) { @@ -224,13 +145,13 @@ export class NotebookMultiCursorController extends Disposable implements INotebo // typing this.anchorDisposables.add(this.anchorCell[1].onWillType((input) => { const collector = new ViewModelEventsCollector(); - this.trackedMatches.forEach(match => { - const controller = this.cursorsControllers.get(match.cellViewModel.uri); + this.trackedCells.forEach(cell => { + const controller = this.cursorsControllers.get(cell.cellViewModel.uri); if (!controller) { // should not happen return; } - if (match.cellViewModel.handle !== this.anchorCell?.[0].handle) { // don't relay to active cell, already has a controller for typing + if (cell.cellViewModel.handle !== this.anchorCell?.[0].handle) { // don't relay to active cell, already has a controller for typing controller.type(collector, input, 'keyboard'); } }); @@ -252,16 +173,16 @@ export class NotebookMultiCursorController extends Disposable implements INotebo // need to keep anchor cursor controller in sync manually (for delete usage), since we don't relay type event to it anchorController.setSelections(new ViewModelEventsCollector(), 'keyboard', activeSelections, CursorChangeReason.Explicit); - this.trackedMatches.forEach(match => { - const controller = this.cursorsControllers.get(match.cellViewModel.uri); + this.trackedCells.forEach(cell => { + const controller = this.cursorsControllers.get(cell.cellViewModel.uri); if (!controller) { return; } // this is used upon exiting the multicursor session to set the selections back to the correct cursor state - match.initialSelection = controller.getSelection(); + cell.initialSelection = controller.getSelection(); // clear tracked selection data as it is invalid once typing begins - match.wordSelections = []; + cell.matchSelections = []; }); this.updateLazyDecorations(); @@ -287,8 +208,8 @@ export class NotebookMultiCursorController extends Disposable implements INotebo }; const translationDir = e.selection.getDirection(); - this.trackedMatches.forEach(match => { - const controller = this.cursorsControllers.get(match.cellViewModel.uri); + this.trackedCells.forEach(cell => { + const controller = this.cursorsControllers.get(cell.cellViewModel.uri); if (!controller) { return; } @@ -309,54 +230,7 @@ export class NotebookMultiCursorController extends Disposable implements INotebo // core actions this.anchorDisposables.add(this.anchorCell[1].onWillTriggerEditorOperationEvent((e) => { - this.trackedMatches.forEach(match => { - if (match.cellViewModel.handle === this.anchorCell?.[0].handle) { - return; - } - - const eventsCollector = new ViewModelEventsCollector(); - const controller = this.cursorsControllers.get(match.cellViewModel.uri); - if (!controller) { - return; - } - switch (e.handlerId) { - case Handler.CompositionStart: - controller.startComposition(eventsCollector); - return; - case Handler.CompositionEnd: - controller.endComposition(eventsCollector, e.source); - return; - case Handler.ReplacePreviousChar: { - const args = >e.payload; - controller.compositionType(eventsCollector, args.text || '', args.replaceCharCnt || 0, 0, 0, e.source); - return; - } - case Handler.CompositionType: { - const args = >e.payload; - controller.compositionType(eventsCollector, args.text || '', args.replacePrevCharCnt || 0, args.replaceNextCharCnt || 0, args.positionDelta || 0, e.source); - return; - } - case Handler.Paste: { - const args = >e.payload; - controller.paste(eventsCollector, args.text || '', args.pasteOnNewLine || false, args.multicursorText || null, e.source); - return; - - // ! this code is for firing the paste event, not sure what that would enable, trace the event listener - // const startPos = XYZ - // const endPos = XYZ - // if (source === 'keyboard') { - // this._onDidPaste.fire({ - // clipboardEvent, - // range: new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column), - // languageId: mode - // }); - // } - } - case Handler.Cut: - controller.cut(eventsCollector, e.source); - return; - } - }); + this.handleEditorOperationEvent(e); })); // exit mode @@ -367,6 +241,161 @@ export class NotebookMultiCursorController extends Disposable implements INotebo })); } + private async syncCursorsControllers() { + this.cursorsDisposables.clear(); // TODO: dial this back for perf and just update the relevant controllers + await Promise.all(this.trackedCells.map(async cell => { + const controller = await this.createCursorController(cell); + if (!controller) { + return; + } + this.cursorsControllers.set(cell.cellViewModel.uri, controller); + + const selections = cell.matchSelections; + controller.setSelections(new ViewModelEventsCollector(), undefined, selections, CursorChangeReason.Explicit); + })); + + this.updateLazyDecorations(); + } + + private async createCursorController(cell: TrackedCell): Promise { + const textModelRef = await this.textModelService.createModelReference(cell.cellViewModel.uri); + const textModel = textModelRef.object.textEditorModel; + if (!textModel) { + return undefined; + } + + const cursorSimpleModel = this.constructCursorSimpleModel(cell.cellViewModel); + const converter = this.constructCoordinatesConverter(); + const editorConfig = cell.editorConfig; + + const controller = this.cursorsDisposables.add(new CursorsController( + textModel, + cursorSimpleModel, + converter, + new CursorConfiguration(textModel.getLanguageId(), textModel.getOptions(), editorConfig, this.languageConfigurationService) + )); + + controller.setSelections(new ViewModelEventsCollector(), undefined, cell.matchSelections, CursorChangeReason.Explicit); + return controller; + } + + private constructCoordinatesConverter(): ICoordinatesConverter { + return { + convertViewPositionToModelPosition(viewPosition: Position): Position { + return viewPosition; + }, + convertViewRangeToModelRange(viewRange: Range): Range { + return viewRange; + }, + validateViewPosition(viewPosition: Position, expectedModelPosition: Position): Position { + return viewPosition; + }, + validateViewRange(viewRange: Range, expectedModelRange: Range): Range { + return viewRange; + }, + convertModelPositionToViewPosition(modelPosition: Position, affinity?: PositionAffinity, allowZeroLineNumber?: boolean, belowHiddenRanges?: boolean): Position { + return modelPosition; + }, + convertModelRangeToViewRange(modelRange: Range, affinity?: PositionAffinity): Range { + return modelRange; + }, + modelPositionIsVisible(modelPosition: Position): boolean { + return true; + }, + getModelLineViewLineCount(modelLineNumber: number): number { + return 1; + }, + getViewLineNumberOfModelPosition(modelLineNumber: number, modelColumn: number): number { + return modelLineNumber; + } + }; + } + + private constructCursorSimpleModel(cell: ICellViewModel): ICursorSimpleModel { + return { + getLineCount(): number { + return cell.textBuffer.getLineCount(); + }, + getLineContent(lineNumber: number): string { + return cell.textBuffer.getLineContent(lineNumber); + }, + getLineMinColumn(lineNumber: number): number { + return cell.textBuffer.getLineMinColumn(lineNumber); + }, + getLineMaxColumn(lineNumber: number): number { + return cell.textBuffer.getLineMaxColumn(lineNumber); + }, + getLineFirstNonWhitespaceColumn(lineNumber: number): number { + return cell.textBuffer.getLineFirstNonWhitespaceColumn(lineNumber); + }, + getLineLastNonWhitespaceColumn(lineNumber: number): number { + return cell.textBuffer.getLineLastNonWhitespaceColumn(lineNumber); + }, + normalizePosition(position: Position, affinity: PositionAffinity): Position { + return position; + }, + getLineIndentColumn(lineNumber: number): number { + return indentOfLine(cell.textBuffer.getLineContent(lineNumber)) + 1; + } + }; + } + + private async handleEditorOperationEvent(e: any) { + this.trackedCells.forEach(cell => { + if (cell.cellViewModel.handle === this.anchorCell?.[0].handle) { + return; + } + + const eventsCollector = new ViewModelEventsCollector(); + const controller = this.cursorsControllers.get(cell.cellViewModel.uri); + if (!controller) { + return; + } + this.executeEditorOperation(controller, eventsCollector, e); + }); + } + + private executeEditorOperation(controller: CursorsController, eventsCollector: ViewModelEventsCollector, e: any) { + switch (e.handlerId) { + case Handler.CompositionStart: + controller.startComposition(eventsCollector); + break; + case Handler.CompositionEnd: + controller.endComposition(eventsCollector, e.source); + break; + case Handler.ReplacePreviousChar: { + const args = >e.payload; + controller.compositionType(eventsCollector, args.text || '', args.replaceCharCnt || 0, 0, 0, e.source); + break; + } + case Handler.CompositionType: { + const args = >e.payload; + controller.compositionType(eventsCollector, args.text || '', args.replacePrevCharCnt || 0, args.replaceNextCharCnt || 0, args.positionDelta || 0, e.source); + break; + } + case Handler.Paste: { + const args = >e.payload; + controller.paste(eventsCollector, args.text || '', args.pasteOnNewLine || false, args.multicursorText || null, e.source); + break; + } + case Handler.Cut: + controller.cut(eventsCollector, e.source); + break; + } + } + + private updateViewModelSelections() { + for (const cell of this.trackedCells) { + const controller = this.cursorsControllers.get(cell.cellViewModel.uri); + if (!controller) { + // should not happen + return; + } + + cell.cellViewModel.setSelections(controller.getSelections()); + } + } + private updateFinalUndoRedo() { const anchorCellModel = this.anchorCell?.[1].getModel(); if (!anchorCellModel) { @@ -377,7 +406,7 @@ export class NotebookMultiCursorController extends Disposable implements INotebo const newElementsMap: ResourceMap = new ResourceMap(); const resources: URI[] = []; - this.trackedMatches.forEach(trackedMatch => { + this.trackedCells.forEach(trackedMatch => { const undoRedoState = trackedMatch.undoRedoHistory; if (!undoRedoState) { return; @@ -429,170 +458,275 @@ export class NotebookMultiCursorController extends Disposable implements INotebo this._nbIsMultiSelectSession.set(false); this.updateFinalUndoRedo(); - this.trackedMatches.forEach(match => { - this.clearDecorations(match); - match.cellViewModel.setSelections([match.initialSelection]); // correct cursor placement upon exiting cmd-d session + this.trackedCells.forEach(cell => { + this.clearDecorations(cell); + cell.cellViewModel.setSelections([cell.initialSelection]); // correct cursor placement upon exiting cmd-d session }); this.anchorDisposables.clear(); this.anchorCell = undefined; this.cursorsDisposables.clear(); this.cursorsControllers.clear(); - this.trackedMatches = []; + this.trackedCells = []; + this.startPosition = undefined; + this.word = ''; } - public async findAndTrackNextSelection(cell: ICellViewModel): Promise { + public async findAndTrackNextSelection(focusedCell: ICellViewModel): Promise { if (this.state === NotebookMultiCursorState.Idle) { // move cursor to end of the symbol + track it, transition to selecting state - const textModel = cell.textModel; + const textModel = focusedCell.textModel; if (!textModel) { return; } - const inputSelection = cell.getSelections()[0]; + const inputSelection = focusedCell.getSelections()[0]; const word = this.getWord(inputSelection, textModel); if (!word) { return; } this.word = word.word; + const index = this.notebookEditor.getCellIndex(focusedCell); + if (index === undefined) { + return; + } + + this.startPosition = { + cellIndex: index, + position: new Position(inputSelection.startLineNumber, word.startColumn), + }; + const newSelection = new Selection( inputSelection.startLineNumber, word.startColumn, inputSelection.startLineNumber, word.endColumn ); - cell.setSelections([newSelection]); + focusedCell.setSelections([newSelection]); this.anchorCell = this.notebookEditor.activeCellAndCodeEditor; - if (!this.anchorCell || this.anchorCell[0].handle !== cell.handle) { + if (!this.anchorCell || this.anchorCell[0].handle !== focusedCell.handle) { throw new Error('Active cell is not the same as the cell passed as context'); } if (!(this.anchorCell[1] instanceof CodeEditorWidget)) { throw new Error('Active cell is not an instance of CodeEditorWidget'); } - textModel.pushStackElement(); - - this.trackedMatches = []; - const editorConfig = this.constructCellEditorOptions(this.anchorCell[0]); - const rawEditorOptions = editorConfig.getRawOptions(); - const cursorConfig: NotebookCursorConfig = { - cursorStyle: cursorStyleFromString(rawEditorOptions.cursorStyle!), - cursorBlinking: cursorBlinkingStyleFromString(rawEditorOptions.cursorBlinking!), - cursorSmoothCaretAnimation: rawEditorOptions.cursorSmoothCaretAnimation! - }; - - const newMatch: TrackedMatch = { - cellViewModel: cell, - initialSelection: inputSelection, - wordSelections: [newSelection], - editorConfig: editorConfig, // cache this in the match so we can create new cursors controllers with the correct language config - cursorConfig: cursorConfig, - decorationIds: [], - undoRedoHistory: this.undoRedoService.getElements(cell.uri) - }; - this.trackedMatches.push(newMatch); + await this.updateTrackedCell(focusedCell, [newSelection]); this._nbIsMultiSelectSession.set(true); this.state = NotebookMultiCursorState.Selecting; this._nbMultiSelectState.set(NotebookMultiCursorState.Selecting); + this._onDidChangeAnchorCell.fire(); } else if (this.state === NotebookMultiCursorState.Selecting) { // use the word we stored from idle state transition to find next match, track it const notebookTextModel = this.notebookEditor.textModel; if (!notebookTextModel) { - return; + return; // should not happen } - const index = this.notebookEditor.getCellIndex(cell); + const index = this.notebookEditor.getCellIndex(focusedCell); if (index === undefined) { - return; + return; // should not happen + } + + if (!this.startPosition) { + return; // should not happen } const findResult = notebookTextModel.findNextMatch( this.word, - { cellIndex: index, position: cell.getSelections()[cell.getSelections().length - 1].getEndPosition() }, + { cellIndex: index, position: focusedCell.getSelections()[focusedCell.getSelections().length - 1].getEndPosition() }, false, true, - USUAL_WORD_SEPARATORS //! might want to get these from the editor config + USUAL_WORD_SEPARATORS, + this.startPosition, ); if (!findResult) { - return; //todo: some sort of message to the user alerting them that there are no more matches? editor does not do this + return; } - const resultCellViewModel = this.notebookEditor.getCellByHandle(findResult.cell.handle); - if (!resultCellViewModel) { + const findResultCellViewModel = this.notebookEditor.getCellByHandle(findResult.cell.handle); + if (!findResultCellViewModel) { return; } - let newMatch: TrackedMatch; - if (findResult.cell.handle === cell.handle) { // match is in the same cell, find tracked entry, update and set selections in viewmodel and cursorController - newMatch = this.trackedMatches.find(match => match.cellViewModel.handle === findResult.cell.handle)!; - newMatch.wordSelections.push(Selection.fromRange(findResult.match.range, SelectionDirection.LTR)); - resultCellViewModel.setSelections(newMatch.wordSelections); + if (findResult.cell.handle === focusedCell.handle) { // match is in the same cell, find tracked entry, update and set selections in viewmodel and cursorController + const selections = [...focusedCell.getSelections(), Selection.fromRange(findResult.match.range, SelectionDirection.LTR)]; + const trackedCell = await this.updateTrackedCell(focusedCell, selections); + findResultCellViewModel.setSelections(trackedCell.matchSelections); - const controller = this.cursorsControllers.get(newMatch.cellViewModel.uri); - if (!controller) { - // should not happen - return; - } - controller.setSelections(new ViewModelEventsCollector(), undefined, newMatch.wordSelections, CursorChangeReason.Explicit); - } else if (findResult.cell.handle !== cell.handle) { // result is in a different cell, move focus there and apply selection, then update anchor - await this.notebookEditor.revealRangeInViewAsync(resultCellViewModel, findResult.match.range); - this.notebookEditor.focusNotebookCell(resultCellViewModel, 'editor'); + } else if (findResult.cell.handle !== focusedCell.handle) { // result is in a different cell, move focus there and apply selection, then update anchor + await this.notebookEditor.revealRangeInViewAsync(findResultCellViewModel, findResult.match.range); + await this.notebookEditor.focusNotebookCell(findResultCellViewModel, 'editor'); - const initialSelection = resultCellViewModel.getSelections()[0]; - const newSelection = Selection.fromRange(findResult.match.range, SelectionDirection.LTR); - resultCellViewModel.setSelections([newSelection]); - if (this.notebookEditor.activeCellAndCodeEditor?.[0].handle !== resultCellViewModel.handle) { - // should not happen - throw new Error('Focused cell does not match the find match data'); - } + const trackedCell = await this.updateTrackedCell(findResultCellViewModel, [Selection.fromRange(findResult.match.range, SelectionDirection.LTR)]); + findResultCellViewModel.setSelections(trackedCell.matchSelections); this.anchorCell = this.notebookEditor.activeCellAndCodeEditor; if (!this.anchorCell || !(this.anchorCell[1] instanceof CodeEditorWidget)) { throw new Error('Active cell is not an instance of CodeEditorWidget'); } - const textModel = await resultCellViewModel.resolveTextModel(); - textModel.pushStackElement(); - - const editorConfig = this.constructCellEditorOptions(this.anchorCell[0]); - const rawEditorOptions = editorConfig.getRawOptions(); - const cursorConfig: NotebookCursorConfig = { - cursorStyle: cursorStyleFromString(rawEditorOptions.cursorStyle!), - cursorBlinking: cursorBlinkingStyleFromString(rawEditorOptions.cursorBlinking!), - cursorSmoothCaretAnimation: rawEditorOptions.cursorSmoothCaretAnimation! - }; - - newMatch = { - cellViewModel: resultCellViewModel, - initialSelection: initialSelection, - wordSelections: [newSelection], - editorConfig: editorConfig, - cursorConfig: cursorConfig, - decorationIds: [], - undoRedoHistory: this.undoRedoService.getElements(resultCellViewModel.uri) - } satisfies TrackedMatch; - this.trackedMatches.push(newMatch); - this._onDidChangeAnchorCell.fire(); // we set the decorations manually for the cell we have just departed, since it blurs // we can find the match with the handle that the find and track request originated - this.initializeMultiSelectDecorations(this.trackedMatches.find(match => match.cellViewModel.handle === cell.handle)); - } else { - // should not happen + this.initializeMultiSelectDecorations(this.trackedCells.find(trackedCell => trackedCell.cellViewModel.handle === focusedCell.handle)); + } + } + } + + public async selectAllMatches(focusedCell: ICellViewModel, matches?: CellFindMatchWithIndex[]): Promise { + const notebookTextModel = this.notebookEditor.textModel; + if (!notebookTextModel) { + return; // should not happen + } + + if (matches) { + await this.handleFindWidgetSelectAllMatches(matches); + } else { + await this.handleCellEditorSelectAllMatches(notebookTextModel, focusedCell); + } + + await this.syncCursorsControllers(); + this.syncAnchorListeners(); + this.updateLazyDecorations(); + } + + private async handleFindWidgetSelectAllMatches(matches: CellFindMatchWithIndex[]) { + // TODO: support selecting state maybe. UX could get confusing since selecting state could be hit via ctrl+d which would have different filters (case sensetive + whole word) + if (this.state !== NotebookMultiCursorState.Idle) { + return; + } + + if (!matches.length) { + return; + } + + await this.notebookEditor.focusNotebookCell(matches[0].cell, 'editor'); + this.anchorCell = this.notebookEditor.activeCellAndCodeEditor; + + this.trackedCells = []; + for (const match of matches) { + this.updateTrackedCell(match.cell, match.contentMatches.map(match => Selection.fromRange(match.range, SelectionDirection.LTR))); + + if (this.anchorCell && match.cell.handle === this.anchorCell[0].handle) { + // only explicitly set the focused cell's selections, the rest are handled by cursor controllers + decorations + match.cell.setSelections(match.contentMatches.map(match => Selection.fromRange(match.range, SelectionDirection.LTR))); + } + } + + this._nbIsMultiSelectSession.set(true); + this.state = NotebookMultiCursorState.Selecting; + this._nbMultiSelectState.set(NotebookMultiCursorState.Selecting); + } + + private async handleCellEditorSelectAllMatches(notebookTextModel: NotebookTextModel, focusedCell: ICellViewModel) { + // can be triggered mid multiselect session, or from idle state + if (this.state === NotebookMultiCursorState.Idle) { + // get word from current selection + rest of notebook objects + const textModel = focusedCell.textModel; + if (!textModel) { + return; + } + const inputSelection = focusedCell.getSelections()[0]; + const word = this.getWord(inputSelection, textModel); + if (!word) { + return; + } + this.word = word.word; + const index = this.notebookEditor.getCellIndex(focusedCell); + if (index === undefined) { return; } + this.startPosition = { + cellIndex: index, + position: new Position(inputSelection.startLineNumber, word.startColumn), + }; + + this.anchorCell = this.notebookEditor.activeCellAndCodeEditor; + if (!this.anchorCell || this.anchorCell[0].handle !== focusedCell.handle) { + throw new Error('Active cell is not the same as the cell passed as context'); + } + if (!(this.anchorCell[1] instanceof CodeEditorWidget)) { + throw new Error('Active cell is not an instance of CodeEditorWidget'); + } + + // get all matches in the notebook + const findResults = notebookTextModel.findMatches(this.word, false, true, USUAL_WORD_SEPARATORS); + + // create the tracked matches for every result, needed for cursor controllers + this.trackedCells = []; + for (const res of findResults) { + await this.updateTrackedCell(res.cell, res.matches.map(match => Selection.fromRange(match.range, SelectionDirection.LTR))); + + if (res.cell.handle === focusedCell.handle) { + const cellViewModel = this.notebookEditor.getCellByHandle(res.cell.handle); + if (cellViewModel) { + cellViewModel.setSelections(res.matches.map(match => Selection.fromRange(match.range, SelectionDirection.LTR))); + } + } + } + + this._nbIsMultiSelectSession.set(true); + this.state = NotebookMultiCursorState.Selecting; + this._nbMultiSelectState.set(NotebookMultiCursorState.Selecting); + + } else if (this.state === NotebookMultiCursorState.Selecting) { + // we will already have a word + some number of tracked matches, need to update them with the rest given findAllMatches result + const findResults = notebookTextModel.findMatches(this.word, false, true, USUAL_WORD_SEPARATORS); + // update existing tracked matches with new selections and create new tracked matches for cells that aren't tracked yet + for (const res of findResults) { + await this.updateTrackedCell(res.cell, res.matches.map(match => Selection.fromRange(match.range, SelectionDirection.LTR))); + } + } + } + + private async updateTrackedCell(cell: ICellViewModel | NotebookCellTextModel, selections: Selection[]) { + const cellViewModel = cell instanceof NotebookCellTextModel ? this.notebookEditor.getCellByHandle(cell.handle) : cell; + if (!cellViewModel) { + throw new Error('Cell not found'); + } + + let trackedMatch = this.trackedCells.find(trackedCell => trackedCell.cellViewModel.handle === cellViewModel.handle); + + if (trackedMatch) { + this.clearDecorations(trackedMatch); // need this to avoid leaking decorations -- TODO: just optimize the lazy decorations fn + trackedMatch.matchSelections = selections; + } else { + const initialSelection = cellViewModel.getSelections()[0]; + const textModel = await cellViewModel.resolveTextModel(); + textModel.pushStackElement(); + + const editorConfig = this.constructCellEditorOptions(cellViewModel); + const rawEditorOptions = editorConfig.getRawOptions(); + const cursorConfig: NotebookCursorConfig = { + cursorStyle: cursorStyleFromString(rawEditorOptions.cursorStyle!), + cursorBlinking: cursorBlinkingStyleFromString(rawEditorOptions.cursorBlinking!), + cursorSmoothCaretAnimation: rawEditorOptions.cursorSmoothCaretAnimation! + }; + + trackedMatch = { + cellViewModel: cellViewModel, + initialSelection: initialSelection, + matchSelections: selections, + editorConfig: editorConfig, + cursorConfig: cursorConfig, + decorationIds: [], + undoRedoHistory: this.undoRedoService.getElements(cellViewModel.uri) + }; + this.trackedCells.push(trackedMatch); } + return trackedMatch; } public async deleteLeft(): Promise { - this.trackedMatches.forEach(match => { - const controller = this.cursorsControllers.get(match.cellViewModel.uri); + this.trackedCells.forEach(cell => { + const controller = this.cursorsControllers.get(cell.cellViewModel.uri); if (!controller) { // should not happen return; @@ -616,8 +750,8 @@ export class NotebookMultiCursorController extends Disposable implements INotebo } public async deleteRight(): Promise { - this.trackedMatches.forEach(match => { - const controller = this.cursorsControllers.get(match.cellViewModel.uri); + this.trackedCells.forEach(cell => { + const controller = this.cursorsControllers.get(cell.cellViewModel.uri); if (!controller) { // should not happen return; @@ -630,7 +764,7 @@ export class NotebookMultiCursorController extends Disposable implements INotebo controller.getSelections(), ); - if (match.cellViewModel.handle !== this.anchorCell?.[0].handle) { + if (cell.cellViewModel.handle !== this.anchorCell?.[0].handle) { const delSelections = CommandExecutor.executeCommands(controller.context.model, controller.getSelections(), commands); if (!delSelections) { return; @@ -638,7 +772,7 @@ export class NotebookMultiCursorController extends Disposable implements INotebo controller.setSelections(new ViewModelEventsCollector(), undefined, delSelections, CursorChangeReason.Explicit); } else { // get the selections from the viewmodel since we run the command manually (for cursor decoration reasons) - controller.setSelections(new ViewModelEventsCollector(), undefined, match.cellViewModel.getSelections(), CursorChangeReason.Explicit); + controller.setSelections(new ViewModelEventsCollector(), undefined, cell.cellViewModel.getSelections(), CursorChangeReason.Explicit); } }); @@ -647,86 +781,74 @@ export class NotebookMultiCursorController extends Disposable implements INotebo async undo() { const models: ITextModel[] = []; - for (const match of this.trackedMatches) { - const model = await match.cellViewModel.resolveTextModel(); + for (const cell of this.trackedCells) { + const model = await cell.cellViewModel.resolveTextModel(); if (model) { models.push(model); } - - const controller = this.cursorsControllers.get(match.cellViewModel.uri); - if (!controller) { - // should not happen - return; - } - controller.setSelections(new ViewModelEventsCollector(), undefined, match.cellViewModel.getSelections(), CursorChangeReason.Explicit); } await Promise.all(models.map(model => model.undo())); + this.updateViewModelSelections(); this.updateLazyDecorations(); } async redo() { const models: ITextModel[] = []; - for (const match of this.trackedMatches) { - const model = await match.cellViewModel.resolveTextModel(); + for (const cell of this.trackedCells) { + const model = await cell.cellViewModel.resolveTextModel(); if (model) { models.push(model); } - - const controller = this.cursorsControllers.get(match.cellViewModel.uri); - if (!controller) { - // should not happen - return; - } - controller.setSelections(new ViewModelEventsCollector(), undefined, match.cellViewModel.getSelections(), CursorChangeReason.Explicit); } await Promise.all(models.map(model => model.redo())); + this.updateViewModelSelections(); this.updateLazyDecorations(); } private constructCellEditorOptions(cell: ICellViewModel): EditorConfiguration { const cellEditorOptions = new CellEditorOptions(this.notebookEditor.getBaseCellEditorOptions(cell.language), this.notebookEditor.notebookOptions, this.configurationService); const options = cellEditorOptions.getUpdatedValue(cell.internalMetadata, cell.uri); + cellEditorOptions.dispose(); return new EditorConfiguration(false, MenuId.EditorContent, options, null, this.accessibilityService); } /** * Updates the multicursor selection decorations for a specific matched cell * - * @param match -- match object containing the viewmodel + selections + * @param cell -- match object containing the viewmodel + selections */ - private initializeMultiSelectDecorations(match: TrackedMatch | undefined, isCurrentWord?: boolean) { - if (!match) { + private initializeMultiSelectDecorations(cell: TrackedCell | undefined) { + if (!cell) { return; } const decorations: IModelDeltaDecoration[] = []; - match.wordSelections.forEach(selection => { + cell.matchSelections.forEach(selection => { // mock cursor at the end of the selection decorations.push({ range: Selection.fromPositions(selection.getEndPosition()), options: { description: '', - className: this.getClassName(match.cursorConfig, true), + className: this.getClassName(cell.cursorConfig, true), } }); }); - match.decorationIds = match.cellViewModel.deltaModelDecorations( - match.decorationIds, + cell.decorationIds = cell.cellViewModel.deltaModelDecorations( + cell.decorationIds, decorations ); } private updateLazyDecorations() { - // for every tracked match that is not in the visible range, dispose of their decorations and update them based off the cursorcontroller - this.trackedMatches.forEach(match => { - if (match.cellViewModel.handle === this.anchorCell?.[0].handle) { + this.trackedCells.forEach(cell => { + if (cell.cellViewModel.handle === this.anchorCell?.[0].handle) { return; } - const controller = this.cursorsControllers.get(match.cellViewModel.uri); + const controller = this.cursorsControllers.get(cell.cellViewModel.uri); if (!controller) { // should not happen return; @@ -743,7 +865,7 @@ export class NotebookMultiCursorController extends Disposable implements INotebo range: selection, options: { description: '', - className: this.getClassName(match.cursorConfig, false), + className: this.getClassName(cell.cursorConfig, false), } }); } @@ -754,36 +876,21 @@ export class NotebookMultiCursorController extends Disposable implements INotebo options: { description: '', zIndex: 10000, - className: this.getClassName(match.cursorConfig, true), + className: this.getClassName(cell.cursorConfig, true), } }); }); - match.decorationIds = match.cellViewModel.deltaModelDecorations( - match.decorationIds, + cell.decorationIds = cell.cellViewModel.deltaModelDecorations( + cell.decorationIds, newDecorations ); - - /** - * TODO: @Yoyokrazy debt - * goal: draw decorations for occurrence higlight on the cursor blink cycle - * - * Trigger WH with delay: x ms (x = cursor blink cycle) - * -> start = Date() - * -> WordHighlighter -> compute - * -> end = Date() - * -> delay = x - ((end - start) % x) - */ - const matchingEditor = this.notebookEditor.codeEditors.find(cellEditor => cellEditor[0] === match.cellViewModel); - if (matchingEditor) { - WordHighlighterContribution.get(matchingEditor[1])?.wordHighlighter?.trigger(); - } }); } - private clearDecorations(match: TrackedMatch) { - match.decorationIds = match.cellViewModel.deltaModelDecorations( - match.decorationIds, + private clearDecorations(cell: TrackedCell) { + cell.decorationIds = cell.cellViewModel.deltaModelDecorations( + cell.decorationIds, [] ); } @@ -865,19 +972,70 @@ export class NotebookMultiCursorController extends Disposable implements INotebo this.anchorDisposables.dispose(); this.cursorsDisposables.dispose(); - this.trackedMatches.forEach(match => { - this.clearDecorations(match); + this.trackedCells.forEach(cell => { + this.clearDecorations(cell); }); - this.trackedMatches = []; + this.trackedCells = []; } } +class NotebookSelectAllFindMatches extends NotebookAction { + constructor() { + super({ + id: NOTEBOOK_SELECT_ALL_FIND_MATCHES_ID, + title: localize('selectAllFindMatches', "Select All Occurrences of Find Match"), + precondition: ContextKeyExpr.and( + ContextKeyExpr.equals('config.notebook.multiCursor.enabled', true), + ), + keybinding: { + when: ContextKeyExpr.or( + ContextKeyExpr.and( + ContextKeyExpr.equals('config.notebook.multiCursor.enabled', true), + NOTEBOOK_IS_ACTIVE_EDITOR, + NOTEBOOK_CELL_EDITOR_FOCUSED, + ), + ContextKeyExpr.and( + ContextKeyExpr.equals('config.notebook.multiCursor.enabled', true), + KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED + ), + ), + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyL, + weight: KeybindingWeight.WorkbenchContrib + } + }); + } + + override async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise { + const editorService = accessor.get(IEditorService); + + const editor = getNotebookEditorFromEditorPane(editorService.activeEditorPane); + if (!editor) { + return; + } + + if (!context.cell) { + return; + } + + const cursorController = editor.getContribution(NotebookMultiCursorController.id); + const findController = editor.getContribution(NotebookFindContrib.id); + + if (findController.widget.isFocused) { + const findModel = findController.widget.findModel; + cursorController.selectAllMatches(context.cell, findModel.findMatches); + } else { + cursorController.selectAllMatches(context.cell); + } + + } +} + class NotebookAddMatchToMultiSelectionAction extends NotebookAction { constructor() { super({ id: NOTEBOOK_ADD_FIND_MATCH_TO_SELECTION_ID, - title: localize('addFindMatchToSelection', "Add Find Match to Selection"), + title: localize('addFindMatchToSelection', "Add Selection to Next Find Match"), precondition: ContextKeyExpr.and( ContextKeyExpr.equals('config.notebook.multiCursor.enabled', true), NOTEBOOK_IS_ACTIVE_EDITOR, @@ -1093,6 +1251,7 @@ class NotebookMultiCursorUndoRedoContribution extends Disposable { registerNotebookContribution(NotebookMultiCursorController.id, NotebookMultiCursorController); registerWorkbenchContribution2(NotebookMultiCursorUndoRedoContribution.ID, NotebookMultiCursorUndoRedoContribution, WorkbenchPhase.BlockRestore); +registerAction2(NotebookSelectAllFindMatches); registerAction2(NotebookAddMatchToMultiSelectionAction); registerAction2(NotebookExitMultiSelectionAction); registerAction2(NotebookDeleteLeftMultiSelectionAction); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookSelectionHighlight.ts b/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookSelectionHighlight.ts new file mode 100644 index 000000000000..305e4d8fde45 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookSelectionHighlight.ts @@ -0,0 +1,174 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from '../../../../../../base/common/event.js'; +import { Disposable, DisposableStore } from '../../../../../../base/common/lifecycle.js'; +import { ICodeEditor } from '../../../../../../editor/browser/editorBrowser.js'; +import { Selection, SelectionDirection } from '../../../../../../editor/common/core/selection.js'; +import { CursorChangeReason } from '../../../../../../editor/common/cursorEvents.js'; +import { FindMatch, IModelDeltaDecoration, ITextModel } from '../../../../../../editor/common/model.js'; +import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; +import { IActiveNotebookEditor, ICellViewModel, INotebookEditor, INotebookEditorContribution } from '../../notebookBrowser.js'; +import { registerNotebookContribution } from '../../notebookEditorExtensions.js'; + +class NotebookSelectionHighlighter extends Disposable implements INotebookEditorContribution { + + static readonly id: string = 'notebook.selectionHighlighter'; + private isEnabled: boolean = false; + + private cellDecorationIds = new Map(); + private anchorCell: [ICellViewModel, ICodeEditor] | undefined; + private readonly anchorDisposables = new DisposableStore(); + + // right now this lets us mimic the more performant cache implementation of the text editor (doesn't need to be a delayer) + // todo: in the future, implement caching and change to a 250ms delay upon recompute + // private readonly runDelayer: Delayer = this._register(new Delayer(0)); + + constructor( + private readonly notebookEditor: INotebookEditor, + @IConfigurationService private readonly configurationService: IConfigurationService, + ) { + super(); + + this.isEnabled = this.configurationService.getValue('editor.selectionHighlight'); + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('editor.selectionHighlight')) { + this.isEnabled = this.configurationService.getValue('editor.selectionHighlight'); + } + })); + + this._register(this.notebookEditor.onDidChangeActiveCell(async () => { + if (!this.isEnabled) { + return; + } + + this.anchorCell = this.notebookEditor.activeCellAndCodeEditor; + if (!this.anchorCell) { + return; + } + + const activeCell = this.notebookEditor.getActiveCell(); + if (!activeCell) { + return; + } + + if (!activeCell.editorAttached) { + await Event.toPromise(activeCell.onDidChangeEditorAttachState); + } + + this.clearNotebookSelectionDecorations(); + + this.anchorDisposables.clear(); + this.anchorDisposables.add(this.anchorCell[1].onDidChangeCursorPosition((e) => { + if (e.reason !== CursorChangeReason.Explicit) { + this.clearNotebookSelectionDecorations(); + return; + } + + if (!this.anchorCell) { + return; + } + + if (this.notebookEditor.hasModel()) { + this.clearNotebookSelectionDecorations(); + this._update(this.notebookEditor); + } + })); + + if (this.notebookEditor.getEditorViewState().editorFocused && this.notebookEditor.hasModel()) { + this._update(this.notebookEditor); + } + })); + } + + private _update(editor: IActiveNotebookEditor) { + if (!this.anchorCell || !this.isEnabled) { + return; + } + + // TODO: isTooLargeForTokenization check, notebook equivalent? + // unlikely that any one cell's textmodel would be too large + + // get the word + const textModel = this.anchorCell[0].textModel; + if (!textModel || textModel.isTooLargeForTokenization()) { + return; + } + const s = this.anchorCell[0].getSelections()[0]; + if (s.startLineNumber !== s.endLineNumber || s.isEmpty()) { + // empty selections do nothing + // multiline forbidden for perf reasons + return; + } + const searchText = this.getSearchText(s, textModel); + if (!searchText) { + return; + } + + const results = editor.textModel.findMatches( + searchText, + false, + true, + null, + ); + + for (const res of results) { + const cell = editor.getCellByHandle(res.cell.handle); + if (!cell) { + continue; + } + + this.updateCellDecorations(cell, res.matches); + } + } + + private updateCellDecorations(cell: ICellViewModel, matches: FindMatch[]) { + const selections: Selection[] = matches.map(m => { + return Selection.fromRange(m.range, SelectionDirection.LTR); + }); + + const newDecorations: IModelDeltaDecoration[] = []; + selections?.map(selection => { + const isEmpty = selection.isEmpty(); + + if (!isEmpty) { + newDecorations.push({ + range: selection, + options: { + description: '', + className: '.nb-selection-highlight', + } + }); + } + }); + + const oldDecorations = this.cellDecorationIds.get(cell) ?? []; + this.cellDecorationIds.set(cell, cell.deltaModelDecorations( + oldDecorations, + newDecorations + )); + } + + private clearNotebookSelectionDecorations() { + this.cellDecorationIds.forEach((_, cell) => { + const cellDecorations = this.cellDecorationIds.get(cell) ?? []; + if (cellDecorations) { + cell.deltaModelDecorations(cellDecorations, []); + this.cellDecorationIds.delete(cell); + } + }); + } + + private getSearchText(selection: Selection, model: ITextModel): string { + return model.getValueInRange(selection).replace(/\r\n/g, '\n'); + } + + override dispose(): void { + super.dispose(); + this.anchorDisposables.dispose(); + } +} + +registerNotebookContribution(NotebookSelectionHighlighter.id, NotebookSelectionHighlighter); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/navigation/arrow.ts b/src/vs/workbench/contrib/notebook/browser/contrib/navigation/arrow.ts index cf1e25c831d8..7e40ee920d0c 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/navigation/arrow.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/navigation/arrow.ts @@ -291,14 +291,13 @@ registerAction2(class extends NotebookCellAction { id: FOCUS_IN_OUTPUT_COMMAND_ID, title: localize('focusOutput', 'Focus In Active Cell Output'), keybinding: [{ - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow, - mac: { primary: KeyMod.WinCtrl | KeyMod.CtrlCmd | KeyCode.DownArrow, }, - weight: KeybindingWeight.WorkbenchContrib - }, - { when: ContextKeyExpr.and(IS_COMPOSITE_NOTEBOOK.negate(), IsWindowsContext), primary: KeyMod.CtrlCmd | KeyCode.DownArrow, weight: KeybindingWeight.WorkbenchContrib + }, { + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.DownArrow, + mac: { primary: KeyMod.WinCtrl | KeyMod.CtrlCmd | KeyCode.DownArrow, }, + weight: KeybindingWeight.WorkbenchContrib }], precondition: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_CELL_HAS_OUTPUTS) }); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookInlineVariables.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookInlineVariables.ts new file mode 100644 index 000000000000..571038352440 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookInlineVariables.ts @@ -0,0 +1,651 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationTokenSource } from '../../../../../../base/common/cancellation.js'; +import { onUnexpectedExternalError } from '../../../../../../base/common/errors.js'; +import { Event } from '../../../../../../base/common/event.js'; +import { Disposable, IDisposable } from '../../../../../../base/common/lifecycle.js'; +import { ResourceMap } from '../../../../../../base/common/map.js'; +import { isEqual } from '../../../../../../base/common/resources.js'; +import { format } from '../../../../../../base/common/strings.js'; +import { Position } from '../../../../../../editor/common/core/position.js'; +import { Range } from '../../../../../../editor/common/core/range.js'; +import { StandardTokenType } from '../../../../../../editor/common/encodedTokenAttributes.js'; +import { InlineValueContext, InlineValueText, InlineValueVariableLookup } from '../../../../../../editor/common/languages.js'; +import { IModelDeltaDecoration, ITextModel } from '../../../../../../editor/common/model.js'; +import { ILanguageFeaturesService } from '../../../../../../editor/common/services/languageFeatures.js'; +import { localize } from '../../../../../../nls.js'; +import { registerAction2 } from '../../../../../../platform/actions/common/actions.js'; +import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; +import { ServicesAccessor } from '../../../../../../platform/instantiation/common/instantiation.js'; +import { createInlineValueDecoration } from '../../../../debug/browser/debugEditorContribution.js'; +import { IDebugService, State } from '../../../../debug/common/debug.js'; +import { NotebookSetting } from '../../../common/notebookCommon.js'; +import { ICellExecutionStateChangedEvent, INotebookExecutionStateService, NotebookExecutionType } from '../../../common/notebookExecutionStateService.js'; +import { INotebookKernelService, VariablesResult } from '../../../common/notebookKernelService.js'; +import { INotebookActionContext, NotebookAction } from '../../controller/coreActions.js'; +import { ICellViewModel, INotebookEditor, INotebookEditorContribution } from '../../notebookBrowser.js'; +import { registerNotebookContribution } from '../../notebookEditorExtensions.js'; + +class InlineSegment { + constructor(public column: number, public text: string) { + } +} + +export class NotebookInlineVariablesController extends Disposable implements INotebookEditorContribution { + + static readonly id: string = 'notebook.inlineVariablesController'; + + private cellDecorationIds = new Map(); + private cellContentListeners = new ResourceMap(); + + private currentCancellationTokenSources = new ResourceMap(); + + private static readonly MAX_CELL_LINES = 5000; // Skip extremely large cells + + constructor( + private readonly notebookEditor: INotebookEditor, + @INotebookKernelService private readonly notebookKernelService: INotebookKernelService, + @INotebookExecutionStateService private readonly notebookExecutionStateService: INotebookExecutionStateService, + @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IDebugService private readonly debugService: IDebugService, + ) { + super(); + + this._register(this.notebookExecutionStateService.onDidChangeExecution(async e => { + const inlineValuesSetting = this.configurationService.getValue<'on' | 'auto' | 'off'>(NotebookSetting.notebookInlineValues); + if (inlineValuesSetting === 'off') { + return; + } + + if (e.type === NotebookExecutionType.cell) { + await this.updateInlineVariables(e); + } + })); + + this._register(Event.runAndSubscribe(this.configurationService.onDidChangeConfiguration, e => { + if (!e || e.affectsConfiguration(NotebookSetting.notebookInlineValues)) { + if (this.configurationService.getValue<'on' | 'auto' | 'off'>(NotebookSetting.notebookInlineValues) === 'off') { + this.clearNotebookInlineDecorations(); + } + } + })); + } + + private async updateInlineVariables(event: ICellExecutionStateChangedEvent): Promise { + if (event.changed) { // undefined -> execution was completed, so return on all else. no code should execute until we know it's an execution completion + return; + } + + const cell = this.notebookEditor.getCellByHandle(event.cellHandle); + if (!cell) { + return; + } + + // Cancel any ongoing request in this cell + const existingSource = this.currentCancellationTokenSources.get(cell.uri); + if (existingSource) { + existingSource.cancel(); + } + + // Create a new CancellationTokenSource for the new request per cell + this.currentCancellationTokenSources.set(cell.uri, new CancellationTokenSource()); + const token = this.currentCancellationTokenSources.get(cell.uri)!.token; + + if (this.debugService.state !== State.Inactive) { + this._clearNotebookInlineDecorations(); + return; + } + + if (!this.notebookEditor.textModel?.uri || !isEqual(this.notebookEditor.textModel.uri, event.notebook)) { + return; + } + + const model = await cell.resolveTextModel(); + if (!model) { + return; + } + + const inlineValuesSetting = this.configurationService.getValue<'on' | 'auto' | 'off'>(NotebookSetting.notebookInlineValues); + const hasInlineValueProvider = this.languageFeaturesService.inlineValuesProvider.has(model); + + // Skip if setting is off or if auto and no provider is registered + if (inlineValuesSetting === 'off' || (inlineValuesSetting === 'auto' && !hasInlineValueProvider)) { + return; + } + + this.clearCellInlineDecorations(cell); + + const inlineDecorations: IModelDeltaDecoration[] = []; + + if (hasInlineValueProvider) { + // use extension based provider, borrowed from https://github.com/microsoft/vscode/blob/main/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts#L679 + const lastLine = model.getLineCount(); + const lastColumn = model.getLineMaxColumn(lastLine); + const ctx: InlineValueContext = { + frameId: 0, // ignored, we won't have a stack from since not in a debug session + stoppedLocation: new Range(lastLine, lastColumn, lastLine, lastColumn) // executing cell by cell, so "stopped" location would just be the end of document + }; + + const providers = this.languageFeaturesService.inlineValuesProvider.ordered(model).reverse(); + const lineDecorations = new Map(); + + const fullCellRange = new Range(1, 1, lastLine, lastColumn); + + const promises = providers.flatMap(provider => Promise.resolve(provider.provideInlineValues(model, fullCellRange, ctx, token)).then(async (result) => { + if (!result) { + return; + } + + const notebook = this.notebookEditor.textModel; + if (!notebook) { + return; + } + + const kernel = this.notebookKernelService.getMatchingKernel(notebook); + const kernelVars: VariablesResult[] = []; + if (result.some(iv => iv.type === 'variable')) { // if anyone will need a lookup, get vars now to avoid needing to do it multiple times + if (!this.notebookEditor.hasModel()) { + return; // should not happen, a cell will be executed + } + const variables = kernel.selected?.provideVariables(event.notebook, undefined, 'named', 0, token); + if (variables) { + for await (const v of variables) { + kernelVars.push(v); + } + } + } + + for (const iv of result) { + let text: string | undefined = undefined; + switch (iv.type) { + case 'text': + text = (iv as InlineValueText).text; + break; + case 'variable': { + const name = (iv as InlineValueVariableLookup).variableName; + if (!name) { + continue; // skip to next var, no valid name to lookup with + } + const value = kernelVars.find(v => v.name === name)?.value; + if (!value) { + continue; + } + text = format('{0} = {1}', name, value); + break; + } + case 'expression': { + continue; // no active debug session, so evaluate would break + } + } + + if (text) { + const line = iv.range.startLineNumber; + let lineSegments = lineDecorations.get(line); + if (!lineSegments) { + lineSegments = []; + lineDecorations.set(line, lineSegments); + } + if (!lineSegments.some(iv => iv.text === text)) { // de-dupe + lineSegments.push(new InlineSegment(iv.range.startColumn, text)); + } + } + } + }, err => { + onUnexpectedExternalError(err); + })); + + await Promise.all(promises); + + // sort line segments and concatenate them into a decoration + lineDecorations.forEach((segments, line) => { + if (segments.length > 0) { + segments.sort((a, b) => a.column - b.column); + const text = segments.map(s => s.text).join(', '); + const editorWidth = cell.layoutInfo.editorWidth; + const fontInfo = cell.layoutInfo.fontInfo; + if (fontInfo && cell.textModel) { + const base = Math.floor((editorWidth - 50) / fontInfo.typicalHalfwidthCharacterWidth); + const lineLength = cell.textModel.getLineLength(line); + const available = Math.max(0, base - lineLength); + inlineDecorations.push(...createInlineValueDecoration(line, text, 'nb', undefined, available)); + } else { + inlineDecorations.push(...createInlineValueDecoration(line, text, 'nb')); + } + } + }); + + } else if (inlineValuesSetting === 'on') { // fallback approach only when setting is 'on' + if (!this.notebookEditor.hasModel()) { + return; // should not happen, a cell will be executed + } + const kernel = this.notebookKernelService.getMatchingKernel(this.notebookEditor.textModel); + const variables = kernel?.selected?.provideVariables(event.notebook, undefined, 'named', 0, token); + if (!variables) { + return; + } + + const vars: VariablesResult[] = []; + for await (const v of variables) { + vars.push(v); + } + const varNames: string[] = vars.map(v => v.name); + + const document = cell.textModel; + if (!document) { + return; + } + + // Skip processing for extremely large cells + if (document.getLineCount() > NotebookInlineVariablesController.MAX_CELL_LINES) { + return; + } + + const inlineDecorations: IModelDeltaDecoration[] = []; + const processedVars = new Set(); + + // Get both function ranges and comment ranges + const functionRanges = this.getFunctionRanges(document); + const commentedRanges = this.getCommentedRanges(document); + const ignoredRanges = [...functionRanges, ...commentedRanges]; + const lineDecorations = new Map(); + + // For each variable name found in the kernel results + for (const varName of varNames) { + if (processedVars.has(varName)) { + continue; + } + + // Look for variable usage globally - using word boundaries to ensure exact matches + const regex = new RegExp(`\\b${varName}\\b(?!\\w)`, 'g'); + let lastMatchOutsideIgnored: { line: number; column: number } | null = null; + let foundMatch = false; + + // Scan lines in reverse to find last occurrence first + const lines = document.getValue().split('\n'); + for (let lineNumber = lines.length - 1; lineNumber >= 0; lineNumber--) { + const line = lines[lineNumber]; + let match: RegExpExecArray | null; + + while ((match = regex.exec(line)) !== null) { + const startIndex = match.index; + const pos = new Position(lineNumber + 1, startIndex + 1); + + // Check if this position is in any ignored range (function or comment) + if (!this.isPositionInRanges(pos, ignoredRanges)) { + lastMatchOutsideIgnored = { + line: lineNumber + 1, + column: startIndex + 1 + }; + foundMatch = true; + break; // Take first match in reverse order (which is last chronologically) + } + } + + if (foundMatch) { + break; // We found our last valid occurrence, no need to check earlier lines + } + } + + if (lastMatchOutsideIgnored) { + const inlineVal = varName + ' = ' + vars.find(v => v.name === varName)?.value; + + let lineSegments = lineDecorations.get(lastMatchOutsideIgnored.line); + if (!lineSegments) { + lineSegments = []; + lineDecorations.set(lastMatchOutsideIgnored.line, lineSegments); + } + if (!lineSegments.some(iv => iv.text === inlineVal)) { // de-dupe + lineSegments.push(new InlineSegment(lastMatchOutsideIgnored.column, inlineVal)); + } + } + + processedVars.add(varName); + } + + // sort line segments and concatenate them into a decoration + lineDecorations.forEach((segments, line) => { + if (segments.length > 0) { + segments.sort((a, b) => a.column - b.column); + const text = segments.map(s => s.text).join(', '); + const editorWidth = cell.layoutInfo.editorWidth; + const fontInfo = cell.layoutInfo.fontInfo; + if (fontInfo && cell.textModel) { + const base = Math.floor((editorWidth - 50) / fontInfo.typicalHalfwidthCharacterWidth); + const lineLength = cell.textModel.getLineLength(line); + const available = Math.max(0, base - lineLength); + inlineDecorations.push(...createInlineValueDecoration(line, text, 'nb', undefined, available)); + } else { + inlineDecorations.push(...createInlineValueDecoration(line, text, 'nb')); + } + } + }); + + if (inlineDecorations.length > 0) { + this.updateCellInlineDecorations(cell, inlineDecorations); + this.initCellContentListener(cell); + } + } + } + + private getFunctionRanges(document: ITextModel): Range[] { + return document.getLanguageId() === 'python' + ? this.getPythonFunctionRanges(document.getValue()) + : this.getBracedFunctionRanges(document.getValue()); + } + + private getPythonFunctionRanges(code: string): Range[] { + const functionRanges: Range[] = []; + const lines = code.split('\n'); + let functionStartLine = -1; + let inFunction = false; + let pythonIndentLevel = -1; + const pythonFunctionDeclRegex = /^(\s*)(async\s+)?(?:def\s+\w+|class\s+\w+)\s*\([^)]*\)\s*:/; + + for (let lineNumber = 0; lineNumber < lines.length; lineNumber++) { + const line = lines[lineNumber]; + + // Check for Python function/class declarations + const pythonMatch = line.match(pythonFunctionDeclRegex); + if (pythonMatch) { + if (inFunction) { + // If we're already in a function and find another at the same or lower indent, close the current one + const currentIndent = pythonMatch[1].length; + if (currentIndent <= pythonIndentLevel) { + functionRanges.push(new Range(functionStartLine + 1, 1, lineNumber, line.length + 1)); + inFunction = false; + } + } + + if (!inFunction) { + inFunction = true; + functionStartLine = lineNumber; + pythonIndentLevel = pythonMatch[1].length; + } + continue; + } + + // Check indentation for Python functions + if (inFunction) { + // Skip empty lines + if (line.trim() === '') { + continue; + } + + // Get the indentation of the current line + const currentIndent = line.match(/^\s*/)?.[0].length ?? 0; + + // If we hit a line with same or lower indentation than where the function started, + // we've exited the function + if (currentIndent <= pythonIndentLevel) { + functionRanges.push(new Range(functionStartLine + 1, 1, lineNumber, line.length + 1)); + inFunction = false; + pythonIndentLevel = -1; + } + } + } + + // Handle case where Python function is at the end of the document + if (inFunction) { + functionRanges.push(new Range(functionStartLine + 1, 1, lines.length, lines[lines.length - 1].length + 1)); + } + + return functionRanges; + } + + private getBracedFunctionRanges(code: string): Range[] { + const functionRanges: Range[] = []; + const lines = code.split('\n'); + let braceDepth = 0; + let functionStartLine = -1; + let inFunction = false; + const functionDeclRegex = /\b(?:function\s+\w+|(?:async\s+)?(?:\w+\s*=\s*)?\([^)]*\)\s*=>|class\s+\w+|(?:public|private|protected|static)?\s*\w+\s*\([^)]*\)\s*{)/; + + for (let lineNumber = 0; lineNumber < lines.length; lineNumber++) { + const line = lines[lineNumber]; + for (const char of line) { + if (char === '{') { + if (!inFunction && functionDeclRegex.test(line)) { + inFunction = true; + functionStartLine = lineNumber; + } + braceDepth++; + } else if (char === '}') { + braceDepth--; + if (braceDepth === 0 && inFunction) { + functionRanges.push(new Range(functionStartLine + 1, 1, lineNumber + 1, line.length + 1)); + inFunction = false; + } + } + } + } + + return functionRanges; + } + + private getCommentedRanges(document: ITextModel): Range[] { + return this._getCommentedRanges(document); + } + + private _getCommentedRanges(document: ITextModel): Range[] { + try { + return this.getCommentedRangesByAccurateTokenization(document); + } catch (e) { + // Fall back to manual parsing if tokenization fails + return this.getCommentedRangesByManualParsing(document); + } + } + + private getCommentedRangesByAccurateTokenization(document: ITextModel): Range[] { + const commentRanges: Range[] = []; + const lineCount = document.getLineCount(); + + // Skip processing for extremely large documents + if (lineCount > NotebookInlineVariablesController.MAX_CELL_LINES) { + return commentRanges; + } + + // Process each line - force tokenization if needed and process tokens in a single pass + for (let lineNumber = 1; lineNumber <= lineCount; lineNumber++) { + // Force tokenization if needed + if (!document.tokenization.hasAccurateTokensForLine(lineNumber)) { + document.tokenization.forceTokenization(lineNumber); + } + + const lineTokens = document.tokenization.getLineTokens(lineNumber); + + // Skip lines with no tokens + if (lineTokens.getCount() === 0) { + continue; + } + + let startCharacter: number | undefined; + + // Check each token in the line + for (let tokenIndex = 0; tokenIndex < lineTokens.getCount(); tokenIndex++) { + const tokenType = lineTokens.getStandardTokenType(tokenIndex); + + if (tokenType === StandardTokenType.Comment || tokenType === StandardTokenType.String || tokenType === StandardTokenType.RegEx) { + if (startCharacter === undefined) { + // Start of a comment or string + startCharacter = lineTokens.getStartOffset(tokenIndex); + } + + const endCharacter = lineTokens.getEndOffset(tokenIndex); + + // Check if this is the end of the comment/string section (either end of line or different token type follows) + const isLastToken = tokenIndex === lineTokens.getCount() - 1; + const nextTokenDifferent = !isLastToken && + lineTokens.getStandardTokenType(tokenIndex + 1) !== tokenType; + + if (isLastToken || nextTokenDifferent) { + // End of comment/string section + commentRanges.push(new Range(lineNumber, startCharacter + 1, lineNumber, endCharacter + 1)); + startCharacter = undefined; + } + } else { + // Reset when we hit a non-comment, non-string token + startCharacter = undefined; + } + } + } + + return commentRanges; + } + + private getCommentedRangesByManualParsing(document: ITextModel): Range[] { + const commentRanges: Range[] = []; + const lines = document.getValue().split('\n'); + const languageId = document.getLanguageId(); + + // Different comment patterns by language + const lineCommentToken = + languageId === 'python' ? '#' : + languageId === 'javascript' || languageId === 'typescript' ? '//' : + null; + + const blockComments = + (languageId === 'javascript' || languageId === 'typescript') ? { start: '/*', end: '*/' } : + null; + + let inBlockComment = false; + let blockCommentStartLine = -1; + let blockCommentStartCol = -1; + + for (let lineNumber = 0; lineNumber < lines.length; lineNumber++) { + const line = lines[lineNumber]; + const trimmedLine = line.trim(); + + // Skip empty lines + if (trimmedLine.length === 0) { + continue; + } + + if (blockComments) { + if (!inBlockComment) { + const startIndex = line.indexOf(blockComments.start); + if (startIndex !== -1) { + inBlockComment = true; + blockCommentStartLine = lineNumber; + blockCommentStartCol = startIndex; + } + } + + if (inBlockComment) { + const endIndex = line.indexOf(blockComments.end); + if (endIndex !== -1) { + commentRanges.push(new Range( + blockCommentStartLine + 1, + blockCommentStartCol + 1, + lineNumber + 1, + endIndex + blockComments.end.length + 1 + )); + inBlockComment = false; + } + continue; + } + } + + if (!inBlockComment && lineCommentToken && line.trimLeft().startsWith(lineCommentToken)) { + const startCol = line.indexOf(lineCommentToken); + commentRanges.push(new Range( + lineNumber + 1, + startCol + 1, + lineNumber + 1, + line.length + 1 + )); + } + } + + // Handle block comment at end of file + if (inBlockComment) { + commentRanges.push(new Range( + blockCommentStartLine + 1, + blockCommentStartCol + 1, + lines.length, + lines[lines.length - 1].length + 1 + )); + } + + return commentRanges; + } + + private isPositionInRanges(position: Position, ranges: Range[]): boolean { + return ranges.some(range => range.containsPosition(position)); + } + + private updateCellInlineDecorations(cell: ICellViewModel, decorations: IModelDeltaDecoration[]) { + const oldDecorations = this.cellDecorationIds.get(cell) ?? []; + this.cellDecorationIds.set(cell, cell.deltaModelDecorations( + oldDecorations, + decorations + )); + } + + private initCellContentListener(cell: ICellViewModel) { + const cellModel = cell.textModel; + if (!cellModel) { + return; // should not happen + } + + // Clear decorations on content change + this.cellContentListeners.set(cell.uri, cellModel.onDidChangeContent(() => { + this.clearCellInlineDecorations(cell); + })); + } + + private clearCellInlineDecorations(cell: ICellViewModel) { + const cellDecorations = this.cellDecorationIds.get(cell) ?? []; + if (cellDecorations) { + cell.deltaModelDecorations(cellDecorations, []); + this.cellDecorationIds.delete(cell); + } + + const listener = this.cellContentListeners.get(cell.uri); + if (listener) { + listener.dispose(); + this.cellContentListeners.delete(cell.uri); + } + } + + private _clearNotebookInlineDecorations() { + this.cellDecorationIds.forEach((_, cell) => { + this.clearCellInlineDecorations(cell); + }); + } + + public clearNotebookInlineDecorations() { + this._clearNotebookInlineDecorations(); + } + + override dispose(): void { + super.dispose(); + this._clearNotebookInlineDecorations(); + this.currentCancellationTokenSources.forEach(source => source.cancel()); + this.currentCancellationTokenSources.clear(); + this.cellContentListeners.forEach(listener => listener.dispose()); + this.cellContentListeners.clear(); + } +} + +registerNotebookContribution(NotebookInlineVariablesController.id, NotebookInlineVariablesController); + +registerAction2(class ClearNotebookInlineValues extends NotebookAction { + constructor() { + super({ + id: 'notebook.clearAllInlineValues', + title: localize('clearAllInlineValues', 'Clear All Inline Values'), + }); + } + + override runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise { + const editor = context.notebookEditor; + const controller = editor.getContribution(NotebookInlineVariablesController.id); + controller.clearNotebookInlineDecorations(); + return Promise.resolve(); + } + +}); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts index 3cc57762731f..dc86e5ba940d 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts @@ -4,12 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { ITreeContextMenuEvent } from '../../../../../../base/browser/ui/tree/tree.js'; -import { IAction } from '../../../../../../base/common/actions.js'; import { RunOnceScheduler } from '../../../../../../base/common/async.js'; import { URI } from '../../../../../../base/common/uri.js'; import * as nls from '../../../../../../nls.js'; import { ILocalizedString } from '../../../../../../platform/action/common/action.js'; -import { createAndFillInContextMenuActions } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; +import { getFlatContextMenuActions } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IMenuService, MenuId } from '../../../../../../platform/actions/common/actions.js'; import { ICommandService } from '../../../../../../platform/commands/common/commands.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; @@ -21,7 +20,6 @@ import { IKeybindingService } from '../../../../../../platform/keybinding/common import { WorkbenchAsyncDataTree } from '../../../../../../platform/list/browser/listService.js'; import { IOpenerService } from '../../../../../../platform/opener/common/opener.js'; import { IQuickInputService } from '../../../../../../platform/quickinput/common/quickInput.js'; -import { ITelemetryService } from '../../../../../../platform/telemetry/common/telemetry.js'; import { IThemeService } from '../../../../../../platform/theme/common/themeService.js'; import { IViewPaneOptions, ViewPane } from '../../../../../browser/parts/views/viewPane.js'; import { IViewDescriptorService } from '../../../../../common/views.js'; @@ -65,11 +63,10 @@ export class NotebookVariablesView extends ViewPane { @IQuickInputService protected quickInputService: IQuickInputService, @ICommandService protected commandService: ICommandService, @IThemeService themeService: IThemeService, - @ITelemetryService telemetryService: ITelemetryService, @IHoverService hoverService: IHoverService, @IMenuService private readonly menuService: IMenuService ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); this._register(this.editorService.onDidActiveEditorChange(() => this.handleActiveEditorChange())); this._register(this.notebookKernelService.onDidNotebookVariablesUpdate(this.handleVariablesChanged.bind(this))); @@ -86,8 +83,8 @@ export class NotebookVariablesView extends ViewPane { super.renderBody(container); this.element.classList.add('debug-pane'); - this.tree = >this.instantiationService.createInstance( - WorkbenchAsyncDataTree, + this.tree = this.instantiationService.createInstance( + WorkbenchAsyncDataTree, 'notebookVariablesTree', container, new NotebookVariablesDelegate(), @@ -121,7 +118,6 @@ export class NotebookVariablesView extends ViewPane { language: element.language, extensionId: element.extensionId }; - const actions: IAction[] = []; const overlayedContext = this.contextKeyService.createOverlay([ [CONTEXT_VARIABLE_NAME.key, element.name], @@ -131,14 +127,19 @@ export class NotebookVariablesView extends ViewPane { [CONTEXT_VARIABLE_LANGUAGE.key, element.language], [CONTEXT_VARIABLE_EXTENSIONID.key, element.extensionId] ]); - const menu = this.menuService.getMenuActions(MenuId.NotebookVariablesContext, overlayedContext, { arg, shouldForwardArgs: true }); - createAndFillInContextMenuActions(menu, actions); + const menuActions = this.menuService.getMenuActions(MenuId.NotebookVariablesContext, overlayedContext, { arg, shouldForwardArgs: true }); + const actions = getFlatContextMenuActions(menuActions); this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, getActions: () => actions }); } + override focus(): void { + super.focus(); + this.tree?.domFocus(); + } + protected override layoutBody(height: number, width: number): void { super.layoutBody(height, width); this.tree?.layout(height, width); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts b/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts index 90839d12a98a..a66112843ab8 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts @@ -42,9 +42,9 @@ import { mainWindow } from '../../../../../../base/browser/window.js'; import { IContextMenuService } from '../../../../../../platform/contextview/browser/contextView.js'; import { Action2, IMenu, IMenuService, MenuId, MenuItemAction, MenuRegistry, registerAction2 } from '../../../../../../platform/actions/common/actions.js'; import { ContextKeyExpr, IContextKeyService, RawContextKey } from '../../../../../../platform/contextkey/common/contextkey.js'; -import { MenuEntryActionViewItem, createAndFillInActionBarActions } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; +import { MenuEntryActionViewItem, getActionBarActions } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IAction } from '../../../../../../base/common/actions.js'; -import { NotebookSectionArgs } from '../../controller/sectionActions.js'; +import { NotebookOutlineEntryArgs } from '../../controller/sectionActions.js'; import { MarkupCellViewModel } from '../../viewModel/markupCellViewModel.js'; import { Delayer, disposableTimeout } from '../../../../../../base/common/async.js'; import { IOutlinePane } from '../../../../outline/browser/outline.js'; @@ -153,8 +153,12 @@ class NotebookOutlineRenderer implements ITreeRenderer, index: number, templateData: NotebookOutlineTemplate, height: number | undefined): void { @@ -210,7 +214,7 @@ class NotebookOutlineRenderer implements ITreeRenderer void) | undefined; @@ -218,13 +222,13 @@ class NotebookOutlineRenderer implements ITreeRenderer { if (dropdownIsVisible) { - const actions = getOutlineToolbarActions(menu, { notebookEditor: this._editor, outlineEntry: entry }); + const actions = getOutlineToolbarActions(menu, { notebookEditor: editor, outlineEntry: entry }); deferredUpdate = () => toolbar.setActions(actions.primary, actions.secondary); return; } - const actions = getOutlineToolbarActions(menu, { notebookEditor: this._editor, outlineEntry: entry }); + const actions = getOutlineToolbarActions(menu, { notebookEditor: editor, outlineEntry: entry }); toolbar.setActions(actions.primary, actions.secondary); })); @@ -249,15 +253,8 @@ class NotebookOutlineRenderer implements ITreeRenderer /^inline/.test(g)); - - return result; +function getOutlineToolbarActions(menu: IMenu, args?: NotebookOutlineEntryArgs): { primary: IAction[]; secondary: IAction[] } { + return getActionBarActions(menu.getActions({ shouldForwardArgs: true, arg: args }), g => /^inline/.test(g)); } class NotebookOutlineAccessibility implements IListAccessibilityProvider { @@ -349,6 +346,28 @@ export class NotebookQuickPickProvider implements IQuickPickDataSource NotebookOutlineConstants.NonHeaderOutlineLevel) // show symbols off + cell is code + is level >7 (nb symbol levels) + ) { + return true; + } + + return false; +} + export class NotebookOutlinePaneProvider implements IDataSource { private readonly _disposables = new DisposableStore(); @@ -384,14 +403,14 @@ export class NotebookOutlinePaneProvider implements IDataSource NotebookOutlineConstants.NonHeaderOutlineLevel) // show symbols off + cell is code + is level >7 (nb symbol levels) - ) { - return true; - } - - return false; - } - *getChildren(element: NotebookCellOutline | OutlineEntry): Iterable { const isOutline = element instanceof NotebookCellOutline; const entries = isOutline ? this.outlineDataSourceRef?.object?.entries ?? [] : element.children; @@ -521,7 +521,9 @@ export class NotebookCellOutline implements IOutline { private _breadcrumbsDataSource!: IBreadcrumbsDataSource; // view settings + private outlineShowCodeCells: boolean; private outlineShowCodeCellSymbols: boolean; + private outlineShowMarkdownHeadersOnly: boolean; // getters get activeElement(): OutlineEntry | undefined { @@ -541,7 +543,13 @@ export class NotebookCellOutline implements IOutline { return this._outlineDataSourceReference?.object?.uri; } get isEmpty(): boolean { - return this._outlineDataSourceReference?.object?.isEmpty ?? true; + if (!this._outlineDataSourceReference?.object?.entries) { + return true; + } + + return !this._outlineDataSourceReference.object.entries.some(entry => { + return !filterEntry(entry, this.outlineShowMarkdownHeadersOnly, this.outlineShowCodeCells, this.outlineShowCodeCellSymbols); + }); } private checkDelayer() { @@ -561,7 +569,9 @@ export class NotebookCellOutline implements IOutline { @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, @INotebookExecutionStateService private readonly _notebookExecutionStateService: INotebookExecutionStateService, ) { + this.outlineShowCodeCells = this._configurationService.getValue(NotebookSetting.outlineShowCodeCells); this.outlineShowCodeCellSymbols = this._configurationService.getValue(NotebookSetting.outlineShowCodeCellSymbols); + this.outlineShowMarkdownHeadersOnly = this._configurationService.getValue(NotebookSetting.outlineShowMarkdownHeadersOnly); this.initializeOutline(); @@ -618,6 +628,10 @@ export class NotebookCellOutline implements IOutline { e.affectsConfiguration(NotebookSetting.outlineShowCodeCellSymbols) || e.affectsConfiguration(NotebookSetting.breadcrumbsShowCodeCells) ) { + this.outlineShowCodeCells = this._configurationService.getValue(NotebookSetting.outlineShowCodeCells); + this.outlineShowCodeCellSymbols = this._configurationService.getValue(NotebookSetting.outlineShowCodeCellSymbols); + this.outlineShowMarkdownHeadersOnly = this._configurationService.getValue(NotebookSetting.outlineShowMarkdownHeadersOnly); + this.delayedRecomputeState(); } })); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants.ts b/src/vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants.ts index 96f82e22e15d..d8dd4e130907 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants.ts @@ -41,7 +41,7 @@ import { IStoredFileWorkingCopy, IStoredFileWorkingCopyModel } from '../../../.. import { IStoredFileWorkingCopySaveParticipant, IStoredFileWorkingCopySaveParticipantContext, IWorkingCopyFileService } from '../../../../../services/workingCopy/common/workingCopyFileService.js'; import { NotebookMultiCursorController, NotebookMultiCursorState } from '../multicursor/notebookMulticursor.js'; -abstract class NotebookSaveParticipant implements IStoredFileWorkingCopySaveParticipant { +export abstract class NotebookSaveParticipant implements IStoredFileWorkingCopySaveParticipant { constructor( private readonly _editorService: IEditorService, ) { } diff --git a/src/vs/workbench/contrib/notebook/browser/controller/cellOperations.ts b/src/vs/workbench/contrib/notebook/browser/controller/cellOperations.ts index 3432d899d1bf..0795f4536e70 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/cellOperations.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/cellOperations.ts @@ -18,6 +18,7 @@ import { CellEditType, CellKind, ICellEditOperation, ICellReplaceEdit, IOutputDt import { cellRangeContains, cellRangesToIndexes, ICellRange } from '../../common/notebookRange.js'; import { localize } from '../../../../../nls.js'; import { INotificationService } from '../../../../../platform/notification/common/notification.js'; +import { INotebookKernelHistoryService } from '../../common/notebookKernelService.js'; export async function changeCellToKind(kind: CellKind, context: INotebookActionContext, language?: string, mime?: string): Promise { const { notebookEditor } = context; @@ -662,7 +663,8 @@ export function insertCell( type: CellKind, direction: 'above' | 'below' = 'above', initialText: string = '', - ui: boolean = false + ui: boolean = false, + kernelHistoryService?: INotebookKernelHistoryService ) { const viewModel = editor.getViewModel() as NotebookViewModel; const activeKernel = editor.activeKernel; @@ -676,6 +678,7 @@ export function insertCell( if (type === CellKind.Code) { const supportedLanguages = activeKernel?.supportedLanguages ?? languageService.getRegisteredLanguageIds(); const defaultLanguage = supportedLanguages[0] || PLAINTEXT_LANGUAGE_ID; + if (cell?.cellKind === CellKind.Code) { language = cell.language; } else if (cell?.cellKind === CellKind.Markup) { @@ -685,6 +688,15 @@ export function insertCell( } else { language = defaultLanguage; } + } else if (!cell && viewModel.length === 0) { + // No cells in notebook - check kernel history + const lastKernels = kernelHistoryService?.getKernels(viewModel.notebookDocument); + if (lastKernels?.all.length) { + const lastKernel = lastKernels.all[0]; + language = lastKernel.supportedLanguages[0] || defaultLanguage; + } else { + language = defaultLanguage; + } } else { if (cell === undefined && direction === 'above') { // insert cell at the very top diff --git a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts index d1e521bd6e54..43b0f24804e0 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts @@ -12,7 +12,7 @@ import { INotebookOutputActionContext, NOTEBOOK_ACTIONS_CATEGORY } from './coreA import { NOTEBOOK_CELL_HAS_HIDDEN_OUTPUTS, NOTEBOOK_CELL_HAS_OUTPUTS } from '../../common/notebookContextKeys.js'; import * as icons from '../notebookIcons.js'; import { ILogService } from '../../../../../platform/log/common/log.js'; -import { copyCellOutput } from '../contrib/clipboard/cellOutputClipboard.js'; +import { copyCellOutput } from '../viewModel/cellOutputTextHelper.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; import { ICellOutputViewModel, ICellViewModel, INotebookEditor, getNotebookEditorFromEditorPane } from '../notebookBrowser.js'; import { CellKind, CellUri } from '../../common/notebookCommon.js'; @@ -121,7 +121,7 @@ registerAction2(class CopyCellOutputAction extends Action2 { }); -function getOutputViewModelFromId(outputId: string, notebookEditor: INotebookEditor): ICellOutputViewModel | undefined { +export function getOutputViewModelFromId(outputId: string, notebookEditor: INotebookEditor): ICellOutputViewModel | undefined { const notebookViewModel = notebookEditor.getViewModel(); if (notebookViewModel) { const codeCells = notebookViewModel.viewCells.filter(cell => cell.cellKind === CellKind.Code) as CodeCellViewModel[]; diff --git a/src/vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions.ts index 00a48c648f2f..7c52036cb9ac 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions.ts @@ -15,7 +15,7 @@ import { ContextKeyExpr } from '../../../../../../platform/contextkey/common/con import { InputFocusedContextKey } from '../../../../../../platform/contextkey/common/contextkeys.js'; import { ServicesAccessor } from '../../../../../../platform/instantiation/common/instantiation.js'; import { KeybindingWeight } from '../../../../../../platform/keybinding/common/keybindingsRegistry.js'; -import { CTX_INLINE_CHAT_DOCUMENT_CHANGED, CTX_INLINE_CHAT_EDIT_MODE, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, CTX_INLINE_CHAT_VISIBLE, EditMode, InlineChatResponseType, MENU_INLINE_CHAT_WIDGET_STATUS } from '../../../../inlineChat/common/inlineChat.js'; +import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, CTX_INLINE_CHAT_VISIBLE, InlineChatResponseType, MENU_INLINE_CHAT_WIDGET_STATUS } from '../../../../inlineChat/common/inlineChat.js'; import { CTX_NOTEBOOK_CELL_CHAT_FOCUSED, CTX_NOTEBOOK_CHAT_HAS_ACTIVE_REQUEST, CTX_NOTEBOOK_CHAT_HAS_AGENT, CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION, CTX_NOTEBOOK_CHAT_USER_DID_EDIT, MENU_CELL_CHAT_INPUT, MENU_CELL_CHAT_WIDGET, MENU_CELL_CHAT_WIDGET_STATUS } from './notebookChatContext.js'; import { NotebookChatController } from './notebookChatController.js'; import { CELL_TITLE_CELL_GROUP_ID, INotebookActionContext, INotebookCellActionContext, NotebookAction, NotebookCellAction, getContextFromActiveEditor, getEditorFromArgsOrActivePane } from '../coreActions.js'; @@ -26,10 +26,9 @@ import { IS_COMPOSITE_NOTEBOOK, NOTEBOOK_CELL_EDITOR_FOCUSED, NOTEBOOK_CELL_GENE import { Iterable } from '../../../../../../base/common/iterator.js'; import { ICodeEditor } from '../../../../../../editor/browser/editorBrowser.js'; import { IEditorService } from '../../../../../services/editor/common/editorService.js'; -import { CONTEXT_CHAT_INPUT_HAS_TEXT } from '../../../../chat/common/chatContextKeys.js'; -import { AbstractInlineChatAction } from '../../../../inlineChat/browser/inlineChatActions.js'; +import { ChatContextKeys } from '../../../../chat/common/chatContextKeys.js'; import { InlineChatController } from '../../../../inlineChat/browser/inlineChatController.js'; -import { HunkInformation } from '../../../../inlineChat/browser/inlineChatSession.js'; +import { EditorAction2 } from '../../../../../../editor/browser/editorExtensions.js'; registerAction2(class extends NotebookAction { constructor() { @@ -671,7 +670,7 @@ registerAction2(class extends NotebookCellAction { }); -export class AcceptChangesAndRun extends AbstractInlineChatAction { +export class AcceptChangesAndRun extends EditorAction2 { constructor() { super({ @@ -684,7 +683,6 @@ export class AcceptChangesAndRun extends AbstractInlineChatAction { precondition: ContextKeyExpr.and( NOTEBOOK_EDITOR_EDITABLE.isEqualTo(true), CTX_INLINE_CHAT_VISIBLE, - ContextKeyExpr.or(CTX_INLINE_CHAT_DOCUMENT_CHANGED.toNegated(), CTX_INLINE_CHAT_EDIT_MODE.notEqualsTo(EditMode.Preview)) ), keybinding: undefined, menu: [{ @@ -693,7 +691,7 @@ export class AcceptChangesAndRun extends AbstractInlineChatAction { order: 2, when: ContextKeyExpr.and( NOTEBOOK_EDITOR_EDITABLE.isEqualTo(true), - CONTEXT_CHAT_INPUT_HAS_TEXT.toNegated(), + ChatContextKeys.inputHasText.toNegated(), CTX_INLINE_CHAT_REQUEST_IN_PROGRESS.toNegated(), CTX_INLINE_CHAT_RESPONSE_TYPE.isEqualTo(InlineChatResponseType.MessagesAndEdits) ) @@ -701,10 +699,11 @@ export class AcceptChangesAndRun extends AbstractInlineChatAction { }); } - override async runInlineChatCommand(accessor: ServicesAccessor, ctrl: InlineChatController, codeEditor: ICodeEditor, hunk?: HunkInformation | any): Promise { + override runEditorCommand(accessor: ServicesAccessor, codeEditor: ICodeEditor) { const editor = getContextFromActiveEditor(accessor.get(IEditorService)); + const ctrl = InlineChatController.get(codeEditor); - if (!editor) { + if (!editor || !ctrl) { return; } diff --git a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts index 74f0bdae3625..c2f474c10399 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts @@ -13,8 +13,8 @@ import { CompletionContext, CompletionItemKind, CompletionList } from '../../../ import { ITextModel } from '../../../../../../editor/common/model.js'; import { ILanguageFeaturesService } from '../../../../../../editor/common/services/languageFeatures.js'; import { localize } from '../../../../../../nls.js'; -import { Action2, registerAction2 } from '../../../../../../platform/actions/common/actions.js'; -import { IContextKey, IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; +import { Action2, MenuId, registerAction2 } from '../../../../../../platform/actions/common/actions.js'; +import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; import { ServicesAccessor } from '../../../../../../platform/instantiation/common/instantiation.js'; import { IQuickInputService, IQuickPickItem } from '../../../../../../platform/quickinput/common/quickInput.js'; import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../../../common/contributions.js'; @@ -23,10 +23,17 @@ import { IChatWidget, IChatWidgetService } from '../../../../chat/browser/chat.j import { ChatInputPart } from '../../../../chat/browser/chatInputPart.js'; import { ChatDynamicVariableModel } from '../../../../chat/browser/contrib/chatDynamicVariables.js'; import { computeCompletionRanges } from '../../../../chat/browser/contrib/chatInputCompletions.js'; -import { ChatAgentLocation, IChatAgentService } from '../../../../chat/common/chatAgents.js'; +import { IChatAgentService } from '../../../../chat/common/chatAgents.js'; +import { ChatAgentLocation } from '../../../../chat/common/constants.js'; +import { ChatContextKeys } from '../../../../chat/common/chatContextKeys.js'; +import { IChatRequestPasteVariableEntry } from '../../../../chat/common/chatModel.js'; import { chatVariableLeader } from '../../../../chat/common/chatParserTypes.js'; +import { NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_OUTPUT_MIME_TYPE_LIST_FOR_CHAT, NOTEBOOK_CELL_OUTPUT_MIMETYPE } from '../../../common/notebookContextKeys.js'; import { INotebookKernelService } from '../../../common/notebookKernelService.js'; -import { getNotebookEditorFromEditorPane } from '../../notebookBrowser.js'; +import { getNotebookEditorFromEditorPane, ICellOutputViewModel, INotebookEditor } from '../../notebookBrowser.js'; +import * as icons from '../../notebookIcons.js'; +import { getOutputViewModelFromId } from '../cellOutputActions.js'; +import { INotebookOutputActionContext, NOTEBOOK_ACTIONS_CATEGORY } from '../coreActions.js'; import './cellChatActions.js'; import { CTX_NOTEBOOK_CHAT_HAS_AGENT } from './notebookChatContext.js'; @@ -94,6 +101,9 @@ class NotebookChatContribution extends Disposable implements IWorkbenchContribut return result; } })); + + // output context + NOTEBOOK_CELL_OUTPUT_MIME_TYPE_LIST_FOR_CHAT.bindTo(contextKeyService).set(['image/png']); } private async addKernelVariableCompletion(widget: IChatWidget, result: CompletionList, info: { insert: Range; replace: Range; varWord: IWordAtPosition | null }, token: CancellationToken) { @@ -223,11 +233,111 @@ export class SelectAndInsertKernelVariableAction extends Action2 { name: variableName, value: variableName, icon: codiconsLibrary.variable, - isDynamic: true }); } } } + +registerAction2(class CopyCellOutputAction extends Action2 { + constructor() { + super({ + id: 'notebook.cellOutput.addToChat', + title: localize('notebookActions.addOutputToChat', "Add Cell Output to Chat"), + menu: { + id: MenuId.NotebookOutputToolbar, + when: ContextKeyExpr.and(NOTEBOOK_CELL_HAS_OUTPUTS, ContextKeyExpr.in(NOTEBOOK_CELL_OUTPUT_MIMETYPE.key, NOTEBOOK_CELL_OUTPUT_MIME_TYPE_LIST_FOR_CHAT.key)), + order: 10 + }, + category: NOTEBOOK_ACTIONS_CATEGORY, + icon: icons.copyIcon, + precondition: ChatContextKeys.enabled + }); + } + + private getNoteboookEditor(editorService: IEditorService, outputContext: INotebookOutputActionContext | { outputViewModel: ICellOutputViewModel } | undefined): INotebookEditor | undefined { + if (outputContext && 'notebookEditor' in outputContext) { + return outputContext.notebookEditor; + } + return getNotebookEditorFromEditorPane(editorService.activeEditorPane); + } + + async run(accessor: ServicesAccessor, outputContext: INotebookOutputActionContext | { outputViewModel: ICellOutputViewModel } | undefined): Promise { + const notebookEditor = this.getNoteboookEditor(accessor.get(IEditorService), outputContext); + + if (!notebookEditor) { + return; + } + + let outputViewModel: ICellOutputViewModel | undefined; + if (outputContext && 'outputId' in outputContext && typeof outputContext.outputId === 'string') { + outputViewModel = getOutputViewModelFromId(outputContext.outputId, notebookEditor); + } else if (outputContext && 'outputViewModel' in outputContext) { + outputViewModel = outputContext.outputViewModel; + } + + if (!outputViewModel) { + // not able to find the output from the provided context, use the active cell + const activeCell = notebookEditor.getActiveCell(); + if (!activeCell) { + return; + } + + if (activeCell.focusedOutputId !== undefined) { + outputViewModel = activeCell.outputsViewModels.find(output => { + return output.model.outputId === activeCell.focusedOutputId; + }); + } else { + outputViewModel = activeCell.outputsViewModels.find(output => output.pickedMimeType?.isTrusted); + } + } + + if (!outputViewModel) { + return; + } + + const mimeType = outputViewModel.pickedMimeType?.mimeType; + + if (mimeType === 'image/png') { + const chatWidgetService = accessor.get(IChatWidgetService); + + const widget = chatWidgetService.lastFocusedWidget; + if (!widget) { + return; + } + + const imageOutput = outputViewModel.model.outputs.find(output => output.mime === mimeType); + if (!imageOutput) { + return; + } + + const attachedVariables = widget.attachmentModel.attachments; + const displayName = localize('cellOutputDisplayname', 'Notebook Cell Output Image'); + let tempDisplayName = displayName; + + for (let appendValue = 2; attachedVariables.some(attachment => attachment.name === tempDisplayName); appendValue++) { + tempDisplayName = `${displayName} ${appendValue}`; + } + + const imageData = imageOutput.data; + const variableEntry: IChatRequestPasteVariableEntry = { + kind: 'paste', + code: '', + language: '', + pastedLines: '', + fileName: 'notebook-cell-output-image-' + outputViewModel.model.outputId, + copiedFrom: undefined, + id: 'notebook-cell-output-image-' + outputViewModel.model.outputId, + name: tempDisplayName, + isImage: true, + value: imageData.buffer, + }; + + widget.attachmentModel.addContext(variableEntry); + } + } + +}); + registerAction2(SelectAndInsertKernelVariableAction); registerWorkbenchContribution2(NotebookChatContribution.ID, NotebookChatContribution, WorkbenchPhase.BlockRestore); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts index bac32e50bd8b..2296896d9d91 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts @@ -29,7 +29,7 @@ import { localize } from '../../../../../../nls.js'; import { IContextKey, IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../../../platform/storage/common/storage.js'; -import { ChatAgentLocation } from '../../../../chat/common/chatAgents.js'; +import { ChatAgentLocation } from '../../../../chat/common/constants.js'; import { ChatModel, IChatModel } from '../../../../chat/common/chatModel.js'; import { IChatService } from '../../../../chat/common/chatService.js'; import { countWords } from '../../../../chat/common/chatWordCounter.js'; @@ -255,7 +255,6 @@ export class NotebookChatController extends Disposable implements INotebookEdito private _strategy: EditStrategy | undefined; private _sessionCtor: CancelablePromise | undefined; - private _warmupRequestCts?: CancellationTokenSource; private _activeRequestCts?: CancellationTokenSource; private readonly _ctxHasActiveRequest: IContextKey; private readonly _ctxCellWidgetFocused: IContextKey; @@ -442,10 +441,7 @@ export class NotebookChatController extends Disposable implements INotebookEdito inlineChatWidget.placeholder = localize('default.placeholder', "Ask a question"); inlineChatWidget.updateInfo(localize('welcome.1', "AI-generated code may be incorrect")); widgetContainer.appendChild(inlineChatWidget.domNode); - this._widgetDisposableStore.add(inlineChatWidget.onDidChangeInput(() => { - this._warmupRequestCts?.dispose(true); - this._warmupRequestCts = undefined; - })); + this._notebookEditor.changeViewZones(accessor => { const notebookViewZone = { diff --git a/src/vs/workbench/contrib/notebook/browser/controller/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/coreActions.ts index 88c60d0414da..6d986184bad3 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/coreActions.ts @@ -10,7 +10,7 @@ import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contex import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; import { getNotebookEditorFromEditorPane, IActiveNotebookEditor, ICellViewModel, cellRangeToViewCells, ICellOutputViewModel } from '../notebookBrowser.js'; -import { IS_COMPOSITE_NOTEBOOK, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_KERNEL_SOURCE_COUNT } from '../../common/notebookContextKeys.js'; +import { INTERACTIVE_WINDOW_IS_ACTIVE_EDITOR, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_KERNEL_SOURCE_COUNT, REPL_NOTEBOOK_IS_ACTIVE_EDITOR } from '../../common/notebookContextKeys.js'; import { ICellRange, isICellRange } from '../../common/notebookRange.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; import { isEditorCommandsContext } from '../../../../common/editor.js'; @@ -34,6 +34,7 @@ export const NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT = KeybindingWeight.EditorContr export const NOTEBOOK_OUTPUT_WEBVIEW_ACTION_WEIGHT = KeybindingWeight.WorkbenchContrib + 1; // higher than Workbench contribution (such as Notebook List View), etc export const enum CellToolbarOrder { + RunSection, EditCell, ExecuteAboveCells, ExecuteCellAndBelow, @@ -135,7 +136,7 @@ export abstract class NotebookAction extends Action2 { desc.f1 = false; const f1Menu = { id: MenuId.CommandPalette, - when: ContextKeyExpr.or(NOTEBOOK_IS_ACTIVE_EDITOR, IS_COMPOSITE_NOTEBOOK) + when: ContextKeyExpr.or(NOTEBOOK_IS_ACTIVE_EDITOR, INTERACTIVE_WINDOW_IS_ACTIVE_EDITOR, REPL_NOTEBOOK_IS_ACTIVE_EDITOR) }; if (!desc.menu) { diff --git a/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts index b7b76f068bc0..3fe9771b282d 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; +import { KeyChord, KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; import { Mimes } from '../../../../../base/common/mime.js'; import { URI } from '../../../../../base/common/uri.js'; import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; @@ -40,6 +40,7 @@ import { INotebookKernelService } from '../../common/notebookKernelService.js'; import { ICellRange } from '../../common/notebookRange.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; import { ILanguageDetectionService } from '../../../../services/languageDetection/common/languageDetectionWorkerService.js'; +import { NotebookInlineVariablesController } from '../contrib/notebookVariables/notebookInlineVariables.js'; const CLEAR_ALL_CELLS_OUTPUTS_COMMAND_ID = 'notebook.clearAllCellsOutputs'; const EDIT_CELL_COMMAND_ID = 'notebook.cell.edit'; @@ -58,7 +59,6 @@ registerAction2(class EditCellAction extends NotebookCellAction { when: ContextKeyExpr.and( NOTEBOOK_CELL_LIST_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey), - NOTEBOOK_EDITOR_EDITABLE.isEqualTo(true), EditorContextKeys.hoverFocused.toNegated(), NOTEBOOK_OUTPUT_INPUT_FOCUSED.toNegated() ), @@ -80,7 +80,7 @@ registerAction2(class EditCellAction extends NotebookCellAction { } async runWithContext(accessor: ServicesAccessor, context: INotebookCellActionContext): Promise { - if (!context.notebookEditor.hasModel() || context.notebookEditor.isReadOnly) { + if (!context.notebookEditor.hasModel()) { return; } @@ -339,6 +339,9 @@ registerAction2(class ClearAllCellOutputsAction extends NotebookAction { if (clearExecutionMetadataEdits.length) { context.notebookEditor.textModel.applyEdits(clearExecutionMetadataEdits, true, undefined, () => undefined, undefined, computeUndoRedo); } + + const controller = editor.getContribution(NotebookInlineVariablesController.id); + controller.clearNotebookInlineDecorations(); } }); @@ -358,6 +361,11 @@ registerAction2(class ChangeCellLanguageAction extends NotebookCellAction { if (e.source.editStateChanged && e.cell.cellKind === CellKind.Markup) { this._foldingModel?.recompute(); - // this._updateEditorFoldingRanges(); } })); @@ -145,6 +144,10 @@ export class FoldingController extends Disposable implements INotebookEditorCont return; } + + recompute() { + this._foldingModel?.recompute(); + } } registerNotebookContribution(FoldingController.id, FoldingController); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/insertCellActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/insertCellActions.ts index dc3fbcc179db..b7bdf36f3478 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/insertCellActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/insertCellActions.ts @@ -18,6 +18,7 @@ import { NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_EDITOR_EDITABLE } from '../../comm import { CellViewModel } from '../viewModel/notebookViewModelImpl.js'; import { CellKind, NotebookSetting } from '../../common/notebookCommon.js'; import { CTX_NOTEBOOK_CHAT_OUTER_FOCUS_POSITION } from './chat/notebookChatContext.js'; +import { INotebookKernelHistoryService } from '../../common/notebookKernelService.js'; const INSERT_CODE_CELL_ABOVE_COMMAND_ID = 'notebook.cell.insertCodeCellAbove'; const INSERT_CODE_CELL_BELOW_COMMAND_ID = 'notebook.cell.insertCodeCellBelow'; @@ -35,13 +36,15 @@ export function insertNewCell(accessor: ServicesAccessor, context: INotebookActi } const languageService = accessor.get(ILanguageService); + const kernelHistoryService = accessor.get(INotebookKernelHistoryService); + if (context.cell) { const idx = context.notebookEditor.getCellIndex(context.cell); - newCell = insertCell(languageService, context.notebookEditor, idx, kind, direction, undefined, true); + newCell = insertCell(languageService, context.notebookEditor, idx, kind, direction, undefined, true, kernelHistoryService); } else { const focusRange = context.notebookEditor.getFocus(); const next = Math.max(focusRange.end - 1, 0); - newCell = insertCell(languageService, context.notebookEditor, next, kind, direction, undefined, true); + newCell = insertCell(languageService, context.notebookEditor, next, kind, direction, undefined, true, kernelHistoryService); } return newCell; @@ -193,7 +196,8 @@ registerAction2(class InsertCodeCellAtTopAction extends NotebookAction { async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise { const languageService = accessor.get(ILanguageService); - const newCell = insertCell(languageService, context.notebookEditor, 0, CellKind.Code, 'above', undefined, true); + const kernelHistoryService = accessor.get(INotebookKernelHistoryService); + const newCell = insertCell(languageService, context.notebookEditor, 0, CellKind.Code, 'above', undefined, true, kernelHistoryService); if (newCell) { await context.notebookEditor.focusNotebookCell(newCell, 'editor'); @@ -220,7 +224,9 @@ registerAction2(class InsertMarkdownCellAtTopAction extends NotebookAction { async runWithContext(accessor: ServicesAccessor, context: INotebookActionContext): Promise { const languageService = accessor.get(ILanguageService); - const newCell = insertCell(languageService, context.notebookEditor, 0, CellKind.Markup, 'above', undefined, true); + const kernelHistoryService = accessor.get(INotebookKernelHistoryService); + + const newCell = insertCell(languageService, context.notebookEditor, 0, CellKind.Markup, 'above', undefined, true, kernelHistoryService); if (newCell) { await context.notebookEditor.focusNotebookCell(newCell, 'editor'); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/layoutActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/layoutActions.ts index df4c6f1fe559..a278485249fd 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/layoutActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/layoutActions.ts @@ -262,11 +262,8 @@ registerAction2(class ToggleNotebookStickyScroll extends Action2 { }, menu: [ { id: MenuId.CommandPalette }, - { - id: MenuId.NotebookStickyScrollContext, - group: 'notebookView', - order: 2 - } + { id: MenuId.NotebookStickyScrollContext, group: 'notebookView', order: 2 }, + { id: MenuId.NotebookToolbarContext, group: 'notebookView', order: 2 } ] }); } diff --git a/src/vs/workbench/contrib/notebook/browser/controller/sectionActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/sectionActions.ts index f51e058a09fa..b532e04ebe69 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/sectionActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/sectionActions.ts @@ -9,20 +9,22 @@ import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contex import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; import { NotebookOutlineContext } from '../contrib/outline/notebookOutline.js'; import { FoldingController } from './foldingController.js'; -import { CellFoldingState, INotebookEditor } from '../notebookBrowser.js'; +import { CellEditState, CellFoldingState, ICellViewModel, INotebookEditor } from '../notebookBrowser.js'; import * as icons from '../notebookIcons.js'; import { OutlineEntry } from '../viewModel/OutlineEntry.js'; import { CellKind } from '../../common/notebookCommon.js'; import { OutlineTarget } from '../../../../services/outline/browser/outline.js'; +import { CELL_TITLE_CELL_GROUP_ID, CellToolbarOrder } from './coreActions.js'; +import { executeSectionCondition } from './executeActions.js'; -export type NotebookSectionArgs = { - notebookEditor: INotebookEditor | undefined; +export type NotebookOutlineEntryArgs = { + notebookEditor: INotebookEditor; outlineEntry: OutlineEntry; }; -export type ValidNotebookSectionArgs = { +export type NotebookCellArgs = { notebookEditor: INotebookEditor; - outlineEntry: OutlineEntry; + cell: ICellViewModel; }; export class NotebookRunSingleCellInSection extends Action2 { @@ -51,8 +53,8 @@ export class NotebookRunSingleCellInSection extends Action2 { }); } - override async run(_accessor: ServicesAccessor, context: NotebookSectionArgs): Promise { - if (!checkSectionContext(context)) { + override async run(_accessor: ServicesAccessor, context: any): Promise { + if (!checkOutlineEntryContext(context)) { return; } @@ -69,7 +71,7 @@ export class NotebookRunCellsInSection extends Action2 { mnemonicTitle: localize({ key: 'mirunCellsInSection', comment: ['&& denotes a mnemonic'] }, "&&Run Cells In Section"), }, shortTitle: localize('runCellsInSection', "Run Cells In Section"), - // icon: icons.executeBelowIcon, // TODO @Yoyokrazy replace this with new icon later + icon: icons.executeIcon, // TODO @Yoyokrazy replace this with new icon later menu: [ { id: MenuId.NotebookStickyScrollContext, @@ -86,27 +88,46 @@ export class NotebookRunCellsInSection extends Action2 { NotebookOutlineContext.CellHasChildren, NotebookOutlineContext.CellHasHeader, ) + }, + { + id: MenuId.NotebookCellTitle, + order: CellToolbarOrder.RunSection, + group: CELL_TITLE_CELL_GROUP_ID, + when: executeSectionCondition } ] }); } - override async run(_accessor: ServicesAccessor, context: NotebookSectionArgs): Promise { - if (!checkSectionContext(context)) { + override async run(_accessor: ServicesAccessor, context: any): Promise { + let cell: ICellViewModel; + if (checkOutlineEntryContext(context)) { + cell = context.outlineEntry.cell; + } else if (checkNotebookCellContext(context)) { + cell = context.cell; + } else { return; } - const cell = context.outlineEntry.cell; - const idx = context.notebookEditor.getViewModel()?.getCellIndex(cell); - if (idx === undefined) { + if (cell.getEditState() === CellEditState.Editing) { + const foldingController = context.notebookEditor.getContribution(FoldingController.id); + foldingController.recompute(); + } + + const cellIdx = context.notebookEditor.getViewModel()?.getCellIndex(cell); + if (cellIdx === undefined) { + return; + } + const sectionIdx = context.notebookEditor.getViewModel()?.getFoldingStartIndex(cellIdx); + if (sectionIdx === undefined) { return; } - const length = context.notebookEditor.getViewModel()?.getFoldedLength(idx); + const length = context.notebookEditor.getViewModel()?.getFoldedLength(sectionIdx); if (length === undefined) { return; } - const cells = context.notebookEditor.getCellsInRange({ start: idx, end: idx + length + 1 }); + const cells = context.notebookEditor.getCellsInRange({ start: sectionIdx, end: sectionIdx + length + 1 }); context.notebookEditor.executeNotebookCells(cells); } } @@ -137,8 +158,8 @@ export class NotebookFoldSection extends Action2 { }); } - override async run(_accessor: ServicesAccessor, context: NotebookSectionArgs): Promise { - if (!checkSectionContext(context)) { + override async run(_accessor: ServicesAccessor, context: any): Promise { + if (!checkOutlineEntryContext(context)) { return; } @@ -181,8 +202,8 @@ export class NotebookExpandSection extends Action2 { }); } - override async run(_accessor: ServicesAccessor, context: NotebookSectionArgs): Promise { - if (!checkSectionContext(context)) { + override async run(_accessor: ServicesAccessor, context: any): Promise { + if (!checkOutlineEntryContext(context)) { return; } @@ -200,15 +221,27 @@ export class NotebookExpandSection extends Action2 { } /** - * Take in context args and check if they exist + * Take in context args and check if they exist. True if action is run from notebook sticky scroll context menu or + * notebook outline context menu. * - * @param context - Notebook Section Context containing a notebook editor and outline entry + * @param context - Notebook Outline Context containing a notebook editor and outline entry * @returns true if context is valid, false otherwise */ -function checkSectionContext(context: NotebookSectionArgs): context is ValidNotebookSectionArgs { +function checkOutlineEntryContext(context: any): context is NotebookOutlineEntryArgs { return !!(context && context.notebookEditor && context.outlineEntry); } +/** + * Take in context args and check if they exist. True if action is run from a cell toolbar menu (potentially from the + * notebook cell container or cell editor context menus, but not tested or implemented atm) + * + * @param context - Notebook Outline Context containing a notebook editor and outline entry + * @returns true if context is valid, false otherwise + */ +function checkNotebookCellContext(context: any): context is NotebookCellArgs { + return !!(context && context.notebookEditor && context.cell); +} + registerAction2(NotebookRunSingleCellInSection); registerAction2(NotebookRunCellsInSection); registerAction2(NotebookFoldSection); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index c7cb5b3ba203..a0a5557ddab7 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -18,8 +18,7 @@ import { IContextMenuService } from '../../../../../platform/contextview/browser import { IMenu, IMenuService, MenuId, MenuItemAction } from '../../../../../platform/actions/common/actions.js'; import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; import { INotificationService } from '../../../../../platform/notification/common/notification.js'; -import { IAction } from '../../../../../base/common/actions.js'; -import { createAndFillInActionBarActions } from '../../../../../platform/actions/browser/menuEntryActionViewItem.js'; +import { getFlatActionBarActions } from '../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IContextKey, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { CodiconActionViewItem } from '../view/cellParts/cellActionView.js'; import { collapsedIcon, expandedIcon } from '../notebookIcons.js'; @@ -201,8 +200,7 @@ class PropertyHeader extends Disposable { private updateMenu() { const metadataChanged = this.accessor.checkIfModified(); if (metadataChanged) { - const actions: IAction[] = []; - createAndFillInActionBarActions(this._menu, { shouldForwardArgs: true }, actions); + const actions = getFlatActionBarActions(this._menu.getActions({ shouldForwardArgs: true })); this._toolbar.setActions(actions); } else { this._toolbar.setActions([]); @@ -385,9 +383,8 @@ export class NotebookDocumentMetadataElement extends Disposable { inputChanged.set(hasChanges); if (hasChanges) { - const actions: IAction[] = []; const menu = this.menuService.getMenuActions(MenuId.NotebookDiffDocumentMetadata, scopedContextKeyService, { shouldForwardArgs: true }); - createAndFillInActionBarActions(menu, actions); + const actions = getFlatActionBarActions(menu); this._toolbar.setActions(actions); } else { this._toolbar.setActions([]); @@ -850,7 +847,7 @@ abstract class AbstractElementRenderer extends Disposable { return; } - const modifiedMetadataSource = getFormattedMetadataJSON(this.notebookEditor.textModel?.transientOptions.transientCellMetadata, this.cell.modified?.metadata || {}, this.cell.modified?.language); + const modifiedMetadataSource = getFormattedMetadataJSON(this.notebookEditor.textModel?.transientOptions.transientCellMetadata, this.cell.modified?.metadata || {}, this.cell.modified?.language, true); modifiedMetadataModel.object.textEditorModel.setValue(modifiedMetadataSource); })); @@ -872,7 +869,7 @@ abstract class AbstractElementRenderer extends Disposable { const originalMetadataSource = getFormattedMetadataJSON(this.notebookEditor.textModel?.transientOptions.transientCellMetadata, this.cell.type === 'insert' ? this.cell.modified!.metadata || {} - : this.cell.original!.metadata || {}); + : this.cell.original!.metadata || {}, undefined, true); const uri = this.cell.type === 'insert' ? this.cell.modified!.uri : this.cell.original!.uri; @@ -1962,9 +1959,8 @@ export class ModifiedElement extends AbstractElementRenderer { inputChanged.set(hasChanges); if (hasChanges) { - const actions: IAction[] = []; const menu = this.menuService.getMenuActions(MenuId.NotebookDiffCellInputTitle, scopedContextKeyService, { shouldForwardArgs: true }); - createAndFillInActionBarActions(menu, actions); + const actions = getFlatActionBarActions(menu); this._toolbar.setActions(actions); } else { this._toolbar.setActions([]); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookCellDiffDecorator.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookCellDiffDecorator.ts new file mode 100644 index 000000000000..8da4d2c230de --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookCellDiffDecorator.ts @@ -0,0 +1,330 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DisposableStore, toDisposable } from '../../../../../../base/common/lifecycle.js'; +import { autorunWithStore, derived, observableFromEvent } from '../../../../../../base/common/observable.js'; +import { INotebookEditor } from '../../notebookBrowser.js'; +import { ThrottledDelayer } from '../../../../../../base/common/async.js'; +import { ICodeEditor, IViewZone } from '../../../../../../editor/browser/editorBrowser.js'; +import { IEditorWorkerService } from '../../../../../../editor/common/services/editorWorker.js'; +import { EditorOption } from '../../../../../../editor/common/config/editorOptions.js'; +import { themeColorFromId } from '../../../../../../base/common/themables.js'; +import { RenderOptions, LineSource, renderLines } from '../../../../../../editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; +import { diffAddDecoration, diffWholeLineAddDecoration, diffDeleteDecoration } from '../../../../../../editor/browser/widget/diffEditor/registrations.contribution.js'; +import { IDocumentDiff } from '../../../../../../editor/common/diff/documentDiffProvider.js'; +import { ITextModel, TrackedRangeStickiness, MinimapPosition, IModelDeltaDecoration, OverviewRulerLane } from '../../../../../../editor/common/model.js'; +import { ModelDecorationOptions } from '../../../../../../editor/common/model/textModel.js'; +import { InlineDecoration, InlineDecorationType } from '../../../../../../editor/common/viewModel.js'; +import { Range } from '../../../../../../editor/common/core/range.js'; +import { NotebookCellTextModel } from '../../../common/model/notebookCellTextModel.js'; +import { DetailedLineRangeMapping } from '../../../../../../editor/common/diff/rangeMapping.js'; +import { minimapGutterAddedBackground, minimapGutterDeletedBackground, minimapGutterModifiedBackground, overviewRulerAddedForeground, overviewRulerDeletedForeground, overviewRulerModifiedForeground } from '../../../../scm/common/quickDiff.js'; +import { INotebookOriginalCellModelFactory } from './notebookOriginalCellModelFactory.js'; + +//TODO: allow client to set read-only - chateditsession should set read-only while making changes +export class NotebookCellDiffDecorator extends DisposableStore { + private _viewZones: string[] = []; + private readonly throttledDecorator = new ThrottledDelayer(50); + private diffForPreviouslyAppliedDecorators?: IDocumentDiff; + + private readonly perEditorDisposables = this.add(new DisposableStore()); + constructor( + notebookEditor: INotebookEditor, + public readonly modifiedCell: NotebookCellTextModel, + public readonly originalCell: NotebookCellTextModel, + private readonly editor: ICodeEditor, + @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, + @INotebookOriginalCellModelFactory private readonly originalCellModelFactory: INotebookOriginalCellModelFactory, + + ) { + super(); + + const onDidChangeVisibleRanges = observableFromEvent(notebookEditor.onDidChangeVisibleRanges, () => notebookEditor.visibleRanges); + const editorObs = derived((r) => { + const visibleRanges = onDidChangeVisibleRanges.read(r); + const visibleCellHandles = visibleRanges.map(range => notebookEditor.getCellsInRange(range)).flat().map(c => c.handle); + if (!visibleCellHandles.includes(modifiedCell.handle)) { + return; + } + const editor = notebookEditor.codeEditors.find(item => item[0].handle === modifiedCell.handle)?.[1]; + if (editor?.getModel() !== this.modifiedCell.textModel) { + return; + } + return editor; + }); + + this.add(autorunWithStore((r, store) => { + const editor = editorObs.read(r); + this.perEditorDisposables.clear(); + + if (editor) { + store.add(editor.onDidChangeModel(() => { + this.perEditorDisposables.clear(); + })); + store.add(editor.onDidChangeModelContent(() => { + this.update(editor); + })); + store.add(editor.onDidChangeConfiguration((e) => { + if (e.hasChanged(EditorOption.fontInfo) || e.hasChanged(EditorOption.lineHeight)) { + this.update(editor); + } + })); + this.update(editor); + } + })); + } + + public update(editor: ICodeEditor): void { + this.throttledDecorator.trigger(() => this._updateImpl(editor)); + } + + private async _updateImpl(editor: ICodeEditor) { + if (this.isDisposed) { + return; + } + if (editor.getOption(EditorOption.inDiffEditor)) { + this.perEditorDisposables.clear(); + return; + } + const model = editor.getModel(); + if (!model || model !== this.modifiedCell.textModel) { + this.perEditorDisposables.clear(); + return; + } + + const originalModel = this.getOrCreateOriginalModel(editor); + if (!originalModel) { + this.perEditorDisposables.clear(); + return; + } + const version = model.getVersionId(); + const diff = await this._editorWorkerService.computeDiff( + originalModel.uri, + model.uri, + { computeMoves: true, ignoreTrimWhitespace: false, maxComputationTimeMs: Number.MAX_SAFE_INTEGER }, + 'advanced' + ); + + + if (this.isDisposed) { + return; + } + + + if (diff && !diff.identical && this.modifiedCell.textModel && originalModel && model === editor.getModel() && editor.getModel()?.getVersionId() === version) { + this._updateWithDiff(editor, originalModel, diff, this.modifiedCell.textModel); + } else { + this.perEditorDisposables.clear(); + } + } + + private _originalModel?: ITextModel; + private getOrCreateOriginalModel(editor: ICodeEditor) { + if (!this._originalModel) { + const model = editor.getModel(); + if (!model) { + return; + } + this._originalModel = this.add(this.originalCellModelFactory.getOrCreate(model.uri, this.originalCell.getValue(), model.getLanguageId(), this.modifiedCell.cellKind)).object; + } + return this._originalModel; + } + + private _updateWithDiff(editor: ICodeEditor, originalModel: ITextModel, diff: IDocumentDiff, currentModel: ITextModel): void { + if (areDiffsEqual(diff, this.diffForPreviouslyAppliedDecorators)) { + return; + } + this.perEditorDisposables.clear(); + const decorations = editor.createDecorationsCollection(); + this.perEditorDisposables.add(toDisposable(() => { + editor.changeViewZones((viewZoneChangeAccessor) => { + for (const id of this._viewZones) { + viewZoneChangeAccessor.removeZone(id); + } + }); + this._viewZones = []; + decorations.clear(); + this.diffForPreviouslyAppliedDecorators = undefined; + })); + + this.diffForPreviouslyAppliedDecorators = diff; + + const chatDiffAddDecoration = ModelDecorationOptions.createDynamic({ + ...diffAddDecoration, + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges + }); + const chatDiffWholeLineAddDecoration = ModelDecorationOptions.createDynamic({ + ...diffWholeLineAddDecoration, + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + }); + const createOverviewDecoration = (overviewRulerColor: string, minimapColor: string) => { + return ModelDecorationOptions.createDynamic({ + description: 'chat-editing-decoration', + overviewRuler: { color: themeColorFromId(overviewRulerColor), position: OverviewRulerLane.Left }, + minimap: { color: themeColorFromId(minimapColor), position: MinimapPosition.Gutter }, + }); + }; + const modifiedDecoration = createOverviewDecoration(overviewRulerModifiedForeground, minimapGutterModifiedBackground); + const addedDecoration = createOverviewDecoration(overviewRulerAddedForeground, minimapGutterAddedBackground); + const deletedDecoration = createOverviewDecoration(overviewRulerDeletedForeground, minimapGutterDeletedBackground); + + editor.changeViewZones((viewZoneChangeAccessor) => { + for (const id of this._viewZones) { + viewZoneChangeAccessor.removeZone(id); + } + this._viewZones = []; + const modifiedVisualDecorations: IModelDeltaDecoration[] = []; + const mightContainNonBasicASCII = originalModel.mightContainNonBasicASCII(); + const mightContainRTL = originalModel.mightContainRTL(); + const renderOptions = RenderOptions.fromEditor(this.editor); + const editorLineCount = currentModel.getLineCount(); + for (const diffEntry of diff.changes) { + + const originalRange = diffEntry.original; + originalModel.tokenization.forceTokenization(Math.max(1, originalRange.endLineNumberExclusive - 1)); + const source = new LineSource( + originalRange.mapToLineArray(l => originalModel.tokenization.getLineTokens(l)), + [], + mightContainNonBasicASCII, + mightContainRTL, + ); + const decorations: InlineDecoration[] = []; + + for (const i of diffEntry.innerChanges || []) { + decorations.push(new InlineDecoration( + i.originalRange.delta(-(diffEntry.original.startLineNumber - 1)), + diffDeleteDecoration.className!, + InlineDecorationType.Regular + )); + + // If the original range is empty, the start line number is 1 and the new range spans the entire file, don't draw an Added decoration + if (!(i.originalRange.isEmpty() && i.originalRange.startLineNumber === 1 && i.modifiedRange.endLineNumber === editorLineCount) && !i.modifiedRange.isEmpty()) { + modifiedVisualDecorations.push({ + range: i.modifiedRange, options: chatDiffAddDecoration + }); + } + } + + // Render an added decoration but don't also render a deleted decoration for newly inserted content at the start of the file + // Note, this is a workaround for the `LineRange.isEmpty()` in diffEntry.original being `false` for newly inserted content + const isCreatedContent = decorations.length === 1 && decorations[0].range.isEmpty() && diffEntry.original.startLineNumber === 1; + + if (!diffEntry.modified.isEmpty && !(isCreatedContent && (diffEntry.modified.endLineNumberExclusive - 1) === editorLineCount)) { + modifiedVisualDecorations.push({ + range: diffEntry.modified.toInclusiveRange()!, + options: chatDiffWholeLineAddDecoration + }); + } + + if (diffEntry.original.isEmpty) { + // insertion + modifiedVisualDecorations.push({ + range: diffEntry.modified.toInclusiveRange()!, + options: addedDecoration + }); + } else if (diffEntry.modified.isEmpty) { + // deletion + modifiedVisualDecorations.push({ + range: new Range(diffEntry.modified.startLineNumber - 1, 1, diffEntry.modified.startLineNumber, 1), + options: deletedDecoration + }); + } else { + // modification + modifiedVisualDecorations.push({ + range: diffEntry.modified.toInclusiveRange()!, + options: modifiedDecoration + }); + } + + const domNode = document.createElement('div'); + domNode.className = 'chat-editing-original-zone view-lines line-delete monaco-mouse-cursor-text'; + const result = renderLines(source, renderOptions, decorations, domNode); + + if (!isCreatedContent) { + + const viewZoneData: IViewZone = { + afterLineNumber: diffEntry.modified.startLineNumber - 1, + heightInLines: result.heightInLines, + domNode, + ordinal: 50000 + 2 // more than https://github.com/microsoft/vscode/blob/bf52a5cfb2c75a7327c9adeaefbddc06d529dcad/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts#L42 + }; + + this._viewZones.push(viewZoneChangeAccessor.addZone(viewZoneData)); + } + } + + decorations.set(modifiedVisualDecorations); + }); + } +} + +function areDiffsEqual(a: IDocumentDiff | undefined, b: IDocumentDiff | undefined): boolean { + if (a && b) { + if (a.changes.length !== b.changes.length) { + return false; + } + if (a.moves.length !== b.moves.length) { + return false; + } + if (!areLineRangeMappinsEqual(a.changes, b.changes)) { + return false; + } + if (!a.moves.some((move, i) => { + const bMove = b.moves[i]; + if (!areLineRangeMappinsEqual(move.changes, bMove.changes)) { + return true; + } + if (move.lineRangeMapping.changedLineCount !== bMove.lineRangeMapping.changedLineCount) { + return true; + } + if (!move.lineRangeMapping.modified.equals(bMove.lineRangeMapping.modified)) { + return true; + } + if (!move.lineRangeMapping.original.equals(bMove.lineRangeMapping.original)) { + return true; + } + return false; + })) { + return false; + } + return true; + } else if (!a && !b) { + return true; + } else { + return false; + } +} + +function areLineRangeMappinsEqual(a: readonly DetailedLineRangeMapping[], b: readonly DetailedLineRangeMapping[]): boolean { + if (a.length !== b.length) { + return false; + } + if (a.some((c, i) => { + const bChange = b[i]; + if (c.changedLineCount !== bChange.changedLineCount) { + return true; + } + if ((c.innerChanges || []).length !== (bChange.innerChanges || []).length) { + return true; + } + if ((c.innerChanges || []).some((innerC, innerIdx) => { + const bInnerC = bChange.innerChanges![innerIdx]; + if (!innerC.modifiedRange.equalsRange(bInnerC.modifiedRange)) { + return true; + } + if (!innerC.originalRange.equalsRange(bInnerC.originalRange)) { + return true; + } + return false; + })) { + return true; + } + + return false; + })) { + return false; + } + return true; +} diff --git a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookDeletedCellDecorator.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookDeletedCellDecorator.ts new file mode 100644 index 000000000000..b75579cab137 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookDeletedCellDecorator.ts @@ -0,0 +1,258 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createTrustedTypesPolicy } from '../../../../../../base/browser/trustedTypes.js'; +import { Disposable, DisposableStore, dispose, toDisposable } from '../../../../../../base/common/lifecycle.js'; +import { splitLines } from '../../../../../../base/common/strings.js'; +import { EditorOption } from '../../../../../../editor/common/config/editorOptions.js'; +import { ILanguageService } from '../../../../../../editor/common/languages/language.js'; +import { tokenizeToString } from '../../../../../../editor/common/languages/textToHtmlTokenizer.js'; +import { NotebookCellTextModel } from '../../../common/model/notebookCellTextModel.js'; +import { NotebookTextModel } from '../../../common/model/notebookTextModel.js'; +import { DefaultLineHeight } from '../diffElementViewModel.js'; +import { CellDiffInfo } from '../notebookDiffViewModel.js'; +import { INotebookEditor } from '../../notebookBrowser.js'; +import * as DOM from '../../../../../../base/browser/dom.js'; +import { MenuWorkbenchToolBar, HiddenItemStrategy } from '../../../../../../platform/actions/browser/toolbar.js'; +import { MenuId } from '../../../../../../platform/actions/common/actions.js'; +import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; +import { ServiceCollection } from '../../../../../../platform/instantiation/common/serviceCollection.js'; +import { IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; + +const ttPolicy = createTrustedTypesPolicy('notebookRenderer', { createHTML: value => value }); + +export interface INotebookDeletedCellDecorator { + getTop(deletedIndex: number): number | undefined; +} + + +export class NotebookDeletedCellDecorator extends Disposable implements INotebookDeletedCellDecorator { + private readonly zoneRemover = this._register(new DisposableStore()); + private readonly createdViewZones = new Map(); + private readonly deletedCellInfos = new Map(); + constructor( + private readonly _notebookEditor: INotebookEditor, + private readonly toolbar: { menuId: MenuId; className: string; telemetrySource?: string; argFactory: (deletedCellIndex: number) => any } | undefined, + @ILanguageService private readonly languageService: ILanguageService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + ) { + super(); + } + + public getTop(deletedIndex: number) { + const info = this.deletedCellInfos.get(deletedIndex); + if (!info) { + return; + } + if (info.previousIndex === -1) { + // deleted cell is before the first real cell + return 0; + } + const cells = this._notebookEditor.getCellsInRange({ start: info.previousIndex, end: info.previousIndex + 1 }); + if (!cells.length) { + return this._notebookEditor.getLayoutInfo().height + info.offset; + } + const cell = cells[0]; + const cellHeight = this._notebookEditor.getHeightOfElement(cell); + const top = this._notebookEditor.getAbsoluteTopOfElement(cell); + return top + cellHeight + info.offset; + } + + reveal(deletedIndex: number) { + const top = this.getTop(deletedIndex); + if (typeof top === 'number') { + this._notebookEditor.focusContainer(); + this._notebookEditor.revealOffsetInCenterIfOutsideViewport(top); + + const info = this.deletedCellInfos.get(deletedIndex); + + if (info) { + const prevIndex = info.previousIndex === -1 ? 0 : info.previousIndex; + this._notebookEditor.setFocus({ start: prevIndex, end: prevIndex }); + this._notebookEditor.setSelections([{ start: prevIndex, end: prevIndex }]); + } + } + } + + public apply(diffInfo: CellDiffInfo[], original: NotebookTextModel): void { + this.clear(); + + let currentIndex = -1; + const deletedCellsToRender: { cells: { cell: NotebookCellTextModel; originalIndex: number; previousIndex: number }[]; index: number } = { cells: [], index: 0 }; + diffInfo.forEach(diff => { + if (diff.type === 'delete') { + const deletedCell = original.cells[diff.originalCellIndex]; + if (deletedCell) { + deletedCellsToRender.cells.push({ cell: deletedCell, originalIndex: diff.originalCellIndex, previousIndex: currentIndex }); + deletedCellsToRender.index = currentIndex; + } + } else { + if (deletedCellsToRender.cells.length) { + this._createWidget(deletedCellsToRender.index + 1, deletedCellsToRender.cells); + deletedCellsToRender.cells.length = 0; + } + currentIndex = diff.modifiedCellIndex; + } + }); + if (deletedCellsToRender.cells.length) { + this._createWidget(deletedCellsToRender.index + 1, deletedCellsToRender.cells); + } + } + + public clear() { + this.deletedCellInfos.clear(); + this.zoneRemover.clear(); + } + + + private _createWidget(index: number, cells: { cell: NotebookCellTextModel; originalIndex: number; previousIndex: number }[]) { + this._createWidgetImpl(index, cells); + } + private async _createWidgetImpl(index: number, cells: { cell: NotebookCellTextModel; originalIndex: number; previousIndex: number }[]) { + const rootContainer = document.createElement('div'); + const widgets: NotebookDeletedCellWidget[] = []; + const heights = await Promise.all(cells.map(async cell => { + const widget = new NotebookDeletedCellWidget(this._notebookEditor, this.toolbar, cell.cell.getValue(), cell.cell.language, rootContainer, cell.originalIndex, this.languageService, this.instantiationService); + widgets.push(widget); + const height = await widget.render(); + this.deletedCellInfos.set(cell.originalIndex, { height, previousIndex: cell.previousIndex, offset: 0 }); + return height; + })); + + Array.from(this.deletedCellInfos.keys()).sort((a, b) => a - b).forEach((originalIndex) => { + const previousDeletedCell = this.deletedCellInfos.get(originalIndex - 1); + if (previousDeletedCell) { + const deletedCell = this.deletedCellInfos.get(originalIndex); + if (deletedCell) { + deletedCell.offset = previousDeletedCell.height + previousDeletedCell.offset; + } + } + }); + + const totalHeight = heights.reduce((prev, curr) => prev + curr, 0); + + this._notebookEditor.changeViewZones(accessor => { + const notebookViewZone = { + afterModelPosition: index, + heightInPx: totalHeight + 4, + domNode: rootContainer + }; + + const id = accessor.addZone(notebookViewZone); + accessor.layoutZone(id); + this.createdViewZones.set(index, id); + this.zoneRemover.add(toDisposable(() => { + if (this.createdViewZones.get(index) === id) { + this.createdViewZones.delete(index); + } + if (!this._notebookEditor.isDisposed) { + this._notebookEditor.changeViewZones(accessor => { + accessor.removeZone(id); + dispose(widgets); + }); + } + })); + }); + } + +} + +export class NotebookDeletedCellWidget extends Disposable { + private readonly container: HTMLElement; + // private readonly toolbar: HTMLElement; + + constructor( + private readonly _notebookEditor: INotebookEditor, + private readonly _toolbarOptions: { menuId: MenuId; className: string; telemetrySource?: string; argFactory: (deletedCellIndex: number) => any } | undefined, + private readonly code: string, + private readonly language: string, + container: HTMLElement, + private readonly _originalIndex: number, + @ILanguageService private readonly languageService: ILanguageService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + ) { + super(); + this.container = DOM.append(container, document.createElement('div')); + this._register(toDisposable(() => { + container.removeChild(this.container); + })); + } + + public async render() { + const code = this.code; + const languageId = this.language; + const codeHtml = await tokenizeToString(this.languageService, code, languageId); + + // const colorMap = this.getDefaultColorMap(); + const fontInfo = this._notebookEditor.getBaseCellEditorOptions(languageId).value; + const fontFamilyVar = '--notebook-editor-font-family'; + const fontSizeVar = '--notebook-editor-font-size'; + const fontWeightVar = '--notebook-editor-font-weight'; + // If we have any editors, then use left layout of one of those. + const editor = this._notebookEditor.codeEditors.map(c => c[1]).find(c => c); + const layoutInfo = editor?.getOptions().get(EditorOption.layoutInfo); + + const style = `` + + `font-family: var(${fontFamilyVar});` + + `font-weight: var(${fontWeightVar});` + + `font-size: var(${fontSizeVar});` + + fontInfo.lineHeight ? `line-height: ${fontInfo.lineHeight}px;` : '' + + layoutInfo?.contentLeft ? `margin-left: ${layoutInfo}px;` : '' + + `white-space: pre;`; + + const rootContainer = this.container; + rootContainer.classList.add('code-cell-row'); + + if (this._toolbarOptions) { + const toolbar = document.createElement('div'); + toolbar.className = this._toolbarOptions?.className; + rootContainer.appendChild(toolbar); + + const scopedInstaService = this._register(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this._notebookEditor.scopedContextKeyService]))); + const toolbarWidget = scopedInstaService.createInstance(MenuWorkbenchToolBar, toolbar, this._toolbarOptions.menuId, { + telemetrySource: this._toolbarOptions.telemetrySource, + hiddenItemStrategy: HiddenItemStrategy.NoHide, + toolbarOptions: { primaryGroup: () => true }, + menuOptions: { + renderShortTitle: true, + arg: this._toolbarOptions.argFactory(this._originalIndex), + }, + }); + + this._store.add(toolbarWidget); + + toolbar.style.position = 'absolute'; + toolbar.style.right = '40px'; + toolbar.style.zIndex = '10'; + toolbar.classList.add('hover'); // Show by default + } + + const container = DOM.append(rootContainer, DOM.$('.cell-inner-container')); + container.style.position = 'relative'; // Add this line + + const focusIndicatorLeft = DOM.append(container, DOM.$('.cell-focus-indicator.cell-focus-indicator-side.cell-focus-indicator-left')); + const cellContainer = DOM.append(container, DOM.$('.cell.code')); + DOM.append(focusIndicatorLeft, DOM.$('div.execution-count-label')); + const editorPart = DOM.append(cellContainer, DOM.$('.cell-editor-part')); + let editorContainer = DOM.append(editorPart, DOM.$('.cell-editor-container')); + editorContainer = DOM.append(editorContainer, DOM.$('.code', { style })); + if (fontInfo.fontFamily) { + editorContainer.style.setProperty(fontFamilyVar, fontInfo.fontFamily); + } + if (fontInfo.fontSize) { + editorContainer.style.setProperty(fontSizeVar, `${fontInfo.fontSize}px`); + } + if (fontInfo.fontWeight) { + editorContainer.style.setProperty(fontWeightVar, fontInfo.fontWeight); + } + editorContainer.innerHTML = (ttPolicy?.createHTML(codeHtml) || codeHtml) as string; + + const lineCount = splitLines(code).length; + const height = (lineCount * (fontInfo.lineHeight || DefaultLineHeight)) + 12 + 12; // We have 12px top and bottom in generated code HTML; + const totalHeight = height + 16 + 16; + + return totalHeight; + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookInlineDiff.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookInlineDiff.ts new file mode 100644 index 000000000000..c0973dd6f860 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookInlineDiff.ts @@ -0,0 +1,170 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from '../../../../../../base/common/event.js'; +import { Disposable, IDisposable } from '../../../../../../base/common/lifecycle.js'; +import { autorun } from '../../../../../../base/common/observable.js'; +import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; +import { NotebookCellTextModel } from '../../../common/model/notebookCellTextModel.js'; +import { NotebookTextModel } from '../../../common/model/notebookTextModel.js'; +import { INotebookEditorWorkerService } from '../../../common/services/notebookWorkerService.js'; +import { CellDiffInfo } from '../notebookDiffViewModel.js'; +import { INotebookEditorContribution, INotebookEditor } from '../../notebookBrowser.js'; +import { registerNotebookContribution } from '../../notebookEditorExtensions.js'; +import { NotebookCellDiffDecorator } from './notebookCellDiffDecorator.js'; +import { NotebookDeletedCellDecorator } from './notebookDeletedCellDecorator.js'; +import { NotebookInsertedCellDecorator } from './notebookInsertedCellDecorator.js'; +import { INotebookLoggingService } from '../../../common/notebookLoggingService.js'; +import { computeDiff } from '../../../common/notebookDiff.js'; +import { InstantiationType, registerSingleton } from '../../../../../../platform/instantiation/common/extensions.js'; +import { INotebookOriginalModelReferenceFactory, NotebookOriginalModelReferenceFactory } from './notebookOriginalModelRefFactory.js'; +import { INotebookOriginalCellModelFactory, OriginalNotebookCellModelFactory } from './notebookOriginalCellModelFactory.js'; + +export class NotebookInlineDiffDecorationContribution extends Disposable implements INotebookEditorContribution { + static ID: string = 'workbench.notebook.inlineDiffDecoration'; + + private previous?: NotebookTextModel; + private insertedCellDecorator: NotebookInsertedCellDecorator | undefined; + private deletedCellDecorator: NotebookDeletedCellDecorator | undefined; + private readonly cellDecorators = new Map(); + private cachedNotebookDiff?: { cellDiffInfo: CellDiffInfo[]; originalVersion: number; version: number }; + private listeners: IDisposable[] = []; + + constructor( + private readonly notebookEditor: INotebookEditor, + @INotebookEditorWorkerService private readonly notebookEditorWorkerService: INotebookEditorWorkerService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @INotebookLoggingService private readonly logService: INotebookLoggingService + ) { + super(); + this.logService.debug('inlineDiff', 'Watching for previous model'); + + this._register(autorun((reader) => { + this.previous = this.notebookEditor.notebookOptions.previousModelToCompare.read(reader); + if (this.previous) { + this.logService.debug('inlineDiff', 'Previous model set'); + if (this.notebookEditor.hasModel()) { + this.initialize(); + } else { + this.logService.debug('inlineDiff', 'Waiting for model to attach'); + this.listeners.push(Event.once(this.notebookEditor.onDidAttachViewModel)(() => this.initialize())); + } + } + })); + } + + private clear() { + + this.listeners.forEach(l => l.dispose()); + this.cellDecorators.forEach((v, cell) => { + v.dispose(); + this.cellDecorators.delete(cell); + }); + this.insertedCellDecorator?.dispose(); + this.deletedCellDecorator?.dispose(); + this.cachedNotebookDiff = undefined; + this.listeners = []; + this.logService.debug('inlineDiff', 'Cleared decorations and listeners'); + } + + override dispose() { + this.logService.debug('inlineDiff', 'Disposing'); + this.clear(); + super.dispose(); + } + + private initialize() { + this.clear(); + + if (!this.previous) { + return; + } + + this.insertedCellDecorator = this.instantiationService.createInstance(NotebookInsertedCellDecorator, this.notebookEditor); + this.deletedCellDecorator = this.instantiationService.createInstance(NotebookDeletedCellDecorator, this.notebookEditor, undefined); + + this._update(); + const onVisibleChange = Event.debounce(this.notebookEditor.onDidChangeVisibleRanges, (e) => e, 100, undefined, undefined, undefined, this._store); + this.listeners.push(onVisibleChange(() => this._update())); + this.listeners.push(this.notebookEditor.onDidChangeModel(() => this._update())); + if (this.notebookEditor.textModel) { + const onContentChange = Event.debounce(this.notebookEditor.textModel!.onDidChangeContent, (_, event) => event, 100, undefined, undefined, undefined, this._store); + const onOriginalContentChange = Event.debounce(this.previous.onDidChangeContent, (_, event) => event, 100, undefined, undefined, undefined, this._store); + this.listeners.push(onContentChange(() => this._update())); + this.listeners.push(onOriginalContentChange(() => this._update())); + } + this.logService.debug('inlineDiff', 'Initialized'); + } + + private async _update() { + const current = this.notebookEditor.getViewModel()?.notebookDocument; + if (!this.previous || !current) { + this.logService.debug('inlineDiff', 'Update skipped - no original or current document'); + return; + } + + if (!this.cachedNotebookDiff || + this.cachedNotebookDiff.originalVersion !== this.previous.versionId || + this.cachedNotebookDiff.version !== current.versionId) { + + let diffInfo: { cellDiffInfo: CellDiffInfo[] } = { cellDiffInfo: [] }; + try { + const notebookDiff = await this.notebookEditorWorkerService.computeDiff(this.previous.uri, current.uri); + diffInfo = computeDiff(this.previous, current, notebookDiff); + } catch (e) { + this.logService.error('inlineDiff', 'Error computing diff:\n' + e); + return; + } + + this.cachedNotebookDiff = { cellDiffInfo: diffInfo.cellDiffInfo, originalVersion: this.previous.versionId, version: current.versionId }; + + this.insertedCellDecorator?.apply(diffInfo.cellDiffInfo); + this.deletedCellDecorator?.apply(diffInfo.cellDiffInfo, this.previous); + } + + await this.updateCells(this.previous, current, this.cachedNotebookDiff.cellDiffInfo); + } + + private async updateCells(original: NotebookTextModel, modified: NotebookTextModel, cellDiffs: CellDiffInfo[]) { + const validDiffDecorators = new Set(); + cellDiffs.forEach((diff) => { + if (diff.type === 'modified') { + const modifiedCell = modified.cells[diff.modifiedCellIndex]; + const originalCell = original.cells[diff.originalCellIndex]; + const editor = this.notebookEditor.codeEditors.find(([vm,]) => vm.handle === modifiedCell.handle)?.[1]; + + if (editor) { + const currentDecorator = this.cellDecorators.get(modifiedCell); + if ((currentDecorator?.modifiedCell !== modifiedCell || currentDecorator?.originalCell !== originalCell)) { + currentDecorator?.dispose(); + const decorator = this.instantiationService.createInstance(NotebookCellDiffDecorator, this.notebookEditor, modifiedCell, originalCell, editor); + this.cellDecorators.set(modifiedCell, decorator); + validDiffDecorators.add(decorator); + this._register(editor.onDidDispose(() => { + decorator.dispose(); + if (this.cellDecorators.get(modifiedCell) === decorator) { + this.cellDecorators.delete(modifiedCell); + } + })); + } else if (currentDecorator) { + validDiffDecorators.add(currentDecorator); + } + } + } + }); + + // Dispose old decorators + this.cellDecorators.forEach((v, cell) => { + if (!validDiffDecorators.has(v)) { + v.dispose(); + this.cellDecorators.delete(cell); + } + }); + } +} + +registerNotebookContribution(NotebookInlineDiffDecorationContribution.ID, NotebookInlineDiffDecorationContribution); +registerSingleton(INotebookOriginalModelReferenceFactory, NotebookOriginalModelReferenceFactory, InstantiationType.Delayed); +registerSingleton(INotebookOriginalCellModelFactory, OriginalNotebookCellModelFactory, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookInlineDiffWidget.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookInlineDiffWidget.ts new file mode 100644 index 000000000000..01dc06e7e40d --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookInlineDiffWidget.ts @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as DOM from '../../../../../../base/browser/dom.js'; +import { CodeWindow } from '../../../../../../base/browser/window.js'; +import { Disposable } from '../../../../../../base/common/lifecycle.js'; +import { EditorExtensionsRegistry } from '../../../../../../editor/browser/editorExtensions.js'; +import { MenuId } from '../../../../../../platform/actions/common/actions.js'; +import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; +import { NotebookTextModel } from '../../../common/model/notebookTextModel.js'; +import { NotebookDiffEditorInput } from '../../../common/notebookDiffEditorInput.js'; +import { NotebookInlineDiffDecorationContribution } from './notebookInlineDiff.js'; +import { INotebookEditorOptions } from '../../notebookBrowser.js'; +import { NotebookEditorExtensionsRegistry } from '../../notebookEditorExtensions.js'; +import { NotebookEditorWidget } from '../../notebookEditorWidget.js'; +import { NotebookOptions } from '../../notebookOptions.js'; +import { IBorrowValue, INotebookEditorService } from '../../services/notebookEditorService.js'; + +export class NotebookInlineDiffWidget extends Disposable { + + private widget: IBorrowValue = { value: undefined }; + private position: DOM.IDomPosition | undefined; + + get editorWidget() { + return this.widget.value; + } + + constructor( + private readonly rootElement: HTMLElement, + private readonly groupId: number, + private readonly window: CodeWindow, + private readonly options: NotebookOptions, + private dimension: DOM.Dimension | undefined, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @INotebookEditorService private readonly widgetService: INotebookEditorService) { + super(); + } + + async show(input: NotebookDiffEditorInput, model: NotebookTextModel | undefined, previousModel: NotebookTextModel | undefined, options: INotebookEditorOptions | undefined) { + if (!this.widget.value) { + this.createNotebookWidget(input, this.groupId, this.rootElement); + } + + if (this.dimension) { + this.widget.value?.layout(this.dimension, this.rootElement, this.position); + } + + if (model) { + await this.widget.value?.setOptions({ ...options }); + this.widget.value?.notebookOptions.previousModelToCompare.set(previousModel, undefined); + + await this.widget.value!.setModel(model, options?.viewState); + } + } + + hide() { + if (this.widget.value) { + this.widget.value.notebookOptions.previousModelToCompare.set(undefined, undefined); + this.widget.value.onWillHide(); + } + } + + setLayout(dimension: DOM.Dimension, position: DOM.IDomPosition) { + this.dimension = dimension; + this.position = position; + } + + private createNotebookWidget(input: NotebookDiffEditorInput, groupId: number, rootElement: HTMLElement | undefined) { + const contributions = NotebookEditorExtensionsRegistry.getSomeEditorContributions([NotebookInlineDiffDecorationContribution.ID]); + const menuIds = { + notebookToolbar: MenuId.NotebookToolbar, + cellTitleToolbar: MenuId.NotebookCellTitle, + cellDeleteToolbar: MenuId.NotebookCellDelete, + cellInsertToolbar: MenuId.NotebookCellBetween, + cellTopInsertToolbar: MenuId.NotebookCellListTop, + cellExecuteToolbar: MenuId.NotebookCellExecute, + cellExecutePrimary: undefined, + }; + const skipContributions = [ + 'editor.contrib.review', + 'editor.contrib.floatingClickMenu', + 'editor.contrib.dirtydiff', + 'editor.contrib.testingOutputPeek', + 'editor.contrib.testingDecorations', + 'store.contrib.stickyScrollController', + 'editor.contrib.findController', + 'editor.contrib.emptyTextEditorHint', + ]; + const cellEditorContributions = EditorExtensionsRegistry.getEditorContributions().filter(c => skipContributions.indexOf(c.id) === -1); + + this.widget = >this.instantiationService.invokeFunction(this.widgetService.retrieveWidget, + groupId, input, { contributions, menuIds, cellEditorContributions, options: this.options }, this.dimension, this.window); + if (this.rootElement && this.widget.value!.getDomNode()) { + this.rootElement.setAttribute('aria-flowto', this.widget.value!.getDomNode().id || ''); + DOM.setParentFlowTo(this.widget.value!.getDomNode(), this.rootElement); + } + } + + override dispose(): void { + super.dispose(); + if (this.widget.value) { + this.widget.value.dispose(); + } + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookInsertedCellDecorator.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookInsertedCellDecorator.ts new file mode 100644 index 000000000000..996497312f34 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookInsertedCellDecorator.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, DisposableStore, toDisposable } from '../../../../../../base/common/lifecycle.js'; +import { CellDiffInfo } from '../notebookDiffViewModel.js'; +import { INotebookEditor } from '../../notebookBrowser.js'; + +export class NotebookInsertedCellDecorator extends Disposable { + private readonly decorators = this._register(new DisposableStore()); + constructor( + private readonly notebookEditor: INotebookEditor, + ) { + super(); + + } + public apply(diffInfo: CellDiffInfo[]) { + const model = this.notebookEditor.textModel; + if (!model) { + return; + } + const cells = diffInfo.filter(diff => diff.type === 'insert').map((diff) => model.cells[diff.modifiedCellIndex]); + const ids = this.notebookEditor.deltaCellDecorations([], cells.map(cell => ({ + handle: cell.handle, + options: { className: 'nb-insertHighlight', outputClassName: 'nb-insertHighlight' } + }))); + this.clear(); + this.decorators.add(toDisposable(() => { + if (!this.notebookEditor.isDisposed) { + this.notebookEditor.deltaCellDecorations(ids, []); + } + })); + } + public clear() { + this.decorators.clear(); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookModifiedCellDecorator.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookModifiedCellDecorator.ts new file mode 100644 index 000000000000..f2993cbb42eb --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookModifiedCellDecorator.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, DisposableStore, toDisposable } from '../../../../../../base/common/lifecycle.js'; +import { CellDiffInfo } from '../notebookDiffViewModel.js'; +import { INotebookEditor } from '../../notebookBrowser.js'; +import { CellKind } from '../../../common/notebookCommon.js'; +import { NotebookCellTextModel } from '../../../common/model/notebookCellTextModel.js'; + +export class NotebookModifiedCellDecorator extends Disposable { + private readonly decorators = this._register(new DisposableStore()); + constructor( + private readonly notebookEditor: INotebookEditor, + ) { + super(); + } + + public apply(diffInfo: CellDiffInfo[]) { + const model = this.notebookEditor.textModel; + if (!model) { + return; + } + + const modifiedMarkdownCells: NotebookCellTextModel[] = []; + for (const diff of diffInfo) { + if (diff.type === 'modified') { + const cell = model.cells[diff.modifiedCellIndex]; + if (cell.cellKind === CellKind.Markup) { + modifiedMarkdownCells.push(cell); + } + } + } + + const ids = this.notebookEditor.deltaCellDecorations([], modifiedMarkdownCells.map(cell => ({ + handle: cell.handle, + options: { outputClassName: 'nb-insertHighlight' } + }))); + + this.clear(); + this.decorators.add(toDisposable(() => { + if (!this.notebookEditor.isDisposed) { + this.notebookEditor.deltaCellDecorations(ids, []); + } + })); + } + public clear() { + this.decorators.clear(); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalCellModelFactory.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalCellModelFactory.ts new file mode 100644 index 000000000000..fe62546116f3 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalCellModelFactory.ts @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IReference, ReferenceCollection } from '../../../../../../base/common/lifecycle.js'; +import { createDecorator, IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; +import { ITextModel } from '../../../../../../editor/common/model.js'; +import { CellKind } from '../../../common/notebookCommon.js'; +import { URI } from '../../../../../../base/common/uri.js'; +import { ILanguageService } from '../../../../../../editor/common/languages/language.js'; +import { IModelService } from '../../../../../../editor/common/services/model.js'; + + +export const INotebookOriginalCellModelFactory = createDecorator('INotebookOriginalCellModelFactory'); + +export interface INotebookOriginalCellModelFactory { + readonly _serviceBrand: undefined; + getOrCreate(uri: URI, cellValue: string, language: string, cellKind: CellKind): IReference; +} + + +export class OriginalNotebookCellModelReferenceCollection extends ReferenceCollection { + constructor(@IModelService private readonly modelService: IModelService, + @ILanguageService private readonly _languageService: ILanguageService, + ) { + super(); + } + + protected override createReferencedObject(_key: string, uri: URI, cellValue: string, language: string, cellKind: CellKind): ITextModel { + const scheme = `${uri.scheme}-chat-edit`; + const originalCellUri = URI.from({ scheme, fragment: uri.fragment, path: uri.path }); + const languageSelection = this._languageService.getLanguageIdByLanguageName(language) ? this._languageService.createById(language) : cellKind === CellKind.Markup ? this._languageService.createById('markdown') : null; + return this.modelService.createModel(cellValue, languageSelection, originalCellUri); + } + protected override destroyReferencedObject(_key: string, model: ITextModel): void { + model.dispose(); + } +} + +export class OriginalNotebookCellModelFactory implements INotebookOriginalCellModelFactory { + readonly _serviceBrand: undefined; + private readonly _data: OriginalNotebookCellModelReferenceCollection; + constructor(@IInstantiationService instantiationService: IInstantiationService) { + this._data = instantiationService.createInstance(OriginalNotebookCellModelReferenceCollection); + } + + getOrCreate(uri: URI, cellValue: string, language: string, cellKind: CellKind): IReference { + return this._data.acquire(uri.toString(), uri, cellValue, language, cellKind); + } +} + + diff --git a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalModelRefFactory.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalModelRefFactory.ts new file mode 100644 index 000000000000..8007f857b454 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalModelRefFactory.ts @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { AsyncReferenceCollection, IReference, ReferenceCollection } from '../../../../../../base/common/lifecycle.js'; +import { IModifiedFileEntry } from '../../../../chat/common/chatEditingService.js'; +import { INotebookService } from '../../../common/notebookService.js'; +import { bufferToStream, VSBuffer } from '../../../../../../base/common/buffer.js'; +import { NotebookTextModel } from '../../../common/model/notebookTextModel.js'; +import { createDecorator, IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; +import { ITextModelService } from '../../../../../../editor/common/services/resolverService.js'; + + +export const INotebookOriginalModelReferenceFactory = createDecorator('INotebookOriginalModelReferenceFactory'); + +export interface INotebookOriginalModelReferenceFactory { + readonly _serviceBrand: undefined; + getOrCreate(fileEntry: IModifiedFileEntry, viewType: string): Promise>; +} + + +export class OriginalNotebookModelReferenceCollection extends ReferenceCollection> { + private readonly modelsToDispose = new Set(); + constructor(@INotebookService private readonly notebookService: INotebookService, + @ITextModelService private readonly modelService: ITextModelService + ) { + super(); + } + + protected override async createReferencedObject(key: string, fileEntry: IModifiedFileEntry, viewType: string): Promise { + this.modelsToDispose.delete(key); + const uri = fileEntry.originalURI; + const model = this.notebookService.getNotebookTextModel(uri); + if (model) { + return model; + } + const modelRef = await this.modelService.createModelReference(uri); + const bytes = VSBuffer.fromString(modelRef.object.textEditorModel.getValue()); + const stream = bufferToStream(bytes); + modelRef.dispose(); + + return this.notebookService.createNotebookTextModel(viewType, uri, stream); + } + protected override destroyReferencedObject(key: string, modelPromise: Promise): void { + this.modelsToDispose.add(key); + + (async () => { + try { + const model = await modelPromise; + + if (!this.modelsToDispose.has(key)) { + // return if model has been acquired again meanwhile + return; + } + + // Finally we can dispose the model + model.dispose(); + } catch (error) { + // ignore + } finally { + this.modelsToDispose.delete(key); // Untrack as being disposed + } + })(); + } +} + +export class NotebookOriginalModelReferenceFactory implements INotebookOriginalModelReferenceFactory { + readonly _serviceBrand: undefined; + private _resourceModelCollection: OriginalNotebookModelReferenceCollection & ReferenceCollection> /* TS Fail */ | undefined = undefined; + private get resourceModelCollection() { + if (!this._resourceModelCollection) { + this._resourceModelCollection = this.instantiationService.createInstance(OriginalNotebookModelReferenceCollection); + } + + return this._resourceModelCollection; + } + + private _asyncModelCollection: AsyncReferenceCollection | undefined = undefined; + private get asyncModelCollection() { + if (!this._asyncModelCollection) { + this._asyncModelCollection = new AsyncReferenceCollection(this.resourceModelCollection); + } + + return this._asyncModelCollection; + } + + constructor(@IInstantiationService private readonly instantiationService: IInstantiationService) { + } + + getOrCreate(fileEntry: IModifiedFileEntry, viewType: string): Promise> { + return this.asyncModelCollection.acquire(fileEntry.originalURI.toString(), fileEntry, viewType); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts index 074630293363..e3438e83bd72 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts @@ -28,6 +28,8 @@ import { NotebookMultiTextDiffEditor } from './notebookMultiDiffEditor.js'; import { Codicon } from '../../../../../base/common/codicons.js'; import type { URI } from '../../../../../base/common/uri.js'; import { TextEditorSelectionRevealType, type ITextEditorOptions } from '../../../../../platform/editor/common/editor.js'; +import product from '../../../../../platform/product/common/product.js'; +import { ctxHasEditorModification, ctxHasRequestInProgress } from '../../../chat/browser/chatEditing/chatEditingEditorContextKeys.js'; // ActiveEditorContext.isEqualTo(SearchEditorConstants.SearchEditorID) @@ -695,6 +697,34 @@ registerAction2(class extends Action2 { } }); +registerAction2(class extends Action2 { + constructor() { + super( + { + id: 'notebook.diff.inline.toggle', + title: localize('notebook.diff.inline.toggle.title', "Toggle Inline View"), + menu: { + id: MenuId.EditorTitle, + group: '1_diff', + order: 10, + when: ContextKeyExpr.and(ActiveEditorContext.isEqualTo(NotebookTextDiffEditor.ID), + ContextKeyExpr.equals('config.notebook.diff.experimental.toggleInline', true), + ctxHasEditorModification.negate(), ctxHasRequestInProgress.negate()) + } + } + ); + } + run(accessor: ServicesAccessor) { + const editorService: IEditorService = accessor.get(IEditorService); + if (editorService.activeEditorPane?.getId() !== NOTEBOOK_DIFF_EDITOR_ID) { + return; + } + + const editor = editorService.activeEditorPane.getControl() as INotebookTextDiffEditor | undefined; + editor?.toggleInlineView(); + } +}); + Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ @@ -712,5 +742,10 @@ Registry.as(ConfigurationExtensions.Configuration).regis default: false, markdownDescription: localize('notebook.diff.ignoreOutputs', "Hide Outputs Differences") }, + 'notebook.diff.experimental.toggleInline': { + type: 'boolean', + default: typeof product.quality === 'string' && product.quality !== 'stable', // only enable as default in insiders + markdownDescription: localize('notebook.diff.toggleInline', "Enable the command to toggle the experimental notebook inline diff editor.") + }, } }); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts index 465a3efeeb45..639e2fcd80d3 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts @@ -48,6 +48,8 @@ import { NotebookDiffViewModel } from './notebookDiffViewModel.js'; import { INotebookService } from '../../common/notebookService.js'; import { DiffEditorHeightCalculatorService, IDiffEditorHeightCalculatorService } from './editorHeightCalculator.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; +import { NotebookInlineDiffWidget } from './inlineDiff/notebookInlineDiffWidget.js'; +import { IObservable, observableValue } from '../../../../../base/common/observable.js'; const $ = DOM.$; @@ -95,13 +97,15 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD private _overflowContainer!: HTMLElement; private _overviewRulerContainer!: HTMLElement; private _overviewRuler!: NotebookDiffOverviewRuler; - private _dimension: DOM.Dimension | null = null; + private _dimension: DOM.Dimension | undefined = undefined; private notebookDiffViewModel?: INotebookDiffViewModel; private _list!: NotebookTextDiffList; private _modifiedWebview: BackLayerWebView | null = null; private _originalWebview: BackLayerWebView | null = null; private _webviewTransparentCover: HTMLElement | null = null; private _fontInfo: FontInfo | undefined; + private _inlineView = false; + private _lastLayoutProperties: { dimension: DOM.Dimension; position: DOM.IDomPosition } | undefined; private readonly _onMouseUp = this._register(new Emitter<{ readonly event: MouseEvent; readonly target: IDiffElementViewModelBase }>()); public readonly onMouseUp = this._onMouseUp.event; @@ -113,18 +117,26 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD private _model: INotebookDiffEditorModel | null = null; private readonly diffEditorCalcuator: IDiffEditorHeightCalculatorService; private readonly _modifiedResourceDisposableStore = this._register(new DisposableStore()); + private inlineDiffWidget: NotebookInlineDiffWidget | undefined; get textModel() { return this._model?.modified.notebook; } + get inlineNotebookEditor() { + if (this._inlineView) { + return this.inlineDiffWidget?.editorWidget; + } + return undefined; + } + private _revealFirst: boolean; private readonly _insetModifyQueueByOutputId = new SequencerByKey(); protected _onDidDynamicOutputRendered = this._register(new Emitter<{ cell: IGenericCellViewModel; output: ICellOutputViewModel }>()); onDidDynamicOutputRendered = this._onDidDynamicOutputRendered.event; - private _notebookOptions: NotebookOptions; + private readonly _notebookOptions: NotebookOptions; get notebookOptions() { return this._notebookOptions; @@ -142,6 +154,8 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD get isDisposed() { return this._isDisposed; } + private readonly _currentChangedIndex = observableValue(this, -1); + readonly currentChangedIndex: IObservable = this._currentChangedIndex; constructor( group: IEditorGroup, @@ -153,7 +167,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD @ITelemetryService telemetryService: ITelemetryService, @IStorageService storageService: IStorageService, @INotebookService private readonly notebookService: INotebookService, - @IEditorService private readonly editorService: IEditorService + @IEditorService private readonly editorService: IEditorService, ) { super(NotebookTextDiffEditor.ID, group, telemetryService, themeService, storageService); this.diffEditorCalcuator = this.instantiationService.createInstance(DiffEditorHeightCalculatorService, this.fontInfo.lineHeight); @@ -274,6 +288,27 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD // throw new Error('Method not implemented.'); } + async toggleInlineView(): Promise { + this._layoutCancellationTokenSource?.dispose(); + + this._inlineView = !this._inlineView; + + if (!this._lastLayoutProperties) { + return; + } + + if (this._inlineView) { + this.layout(this._lastLayoutProperties?.dimension, this._lastLayoutProperties?.position); + this.inlineDiffWidget?.show(this.input as NotebookDiffEditorInput, this._model?.modified.notebook, this._model?.original.notebook, this._options as INotebookEditorOptions | undefined); + } else { + this.layout(this._lastLayoutProperties?.dimension, this._lastLayoutProperties?.position); + this.inlineDiffWidget?.hide(); + } + + this._layoutCancellationTokenSource = new CancellationTokenSource(); + this.updateLayout(this._layoutCancellationTokenSource.token); + } + protected createEditor(parent: HTMLElement): void { this._rootElement = DOM.append(parent, DOM.$('.notebook-text-diff-editor')); this._overflowContainer = document.createElement('div'); @@ -338,6 +373,8 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD } ); + this.inlineDiffWidget = this._register(this.instantiationService.createInstance(NotebookInlineDiffWidget, this._rootElement, this.group.id, this.window, this.notebookOptions, this._dimension)); + this._register(this._list); this._register(this._list.onMouseUp(e => { if (e.element) { @@ -430,6 +467,8 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD } override async setInput(input: NotebookDiffEditorInput, options: INotebookEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise { + this.inlineDiffWidget?.hide(); + await super.setInput(input, options, context, token); const model = await input.resolve(); @@ -443,6 +482,14 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD return; } + if (this._inlineView) { + this._listViewContainer.style.display = 'none'; + this.inlineDiffWidget?.show(input, model.modified.notebook, model.original.notebook, options); + } else { + this._listViewContainer.style.display = 'block'; + this.inlineDiffWidget?.hide(); + } + this._revealFirst = true; this._modifiedResourceDisposableStore.clear(); @@ -472,6 +519,13 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD await this.updateLayout(this._layoutCancellationTokenSource.token, options?.cellSelections ? cellRangesToIndexes(options.cellSelections) : undefined); } + override setVisible(visible: boolean): void { + super.setVisible(visible); + if (!visible) { + this.inlineDiffWidget?.hide(); + } + } + private _detachModel() { this._localStore.clear(); this._originalWebview?.dispose(); @@ -515,6 +569,17 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD updateInsets(); })); + this._localStore.add(this._list.onDidChangeFocus((e) => { + if (e.indexes.length && this.notebookDiffViewModel && e.indexes[0] < this.notebookDiffViewModel.items.length) { + const selectedItem = this.notebookDiffViewModel.items[e.indexes[0]]; + const changedItems = this.notebookDiffViewModel.items.filter(item => item.type !== 'unchanged' && item.type !== 'unchangedMetadata' && item.type !== 'placeholder'); + if (selectedItem && selectedItem?.type !== 'placeholder' && selectedItem?.type !== 'unchanged' && selectedItem?.type !== 'unchangedMetadata') { + return this._currentChangedIndex.set(changedItems.indexOf(selectedItem), undefined); + } + } + return this._currentChangedIndex.set(-1, undefined); + })); + this._localStore.add(this._eventDispatcher.onDidChangeCellLayout(() => { updateInsets(); })); @@ -524,7 +589,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this._originalWebview?.removeInsets([...this._originalWebview?.insetMapping.keys()]); this._modifiedWebview?.removeInsets([...this._modifiedWebview?.insetMapping.keys()]); - if (this._revealFirst && typeof e.firstChangeIndex === 'number' && e.firstChangeIndex < this._list.length) { + if (this._revealFirst && typeof e.firstChangeIndex === 'number' && e.firstChangeIndex > -1 && e.firstChangeIndex < this._list.length) { this._revealFirst = false; this._list.setFocus([e.firstChangeIndex]); this._list.reveal(e.firstChangeIndex, 0.3); @@ -623,8 +688,9 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this._list.updateElementHeight2(cell, height); }; - if (this.pendingLayouts.has(cell)) { - this.pendingLayouts.get(cell)!.dispose(); + let disposable = this.pendingLayouts.get(cell); + if (disposable) { + this._localStore.delete(disposable); } let r: () => void; @@ -634,11 +700,13 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD relayout(cell, height); r(); }); - - this.pendingLayouts.set(cell, toDisposable(() => { + disposable = toDisposable(() => { layoutDisposable.dispose(); r(); - })); + }); + this._localStore.add(disposable); + + this.pendingLayouts.set(cell, disposable); return new Promise(resolve => { r = resolve; }); } @@ -651,6 +719,33 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this._list.triggerScrollFromMouseWheelEvent(event); } + firstChange(): void { + if (!this.notebookDiffViewModel) { + return; + } + // go to the first one + const currentViewModels = this.notebookDiffViewModel.items; + const index = currentViewModels.findIndex(vm => vm.type !== 'unchanged' && vm.type !== 'unchangedMetadata' && vm.type !== 'placeholder'); + if (index >= 0) { + this._list.setFocus([index]); + this._list.reveal(index); + } + } + + lastChange(): void { + if (!this.notebookDiffViewModel) { + return; + } + // go to the first one + const currentViewModels = this.notebookDiffViewModel.items; + const item = currentViewModels.slice().reverse().find(vm => vm.type !== 'unchanged' && vm.type !== 'unchangedMetadata' && vm.type !== 'placeholder'); + const index = item ? currentViewModels.indexOf(item) : -1; + if (index >= 0) { + this._list.setFocus([index]); + this._list.reveal(index); + } + } + previousChange(): void { if (!this.notebookDiffViewModel) { return; @@ -666,7 +761,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD const currentViewModels = this.notebookDiffViewModel.items; while (prevChangeIndex >= 0) { const vm = currentViewModels[prevChangeIndex]; - if (vm.type !== 'unchanged' && vm.type !== 'placeholder') { + if (vm.type !== 'unchanged' && vm.type !== 'unchangedMetadata' && vm.type !== 'placeholder') { break; } @@ -678,7 +773,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this._list.reveal(prevChangeIndex); } else { // go to the last one - const index = findLastIdx(currentViewModels, vm => vm.type !== 'unchanged' && vm.type !== 'placeholder'); + const index = findLastIdx(currentViewModels, vm => vm.type !== 'unchanged' && vm.type !== 'unchangedMetadata' && vm.type !== 'placeholder'); if (index >= 0) { this._list.setFocus([index]); this._list.reveal(index); @@ -701,7 +796,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD const currentViewModels = this.notebookDiffViewModel.items; while (nextChangeIndex < currentViewModels.length) { const vm = currentViewModels[nextChangeIndex]; - if (vm.type !== 'unchanged' && vm.type !== 'placeholder') { + if (vm.type !== 'unchanged' && vm.type !== 'unchangedMetadata' && vm.type !== 'placeholder') { break; } @@ -713,7 +808,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this._list.reveal(nextChangeIndex); } else { // go to the first one - const index = currentViewModels.findIndex(vm => vm.type !== 'unchanged' && vm.type !== 'placeholder'); + const index = currentViewModels.findIndex(vm => vm.type !== 'unchanged' && vm.type !== 'unchangedMetadata' && vm.type !== 'placeholder'); if (index >= 0) { this._list.setFocus([index]); this._list.reveal(index); @@ -821,6 +916,8 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD } override clearInput(): void { + this.inlineDiffWidget?.hide(); + super.clearInput(); this._modifiedResourceDisposableStore.clear(); @@ -852,7 +949,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD }; } - layout(dimension: DOM.Dimension, _position: DOM.IDomPosition): void { + layout(dimension: DOM.Dimension, position: DOM.IDomPosition): void { this._rootElement.classList.toggle('mid-width', dimension.width < 1000 && dimension.width >= 600); this._rootElement.classList.toggle('narrow-width', dimension.width < 600); const overviewRulerEnabled = this.isOverviewRulerEnabled(); @@ -861,27 +958,36 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this._listViewContainer.style.height = `${dimension.height}px`; this._listViewContainer.style.width = `${this._dimension.width}px`; - this._list?.layout(this._dimension.height, this._dimension.width); + if (this._inlineView) { + this._listViewContainer.style.display = 'none'; + this.inlineDiffWidget?.setLayout(dimension, position); + } else { + this.inlineDiffWidget?.hide(); + this._listViewContainer.style.display = 'block'; + this._list?.layout(this._dimension.height, this._dimension.width); - if (this._modifiedWebview) { - this._modifiedWebview.element.style.width = `calc(50% - 16px)`; - this._modifiedWebview.element.style.left = `calc(50%)`; - } + if (this._modifiedWebview) { + this._modifiedWebview.element.style.width = `calc(50% - 16px)`; + this._modifiedWebview.element.style.left = `calc(50%)`; + } - if (this._originalWebview) { - this._originalWebview.element.style.width = `calc(50% - 16px)`; - this._originalWebview.element.style.left = `16px`; - } + if (this._originalWebview) { + this._originalWebview.element.style.width = `calc(50% - 16px)`; + this._originalWebview.element.style.left = `16px`; + } - if (this._webviewTransparentCover) { - this._webviewTransparentCover.style.height = `${this._dimension.height}px`; - this._webviewTransparentCover.style.width = `${this._dimension.width}px`; - } + if (this._webviewTransparentCover) { + this._webviewTransparentCover.style.height = `${this._dimension.height}px`; + this._webviewTransparentCover.style.width = `${this._dimension.width}px`; + } - if (overviewRulerEnabled) { - this._overviewRuler.layout(); + if (overviewRulerEnabled) { + this._overviewRuler.layout(); + } } + this._lastLayoutProperties = { dimension, position }; + this._eventDispatcher?.emit([new NotebookDiffLayoutChangedEvent({ width: true, fontInfo: true }, this.getLayoutInfo())]); } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts index 2cc1ef95b7d8..3e435fad6673 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CellLayoutState, ICellOutputViewModel, ICommonCellInfo, IGenericCellViewModel, IInsetRenderOutput } from '../notebookBrowser.js'; +import { CellLayoutState, ICellOutputViewModel, ICommonCellInfo, IGenericCellViewModel, IInsetRenderOutput, INotebookEditor } from '../notebookBrowser.js'; import { DiffElementCellViewModelBase, IDiffElementViewModelBase } from './diffElementViewModel.js'; import { Event } from '../../../../../base/common/event.js'; import { BareFontInfo } from '../../../../../editor/common/config/fontInfo.js'; @@ -18,6 +18,7 @@ import { WorkbenchToolBar } from '../../../../../platform/actions/browser/toolba import { DiffEditorWidget } from '../../../../../editor/browser/widget/diffEditor/diffEditorWidget.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { localize } from '../../../../../nls.js'; +import { IObservable } from '../../../../../base/common/observable.js'; export enum DiffSide { Original = 0, @@ -31,6 +32,8 @@ export interface IDiffCellInfo extends ICommonCellInfo { export interface INotebookTextDiffEditor { notebookOptions: NotebookOptions; readonly textModel?: NotebookTextModel; + inlineNotebookEditor: INotebookEditor | undefined; + readonly currentChangedIndex: IObservable; onMouseUp: Event<{ readonly event: MouseEvent; readonly target: IDiffElementViewModelBase }>; onDidScroll: Event; onDidDynamicOutputRendered: Event<{ cell: IGenericCellViewModel; output: ICellOutputViewModel }>; @@ -53,8 +56,11 @@ export interface INotebookTextDiffEditor { focusNextNotebookCell(cell: IGenericCellViewModel, focus: 'editor' | 'container' | 'output'): Promise; updateOutputHeight(cellInfo: ICommonCellInfo, output: ICellOutputViewModel, height: number, isInit: boolean): void; deltaCellOutputContainerClassNames(diffSide: DiffSide, cellId: string, added: string[], removed: string[]): void; + firstChange(): void; + lastChange(): void; previousChange(): void; nextChange(): void; + toggleInlineView(): void; } export interface IDiffNestedCellViewModel { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts index dd7de68ed52b..5cd62062e26f 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts @@ -6,6 +6,7 @@ import './notebookDiff.css'; import { IListMouseEvent, IListRenderer, IListVirtualDelegate } from '../../../../../base/browser/ui/list/list.js'; import * as DOM from '../../../../../base/browser/dom.js'; +import * as domStylesheets from '../../../../../base/browser/domStylesheets.js'; import { IListOptions, IListStyles, isMonacoEditor, IStyleController, MouseController } from '../../../../../base/browser/ui/list/listWidget.js'; import { DisposableStore, IDisposable } from '../../../../../base/common/lifecycle.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; @@ -481,7 +482,7 @@ export class NotebookTextDiffList extends WorkbenchList div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; } `); } if (styles.listFocusAndSelectionForeground) { content.push(` - .monaco-drag-image, + .monaco-drag-image${suffix}, .monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; } `); } @@ -550,7 +551,7 @@ export class NotebookTextDiffList extends WorkbenchList div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; } `); } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts index f24aabe73b4c..912e77093692 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts @@ -57,7 +57,7 @@ export class NotebookDiffOverviewRuler extends Themable { })); this._register(this.themeService.onDidColorThemeChange(e => { - const colorChanged = this.applyColors(e); + const colorChanged = this.applyColors(e.theme); if (colorChanged) { this._scheduleRender(); } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffViewModel.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffViewModel.ts index 930a8e5c39ae..6ef165a48213 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffViewModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from '../../../../../base/common/cancellation.js'; -import { IDiffResult, IDiffChange } from '../../../../../base/common/diff/diff.js'; +import { IDiffResult } from '../../../../../base/common/diff/diff.js'; import { Emitter, type IValueWithChangeEvent } from '../../../../../base/common/event.js'; import { Disposable, DisposableStore, dispose } from '../../../../../base/common/lifecycle.js'; import { Schemas } from '../../../../../base/common/network.js'; @@ -17,11 +17,12 @@ import { DiffElementCellViewModelBase, DiffElementPlaceholderViewModel, IDiffEle import { NotebookDiffEditorEventDispatcher } from './eventDispatcher.js'; import { INotebookDiffViewModel, INotebookDiffViewModelUpdateEvent, NOTEBOOK_DIFF_ITEM_DIFF_STATE, NOTEBOOK_DIFF_ITEM_KIND } from './notebookDiffEditorBrowser.js'; import { NotebookTextModel } from '../../common/model/notebookTextModel.js'; -import { CellUri, INotebookDiffEditorModel, INotebookDiffResult } from '../../common/notebookCommon.js'; +import { CellUri, INotebookDiffEditorModel } from '../../common/notebookCommon.js'; import { INotebookService } from '../../common/notebookService.js'; import { INotebookEditorWorkerService } from '../../common/services/notebookWorkerService.js'; import { IDiffEditorHeightCalculatorService } from './editorHeightCalculator.js'; import { raceCancellation } from '../../../../../base/common/async.js'; +import { computeDiff } from '../../common/notebookDiff.js'; export class NotebookDiffViewModel extends Disposable implements INotebookDiffViewModel, IValueWithChangeEvent { private readonly placeholderAndRelatedCells = new Map(); @@ -138,9 +139,9 @@ export class NotebookDiffViewModel extends Disposable implements INotebookDiffVi return; } - prettyChanges(this.model, diffResult.cellsDiff); + prettyChanges(this.model.original.notebook, this.model.modified.notebook, diffResult.cellsDiff); - const { cellDiffInfo, firstChangeIndex } = computeDiff(this.model, diffResult); + const { cellDiffInfo, firstChangeIndex } = computeDiff(this.model.original.notebook, this.model.modified.notebook, diffResult); if (isEqual(cellDiffInfo, this.originalCellViewModels, this.model)) { return; } else { @@ -367,7 +368,7 @@ export class NotebookDiffViewModel extends Disposable implements INotebookDiffVi /** * making sure that swapping cells are always translated to `insert+delete`. */ -export function prettyChanges(model: INotebookDiffEditorModel, diffResult: IDiffResult) { +export function prettyChanges(original: NotebookTextModel, modified: NotebookTextModel, diffResult: IDiffResult) { const changes = diffResult.changes; for (let i = 0; i < diffResult.changes.length - 1; i++) { // then we know there is another change after current one @@ -383,8 +384,8 @@ export function prettyChanges(model: INotebookDiffEditorModel, diffResult: IDiff && next.originalLength === 0 && next.modifiedStart === y + 1 && next.modifiedLength === 1 - && model.original.notebook.cells[x].getHashValue() === model.modified.notebook.cells[y + 1].getHashValue() - && model.original.notebook.cells[x + 1].getHashValue() === model.modified.notebook.cells[y].getHashValue() + && original.cells[x].getHashValue() === modified.cells[y + 1].getHashValue() + && original.cells[x + 1].getHashValue() === modified.cells[y].getHashValue() ) { // this is a swap curr.originalStart = x; @@ -402,7 +403,7 @@ export function prettyChanges(model: INotebookDiffEditorModel, diffResult: IDiff } } -type CellDiffInfo = { +export type CellDiffInfo = { originalCellIndex: number; modifiedCellIndex: number; type: 'unchanged' | 'modified'; @@ -415,64 +416,7 @@ type CellDiffInfo = { modifiedCellIndex: number; type: 'insert'; }; -function computeDiff(model: INotebookDiffEditorModel, diffResult: INotebookDiffResult) { - const cellChanges = diffResult.cellsDiff.changes; - const cellDiffInfo: CellDiffInfo[] = []; - const originalModel = model.original.notebook; - const modifiedModel = model.modified.notebook; - let originalCellIndex = 0; - let modifiedCellIndex = 0; - - let firstChangeIndex = -1; - - for (let i = 0; i < cellChanges.length; i++) { - const change = cellChanges[i]; - // common cells - - for (let j = 0; j < change.originalStart - originalCellIndex; j++) { - const originalCell = originalModel.cells[originalCellIndex + j]; - const modifiedCell = modifiedModel.cells[modifiedCellIndex + j]; - if (originalCell.getHashValue() === modifiedCell.getHashValue()) { - cellDiffInfo.push({ - originalCellIndex: originalCellIndex + j, - modifiedCellIndex: modifiedCellIndex + j, - type: 'unchanged' - }); - } else { - if (firstChangeIndex === -1) { - firstChangeIndex = cellDiffInfo.length; - } - cellDiffInfo.push({ - originalCellIndex: originalCellIndex + j, - modifiedCellIndex: modifiedCellIndex + j, - type: 'modified' - }); - } - } - - const modifiedLCS = computeModifiedLCS(change, originalModel, modifiedModel); - if (modifiedLCS.length && firstChangeIndex === -1) { - firstChangeIndex = cellDiffInfo.length; - } - - cellDiffInfo.push(...modifiedLCS); - originalCellIndex = change.originalStart + change.originalLength; - modifiedCellIndex = change.modifiedStart + change.modifiedLength; - } - for (let i = originalCellIndex; i < originalModel.cells.length; i++) { - cellDiffInfo.push({ - originalCellIndex: i, - modifiedCellIndex: i - originalCellIndex + modifiedCellIndex, - type: 'unchanged' - }); - } - - return { - cellDiffInfo, - firstChangeIndex - }; -} function isEqual(cellDiffInfo: CellDiffInfo[], viewModels: IDiffElementViewModelBase[], model: INotebookDiffEditorModel) { if (cellDiffInfo.length !== viewModels.length) { return false; @@ -512,40 +456,6 @@ function isEqual(cellDiffInfo: CellDiffInfo[], viewModels: IDiffElementViewModel return true; } - -function computeModifiedLCS(change: IDiffChange, originalModel: NotebookTextModel, modifiedModel: NotebookTextModel) { - const result: CellDiffInfo[] = []; - // modified cells - const modifiedLen = Math.min(change.originalLength, change.modifiedLength); - - for (let j = 0; j < modifiedLen; j++) { - const isTheSame = originalModel.cells[change.originalStart + j].equal(modifiedModel.cells[change.modifiedStart + j]); - result.push({ - originalCellIndex: change.originalStart + j, - modifiedCellIndex: change.modifiedStart + j, - type: isTheSame ? 'unchanged' : 'modified' - }); - } - - for (let j = modifiedLen; j < change.originalLength; j++) { - // deletion - result.push({ - originalCellIndex: change.originalStart + j, - type: 'delete' - }); - } - - for (let j = modifiedLen; j < change.modifiedLength; j++) { - result.push({ - modifiedCellIndex: change.modifiedStart + j, - type: 'insert' - }); - } - - return result; -} - - export abstract class NotebookMultiDiffEditorItem extends MultiDiffEditorItem { constructor( originalUri: URI | undefined, diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css index 345210b83614..6c2f0f8c545e 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -33,9 +33,6 @@ z-index: 600 !important; } -.monaco-workbench .notebookOverlay .cell-list-container .monaco-editor .overlayWidgets { - z-index: 638 !important; -} .monaco-workbench .notebookOverlay .cell-list-container .overflowingContentWidgets > div.parameter-hints-widget { z-index: 639 !important; @@ -338,7 +335,7 @@ } .monaco-workbench .notebookOverlay .monaco-list:focus-within .monaco-list-row .codicon:not(.suggest-icon) { - color: inherit; + color: var(--vscode-icon-foreground); } .monaco-workbench .notebookOverlay > .cell-list-container .notebook-overview-ruler-container { @@ -452,6 +449,18 @@ background-color: var(--vscode-notebook-symbolHighlightBackground) !important; } +/** Cell execution inline vars */ +.nb-inline-value { + background-color: var(--vscode-editorInlayHint-background); + color: var(--vscode-editorInlayHint-foreground) !important; + font-size: 90%; +} + +/** Notebook Textual Selection Highlight */ +.nb-selection-highlight { + background-color: var(--vscode-editor-selectionHighlightBackground); +} + /** Cell Multi-Cursor Cursor highlight */ /* -- base selection styling -- */ .nb-multicursor-selection { @@ -646,9 +655,3 @@ .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row.nb-chatGenerationHighlight { background-color: var(--vscode-notebook-selectedCellBackground) !important; } - -/** Avoid Zone Widget hide cell editor focus indicator partially **/ -.monaco-workbench .notebookOverlay .cell-list-container .monaco-editor .zone-widget { - margin-left: 1px; - margin-right: 1px; -} diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookCellStatusBar.css b/src/vs/workbench/contrib/notebook/browser/media/notebookCellStatusBar.css index 8f5eb5dc052a..64dd58eecd25 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebookCellStatusBar.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebookCellStatusBar.css @@ -68,3 +68,11 @@ .monaco-workbench .notebookOverlay .cell-statusbar-container.is-active-cell .cell-status-item-show-when-active { display: initial; } + +/* Ensure execution status icons always maintain their themed colors */ +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-statusbar-container .cell-status-item .codicon-notebook-state-success { + color: var(--vscode-notebookStatusSuccessIcon-foreground); +} +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-statusbar-container .cell-status-item .codicon-notebook-state-error { + color: var(--vscode-notebookStatusErrorIcon-foreground); +} diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookChatEditController.css b/src/vs/workbench/contrib/notebook/browser/media/notebookChatEditController.css new file mode 100644 index 000000000000..6be9aff0364f --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/media/notebookChatEditController.css @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .view-zones .cell-editor-container > div { + padding: 12px 16px; +} + +/** Cell delete higlight */ +.monaco-workbench .notebookOverlay > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .view-zones .cell-inner-container { + background-color: var(--vscode-diffEditor-removedLineBackground); + padding: 8px 0; + margin-bottom: 16px; +} + +/** Cell insert higlight */ +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-insertHighlight .cell-focus-indicator, +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row.nb-insertHighlight { + background-color: var(--vscode-diffEditor-insertedLineBackground, var(--vscode-diffEditor-insertedTextBackground)) !important; +} + +.notebookOverlay .cell .cell-statusbar-container .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-insertHighlight .cell-focus-indicator .cell-inner-container, +.notebookOverlay .cell .cell-statusbar-container .monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row.nb-insertHighlight .cell-focus-indicator .cell-inner-container, +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-insertHighlight .monaco-editor-background, +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row.nb-insertHighlight .monaco-editor-background, +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-insertHighlight .margin-view-overlays, +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row.nb-insertHighlight .margin-view-overlays, +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.code-cell-row.nb-insertHighlight .cell-statusbar-container { + background-color: var(--vscode-diffEditor-insertedLineBackground, var(--vscode-diffEditor-insertedTextBackground)) !important; +} +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row.markdown-cell-row.nb-insertHighlight .cell-statusbar-container { + background-color: inherit !important; +} + +.monaco-workbench .notebookOverlay .view-zones .cell-editor-part { + outline: solid 1px var(--vscode-notebook-cellBorderColor); +} + + +.monaco-workbench .notebookOverlay .view-zones .cell-editor-container > div > div { + line-height: initial; + overflow-x: hidden; + font-family: var(--notebook-editor-font-family); +} + +.monaco-workbench .notebookOverlay .view-zones .cell-editor-container > div > div span { + font-family: var(--notebook-editor-font-family); + font-size: var(--notebook-editor-font-size); + font-weight: var(--notebook-editor-font-weight); +} + +.monaco-workbench .notebookOverlay .view-zones .cell-editor-part { + outline: solid 1px var(--vscode-notebook-cellBorderColor); +} diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookChatEditorOverlay.css b/src/vs/workbench/contrib/notebook/browser/media/notebookChatEditorOverlay.css new file mode 100644 index 000000000000..6de9ee650503 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/media/notebookChatEditorOverlay.css @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.notebook-chat-editor-overlay-widget { + position: absolute; + /** Based on chat widget for regular editors **/ + right: 28px; + /** Based on chat widget for regular editors **/ + bottom: 23px; + /** In notebook.css we set this to 22px, we need to revert this to standards **/ + line-height: 1.4em; +} + +/** Copied from src/vs/workbench/contrib/chat/browser/media/chatEditorOverlay.css **/ +/** Copied until we unify these, for now its separate **/ +.notebook-chat-editor-overlay-widget { + padding: 0px; + color: var(--vscode-button-foreground); + background-color: var(--vscode-button-background); + border-radius: 5px; + border: 1px solid var(--vscode-contrastBorder); + display: flex; + align-items: center; + z-index: 30; +} + +.notebook-chat-editor-overlay-widget .chat-editor-overlay-progress { + display: none; + padding: 0px 5px; + font-size: 12px; +} + +.notebook-chat-editor-overlay-widget.busy .chat-editor-overlay-progress { + display: inherit; +} + +.notebook-chat-editor-overlay-widget .action-item > .action-label { + padding: 5px; + font-size: 12px; +} + +.notebook-chat-editor-overlay-widget .action-item:first-child > .action-label { + padding-left: 9px; +} + +.notebook-chat-editor-overlay-widget .action-item:last-child > .action-label { + padding-right: 9px; +} + +.notebook-chat-editor-overlay-widget.busy .chat-editor-overlay-progress .codicon, +.notebook-chat-editor-overlay-widget .action-item > .action-label.codicon { + color: var(--vscode-button-foreground); +} + +.notebook-chat-editor-overlay-widget .action-item.disabled > .action-label.codicon::before, +.notebook-chat-editor-overlay-widget .action-item.disabled > .action-label.codicon, +.notebook-chat-editor-overlay-widget .action-item.disabled > .action-label, +.notebook-chat-editor-overlay-widget .action-item.disabled > .action-label:hover { + color: var(--vscode-button-foreground); + opacity: 0.7; +} diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css b/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css index 25fa90546d69..a723d49cfe0d 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebookEditorStickyScroll.css @@ -38,10 +38,8 @@ .monaco-workbench .notebookOverlay .notebook-sticky-scroll-container - .notebook-sticky-scroll-element - .notebook-sticky-scroll-header:hover { + .notebook-sticky-scroll-element:hover { background-color: var(--vscode-editorStickyScrollHover-background); - width: 100%; cursor: pointer; } @@ -55,13 +53,11 @@ .monaco-workbench.hc-light .notebookOverlay .notebook-sticky-scroll-container - .notebook-sticky-scroll-element - .notebook-sticky-scroll-header:hover, + .notebook-sticky-scroll-element:hover, .monaco-workbench.hc-black .notebookOverlay .notebook-sticky-scroll-container - .notebook-sticky-scroll-element - .notebook-sticky-scroll-header:hover { + .notebook-sticky-scroll-element:hover { outline: 1px dashed var(--vscode-contrastActiveBorder); outline-offset: -2px; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 857c3a95aa0d..10a50d99cdf5 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -99,6 +99,8 @@ import './contrib/execute/executionEditorProgress.js'; import './contrib/kernelDetection/notebookKernelDetection.js'; import './contrib/cellDiagnostics/cellDiagnostics.js'; import './contrib/multicursor/notebookMulticursor.js'; +import './contrib/multicursor/notebookSelectionHighlight.js'; +import './contrib/notebookVariables/notebookInlineVariables.js'; // Diff Editor Contribution import './diff/notebookDiffActions.js'; @@ -446,7 +448,7 @@ class CellInfoContentProvider { for (const cell of ref.object.notebook.cells) { if (cell.handle === data.handle) { const cellIndex = ref.object.notebook.cells.indexOf(cell); - const metadataSource = getFormattedMetadataJSON(ref.object.notebook.transientOptions.transientCellMetadata, cell.metadata, cell.language); + const metadataSource = getFormattedMetadataJSON(ref.object.notebook.transientOptions.transientCellMetadata, cell.metadata, cell.language, true); result = this._modelService.createModel( metadataSource, mode, @@ -454,9 +456,9 @@ class CellInfoContentProvider { ); this._disposables.push(disposables.add(ref.object.notebook.onDidChangeContent(e => { if (result && e.rawEvents.some(event => (event.kind === NotebookCellsChangeType.ChangeCellMetadata || event.kind === NotebookCellsChangeType.ChangeCellLanguage) && event.index === cellIndex)) { - const value = getFormattedMetadataJSON(ref.object.notebook.transientOptions.transientCellMetadata, cell.metadata, cell.language); + const value = getFormattedMetadataJSON(ref.object.notebook.transientOptions.transientCellMetadata, cell.metadata, cell.language, true); if (result.getValue() !== value) { - result.setValue(getFormattedMetadataJSON(ref.object.notebook.transientOptions.transientCellMetadata, cell.metadata, cell.language)); + result.setValue(value); } } }))); @@ -954,6 +956,16 @@ configurationRegistry.registerConfiguration({ default: 'visible', tags: ['notebookLayout'] }, + [NotebookSetting.cellExecutionTimeVerbosity]: { + description: nls.localize('notebook.cellExecutionTimeVerbosity.description', "Controls the verbosity of the cell execution time in the cell status bar."), + type: 'string', + enum: ['default', 'verbose'], + enumDescriptions: [ + nls.localize('notebook.cellExecutionTimeVerbosity.default.description', "The cell execution duration is visible, with advanced information in the hover tooltip."), + nls.localize('notebook.cellExecutionTimeVerbosity.verbose.description', "The cell last execution timestamp and duration are visible, with advanced information in the hover tooltip.")], + default: 'default', + tags: ['notebookLayout'] + }, [NotebookSetting.textDiffEditorPreview]: { description: nls.localize('notebook.diff.enablePreview.description', "Whether to use the enhanced text diff editor for notebook."), type: 'boolean', @@ -1141,7 +1153,7 @@ configurationRegistry.registerConfiguration({ markdownEnumDescriptions: DefaultFormatter.extensionDescriptions }, [NotebookSetting.formatOnSave]: { - markdownDescription: nls.localize('notebook.formatOnSave', "Format a notebook on save. A formatter must be available, the file must not be saved after delay, and the editor must not be shutting down."), + markdownDescription: nls.localize('notebook.formatOnSave', "Format a notebook on save. A formatter must be available and the editor must not be shutting down. When {0} is set to `afterDelay`, the file will only be formatted when saved explicitly.", '`#files.autoSave#`'), type: 'boolean', tags: ['notebookLayout'], default: false @@ -1211,14 +1223,24 @@ configurationRegistry.registerConfiguration({ [NotebookSetting.cellGenerate]: { markdownDescription: nls.localize('notebook.cellGenerate', "Enable experimental generate action to create code cell with inline chat enabled."), type: 'boolean', - default: typeof product.quality === 'string' && product.quality !== 'stable', - tags: ['experimental'] + default: true }, [NotebookSetting.notebookVariablesView]: { markdownDescription: nls.localize('notebook.VariablesView.description', "Enable the experimental notebook variables view within the debug panel."), type: 'boolean', default: false }, + [NotebookSetting.notebookInlineValues]: { + markdownDescription: nls.localize('notebook.inlineValues.description', "Control whether to show inline values within notebook code cells after cell execution. Values will remain until the cell is edited, re-executed, or explicitly cleared via the Clear All Outputs toolbar button or the `Notebook: Clear Inline Values` command."), + type: 'string', + enum: ['on', 'auto', 'off'], + enumDescriptions: [ + nls.localize('notebook.inlineValues.on', "Always show inline values, with a regex fallback if no inline value provider is registered. Note: There may be a performance impact in larger cells if the fallback is used."), + nls.localize('notebook.inlineValues.auto', "Show inline values only when an inline value provider is registered."), + nls.localize('notebook.inlineValues.off', "Never show inline values."), + ], + default: 'off' + }, [NotebookSetting.cellFailureDiagnostics]: { markdownDescription: nls.localize('notebook.cellFailureDiagnostics', "Show available diagnostics for cell failures."), type: 'boolean', @@ -1234,5 +1256,11 @@ configurationRegistry.registerConfiguration({ type: 'boolean', default: false }, + [NotebookSetting.markupFontFamily]: { + markdownDescription: nls.localize('notebook.markup.fontFamily', "Controls the font family of rendered markup in notebooks. When left blank, this will fall back to the default workbench font family."), + type: 'string', + default: '', + tags: ['notebookLayout'] + } } }); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts b/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts index 9e2118bcb10b..45d8a96ba202 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityHelp.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; -import { IAccessibleViewImplentation } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js'; +import { IAccessibleViewImplementation } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js'; import { IS_COMPOSITE_NOTEBOOK, NOTEBOOK_EDITOR_FOCUSED } from '../common/notebookContextKeys.js'; import { localize } from '../../../../nls.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; @@ -14,7 +14,7 @@ import { IVisibleEditorPane } from '../../../common/editor.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; -export class NotebookAccessibilityHelp implements IAccessibleViewImplentation { +export class NotebookAccessibilityHelp implements IAccessibleViewImplementation { readonly priority = 105; readonly name = 'notebook'; readonly when = ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, IS_COMPOSITE_NOTEBOOK.negate()); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityProvider.ts b/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityProvider.ts index 3e358ecdc01c..e035bb1179c3 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityProvider.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookAccessibilityProvider.ts @@ -15,36 +15,61 @@ import { AccessibilityCommandId } from '../../accessibility/common/accessibility import { CellViewModel, NotebookViewModel } from './viewModel/notebookViewModelImpl.js'; import { CellKind, NotebookCellExecutionState } from '../common/notebookCommon.js'; import { ICellExecutionStateChangedEvent, IExecutionStateChangedEvent, INotebookExecutionStateService, NotebookExecutionType } from '../common/notebookExecutionStateService.js'; +import { getAllOutputsText } from './viewModel/cellOutputTextHelper.js'; +import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; +import { alert } from '../../../../base/browser/ui/aria/aria.js'; + +type executionUpdate = { cellHandle: number; state: NotebookCellExecutionState | undefined }; export class NotebookAccessibilityProvider extends Disposable implements IListAccessibilityProvider { private readonly _onDidAriaLabelChange = new Emitter(); private readonly onDidAriaLabelChange = this._onDidAriaLabelChange.event; constructor( - private readonly notebookExecutionStateService: INotebookExecutionStateService, private readonly viewModel: () => NotebookViewModel | undefined, - private readonly keybindingService: IKeybindingService, - private readonly configurationService: IConfigurationService, - private readonly isReplHistory: boolean + private readonly isReplHistory: boolean, + @INotebookExecutionStateService private readonly notebookExecutionStateService: INotebookExecutionStateService, + @IKeybindingService private readonly keybindingService: IKeybindingService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IAccessibilityService private readonly accessibilityService: IAccessibilityService ) { super(); - this._register(Event.debounce( + this._register(Event.debounce( this.notebookExecutionStateService.onDidChangeExecution, - (last: number[] | undefined, e: ICellExecutionStateChangedEvent | IExecutionStateChangedEvent) => this.mergeEvents(last, e), + (last: executionUpdate[] | undefined, e: ICellExecutionStateChangedEvent | IExecutionStateChangedEvent) => this.mergeEvents(last, e), 100 - )((cellHandles: number[]) => { + )((updates: executionUpdate[]) => { + if (!updates.length) { + return; + } const viewModel = this.viewModel(); if (viewModel) { - for (const handle of cellHandles) { - const cellModel = viewModel.getCellByHandle(handle); + for (const update of updates) { + const cellModel = viewModel.getCellByHandle(update.cellHandle); if (cellModel) { this._onDidAriaLabelChange.fire(cellModel as CellViewModel); } } + + const lastUpdate = updates[updates.length - 1]; + if (this.shouldReadCellOutputs(lastUpdate.state)) { + const cell = viewModel.getCellByHandle(lastUpdate.cellHandle); + if (cell && cell.outputsViewModels.length) { + const text = getAllOutputsText(viewModel.notebookDocument, cell, true); + alert(text); + } + } } }, this)); } + private shouldReadCellOutputs(state: NotebookCellExecutionState | undefined): boolean { + return state === undefined // execution completed + && this.isReplHistory + && this.accessibilityService.isScreenReaderOptimized() + && this.configurationService.getValue('accessibility.replEditor.readLastExecutionOutput'); + } + get verbositySettingId() { return this.isReplHistory ? AccessibilityVerbositySettingId.ReplEditor : @@ -61,20 +86,20 @@ export class NotebookAccessibilityProvider extends Disposable implements IListAc const index = viewModel.getCellIndex(element); if (index >= 0) { - return this.getLabel(index, element); + return this.getLabel(element); } return ''; }); } - private createItemLabel(executionLabel: string, index: number, cellKind: CellKind) { + private createItemLabel(executionLabel: string, cellKind: CellKind) { return this.isReplHistory ? - `item${executionLabel}` : + `cell${executionLabel}` : `${cellKind === CellKind.Markup ? 'markdown' : 'code'} cell${executionLabel}`; } - private getLabel(index: number, element: CellViewModel) { + private getLabel(element: CellViewModel) { const executionState = this.notebookExecutionStateService.getCellExecution(element.uri)?.state; const executionLabel = executionState === NotebookCellExecutionState.Executing @@ -83,7 +108,7 @@ export class NotebookAccessibilityProvider extends Disposable implements IListAc ? ', pending' : ''; - return this.createItemLabel(executionLabel, index, element.cellKind); + return this.createItemLabel(executionLabel, element.cellKind); } private get widgetAriaLabelName() { @@ -103,13 +128,15 @@ export class NotebookAccessibilityProvider extends Disposable implements IListAc return this.widgetAriaLabelName; } - private mergeEvents(last: number[] | undefined, e: ICellExecutionStateChangedEvent | IExecutionStateChangedEvent): number[] { + private mergeEvents(last: executionUpdate[] | undefined, e: ICellExecutionStateChangedEvent | IExecutionStateChangedEvent): executionUpdate[] { const viewModel = this.viewModel(); const result = last || []; if (viewModel && e.type === NotebookExecutionType.cell && e.affectsNotebook(viewModel.uri)) { - if (result.indexOf(e.cellHandle) < 0) { - result.push(e.cellHandle); + const index = result.findIndex(update => update.cellHandle === e.cellHandle); + if (index >= 0) { + result.splice(index, 1); } + result.push({ cellHandle: e.cellHandle, state: e.changed?.state }); } return result; } diff --git a/src/vs/workbench/contrib/notebook/browser/notebookAccessibleView.ts b/src/vs/workbench/contrib/notebook/browser/notebookAccessibleView.ts index 78e9d118af73..56e6d347e4e2 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookAccessibleView.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookAccessibleView.ts @@ -3,19 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { AccessibleViewProviderId, AccessibleViewType, AccessibleContentProvider } from '../../../../platform/accessibility/browser/accessibleView.js'; -import { IAccessibleViewImplentation } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js'; +import { IAccessibleViewImplementation } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { AccessibilityVerbositySettingId } from '../../accessibility/browser/accessibilityConfiguration.js'; import { getNotebookEditorFromEditorPane } from './notebookBrowser.js'; -import { NOTEBOOK_OUTPUT_FOCUSED } from '../common/notebookContextKeys.js'; +import { NOTEBOOK_CELL_LIST_FOCUSED } from '../common/notebookContextKeys.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; +import { InputFocusedContext } from '../../../../platform/contextkey/common/contextkeys.js'; +import { getAllOutputsText } from './viewModel/cellOutputTextHelper.js'; -export class NotebookAccessibleView implements IAccessibleViewImplentation { +export class NotebookAccessibleView implements IAccessibleViewImplementation { readonly priority = 100; readonly name = 'notebook'; readonly type = AccessibleViewType.View; - readonly when = ContextKeyExpr.and(NOTEBOOK_OUTPUT_FOCUSED, ContextKeyExpr.equals('resourceExtname', '.ipynb')); + readonly when = ContextKeyExpr.and(NOTEBOOK_CELL_LIST_FOCUSED, InputFocusedContext.toNegated()); getProvider(accessor: ServicesAccessor) { const editorService = accessor.get(IEditorService); return getAccessibleOutputProvider(editorService); @@ -35,38 +37,7 @@ export function getAccessibleOutputProvider(editorService: IEditorService) { } const viewCell = notebookViewModel.viewCells[selections[0].start]; - let outputContent = ''; - const decoder = new TextDecoder(); - for (let i = 0; i < viewCell.outputsViewModels.length; i++) { - const outputViewModel = viewCell.outputsViewModels[i]; - const outputTextModel = viewCell.model.outputs[i]; - const [mimeTypes, pick] = outputViewModel.resolveMimeTypes(notebookEditor.textModel, undefined); - const mimeType = mimeTypes[pick].mimeType; - let buffer = outputTextModel.outputs.find(output => output.mime === mimeType); - - if (!buffer || mimeType.startsWith('image')) { - buffer = outputTextModel.outputs.find(output => !output.mime.startsWith('image')); - } - - let text = `${mimeType}`; // default in case we can't get the text value for some reason. - if (buffer) { - const charLimit = 100_000; - text = decoder.decode(buffer.data.slice(0, charLimit).buffer); - - if (buffer.data.byteLength > charLimit) { - text = text + '...(truncated)'; - } - - if (mimeType.endsWith('error')) { - text = text.replace(/\\u001b\[[0-9;]*m/gi, '').replaceAll('\\n', '\n'); - } - } - - const index = viewCell.outputsViewModels.length > 1 - ? `Cell output ${i + 1} of ${viewCell.outputsViewModels.length}\n` - : ''; - outputContent = outputContent.concat(`${index}${text}\n`); - } + const outputContent = getAllOutputsText(notebookDocument, viewCell); if (!outputContent) { return; @@ -78,7 +49,7 @@ export function getAccessibleOutputProvider(editorService: IEditorService) { () => { return outputContent; }, () => { notebookEditor?.setFocus(selections[0]); - activePane?.focus(); + notebookEditor.focus(); }, AccessibilityVerbositySettingId.Notebook, ); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index a14d5f6ca450..b37aaca062af 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -32,6 +32,8 @@ import { IEditorCommentsOptions, IEditorOptions } from '../../../../editor/commo import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { IObservable } from '../../../../base/common/observable.js'; +import { NotebookTextDiffEditor } from './diff/notebookDiffEditor.js'; +import { INotebookTextDiffEditor } from './diff/notebookDiffEditorBrowser.js'; //#region Shared commands export const EXPAND_CELL_INPUT_COMMAND_ID = 'notebook.cell.expandCellInput'; @@ -464,8 +466,10 @@ export interface INotebookViewModel { setTrackedRange(id: string | null, newRange: ICellRange | null, newStickiness: TrackedRangeStickiness): string | null; getSelections(): ICellRange[]; getCellIndex(cell: ICellViewModel): number; + getMostRecentlyExecutedCell(): ICellViewModel | undefined; deltaCellStatusBarItems(oldItems: string[], newItems: INotebookDeltaCellStatusBarItems[]): string[]; getFoldedLength(index: number): number; + getFoldingStartIndex(index: number): number; replaceOne(cell: ICellViewModel, range: Range, text: string): Promise; replaceAll(matches: CellFindMatchWithIndex[], texts: string[]): Promise; } @@ -492,7 +496,6 @@ export interface INotebookEditor { readonly onDidChangeActiveKernel: Event; readonly onMouseUp: Event; readonly onMouseDown: Event; - //#endregion //#region readonly properties @@ -889,6 +892,10 @@ export function getNotebookEditorFromEditorPane(editorPane?: IEditorPane): INote return editorPane.getControl() as INotebookEditor | undefined; } + if (editorPane.getId() === NotebookTextDiffEditor.ID) { + return (editorPane.getControl() as INotebookTextDiffEditor).inlineNotebookEditor; + } + const input = editorPane.input; const isCompositeNotebook = input && isCompositeNotebookEditorInput(input); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 743b101a4f30..b289cdb6142c 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -45,7 +45,6 @@ import { EnablementState } from '../../../services/extensionManagement/common/ex import { IWorkingCopyBackupService } from '../../../services/workingCopy/common/workingCopyBackup.js'; import { streamToBuffer } from '../../../../base/common/buffer.js'; import { ILogService } from '../../../../platform/log/common/log.js'; -import { INotebookEditorWorkerService } from '../common/services/notebookWorkerService.js'; import { IPreferencesService } from '../../../services/preferences/common/preferences.js'; import { IActionViewItemOptions } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; import { StopWatch } from '../../../../base/common/stopwatch.js'; @@ -96,7 +95,6 @@ export class NotebookEditor extends EditorPane implements INotebookEditorPane, I @IExtensionsWorkbenchService private readonly _extensionsWorkbenchService: IExtensionsWorkbenchService, @IWorkingCopyBackupService private readonly _workingCopyBackupService: IWorkingCopyBackupService, @ILogService private readonly logService: ILogService, - @INotebookEditorWorkerService private readonly _notebookEditorWorkerService: INotebookEditorWorkerService, @IPreferencesService private readonly _preferencesService: IPreferencesService ) { super(NotebookEditor.ID, group, telemetryService, themeService, storageService); @@ -146,7 +144,7 @@ export class NotebookEditor extends EditorPane implements INotebookEditorPane, I override getActionViewItem(action: IAction, options: IActionViewItemOptions): IActionViewItem | undefined { if (action.id === SELECT_KERNEL_ID) { // this is being disposed by the consumer - return this._instantiationService.createInstance(NotebooKernelActionViewItem, action, this, options); + return this._register(this._instantiationService.createInstance(NotebooKernelActionViewItem, action, this, options)); } return undefined; } @@ -334,7 +332,7 @@ export class NotebookEditor extends EditorPane implements INotebookEditorPane, I } this._handlePerfMark(perf, input, model.notebook); - this._handlePromptRecommendations(model.notebook); + this._onDidChangeControl.fire(); } catch (e) { this.logService.warn('NotebookEditorWidget#setInput failed', e); if (isEditorOpenError(e)) { @@ -506,24 +504,6 @@ export class NotebookEditor extends EditorPane implements INotebookEditorPane, I }); } - private _handlePromptRecommendations(model: NotebookTextModel) { - this._notebookEditorWorkerService.canPromptRecommendation(model.uri).then(shouldPrompt => { - type WorkbenchNotebookShouldPromptRecommendationClassification = { - owner: 'rebornix'; - comment: 'The notebook file metrics. Used to get a better understanding of if we should prompt for notebook extension recommendations'; - shouldPrompt: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Should we prompt for notebook extension recommendations' }; - }; - - type WorkbenchNotebookShouldPromptRecommendationEvent = { - shouldPrompt: boolean; - }; - - this.telemetryService.publicLog2('notebook/shouldPromptRecommendation', { - shouldPrompt: shouldPrompt - }); - }); - } - override clearInput(): void { this._inputListener.clear(); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 1d4ee1948bea..9e0b6f8cf18b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -17,7 +17,10 @@ import './media/notebookCellOutput.css'; import './media/notebookEditorStickyScroll.css'; import './media/notebookKernelActionViewItem.css'; import './media/notebookOutline.css'; +import './media/notebookChatEditController.css'; +import './media/notebookChatEditorOverlay.css'; import * as DOM from '../../../../base/browser/dom.js'; +import * as domStylesheets from '../../../../base/browser/domStylesheets.js'; import { IMouseWheelEvent, StandardMouseEvent } from '../../../../base/browser/mouseEvent.js'; import { IListContextMenuEvent } from '../../../../base/browser/ui/list/list.js'; import { mainWindow } from '../../../../base/browser/window.js'; @@ -77,7 +80,6 @@ import { NotebookTextModel } from '../common/model/notebookTextModel.js'; import { CellEditType, CellKind, INotebookFindOptions, NotebookFindScopeType, RENDERER_NOT_AVAILABLE, SelectionStateType } from '../common/notebookCommon.js'; import { NOTEBOOK_CURSOR_NAVIGATION_MODE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_OUTPUT_FOCUSED, NOTEBOOK_OUTPUT_INPUT_FOCUSED } from '../common/notebookContextKeys.js'; import { INotebookExecutionService } from '../common/notebookExecutionService.js'; -import { INotebookExecutionStateService } from '../common/notebookExecutionStateService.js'; import { INotebookKernelService } from '../common/notebookKernelService.js'; import { NotebookOptions, OutputInnerContainerTopPadding } from './notebookOptions.js'; import { cellRangesToIndexes, ICellRange } from '../common/notebookRange.js'; @@ -96,12 +98,12 @@ import { Schemas } from '../../../../base/common/network.js'; import { DropIntoEditorController } from '../../../../editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.js'; import { CopyPasteController } from '../../../../editor/contrib/dropOrPasteInto/browser/copyPasteController.js'; import { NotebookStickyScroll } from './viewParts/notebookEditorStickyScroll.js'; -import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { PixelRatio } from '../../../../base/browser/pixelRatio.js'; import { PreventDefaultContextMenuItemsContextKeyName } from '../../webview/browser/webview.contribution.js'; import { NotebookAccessibilityProvider } from './notebookAccessibilityProvider.js'; import { NotebookHorizontalTracker } from './viewParts/notebookHorizontalTracker.js'; import { NotebookCellEditorPool } from './view/notebookCellEditorPool.js'; +import { InlineCompletionsController } from '../../../../editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.js'; const $ = DOM.$; @@ -281,7 +283,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } get visibleRanges() { - return this._list.visibleRanges || []; + return this._list ? (this._list.visibleRanges || []) : []; } private _baseCellEditorOptions = new Map(); @@ -314,10 +316,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD @IContextMenuService private readonly contextMenuService: IContextMenuService, @ITelemetryService private readonly telemetryService: ITelemetryService, @INotebookExecutionService private readonly notebookExecutionService: INotebookExecutionService, - @INotebookExecutionStateService private readonly notebookExecutionStateService: INotebookExecutionStateService, @IEditorProgressService private editorProgressService: IEditorProgressService, @INotebookLoggingService private readonly logService: INotebookLoggingService, - @IKeybindingService private readonly keybindingService: IKeybindingService, ) { super(); @@ -640,7 +640,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } private _createLayoutStyles(): void { - this._styleElement = DOM.createStyleSheet(this._body); + this._styleElement = domStylesheets.createStyleSheet(this._body); const { cellRightMargin, cellTopMargin, @@ -816,6 +816,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .code-cell-row div.cell.code { margin-left: ${getCellEditorContainerLeftMargin}px; }`); + // Chat Edit, deleted Cell Overlay + styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .view-zones .code-cell-row div.cell.code { margin-left: ${getCellEditorContainerLeftMargin}px; }`); + // Chat Edit, deleted Cell Overlay + styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .view-zones .code-cell-row div.cell { margin-right: ${cellRightMargin}px; }`); styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row div.cell { margin-right: ${cellRightMargin}px; }`); styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row > .cell-inner-container { padding-top: ${cellTopMargin}px; }`); styleSheets.push(`.notebookOverlay .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .markdown-cell-row > .cell-inner-container { padding-bottom: ${markdownCellBottomMargin}px; padding-top: ${markdownCellTopMargin}px; }`); @@ -852,7 +856,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-shadow-container-bottom { top: ${cellBottomMargin}px; }`); styleSheets.push(` - .notebookOverlay .monaco-list .monaco-list-row:has(+ .monaco-list-row.selected) .cell-focus-indicator-bottom { + .notebookOverlay .monaco-list.selection-multiple .monaco-list-row:has(+ .monaco-list-row.selected) .cell-focus-indicator-bottom { height: ${bottomToolbarGap + cellBottomMargin}px; } `); @@ -931,7 +935,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._listDelegate = this.instantiationService.createInstance(NotebookCellListDelegate, DOM.getWindow(this.getDomNode())); this._register(this._listDelegate); - const accessibilityProvider = new NotebookAccessibilityProvider(this.notebookExecutionStateService, () => this.viewModel, this.keybindingService, this.configurationService, this.isReplHistory); + const accessibilityProvider = this.instantiationService.createInstance(NotebookAccessibilityProvider, () => this.viewModel, this.isReplHistory); this._register(accessibilityProvider); this._list = this.instantiationService.createInstance( @@ -1169,18 +1173,21 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD scheme: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'File system provider scheme for the resource' }; ext: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'File extension for the resource' }; viewType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'View type of the notebook editor' }; + isRepl: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the notebook editor is within a REPL editor' }; }; type WorkbenchNotebookOpenEvent = { scheme: string; ext: string; viewType: string; + isRepl: boolean; }; this.telemetryService.publicLog2('notebook/editorOpened', { scheme: textModel.uri.scheme, ext: extname(textModel.uri), - viewType: textModel.viewType + viewType: textModel.viewType, + isRepl: this.isReplHistory }); } else { this.restoreListViewState(viewState); @@ -1607,6 +1614,28 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } })); + store.add(cell.onCellDecorationsChanged(e => { + e.added.forEach(options => { + if (options.className) { + this.deltaCellContainerClassNames(cell.id, [options.className], []); + } + + if (options.outputClassName) { + this.deltaCellContainerClassNames(cell.id, [options.outputClassName], []); + } + }); + + e.removed.forEach(options => { + if (options.className) { + this.deltaCellContainerClassNames(cell.id, [], [options.className]); + } + + if (options.outputClassName) { + this.deltaCellContainerClassNames(cell.id, [], [options.outputClassName]); + } + }); + })); + return store; } @@ -2034,6 +2063,13 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD CopyPasteController.get(editor)?.clearWidgets(); } }); + + this._renderedEditors.forEach((editor, cell) => { + const controller = InlineCompletionsController.get(editor); + if (controller?.model.get()?.inlineEditState.get()) { + editor.render(true); + } + }); } private editorHasDomFocus(): boolean { @@ -2307,6 +2343,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD //#region Cell operations/layout API private _pendingLayouts: WeakMap | null = new WeakMap(); + private _layoutDisposables: Set = new Set(); async layoutNotebookCell(cell: ICellViewModel, height: number, context?: CellLayoutContext): Promise { this._debug('layout cell', cell.handle, height); const viewIndex = this._list.getViewIndex(cell); @@ -2339,6 +2376,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return; } + const pendingLayout = this._pendingLayouts?.get(cell); this._pendingLayouts?.delete(cell); if (!this.hasEditorFocus()) { @@ -2357,15 +2395,21 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._list.updateElementHeight2(cell, height); deferred.complete(undefined); + if (pendingLayout) { + pendingLayout.dispose(); + this._layoutDisposables.delete(pendingLayout); + } }; if (this._list.inRenderingTransaction) { const layoutDisposable = DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this.getDomNode()), doLayout); - this._pendingLayouts?.set(cell, toDisposable(() => { + const disposable = toDisposable(() => { layoutDisposable.dispose(); deferred.complete(undefined); - })); + }); + this._pendingLayouts?.set(cell, disposable); + this._layoutDisposables.add(disposable); } else { doLayout(); } @@ -2423,6 +2467,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD cell.focusedOutputId = undefined; if (focusItem === 'editor') { + cell.isInputCollapsed = false; this.focusElement(cell); this._list.focusView(); @@ -2448,6 +2493,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } else { await this.revealInView(cell); } + } } @@ -2594,6 +2640,37 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return Promise.all(requests); } + private async _warmupSelection(includeOutput: boolean, selectedCellRanges: ICellRange[]) { + if (!this.hasModel() || !this.viewModel) { + return; + } + + const cells = this.viewModel.viewCells; + const requests = []; + + for (const range of selectedCellRanges) { + for (let i = range.start; i < range.end; i++) { + if (cells[i].cellKind === CellKind.Markup && !this._webview!.markupPreviewMapping.has(cells[i].id)) { + requests.push(this.createMarkupPreview(cells[i])); + } + } + } + + if (includeOutput && this._list) { + for (const range of selectedCellRanges) { + for (let i = range.start; i < range.end; i++) { + const cell = this._list.element(i); + + if (cell?.cellKind === CellKind.Code) { + requests.push(this._warmupCell((cell as CodeCellViewModel))); + } + } + } + } + + return Promise.all(requests); + } + async find(query: string, options: INotebookFindOptions, token: CancellationToken, skipWarmup: boolean = false, shouldGetSearchPreviewInfo = false, ownerID?: string): Promise { if (!this._notebookViewModel) { return []; @@ -2618,10 +2695,14 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD }); if (this._webview) { - // request all outputs to be rendered + // request all or some outputs to be rendered // measure perf const start = Date.now(); - await this._warmupAll(!!options.includeOutput); + if (options.findScope && options.findScope.findScopeType === NotebookFindScopeType.Cells && options.findScope.selectedCellRanges) { + await this._warmupSelection(!!options.includeOutput, options.findScope.selectedCellRanges); + } else { + await this._warmupAll(!!options.includeOutput); + } const end = Date.now(); this.logService.debug('Find', `Warmup time: ${end - start}ms`); @@ -3062,12 +3143,16 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD const cell = this.viewModel?.viewCells.find(vc => vc.handle === cellInfo.cellHandle); if (cell && cell instanceof CodeCellViewModel) { const outputIndex = cell.outputsViewModels.indexOf(output); - this._debug('update cell output', cell.handle, outputHeight); - cell.updateOutputHeight(outputIndex, outputHeight, source); - this.layoutNotebookCell(cell, cell.layoutInfo.totalHeight); + if (outputIndex > -1) { + this._debug('update cell output', cell.handle, outputHeight); + cell.updateOutputHeight(outputIndex, outputHeight, source); + this.layoutNotebookCell(cell, cell.layoutInfo.totalHeight); - if (isInit) { - this._onDidRenderOutput.fire(output); + if (isInit) { + this._onDidRenderOutput.fire(output); + } + } else { + this._debug('tried to update cell output that does not exist'); } } } @@ -3190,6 +3275,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._webview?.dispose(); this._webview = null; + this._layoutDisposables.forEach(d => d.dispose()); + this.notebookEditorService.removeNotebookEditor(this); dispose(this._contributions.values()); this._contributions.clear(); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookOptions.ts b/src/vs/workbench/contrib/notebook/browser/notebookOptions.ts index 634b9cdb5bb1..5edd1b7272e2 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookOptions.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookOptions.ts @@ -7,6 +7,7 @@ import { PixelRatio } from '../../../../base/browser/pixelRatio.js'; import { CodeWindow } from '../../../../base/browser/window.js'; import { Emitter } from '../../../../base/common/event.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; +import { observableValue } from '../../../../base/common/observable.js'; import { isObject } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; import { FontMeasurements } from '../../../../editor/browser/config/fontMeasurements.js'; @@ -14,6 +15,7 @@ import { ICodeEditorService } from '../../../../editor/browser/services/codeEdit import { IEditorOptions } from '../../../../editor/common/config/editorOptions.js'; import { BareFontInfo } from '../../../../editor/common/config/fontInfo.js'; import { ConfigurationTarget, IConfigurationChangeEvent, IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { NotebookTextModel } from '../common/model/notebookTextModel.js'; import { InteractiveWindowCollapseCodeCells, NotebookCellDefaultCollapseConfig, NotebookCellInternalMetadata, NotebookSetting, ShowCellStatusBarType } from '../common/notebookCommon.js'; import { INotebookExecutionStateService } from '../common/notebookExecutionStateService.js'; @@ -53,6 +55,8 @@ export interface NotebookDisplayOptions { // TODO @Yoyokrazy rename to a more ge 'editor.tabSize': number; 'editor.insertSpaces': boolean; }> | undefined; + markupFontFamily: string; + disableRulers: boolean | undefined; } export interface NotebookLayoutConfiguration { @@ -108,6 +112,7 @@ export interface NotebookOptionsChangeEvent { readonly outputLinkifyFilePaths?: boolean; readonly minimalError?: boolean; readonly readonly?: boolean; + readonly markupFontFamily?: boolean; } const defaultConfigConstants = Object.freeze({ @@ -136,10 +141,12 @@ export class NotebookOptions extends Disposable { readonly onDidChangeOptions = this._onDidChangeOptions.event; private _editorTopPadding: number = 12; + readonly previousModelToCompare = observableValue('previousModelToCompare', undefined); + constructor( readonly targetWindow: CodeWindow, private isReadonly: boolean, - private readonly overrides: { cellToolbarInteraction: string; globalToolbar: boolean; stickyScrollEnabled: boolean; dragAndDropEnabled: boolean } | undefined, + private readonly overrides: { cellToolbarInteraction: string; globalToolbar: boolean; stickyScrollEnabled: boolean; dragAndDropEnabled: boolean; disableRulers: boolean } | undefined, @IConfigurationService private readonly configurationService: IConfigurationService, @INotebookExecutionStateService private readonly notebookExecutionStateService: INotebookExecutionStateService, @ICodeEditorService private readonly codeEditorService: ICodeEditorService, @@ -213,6 +220,7 @@ export class NotebookOptions extends Disposable { const outputLineLimit = this.configurationService.getValue(NotebookSetting.textOutputLineLimit) ?? 30; const linkifyFilePaths = this.configurationService.getValue(NotebookSetting.LinkifyOutputFilePaths) ?? true; const minimalErrors = this.configurationService.getValue(NotebookSetting.minimalErrorRendering); + const markupFontFamily = this.configurationService.getValue(NotebookSetting.markupFontFamily); const editorTopPadding = this._computeEditorTopPadding(); @@ -259,7 +267,9 @@ export class NotebookOptions extends Disposable { outputWordWrap: outputWordWrap, outputLineLimit: outputLineLimit, outputLinkifyFilePaths: linkifyFilePaths, - outputMinimalError: minimalErrors + outputMinimalError: minimalErrors, + markupFontFamily, + disableRulers: overrides?.disableRulers, }; this._register(this.configurationService.onDidChangeConfiguration(e => { @@ -426,6 +436,7 @@ export class NotebookOptions extends Disposable { const outputWordWrap = e.affectsConfiguration(NotebookSetting.outputWordWrap); const outputLinkifyFilePaths = e.affectsConfiguration(NotebookSetting.LinkifyOutputFilePaths); const minimalError = e.affectsConfiguration(NotebookSetting.minimalErrorRendering); + const markupFontFamily = e.affectsConfiguration(NotebookSetting.markupFontFamily); if ( !cellStatusBarVisibility @@ -454,7 +465,8 @@ export class NotebookOptions extends Disposable { && !outputScrolling && !outputWordWrap && !outputLinkifyFilePaths - && !minimalError) { + && !minimalError + && !markupFontFamily) { return; } @@ -569,6 +581,10 @@ export class NotebookOptions extends Disposable { configuration.outputMinimalError = this.configurationService.getValue(NotebookSetting.minimalErrorRendering); } + if (markupFontFamily) { + configuration.markupFontFamily = this.configurationService.getValue(NotebookSetting.markupFontFamily); + } + this._layoutConfiguration = Object.freeze(configuration); // trigger event @@ -599,7 +615,8 @@ export class NotebookOptions extends Disposable { outputScrolling, outputWordWrap, outputLinkifyFilePaths, - minimalError + minimalError, + markupFontFamily }); } @@ -814,7 +831,8 @@ export class NotebookOptions extends Disposable { outputWordWrap: this._layoutConfiguration.outputWordWrap, outputLineLimit: this._layoutConfiguration.outputLineLimit, outputLinkifyFilePaths: this._layoutConfiguration.outputLinkifyFilePaths, - minimalError: this._layoutConfiguration.outputMinimalError + minimalError: this._layoutConfiguration.outputMinimalError, + markupFontFamily: this._layoutConfiguration.markupFontFamily }; } @@ -838,7 +856,8 @@ export class NotebookOptions extends Disposable { outputWordWrap: this._layoutConfiguration.outputWordWrap, outputLineLimit: this._layoutConfiguration.outputLineLimit, outputLinkifyFilePaths: false, - minimalError: false + minimalError: false, + markupFontFamily: this._layoutConfiguration.markupFontFamily }; } diff --git a/src/vs/workbench/contrib/notebook/browser/replEditorAccessibleView.ts b/src/vs/workbench/contrib/notebook/browser/replEditorAccessibleView.ts new file mode 100644 index 000000000000..7c55c37b9019 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/replEditorAccessibleView.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; +import { AccessibleViewType, AccessibleContentProvider, AccessibleViewProviderId } from '../../../../platform/accessibility/browser/accessibleView.js'; +import { IAccessibleViewImplementation } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js'; +import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; +import { IEditorService } from '../../../services/editor/common/editorService.js'; +import { AccessibilityVerbositySettingId } from '../../accessibility/browser/accessibilityConfiguration.js'; +import { isReplEditorControl } from '../../replNotebook/browser/replEditor.js'; +import { IS_COMPOSITE_NOTEBOOK, NOTEBOOK_CELL_LIST_FOCUSED } from '../common/notebookContextKeys.js'; +import { getAllOutputsText } from './viewModel/cellOutputTextHelper.js'; + +/** + * The REPL input is already accessible, so we can show a view for the most recent execution output. + */ +export class ReplEditorAccessibleView implements IAccessibleViewImplementation { + readonly priority = 100; + readonly name = 'replEditorInput'; + readonly type = AccessibleViewType.View; + readonly when = ContextKeyExpr.and(IS_COMPOSITE_NOTEBOOK, NOTEBOOK_CELL_LIST_FOCUSED.negate()); + getProvider(accessor: ServicesAccessor) { + const editorService = accessor.get(IEditorService); + return getAccessibleOutputProvider(editorService); + } +} + +export function getAccessibleOutputProvider(editorService: IEditorService) { + const editorControl = editorService.activeEditorPane?.getControl(); + + if (editorControl && isReplEditorControl(editorControl) && editorControl.notebookEditor) { + const notebookEditor = editorControl.notebookEditor; + const viewModel = notebookEditor?.getViewModel(); + if (notebookEditor && viewModel) { + // last cell of the viewmodel is the last cell history + const lastCellIndex = viewModel.length - 1; + if (lastCellIndex >= 0) { + const cell = viewModel.viewCells[lastCellIndex]; + const outputContent = getAllOutputsText(viewModel.notebookDocument, cell); + + if (outputContent) { + return new AccessibleContentProvider( + AccessibleViewProviderId.Notebook, + { type: AccessibleViewType.View }, + () => { return outputContent; }, + () => { + editorControl.activeCodeEditor?.focus(); + }, + AccessibilityVerbositySettingId.ReplEditor, + ); + } + } + } + } + + return; +} diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts index a292db61aa19..c87d4ec7cc9f 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorService.ts @@ -5,7 +5,6 @@ import { CodeWindow } from '../../../../../base/browser/window.js'; import { createDecorator, ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; -import { NotebookEditorInput } from '../../common/notebookEditorInput.js'; import { INotebookEditor, INotebookEditorCreationOptions } from '../notebookBrowser.js'; import { Event } from '../../../../../base/common/event.js'; import { Dimension } from '../../../../../base/browser/dom.js'; @@ -21,7 +20,7 @@ export interface IBorrowValue { export interface INotebookEditorService { _serviceBrand: undefined; - retrieveWidget(accessor: ServicesAccessor, groupId: number, input: NotebookEditorInput, creationOptions?: INotebookEditorCreationOptions, dimension?: Dimension, codeWindow?: CodeWindow): IBorrowValue; + retrieveWidget(accessor: ServicesAccessor, groupId: number, input: { resource: URI; typeId: string }, creationOptions?: INotebookEditorCreationOptions, dimension?: Dimension, codeWindow?: CodeWindow): IBorrowValue; retrieveExistingWidgetFromURI(resource: URI): IBorrowValue | undefined; retrieveAllExistingWidgets(): IBorrowValue[]; @@ -31,4 +30,5 @@ export interface INotebookEditorService { removeNotebookEditor(editor: INotebookEditor): void; getNotebookEditor(editorId: string): INotebookEditor | undefined; listNotebookEditors(): readonly INotebookEditor[]; + updateReplContextKey(uri: string): void; } diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts index 412c6f77621c..c4612d460489 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts @@ -17,10 +17,11 @@ import { GroupIdentifier, GroupModelChangeKind } from '../../../../common/editor import { Dimension } from '../../../../../base/browser/dom.js'; import { URI } from '../../../../../base/common/uri.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; -import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; -import { InteractiveWindowOpen } from '../../common/notebookContextKeys.js'; +import { IContextKey, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; +import { InteractiveWindowOpen, MOST_RECENT_REPL_EDITOR } from '../../common/notebookContextKeys.js'; import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js'; import { IEditorProgressService } from '../../../../../platform/progress/common/progress.js'; +import { NotebookDiffEditorInput } from '../../common/notebookDiffEditorInput.js'; export class NotebookEditorWidgetService implements INotebookEditorService { @@ -38,7 +39,9 @@ export class NotebookEditorWidgetService implements INotebookEditorService { readonly onDidAddNotebookEditor = this._onNotebookEditorAdd.event; readonly onDidRemoveNotebookEditor = this._onNotebookEditorsRemove.event; - private readonly _borrowableEditors = new Map>(); + private readonly _mostRecentRepl: IContextKey; + + private readonly _borrowableEditors = new Map>(); constructor( @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @@ -55,7 +58,9 @@ export class NotebookEditorWidgetService implements INotebookEditorService { return; } - const inputs = e.editor instanceof NotebookEditorInput ? [e.editor] : (isCompositeNotebookEditorInput(e.editor) ? e.editor.editorInputs : []); + const inputs = e.editor instanceof NotebookEditorInput || e.editor instanceof NotebookDiffEditorInput + ? [e.editor] + : (isCompositeNotebookEditorInput(e.editor) ? e.editor.editorInputs : []); inputs.forEach(input => { const widgets = widgetMap.get(input.resource); const index = widgets?.findIndex(widget => widget.editorType === input.typeId); @@ -65,6 +70,7 @@ export class NotebookEditorWidgetService implements INotebookEditorService { const value = widgets.splice(index, 1)[0]; value.token = undefined; this._disposeWidget(value.widget); + value.disposableStore.dispose(); value.widget = (undefined); // unset the widget so that others that still hold a reference don't harm us }); })); @@ -98,11 +104,13 @@ export class NotebookEditorWidgetService implements INotebookEditorService { for (const value of values) { value.token = undefined; this._disposeWidget(value.widget); + value.disposableStore.dispose(); } } } })); + this._mostRecentRepl = MOST_RECENT_REPL_EDITOR.bindTo(contextKeyService); const interactiveWindowOpen = InteractiveWindowOpen.bindTo(contextKeyService); this._disposables.add(editorService.onDidEditorsChange(e => { if (e.event.kind === GroupModelChangeKind.EDITOR_OPEN && !interactiveWindowOpen.get()) { @@ -125,6 +133,11 @@ export class NotebookEditorWidgetService implements INotebookEditorService { listeners.forEach(listener => listener.dispose()); }); this.groupListener.clear(); + this._borrowableEditors.forEach(widgetMap => { + widgetMap.forEach(widgets => { + widgets.forEach(widget => widget.disposableStore.dispose()); + }); + }); } // --- group-based editor borrowing... @@ -197,7 +210,7 @@ export class NotebookEditorWidgetService implements INotebookEditorService { return ret; } - retrieveWidget(accessor: ServicesAccessor, groupId: number, input: NotebookEditorInput, creationOptions?: INotebookEditorCreationOptions, initialDimension?: Dimension, codeWindow?: CodeWindow): IBorrowValue { + retrieveWidget(accessor: ServicesAccessor, groupId: number, input: { resource: URI; typeId: string }, creationOptions?: INotebookEditorCreationOptions, initialDimension?: Dimension, codeWindow?: CodeWindow): IBorrowValue { let value = this._borrowableEditors.get(groupId)?.get(input.resource)?.find(widget => widget.editorType === input.typeId); @@ -205,9 +218,10 @@ export class NotebookEditorWidgetService implements INotebookEditorService { // NEW widget const editorGroupContextKeyService = accessor.get(IContextKeyService); const editorGroupEditorProgressService = accessor.get(IEditorProgressService); - const widget = this.createWidget(editorGroupContextKeyService, editorGroupEditorProgressService, creationOptions, codeWindow, initialDimension); + const widgetDisposeStore = new DisposableStore(); + const widget = this.createWidget(editorGroupContextKeyService, widgetDisposeStore, editorGroupEditorProgressService, creationOptions, codeWindow, initialDimension); const token = this._tokenPool++; - value = { widget, editorType: input.typeId, token }; + value = { widget, editorType: input.typeId, token, disposableStore: widgetDisposeStore }; let map = this._borrowableEditors.get(groupId); if (!map) { @@ -217,7 +231,6 @@ export class NotebookEditorWidgetService implements INotebookEditorService { const values = map.get(input.resource) ?? []; values.push(value); map.set(input.resource, values); - } else { // reuse a widget which was either free'ed before or which // is simply being reused... @@ -228,10 +241,10 @@ export class NotebookEditorWidgetService implements INotebookEditorService { } // protected for unit testing overrides - protected createWidget(editorGroupContextKeyService: IContextKeyService, editorGroupEditorProgressService: IEditorProgressService, creationOptions?: INotebookEditorCreationOptions, codeWindow?: CodeWindow, initialDimension?: Dimension) { - const notebookInstantiationService = this.instantiationService.createChild(new ServiceCollection( + protected createWidget(editorGroupContextKeyService: IContextKeyService, widgetDisposeStore: DisposableStore, editorGroupEditorProgressService: IEditorProgressService, creationOptions?: INotebookEditorCreationOptions, codeWindow?: CodeWindow, initialDimension?: Dimension) { + const notebookInstantiationService = widgetDisposeStore.add(this.instantiationService.createChild(new ServiceCollection( [IContextKeyService, editorGroupContextKeyService], - [IEditorProgressService, editorGroupEditorProgressService])); + [IEditorProgressService, editorGroupEditorProgressService]))); const ctorOptions = creationOptions ?? getDefaultNotebookCreationOptions(); const widget = notebookInstantiationService.createInstance(NotebookEditorWidget, { ...ctorOptions, @@ -256,10 +269,14 @@ export class NotebookEditorWidgetService implements INotebookEditorService { } removeNotebookEditor(editor: INotebookEditor): void { + const notebookUri = editor.getViewModel()?.notebookDocument.uri; if (this._notebookEditors.has(editor.getId())) { this._notebookEditors.delete(editor.getId()); this._onNotebookEditorsRemove.fire(editor); } + if (this._mostRecentRepl.get() === notebookUri?.toString()) { + this._mostRecentRepl.reset(); + } } getNotebookEditor(editorId: string): INotebookEditor | undefined { @@ -269,4 +286,8 @@ export class NotebookEditorWidgetService implements INotebookEditorService { listNotebookEditors(): readonly INotebookEditor[] { return [...this._notebookEditors].map(e => e[1]); } + + updateReplContextKey(uri: string): void { + this._mostRecentRepl.set(uri); + } } diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookExecutionStateServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookExecutionStateServiceImpl.ts index 6541654dc3ed..fb4f67329e30 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookExecutionStateServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookExecutionStateServiceImpl.ts @@ -27,6 +27,7 @@ export class NotebookExecutionStateService extends Disposable implements INotebo private readonly _notebookListeners = new ResourceMap(); private readonly _cellListeners = new ResourceMap(); private readonly _lastFailedCells = new ResourceMap(); + private readonly _lastCompletedCellHandles = new ResourceMap(); private readonly _onDidChangeExecution = this._register(new Emitter()); onDidChangeExecution = this._onDidChangeExecution.event; @@ -48,6 +49,10 @@ export class NotebookExecutionStateService extends Disposable implements INotebo return failedCell?.visible ? failedCell.cellHandle : undefined; } + getLastCompletedCellForNotebook(notebook: URI): number | undefined { + return this._lastCompletedCellHandles.get(notebook); + } + forceCancelNotebookExecutions(notebookUri: URI): void { const notebookCellExecutions = this._executions.get(notebookUri); if (notebookCellExecutions) { @@ -119,6 +124,7 @@ export class NotebookExecutionStateService extends Disposable implements INotebo this._accessibilitySignalService.playSignal(AccessibilitySignal.notebookCellFailed); this._setLastFailedCell(notebookUri, cellHandle); } + this._lastCompletedCellHandles.set(notebookUri, cellHandle); } this._onDidChangeExecution.fire(new NotebookCellExecutionEvent(notebookUri, cellHandle)); diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl.ts index e0ae64391ac6..5ae7776e3d45 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookKernelHistoryServiceImpl.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; +import { Disposable } from '../../../../../base/common/lifecycle.js'; import { LinkedMap, Touch } from '../../../../../base/common/map.js'; import { localize2 } from '../../../../../nls.js'; import { Categories } from '../../../../../platform/action/common/actionCommonCategories.js'; @@ -36,7 +36,7 @@ export class NotebookKernelHistoryService extends Disposable implements INoteboo this._loadState(); this._register(this._storageService.onWillSaveState(() => this._saveState())); - this._register(this._storageService.onDidChangeValue(StorageScope.WORKSPACE, NotebookKernelHistoryService.STORAGE_KEY, this._register(new DisposableStore()))(() => { + this._register(this._storageService.onDidChangeValue(StorageScope.WORKSPACE, NotebookKernelHistoryService.STORAGE_KEY, this._store)(() => { this._loadState(); })); } diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookLoggingServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookLoggingServiceImpl.ts index 37aa5e99befc..93e248e3c745 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookLoggingServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookLoggingServiceImpl.ts @@ -7,6 +7,7 @@ import * as nls from '../../../../../nls.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; import { INotebookLoggingService } from '../../common/notebookLoggingService.js'; import { ILogger, ILoggerService } from '../../../../../platform/log/common/log.js'; +import { windowLogGroup } from '../../../../services/log/common/logConstants.js'; const logChannelId = 'notebook.rendering'; @@ -20,7 +21,7 @@ export class NotebookLoggingService extends Disposable implements INotebookLoggi @ILoggerService loggerService: ILoggerService, ) { super(); - this._logger = this._register(loggerService.createLogger(logChannelId, { name: nls.localize('renderChannelName', "Notebook") })); + this._logger = this._register(loggerService.createLogger(logChannelId, { name: nls.localize('renderChannelName', "Notebook"), group: windowLogGroup })); } debug(category: string, output: string): void { diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts index c57cb325bb36..f2925b1b8c87 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts @@ -28,7 +28,7 @@ import { INotebookEditorOptions } from '../notebookBrowser.js'; import { NotebookDiffEditorInput } from '../../common/notebookDiffEditorInput.js'; import { NotebookCellTextModel } from '../../common/model/notebookCellTextModel.js'; import { NotebookTextModel } from '../../common/model/notebookTextModel.js'; -import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellUri, NotebookSetting, INotebookContributionData, INotebookExclusiveDocumentFilter, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, MimeTypeDisplayOrder, NotebookEditorPriority, NotebookRendererMatch, NOTEBOOK_DISPLAY_ORDER, RENDERER_EQUIVALENT_EXTENSIONS, RENDERER_NOT_AVAILABLE, NotebookExtensionDescription, INotebookStaticPreloadInfo } from '../../common/notebookCommon.js'; +import { ACCESSIBLE_NOTEBOOK_DISPLAY_ORDER, CellUri, NotebookSetting, INotebookContributionData, INotebookExclusiveDocumentFilter, INotebookRendererInfo, INotebookTextModel, IOrderedMimeType, IOutputDto, MimeTypeDisplayOrder, NotebookEditorPriority, NotebookRendererMatch, NOTEBOOK_DISPLAY_ORDER, RENDERER_EQUIVALENT_EXTENSIONS, RENDERER_NOT_AVAILABLE, NotebookExtensionDescription, INotebookStaticPreloadInfo, NotebookData } from '../../common/notebookCommon.js'; import { NotebookEditorInput } from '../../common/notebookEditorInput.js'; import { INotebookEditorModelResolverService } from '../../common/notebookEditorModelResolverService.js'; import { NotebookOutputRendererInfo, NotebookStaticPreloadInfo as NotebookStaticPreloadInfo } from '../../common/notebookOutputRenderer.js'; @@ -42,9 +42,12 @@ import { IUriIdentityService } from '../../../../../platform/uriIdentity/common/ import { INotebookDocument, INotebookDocumentService } from '../../../../services/notebook/common/notebookDocumentService.js'; import { MergeEditorInput } from '../../../mergeEditor/browser/mergeEditorInput.js'; import type { EditorInputWithOptions, IResourceDiffEditorInput, IResourceMergeEditorInput } from '../../../../common/editor.js'; -import { streamToBuffer, VSBuffer, VSBufferReadableStream } from '../../../../../base/common/buffer.js'; +import { bufferToStream, streamToBuffer, VSBuffer, VSBufferReadableStream } from '../../../../../base/common/buffer.js'; import type { IEditorGroup } from '../../../../services/editor/common/editorGroupsService.js'; import { NotebookMultiDiffEditorInput } from '../diff/notebookMultiDiffEditorInput.js'; +import { SnapshotContext } from '../../../../services/workingCopy/common/fileWorkingCopy.js'; +import { CancellationToken } from '../../../../../base/common/cancellation.js'; +import { CancellationError } from '../../../../../base/common/errors.js'; export class NotebookProviderInfoStore extends Disposable { @@ -209,7 +212,7 @@ export class NotebookProviderInfoStore extends Disposable { // untitled notebooks are disposed when they get saved. we should not hold a reference // to such a disposed notebook and therefore dispose the reference as well - ref.object.notebook.onWillDispose(() => { + Event.once(ref.object.notebook.onWillDispose)(() => { ref.dispose(); }); @@ -776,6 +779,57 @@ export class NotebookService extends Disposable implements INotebookService { return notebookModel; } + async createNotebookTextDocumentSnapshot(uri: URI, context: SnapshotContext, token: CancellationToken): Promise { + const model = this.getNotebookTextModel(uri); + + if (!model) { + throw new Error(`notebook for ${uri} doesn't exist`); + } + + const info = await this.withNotebookDataProvider(model.viewType); + + if (!(info instanceof SimpleNotebookProviderInfo)) { + throw new Error('CANNOT open file notebook with this provider'); + } + + const serializer = info.serializer; + const outputSizeLimit = this._configurationService.getValue(NotebookSetting.outputBackupSizeLimit) * 1024; + const data: NotebookData = model.createSnapshot({ context: context, outputSizeLimit: outputSizeLimit, transientOptions: serializer.options }); + const indentAmount = model.metadata.indentAmount; + if (typeof indentAmount === 'string' && indentAmount) { + // This is required for ipynb serializer to preserve the whitespace in the notebook. + data.metadata.indentAmount = indentAmount; + } + const bytes = await serializer.notebookToData(data); + + if (token.isCancellationRequested) { + throw new CancellationError(); + } + return bufferToStream(bytes); + } + + async restoreNotebookTextModelFromSnapshot(uri: URI, viewType: string, snapshot: VSBufferReadableStream): Promise { + const model = this.getNotebookTextModel(uri); + + if (!model) { + throw new Error(`notebook for ${uri} doesn't exist`); + } + + const info = await this.withNotebookDataProvider(model.viewType); + + if (!(info instanceof SimpleNotebookProviderInfo)) { + throw new Error('CANNOT open file notebook with this provider'); + } + + const serializer = info.serializer; + + const bytes = await streamToBuffer(snapshot); + const data = await info.serializer.dataToNotebook(bytes); + model.restoreSnapshot(data, serializer.options); + + return model; + } + getNotebookTextModel(uri: URI): NotebookTextModel | undefined { return this._models.get(uri)?.model; } @@ -816,6 +870,21 @@ export class NotebookService extends Disposable implements INotebookService { return [...this.notebookProviderInfoStore]; } + hasSupportedNotebooks(resource: URI): boolean { + if (this._models.has(resource)) { + // it might be untitled + return true; + } + + const contribution = this.notebookProviderInfoStore.getContributedNotebook(resource); + if (!contribution.length) { + return false; + } + return contribution.some(info => info.matches(resource) && + (info.priority === RegisteredEditorPriority.default || info.priority === RegisteredEditorPriority.exclusive) + ); + } + getContributedNotebookType(viewType: string): NotebookProviderInfo | undefined { return this.notebookProviderInfoStore.get(viewType); } diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookWorkerServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookWorkerServiceImpl.ts index 4a0527917e60..d45bdc91ad19 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookWorkerServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookWorkerServiceImpl.ts @@ -65,6 +65,7 @@ class WorkerManager extends Disposable { // this._lastWorkerUsedTime = (new Date()).getTime(); if (!this._editorWorkerClient) { this._editorWorkerClient = new NotebookWorkerClient(this._notebookService, this._modelService); + this._register(this._editorWorkerClient); } return Promise.resolve(this._editorWorkerClient); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView.ts index f9b8bb771c50..6e742fa265eb 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellActionView.ts @@ -55,7 +55,7 @@ export class UnifiedSubmenuActionView extends SubmenuEntryActionViewItem { constructor( action: SubmenuItemAction, options: IMenuEntryActionViewItemOptions | undefined, - readonly renderLabel: boolean, + private readonly _renderLabel: boolean, readonly subActionProvider: IActionProvider, readonly subActionViewItemProvider: IActionViewItemProvider | undefined, @IKeybindingService _keybindingService: IKeybindingService, @@ -108,13 +108,13 @@ export class UnifiedSubmenuActionView extends SubmenuEntryActionViewItem { element.classList.add(...iconClasses); } - if (this.renderLabel) { + if (this._renderLabel) { this._actionLabel.classList.add('notebook-label'); this._actionLabel.innerText = this._action.label; this._hover?.update(primaryAction.tooltip.length ? primaryAction.tooltip : primaryAction.label); } } else { - if (this.renderLabel) { + if (this._renderLabel) { this._actionLabel.classList.add('notebook-label'); this._actionLabel.innerText = this._action.label; this._hover?.update(this._action.tooltip.length ? this._action.tooltip : this._action.label); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts index bddd86c74ae8..276dd3b8bc47 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts @@ -71,7 +71,7 @@ export class CellComments extends CellContentPart { { actionRunner: () => { }, - collapse: () => { } + collapse: async () => { return true; } } ) as unknown as CommentThreadWidget; widgetDisposables.add(widget); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts index 833605ad37b1..1ea7c34b6bcc 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellContextKeys.ts @@ -103,7 +103,7 @@ export class CellContextKeyManager extends Disposable { if (element instanceof CodeCellViewModel) { this.elementDisposables.add(element.onDidChangeOutputs(() => this.updateForOutputs())); this.elementDisposables.add(autorun(reader => { - this.cellHasErrorDiagnostics.set(!!reader.readObservable(element.excecutionError)); + this.cellHasErrorDiagnostics.set(!!reader.readObservable(element.executionErrorDiagnostic)); })); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDecorations.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDecorations.ts index 98340a20af7e..9068b002c64b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDecorations.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDecorations.ts @@ -59,22 +59,12 @@ export class CellDecorations extends CellContentPart { e.added.forEach(options => { if (options.className && this.currentCell) { this.rootContainer.classList.add(options.className); - this.notebookEditor.deltaCellContainerClassNames(this.currentCell.id, [options.className], []); - } - - if (options.outputClassName && this.currentCell) { - this.notebookEditor.deltaCellContainerClassNames(this.currentCell.id, [options.outputClassName], []); } }); e.removed.forEach(options => { if (options.className && this.currentCell) { this.rootContainer.classList.remove(options.className); - this.notebookEditor.deltaCellContainerClassNames(this.currentCell.id, [], [options.className]); - } - - if (options.outputClassName && this.currentCell) { - this.notebookEditor.deltaCellContainerClassNames(this.currentCell.id, [], [options.outputClassName]); } }); })); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts index 743fdc10a261..58ae569e6e4e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts @@ -133,14 +133,19 @@ export class CellEditorOptions extends CellContentPart implements ITextModelUpda break; } + const overrides: Partial = {}; if (value.lineNumbers !== cellRenderLineNumber) { - return { - ...value, - ...{ lineNumbers: cellRenderLineNumber } - }; - } else { - return Object.assign({}, value); + overrides.lineNumbers = cellRenderLineNumber; + } + + if (this.notebookOptions.getLayoutConfiguration().disableRulers) { + overrides.rulers = []; } + + return { + ...value, + ...overrides, + }; } getUpdatedValue(internalMetadata: NotebookCellInternalMetadata, cellUri: URI): IEditorOptions { diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts index e561cf6e6260..dfeb14060791 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts @@ -6,12 +6,12 @@ import * as DOM from '../../../../../../base/browser/dom.js'; import { FastDomNode } from '../../../../../../base/browser/fastDomNode.js'; import { renderMarkdown } from '../../../../../../base/browser/markdownRenderer.js'; -import { Action, IAction } from '../../../../../../base/common/actions.js'; +import { Action } from '../../../../../../base/common/actions.js'; import { IMarkdownString } from '../../../../../../base/common/htmlContent.js'; import { Disposable, DisposableStore } from '../../../../../../base/common/lifecycle.js'; import { MarshalledId } from '../../../../../../base/common/marshallingIds.js'; import * as nls from '../../../../../../nls.js'; -import { createAndFillInActionBarActions } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; +import { getActionBarActions } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { WorkbenchToolBar } from '../../../../../../platform/actions/browser/toolbar.js'; import { IMenuService, MenuId } from '../../../../../../platform/actions/common/actions.js'; import { IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; @@ -31,9 +31,9 @@ import { INotebookExecutionStateService } from '../../../common/notebookExecutio import { INotebookKernel } from '../../../common/notebookKernelService.js'; import { INotebookService } from '../../../common/notebookService.js'; import { COPY_OUTPUT_COMMAND_ID } from '../../controller/cellOutputActions.js'; -import { TEXT_BASED_MIMETYPES } from '../../contrib/clipboard/cellOutputClipboard.js'; import { autorun, observableValue } from '../../../../../../base/common/observable.js'; -import { NOTEBOOK_CELL_HAS_HIDDEN_OUTPUTS, NOTEBOOK_CELL_IS_FIRST_OUTPUT } from '../../../common/notebookContextKeys.js'; +import { NOTEBOOK_CELL_HAS_HIDDEN_OUTPUTS, NOTEBOOK_CELL_IS_FIRST_OUTPUT, NOTEBOOK_CELL_OUTPUT_MIMETYPE } from '../../../common/notebookContextKeys.js'; +import { TEXT_BASED_MIMETYPES } from '../../viewModel/cellOutputTextHelper.js'; interface IMimeTypeRenderer extends IQuickPickItem { index: number; @@ -327,16 +327,16 @@ class CellOutputElement extends Disposable { const menuContextKeyService = this.toolbarDisposables.add(this.contextKeyService.createScoped(outputItemDiv)); const hasHiddenOutputs = NOTEBOOK_CELL_HAS_HIDDEN_OUTPUTS.bindTo(menuContextKeyService); const isFirstCellOutput = NOTEBOOK_CELL_IS_FIRST_OUTPUT.bindTo(menuContextKeyService); + const cellOutputMimetype = NOTEBOOK_CELL_OUTPUT_MIMETYPE.bindTo(menuContextKeyService); isFirstCellOutput.set(index === 0); + if (mimeTypes[index]) { + cellOutputMimetype.set(mimeTypes[index].mimeType); + } this.toolbarDisposables.add(autorun((reader) => { hasHiddenOutputs.set(reader.readObservable(this.cellOutputContainer.hasHiddenOutputs)); })); const menu = this.toolbarDisposables.add(this.menuService.createMenu(MenuId.NotebookOutputToolbar, menuContextKeyService)); const updateMenuToolbar = () => { - const primary: IAction[] = []; - let secondary: IAction[] = []; - const result = { primary, secondary }; - - createAndFillInActionBarActions(menu!, { shouldForwardArgs: true }, result, () => false); + let { secondary } = getActionBarActions(menu!.getActions({ shouldForwardArgs: true }), () => false); if (!isCopyEnabled) { secondary = secondary.filter((action) => action.id !== COPY_OUTPUT_COMMAND_ID); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellStatusPart.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellStatusPart.ts index f58ff0aa23fd..a23d2f8b28ed 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellStatusPart.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellStatusPart.ts @@ -79,7 +79,7 @@ export class CellEditorStatusBar extends CellContentPart { readonly showHover = (options: IHoverDelegateOptions) => { options.position = options.position ?? {}; options.position.hoverPosition = HoverPosition.ABOVE; - return hoverService.showHover(options); + return hoverService.showInstantHover(options); }; readonly placement = 'element'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts index 4f33acf6f21a..0a4355b1cf75 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellToolbars.ts @@ -10,7 +10,7 @@ import { disposableTimeout } from '../../../../../../base/common/async.js'; import { Emitter, Event } from '../../../../../../base/common/event.js'; import { MarshalledId } from '../../../../../../base/common/marshallingIds.js'; import { ServicesAccessor } from '../../../../../../editor/browser/editorExtensions.js'; -import { createActionViewItem, createAndFillInActionBarActions, MenuEntryActionViewItem } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; +import { createActionViewItem, getActionBarActions, MenuEntryActionViewItem, PrimaryAndSecondaryActions } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IMenu, IMenuService, MenuId, MenuItemAction } from '../../../../../../platform/actions/common/actions.js'; import { IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService } from '../../../../../../platform/contextview/browser/contextView.js'; @@ -270,14 +270,8 @@ export class CellTitleToolbarPart extends CellOverlayPart { } } -function getCellToolbarActions(menu: IMenu): { primary: IAction[]; secondary: IAction[] } { - const primary: IAction[] = []; - const secondary: IAction[] = []; - const result = { primary, secondary }; - - createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); - - return result; +function getCellToolbarActions(menu: IMenu): PrimaryAndSecondaryActions { + return getActionBarActions(menu.getActions({ shouldForwardArgs: true }), g => /^inline/.test(g)); } function createDeleteToolbar(accessor: ServicesAccessor, container: HTMLElement, hoverDelegate: IHoverDelegate, elementClass?: string): ToolBar { diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts index 1bdbbade4389..e5cb2273734e 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCellRunToolbar.ts @@ -10,7 +10,7 @@ import { MarshalledId } from '../../../../../../base/common/marshallingIds.js'; import { EditorContextKeys } from '../../../../../../editor/common/editorContextKeys.js'; import { localize } from '../../../../../../nls.js'; import { DropdownWithPrimaryActionViewItem } from '../../../../../../platform/actions/browser/dropdownWithPrimaryActionViewItem.js'; -import { createAndFillInActionBarActions } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; +import { getActionBarActions } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IMenu, IMenuService, MenuId, MenuItemAction } from '../../../../../../platform/actions/common/actions.js'; import { IContextKeyService, IScopedContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; import { InputFocusedContext } from '../../../../../../platform/contextkey/common/contextkeys.js'; @@ -72,13 +72,7 @@ export class RunToolbar extends CellContentPart { } getCellToolbarActions(menu: IMenu): { primary: IAction[]; secondary: IAction[] } { - const primary: IAction[] = []; - const secondary: IAction[] = []; - const result = { primary, secondary }; - - createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, result, g => /^inline/.test(g)); - - return result; + return getActionBarActions(menu.getActions({ shouldForwardArgs: true }), g => /^inline/.test(g)); } private createRunCellToolbar(container: HTMLElement, cellContainer: HTMLElement, contextKeyService: IContextKeyService) { diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellEditorPool.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellEditorPool.ts index a7d750ea0510..973f914aecba 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellEditorPool.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellEditorPool.ts @@ -64,7 +64,7 @@ export class NotebookCellEditorPool extends Disposable { }, { contributions: this.notebookEditor.creationOptions.cellEditorContributions })); - + editorOptions.dispose(); this._isInitialized = true; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index 2ee5aed69030..ff0678e8b251 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from '../../../../../base/browser/dom.js'; +import * as domStylesheetsJs from '../../../../../base/browser/domStylesheets.js'; import { IMouseWheelEvent } from '../../../../../base/browser/mouseEvent.js'; import { IListRenderer, IListVirtualDelegate, ListError } from '../../../../../base/browser/ui/list/list.js'; import { IListStyles, IStyleController } from '../../../../../base/browser/ui/list/listWidget.js'; @@ -1035,7 +1036,7 @@ export class NotebookCellList extends WorkbenchList implements ID } const editorAttachedPromise = new Promise((resolve, reject) => { - element.onDidChangeEditorAttachState(() => { + Event.once(element.onDidChangeEditorAttachState)(() => { element.editorAttached ? resolve() : reject(); }); }); @@ -1167,7 +1168,8 @@ export class NotebookCellList extends WorkbenchList implements ID const wrapperBottom = this.getViewScrollBottom(); if (offset < scrollTop || offset > wrapperBottom) { - this.view.setScrollTop(offset - this.view.renderHeight / 2); + const newTop = Math.max(0, offset - this.view.renderHeight / 2); + this.view.setScrollTop(newTop); } } @@ -1177,7 +1179,7 @@ export class NotebookCellList extends WorkbenchList implements ID domElementOfElement(element: ICellViewModel): HTMLElement | null { const index = this._getViewIndexUpperBound(element); - if (index >= 0) { + if (index >= 0 && index < this.length) { return this.view.domElement(index); } @@ -1316,7 +1318,7 @@ export class NotebookCellList extends WorkbenchList implements ID override style(styles: IListStyles) { const selectorSuffix = this.view.domId; if (!this.styleElement) { - this.styleElement = DOM.createStyleSheet(this.view.domNode); + this.styleElement = domStylesheetsJs.createStyleSheet(this.view.domNode); } const suffix = selectorSuffix && `.${selectorSuffix}`; const content: string[] = []; @@ -1345,14 +1347,14 @@ export class NotebookCellList extends WorkbenchList implements ID if (styles.listFocusAndSelectionBackground) { content.push(` - .monaco-drag-image, + .monaco-drag-image${suffix}, .monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; } `); } if (styles.listFocusAndSelectionForeground) { content.push(` - .monaco-drag-image, + .monaco-drag-image${suffix}, .monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; } `); } @@ -1385,7 +1387,7 @@ export class NotebookCellList extends WorkbenchList implements ID if (styles.listFocusOutline) { content.push(` - .monaco-drag-image, + .monaco-drag-image${suffix}, .monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; } `); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellListView.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellListView.ts index 16a00de19770..7034d6bb0376 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellListView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellListView.ts @@ -189,9 +189,7 @@ export class NotebookCellsLayout implements IRangeMap { const index = afterPosition - 1; const previousItemPosition = this._prefixSumComputer.getPrefixSum(index); const previousItemSize = this._items[index].size; - const previousWhitespace = this._whitespace.filter(ws => (ws.afterPosition <= afterPosition - 1 && ws.afterPosition > 0)); - const whitespaceBefore = previousWhitespace.reduce((acc, ws) => acc + ws.size, 0); - return previousItemPosition + previousItemSize + whitespaceBeforeFirstItem + this.paddingTop + whitespaceBefore; + return previousItemPosition + previousItemSize + whitespaceBeforeFirstItem + this.paddingTop; } indexAt(position: number): number { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 8eb293b41bac..21e6cf46c139 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -121,6 +121,7 @@ interface BacklayerWebviewOptions { readonly outputLineLimit: number; readonly outputLinkifyFilePaths: boolean; readonly minimalError: boolean; + readonly markupFontFamily: string; } @@ -281,6 +282,7 @@ export class BackLayerWebView extends Themable { key: 'notebook.error.rendererFallbacksExhausted', comment: ['$0 is a placeholder for the mime type'] }, "Could not render content for '$0'"), + 'notebook-markup-font-family': this.options.markupFontFamily, }; } @@ -341,6 +343,10 @@ export class BackLayerWebView extends Themable { width: 100%; } + #container .cell_container.nb-insertHighlight div.output_container div.output { + background-color: var(--vscode-diffEditor-insertedLineBackground, var(--vscode-diffEditor-insertedTextBackground)); + } + #container > div > div > div.output { font-size: var(--notebook-cell-output-font-size); width: var(--notebook-output-width); @@ -370,6 +376,7 @@ export class BackLayerWebView extends Themable { font-size: var(--notebook-markup-font-size); line-height: var(--notebook-markdown-line-height); color: var(--theme-ui-foreground); + font-family: var(--notebook-markup-font-family); } #container div.preview.draggable { @@ -397,6 +404,10 @@ export class BackLayerWebView extends Themable { background-color: var(--theme-notebook-symbol-highlight-background); } + #container .markup > div.nb-insertHighlight { + background-color: var(--vscode-diffEditor-insertedLineBackground, var(--vscode-diffEditor-insertedTextBackground)); + } + #container .nb-symbolHighlight .output_container .output { background-color: var(--theme-notebook-symbol-highlight-background); } @@ -783,7 +794,8 @@ export class BackLayerWebView extends Themable { 'workbench.action.openSettings', '_notebook.selectKernel', // TODO@rebornix explore open output channel with name command - 'jupyter.viewOutput' + 'jupyter.viewOutput', + 'jupyter.createPythonEnvAndSelectController', ], }); return; @@ -923,8 +935,8 @@ export class BackLayerWebView extends Themable { } case 'notebookPerformanceMessage': { this.notebookEditor.updatePerformanceMetadata(data.cellId, data.executionId, data.duration, data.rendererId); - if (data.mimeType && data.outputSize && data.rendererId === 'vscode.builtin-renderer') { - this._sendPerformanceData(data.mimeType, data.outputSize, data.duration); + if (data.outputSize && data.rendererId === 'vscode.builtin-renderer') { + this._sendPerformanceData(data.outputSize, data.duration); } break; } @@ -944,23 +956,20 @@ export class BackLayerWebView extends Themable { return initializePromise.p; } - private _sendPerformanceData(mimeType: string, outputSize: number, renderTime: number) { + private _sendPerformanceData(outputSize: number, renderTime: number) { type NotebookOutputRenderClassification = { owner: 'amunger'; comment: 'Track performance data for output rendering'; - mimeType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Presentation type of the output.' }; outputSize: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Size of the output data buffer.'; isMeasurement: true }; renderTime: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Time spent rendering output.'; isMeasurement: true }; }; type NotebookOutputRenderEvent = { - mimeType: string; outputSize: number; renderTime: number; }; const telemetryData = { - mimeType, outputSize, renderTime }; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 6c0a4f516d37..84589a5ea0b7 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -84,7 +84,7 @@ export class NotebookCellListDelegate extends Disposable implements IListVirtual } } -abstract class AbstractCellRenderer { +abstract class AbstractCellRenderer extends Disposable { protected readonly editorOptions: CellEditorOptions; constructor( @@ -99,11 +99,12 @@ abstract class AbstractCellRenderer { language: string, protected dndController: CellDragAndDropController | undefined ) { - this.editorOptions = new CellEditorOptions(this.notebookEditor.getBaseCellEditorOptions(language), this.notebookEditor.notebookOptions, configurationService); + super(); + this.editorOptions = this._register(new CellEditorOptions(this.notebookEditor.getBaseCellEditorOptions(language), this.notebookEditor.notebookOptions, configurationService)); } - dispose() { - this.editorOptions.dispose(); + override dispose() { + super.dispose(); this.dndController = undefined; } } @@ -199,7 +200,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen editorContainer, foldingIndicator, templateDisposables, - elementDisposables: new DisposableStore(), + elementDisposables: templateDisposables.add(new DisposableStore()), cellParts, toJSON: () => { return {}; } }; @@ -225,7 +226,6 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen } disposeTemplate(templateData: MarkdownCellRenderTemplate): void { - templateData.elementDisposables.dispose(); templateData.templateDisposables.dispose(); } @@ -366,7 +366,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende outputShowMoreContainer, editor, templateDisposables, - elementDisposables: new DisposableStore(), + elementDisposables: templateDisposables.add(new DisposableStore()), cellParts, toJSON: () => { return {}; } }; @@ -397,7 +397,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende } disposeTemplate(templateData: CodeCellRenderTemplate): void { - templateData.templateDisposables.clear(); + templateData.templateDisposables.dispose(); } disposeElement(element: ICellViewModel, index: number, templateData: CodeCellRenderTemplate, height: number | undefined): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts index b18398d54971..073737f5b79b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -486,7 +486,6 @@ export interface IPerformanceMessage extends BaseToWebviewMessage { readonly duration: number; readonly rendererId: string; readonly outputSize?: number; - readonly mimeType?: string; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index da6d359d55e9..f13d9a5e510b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -19,6 +19,43 @@ import type * as rendererApi from 'vscode-notebook-renderer'; // function. Imports are not allowed. This is stringified and injected into // the webview. +// Minimal HTML sanitizer implementation using browser's DOMParser, +// which removes