diff --git a/.config/1espt/PipelineAutobaseliningConfig.yml b/.config/1espt/PipelineAutobaseliningConfig.yml new file mode 100644 index 00000000000..68328e72329 --- /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 00000000000..55412ff93e5 --- /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/devcontainer.json b/.devcontainer/devcontainer.json index 88e150fc445..75076a0f8b6 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,5 +1,5 @@ { - "name": "Void", + "name": "Code - OSS", "build": { "dockerfile": "Dockerfile" }, diff --git a/.devcontainer/install-vscode.sh b/.devcontainer/install-vscode.sh old mode 100644 new mode 100755 diff --git a/.devcontainer/post-create.sh b/.devcontainer/post-create.sh old mode 100644 new mode 100755 diff --git a/.eslintignore b/.eslint-ignore similarity index 86% rename from .eslintignore rename to .eslint-ignore index 1ddb033043b..969f7a5b805 100644 --- a/.eslintignore +++ b/.eslint-ignore @@ -13,6 +13,9 @@ **/extensions/notebook-renderers/renderer-out/index.js **/extensions/open-remote-ssh/out/extension.js **/extensions/simple-browser/media/index.js +**/extensions/terminal-suggest/src/completions/upstream/** +**/extensions/terminal-suggest/src/shell/zshBuiltinsCache.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 @@ -31,15 +34,7 @@ **/src/vs/*/**/*.d.ts **/src/vs/base/test/common/filters.perf.data.js **/src/vs/loader.js -**/src2/**/dompurify.js -**/src2/**/marked.js -**/src2/**/semver.js -**/src2/typings/**/*.d.ts -**/src2/vs/*/**/*.d.ts -**/src2/vs/base/test/common/filters.perf.data.js -**/src2/vs/loader.js **/test/unit/assert.js -**/test/unit/assert-esm.js **/test/automation/out/** **/typings/** !.vscode diff --git a/.eslintplugin/code-amd-node-module.ts b/.eslint-plugin-local/code-amd-node-module.ts similarity index 98% rename from .eslintplugin/code-amd-node-module.ts rename to .eslint-plugin-local/code-amd-node-module.ts index 35c89dcb219..ff7ef6ab33b 100644 --- a/.eslintplugin/code-amd-node-module.ts +++ b/.eslint-plugin-local/code-amd-node-module.ts @@ -12,7 +12,8 @@ export = new class ApiProviderNaming implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { amdX: 'Use `import type` for import declarations, use `amdX#importAMDNodeModule` for import expressions' - } + }, + schema: false, }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslintplugin/code-declare-service-brand.ts b/.eslint-plugin-local/code-declare-service-brand.ts similarity index 96% rename from .eslintplugin/code-declare-service-brand.ts rename to .eslint-plugin-local/code-declare-service-brand.ts index 402d3638e7a..f2d28bbfc01 100644 --- a/.eslintplugin/code-declare-service-brand.ts +++ b/.eslint-plugin-local/code-declare-service-brand.ts @@ -8,7 +8,8 @@ import * as eslint from 'eslint'; export = new class DeclareServiceBrand implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { - fixable: 'code' + fixable: 'code', + schema: false, }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslintplugin/code-ensure-no-disposables-leak-in-test.ts b/.eslint-plugin-local/code-ensure-no-disposables-leak-in-test.ts similarity index 97% rename from .eslintplugin/code-ensure-no-disposables-leak-in-test.ts rename to .eslint-plugin-local/code-ensure-no-disposables-leak-in-test.ts index 9d3044f50f4..56a1d4a70ad 100644 --- a/.eslintplugin/code-ensure-no-disposables-leak-in-test.ts +++ b/.eslint-plugin-local/code-ensure-no-disposables-leak-in-test.ts @@ -13,7 +13,8 @@ export = new class EnsureNoDisposablesAreLeakedInTestSuite implements eslint.Rul messages: { ensure: 'Suites should include a call to `ensureNoDisposablesAreLeakedInTestSuite()` to ensure no disposables are leaked in tests.' }, - fixable: 'code' + fixable: 'code', + schema: false, }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslintplugin/code-import-patterns.ts b/.eslint-plugin-local/code-import-patterns.ts similarity index 98% rename from .eslintplugin/code-import-patterns.ts rename to .eslint-plugin-local/code-import-patterns.ts index 460409514bc..e4fe52412e6 100644 --- a/.eslintplugin/code-import-patterns.ts +++ b/.eslint-plugin-local/code-import-patterns.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { TSESTree } from '@typescript-eslint/utils'; import * as path from 'path'; import minimatch from 'minimatch'; import { createImportRuleListener } from './utils'; @@ -50,7 +50,8 @@ export = new class implements eslint.Rule.RuleModule { }, docs: { url: 'https://github.com/microsoft/vscode/wiki/Source-Code-Organization' - } + }, + schema: false, }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { @@ -249,7 +250,7 @@ export = new class implements eslint.Rule.RuleModule { const relativeFilename = getRelativeFilename(context); importPath = path.posix.join(path.posix.dirname(relativeFilename), importPath); if (/^src\/vs\//.test(importPath)) { - // resolve using AMD base url + // resolve using base url importPath = importPath.substring('src/'.length); } } diff --git a/.eslintplugin/code-layering.ts b/.eslint-plugin-local/code-layering.ts similarity index 93% rename from .eslintplugin/code-layering.ts rename to .eslint-plugin-local/code-layering.ts index cca72eeec71..f8b769a1bf6 100644 --- a/.eslintplugin/code-layering.ts +++ b/.eslint-plugin-local/code-layering.ts @@ -20,7 +20,18 @@ export = new class implements eslint.Rule.RuleModule { }, docs: { url: 'https://github.com/microsoft/vscode/wiki/Source-Code-Organization' - } + }, + schema: [ + { + type: 'object', + additionalProperties: { + type: 'array', + items: { + type: 'string' + } + } + } + ] }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslint-plugin-local/code-limited-top-functions.ts b/.eslint-plugin-local/code-limited-top-functions.ts new file mode 100644 index 00000000000..97eef9a6e9d --- /dev/null +++ b/.eslint-plugin-local/code-limited-top-functions.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; +import { dirname, relative } from 'path'; +import minimatch from 'minimatch'; + +export = new class implements eslint.Rule.RuleModule { + + readonly meta: eslint.Rule.RuleMetaData = { + messages: { + layerbreaker: 'You are only allowed to define limited top level functions.' + }, + schema: { + type: "array", + items: { + type: "object", + additionalProperties: { + type: "array", + items: { + type: "string" + } + } + } + } + }; + + create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + let fileRelativePath = relative(dirname(__dirname), context.getFilename()); + if (!fileRelativePath.endsWith('/')) { + fileRelativePath += '/'; + } + const ruleArgs = >context.options[0]; + + const matchingKey = Object.keys(ruleArgs).find(key => fileRelativePath.startsWith(key) || minimatch(fileRelativePath, key)); + if (!matchingKey) { + // nothing + return {}; + } + + const restrictedFunctions = ruleArgs[matchingKey]; + + return { + FunctionDeclaration: (node: any) => { + const isTopLevel = node.parent.type === 'Program'; + const functionName = node.id.name; + if (isTopLevel && !restrictedFunctions.includes(node.id.name)) { + context.report({ + node, + message: `Top-level function '${functionName}' is restricted in this file. Allowed functions are: ${restrictedFunctions.join(', ')}.` + }); + } + }, + ExportNamedDeclaration(node: any) { + if (node.declaration && node.declaration.type === 'FunctionDeclaration') { + const functionName = node.declaration.id.name; + const isTopLevel = node.parent.type === 'Program'; + if (isTopLevel && !restrictedFunctions.includes(node.declaration.id.name)) { + context.report({ + node, + message: `Top-level function '${functionName}' is restricted in this file. Allowed functions are: ${restrictedFunctions.join(', ')}.` + }); + } + } + } + } + } +}; diff --git a/.eslintplugin/code-must-use-result.ts b/.eslint-plugin-local/code-must-use-result.ts similarity index 91% rename from .eslintplugin/code-must-use-result.ts rename to .eslint-plugin-local/code-must-use-result.ts index 2127206f8c4..e59b1920f2e 100644 --- a/.eslintplugin/code-must-use-result.ts +++ b/.eslint-plugin-local/code-must-use-result.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { TSESTree } from '@typescript-eslint/utils'; const VALID_USES = new Set([ TSESTree.AST_NODE_TYPES.AwaitExpression, @@ -12,6 +12,9 @@ const VALID_USES = new Set([ ]); export = new class MustUseResults implements eslint.Rule.RuleModule { + readonly meta: eslint.Rule.RuleMetaData = { + schema: false + } create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslintplugin/code-must-use-super-dispose.ts b/.eslint-plugin-local/code-must-use-super-dispose.ts similarity index 100% rename from .eslintplugin/code-must-use-super-dispose.ts rename to .eslint-plugin-local/code-must-use-super-dispose.ts diff --git a/.eslintplugin/code-no-dangerous-type-assertions.ts b/.eslint-plugin-local/code-no-dangerous-type-assertions.ts similarity index 95% rename from .eslintplugin/code-no-dangerous-type-assertions.ts rename to .eslint-plugin-local/code-no-dangerous-type-assertions.ts index eecd4048e43..233fae02c82 100644 --- a/.eslintplugin/code-no-dangerous-type-assertions.ts +++ b/.eslint-plugin-local/code-no-dangerous-type-assertions.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { TSESTree } from '@typescript-eslint/utils'; export = new class NoDangerousTypeAssertions implements eslint.Rule.RuleModule { diff --git a/.eslintplugin/code-no-global-document-listener.ts b/.eslint-plugin-local/code-no-global-document-listener.ts similarity index 100% rename from .eslintplugin/code-no-global-document-listener.ts rename to .eslint-plugin-local/code-no-global-document-listener.ts diff --git a/.eslintplugin/code-no-native-private.ts b/.eslint-plugin-local/code-no-native-private.ts similarity index 98% rename from .eslintplugin/code-no-native-private.ts rename to .eslint-plugin-local/code-no-native-private.ts index 4d6be23b8f3..e2d20694ca8 100644 --- a/.eslintplugin/code-no-native-private.ts +++ b/.eslint-plugin-local/code-no-native-private.ts @@ -10,7 +10,8 @@ export = new class ApiProviderNaming implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { slow: 'Native private fields are much slower and should only be used when needed. Ignore this warning if you know what you are doing, use compile-time private otherwise. See https://github.com/microsoft/vscode/issues/185991#issuecomment-1614468158 for details', - } + }, + schema: false, }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslintplugin/code-no-nls-in-standalone-editor.ts b/.eslint-plugin-local/code-no-nls-in-standalone-editor.ts similarity index 98% rename from .eslintplugin/code-no-nls-in-standalone-editor.ts rename to .eslint-plugin-local/code-no-nls-in-standalone-editor.ts index 90c80dee70c..19ad65ee871 100644 --- a/.eslintplugin/code-no-nls-in-standalone-editor.ts +++ b/.eslint-plugin-local/code-no-nls-in-standalone-editor.ts @@ -12,7 +12,8 @@ export = new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule readonly meta: eslint.Rule.RuleMetaData = { messages: { noNls: 'Not allowed to import vs/nls in standalone editor modules. Use standaloneStrings.ts' - } + }, + schema: false, }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslintplugin/code-no-potentially-unsafe-disposables.ts b/.eslint-plugin-local/code-no-potentially-unsafe-disposables.ts similarity index 100% rename from .eslintplugin/code-no-potentially-unsafe-disposables.ts rename to .eslint-plugin-local/code-no-potentially-unsafe-disposables.ts diff --git a/.eslintplugin/code-no-runtime-import.ts b/.eslint-plugin-local/code-no-runtime-import.ts similarity index 100% rename from .eslintplugin/code-no-runtime-import.ts rename to .eslint-plugin-local/code-no-runtime-import.ts diff --git a/.eslintplugin/code-no-standalone-editor.ts b/.eslint-plugin-local/code-no-standalone-editor.ts similarity index 98% rename from .eslintplugin/code-no-standalone-editor.ts rename to .eslint-plugin-local/code-no-standalone-editor.ts index 898886d17d2..3fad6719581 100644 --- a/.eslintplugin/code-no-standalone-editor.ts +++ b/.eslint-plugin-local/code-no-standalone-editor.ts @@ -15,7 +15,8 @@ export = new class NoNlsInStandaloneEditorRule implements eslint.Rule.RuleModule }, docs: { url: 'https://github.com/microsoft/vscode/wiki/Source-Code-Organization' - } + }, + schema: false, }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslintplugin/code-no-static-self-ref.ts b/.eslint-plugin-local/code-no-static-self-ref.ts similarity index 86% rename from .eslintplugin/code-no-static-self-ref.ts rename to .eslint-plugin-local/code-no-static-self-ref.ts index 7c6e13032ae..52edfb254f6 100644 --- a/.eslintplugin/code-no-static-self-ref.ts +++ b/.eslint-plugin-local/code-no-static-self-ref.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { TSESTree } from '@typescript-eslint/utils'; /** * WORKAROUND for https://github.com/evanw/esbuild/issues/3823 @@ -15,7 +15,7 @@ export = new class implements eslint.Rule.RuleModule { function checkProperty(inNode: any) { - const classDeclaration = context.getAncestors().find(node => node.type === 'ClassDeclaration'); + const classDeclaration = context.sourceCode.getAncestors(inNode).find(node => node.type === 'ClassDeclaration'); const propertyDefinition = inNode; if (!classDeclaration || !classDeclaration.id?.name) { @@ -33,7 +33,7 @@ export = new class implements eslint.Rule.RuleModule { } const name = classDeclaration.id.name; - const valueText = context.getSourceCode().getText(propertyDefinition.value) + const valueText = context.sourceCode.getText(propertyDefinition.value) if (valueText.includes(name + '.')) { diff --git a/.eslintplugin/code-no-test-async-suite.ts b/.eslint-plugin-local/code-no-test-async-suite.ts similarity index 94% rename from .eslintplugin/code-no-test-async-suite.ts rename to .eslint-plugin-local/code-no-test-async-suite.ts index 41d15d28636..7d5fadfad0d 100644 --- a/.eslintplugin/code-no-test-async-suite.ts +++ b/.eslint-plugin-local/code-no-test-async-suite.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { TSESTree } from '@typescript-eslint/utils'; import * as eslint from 'eslint'; function isCallExpression(node: TSESTree.Node): node is TSESTree.CallExpression { diff --git a/.eslintplugin/code-no-test-only.ts b/.eslint-plugin-local/code-no-test-only.ts similarity index 100% rename from .eslintplugin/code-no-test-only.ts rename to .eslint-plugin-local/code-no-test-only.ts diff --git a/.eslintplugin/code-no-unexternalized-strings.ts b/.eslint-plugin-local/code-no-unexternalized-strings.ts similarity index 98% rename from .eslintplugin/code-no-unexternalized-strings.ts rename to .eslint-plugin-local/code-no-unexternalized-strings.ts index 17a8f900efd..abb3980eb54 100644 --- a/.eslintplugin/code-no-unexternalized-strings.ts +++ b/.eslint-plugin-local/code-no-unexternalized-strings.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/experimental-utils'; +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/utils'; function isStringLiteral(node: TSESTree.Node | null | undefined): node is TSESTree.StringLiteral { return !!node && node.type === AST_NODE_TYPES.Literal && typeof node.value === 'string'; @@ -24,7 +24,8 @@ export = new class NoUnexternalizedStrings implements eslint.Rule.RuleModule { badKey: 'The key \'{{key}}\' doesn\'t conform to a valid localize identifier.', duplicateKey: 'Duplicate key \'{{key}}\' with different message value.', badMessage: 'Message argument to \'{{message}}\' must be a string literal.' - } + }, + schema: false, }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslintplugin/code-no-unused-expressions.ts b/.eslint-plugin-local/code-no-unused-expressions.ts similarity index 97% rename from .eslintplugin/code-no-unused-expressions.ts rename to .eslint-plugin-local/code-no-unused-expressions.ts index 450365de2c6..14f2f53d47f 100644 --- a/.eslintplugin/code-no-unused-expressions.ts +++ b/.eslint-plugin-local/code-no-unused-expressions.ts @@ -12,7 +12,7 @@ */ import * as eslint from 'eslint'; -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { TSESTree } from '@typescript-eslint/utils'; import * as ESTree from 'estree'; //------------------------------------------------------------------------------ @@ -141,7 +141,7 @@ module.exports = { return { ExpressionStatement(node: TSESTree.ExpressionStatement) { - if (!isValidExpression(node.expression) && !isDirective(node, context.getAncestors())) { + if (!isValidExpression(node.expression) && !isDirective(node, context.sourceCode.getAncestors(node))) { context.report({ node: node, message: `Expected an assignment or function call and instead saw an expression. ${node.expression}` }); } } diff --git a/.eslintplugin/code-parameter-properties-must-have-explicit-accessibility.ts b/.eslint-plugin-local/code-parameter-properties-must-have-explicit-accessibility.ts similarity index 95% rename from .eslintplugin/code-parameter-properties-must-have-explicit-accessibility.ts rename to .eslint-plugin-local/code-parameter-properties-must-have-explicit-accessibility.ts index 458afd5b0ba..c9837052fa5 100644 --- a/.eslintplugin/code-parameter-properties-must-have-explicit-accessibility.ts +++ b/.eslint-plugin-local/code-parameter-properties-must-have-explicit-accessibility.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { TSESTree } from '@typescript-eslint/utils'; /** * Enforces that all parameter properties have an explicit access modifier (public, protected, private). diff --git a/.eslintplugin/code-translation-remind.ts b/.eslint-plugin-local/code-translation-remind.ts similarity index 96% rename from .eslintplugin/code-translation-remind.ts rename to .eslint-plugin-local/code-translation-remind.ts index 1ce01107a72..cceaba4c419 100644 --- a/.eslintplugin/code-translation-remind.ts +++ b/.eslint-plugin-local/code-translation-remind.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { TSESTree } from '@typescript-eslint/utils'; import { readFileSync } from 'fs'; import { createImportRuleListener } from './utils'; @@ -16,7 +16,8 @@ export = new class TranslationRemind implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { missing: 'Please add \'{{resource}}\' to ./build/lib/i18n.resources.json file to use translations here.' - } + }, + schema: false, }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslintplugin/index.js b/.eslint-plugin-local/index.js similarity index 100% rename from .eslintplugin/index.js rename to .eslint-plugin-local/index.js diff --git a/.eslint-plugin-local/package.json b/.eslint-plugin-local/package.json new file mode 100644 index 00000000000..a0df0c86778 --- /dev/null +++ b/.eslint-plugin-local/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} diff --git a/.eslintplugin/tsconfig.json b/.eslint-plugin-local/tsconfig.json similarity index 100% rename from .eslintplugin/tsconfig.json rename to .eslint-plugin-local/tsconfig.json diff --git a/.eslintplugin/utils.ts b/.eslint-plugin-local/utils.ts similarity index 95% rename from .eslintplugin/utils.ts rename to .eslint-plugin-local/utils.ts index 428832e9cf9..b7457884f85 100644 --- a/.eslintplugin/utils.ts +++ b/.eslint-plugin-local/utils.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { TSESTree } from '@typescript-eslint/utils'; export function createImportRuleListener(validateImport: (node: TSESTree.Literal, value: string) => any): eslint.Rule.RuleListener { diff --git a/.eslintplugin/vscode-dts-cancellation.ts b/.eslint-plugin-local/vscode-dts-cancellation.ts similarity index 92% rename from .eslintplugin/vscode-dts-cancellation.ts rename to .eslint-plugin-local/vscode-dts-cancellation.ts index 6e253a898ca..5e8e875af21 100644 --- a/.eslintplugin/vscode-dts-cancellation.ts +++ b/.eslint-plugin-local/vscode-dts-cancellation.ts @@ -4,14 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/experimental-utils'; +import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils'; export = new class ApiProviderNaming implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { noToken: 'Function lacks a cancellation token, preferable as last argument', - } + }, + schema: false, }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslintplugin/vscode-dts-create-func.ts b/.eslint-plugin-local/vscode-dts-create-func.ts similarity index 91% rename from .eslintplugin/vscode-dts-create-func.ts rename to .eslint-plugin-local/vscode-dts-create-func.ts index 295d099da7c..01db244ce76 100644 --- a/.eslintplugin/vscode-dts-create-func.ts +++ b/.eslint-plugin-local/vscode-dts-create-func.ts @@ -4,13 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/experimental-utils'; +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/utils'; export = new class ApiLiteralOrTypes implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { docs: { url: 'https://github.com/microsoft/vscode/wiki/Extension-API-guidelines#creating-objects' }, - messages: { sync: '`createXYZ`-functions are constructor-replacements and therefore must return sync', } + messages: { sync: '`createXYZ`-functions are constructor-replacements and therefore must return sync', }, + schema: false, }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslintplugin/vscode-dts-event-naming.ts b/.eslint-plugin-local/vscode-dts-event-naming.ts similarity index 96% rename from .eslintplugin/vscode-dts-event-naming.ts rename to .eslint-plugin-local/vscode-dts-event-naming.ts index 5e767c6e257..c27d934f4f9 100644 --- a/.eslintplugin/vscode-dts-event-naming.ts +++ b/.eslint-plugin-local/vscode-dts-event-naming.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/experimental-utils'; +import { TSESTree, AST_NODE_TYPES } from '@typescript-eslint/utils'; export = new class ApiEventNaming implements eslint.Rule.RuleModule { @@ -19,7 +19,8 @@ export = new class ApiEventNaming implements eslint.Rule.RuleModule { verb: 'Unknown verb \'{{verb}}\' - is this really a verb? Iff so, then add this verb to the configuration', subject: 'Unknown subject \'{{subject}}\' - This subject has not been used before but it should refer to something in the API', unknown: 'UNKNOWN event declaration, lint-rule needs tweaking' - } + }, + schema: false, }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslintplugin/vscode-dts-interface-naming.ts b/.eslint-plugin-local/vscode-dts-interface-naming.ts similarity index 92% rename from .eslintplugin/vscode-dts-interface-naming.ts rename to .eslint-plugin-local/vscode-dts-interface-naming.ts index 59112bcb29d..6b33f9c5343 100644 --- a/.eslintplugin/vscode-dts-interface-naming.ts +++ b/.eslint-plugin-local/vscode-dts-interface-naming.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { TSESTree } from '@typescript-eslint/utils'; export = new class ApiInterfaceNaming implements eslint.Rule.RuleModule { @@ -13,7 +13,8 @@ export = new class ApiInterfaceNaming implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { naming: 'Interfaces must not be prefixed with uppercase `I`', - } + }, + schema: false, }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslintplugin/vscode-dts-literal-or-types.ts b/.eslint-plugin-local/vscode-dts-literal-or-types.ts similarity index 87% rename from .eslintplugin/vscode-dts-literal-or-types.ts rename to .eslint-plugin-local/vscode-dts-literal-or-types.ts index 01d1d1d8586..44ef0fd2a7c 100644 --- a/.eslintplugin/vscode-dts-literal-or-types.ts +++ b/.eslint-plugin-local/vscode-dts-literal-or-types.ts @@ -4,13 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { TSESTree } from '@typescript-eslint/utils'; export = new class ApiLiteralOrTypes implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { docs: { url: 'https://github.com/microsoft/vscode/wiki/Extension-API-guidelines#enums' }, - messages: { useEnum: 'Use enums, not literal-or-types', } + messages: { useEnum: 'Use enums, not literal-or-types', }, + schema: false, }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslintplugin/vscode-dts-provider-naming.ts b/.eslint-plugin-local/vscode-dts-provider-naming.ts similarity index 94% rename from .eslintplugin/vscode-dts-provider-naming.ts rename to .eslint-plugin-local/vscode-dts-provider-naming.ts index 284f123420f..db8350dd9bc 100644 --- a/.eslintplugin/vscode-dts-provider-naming.ts +++ b/.eslint-plugin-local/vscode-dts-provider-naming.ts @@ -4,14 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { TSESTree } from '@typescript-eslint/utils'; export = new class ApiProviderNaming implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { naming: 'A provider should only have functions like provideXYZ or resolveXYZ', - } + }, + schema: false, }; private static _providerFunctionNames = /^(provide|resolve|prepare).+/; diff --git a/.eslintplugin/vscode-dts-string-type-literals.ts b/.eslint-plugin-local/vscode-dts-string-type-literals.ts similarity index 79% rename from .eslintplugin/vscode-dts-string-type-literals.ts rename to .eslint-plugin-local/vscode-dts-string-type-literals.ts index 8c3ead14427..bca084c4af6 100644 --- a/.eslintplugin/vscode-dts-string-type-literals.ts +++ b/.eslint-plugin-local/vscode-dts-string-type-literals.ts @@ -4,21 +4,21 @@ *--------------------------------------------------------------------------------------------*/ import * as eslint from 'eslint'; -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { TSESTree } from '@typescript-eslint/utils'; export = new class ApiTypeDiscrimination implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { docs: { url: 'https://github.com/microsoft/vscode/wiki/Extension-API-guidelines' }, messages: { - noTypeDiscrimination: 'Do not use type descrimination properties' - } + noTypeDiscrimination: 'Do not use type discrimination properties' + }, + schema: false, }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { return { - ['TSPropertySignature[optional=undefined] TSTypeAnnotation TSLiteralType Literal']: (node: any) => { - + ['TSPropertySignature[optional=false] TSTypeAnnotation TSLiteralType Literal']: (node: any) => { const raw = String((node).raw) if (/^('|").*\1$/.test(raw)) { 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 00000000000..904feaeec36 --- /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/.eslintplugin/vscode-dts-use-thenable.ts b/.eslint-plugin-local/vscode-dts-use-thenable.ts similarity index 97% rename from .eslintplugin/vscode-dts-use-thenable.ts rename to .eslint-plugin-local/vscode-dts-use-thenable.ts index 51112482099..683394ad115 100644 --- a/.eslintplugin/vscode-dts-use-thenable.ts +++ b/.eslint-plugin-local/vscode-dts-use-thenable.ts @@ -10,7 +10,8 @@ export = new class ApiEventNaming implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { usage: 'Use the Thenable-type instead of the Promise type', - } + }, + schema: false, }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslintplugin/vscode-dts-vscode-in-comments.ts b/.eslint-plugin-local/vscode-dts-vscode-in-comments.ts similarity index 98% rename from .eslintplugin/vscode-dts-vscode-in-comments.ts rename to .eslint-plugin-local/vscode-dts-vscode-in-comments.ts index 8ced8eb25fd..80d3b7003d7 100644 --- a/.eslintplugin/vscode-dts-vscode-in-comments.ts +++ b/.eslint-plugin-local/vscode-dts-vscode-in-comments.ts @@ -11,7 +11,8 @@ export = new class ApiVsCodeInComments implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { comment: `Don't use the term 'vs code' in comments` - } + }, + schema: false, }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { diff --git a/.eslintplugin/vscode-dts-region-comments.ts b/.eslintplugin/vscode-dts-region-comments.ts deleted file mode 100644 index 63139a50e3b..00000000000 --- a/.eslintplugin/vscode-dts-region-comments.ts +++ /dev/null @@ -1,39 +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/', - } - }; - - 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/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 16884e64932..00000000000 --- a/.eslintrc.json +++ /dev/null @@ -1,1230 +0,0 @@ -{ - "root": true, - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 6, - "sourceType": "module" - }, - "plugins": [ - "@typescript-eslint", - "jsdoc", - "header", - "local" - // "react" // Void - ], - "rules": { - "constructor-super": "warn", - "curly": "off", // <-- Void - "eqeqeq": "warn", - "prefer-const": [ - "warn", - { - "destructuring": "all" - } - ], - "no-buffer-constructor": "warn", - "no-caller": "warn", - "no-case-declarations": "warn", - "no-debugger": "warn", - "no-duplicate-case": "warn", - "no-duplicate-imports": "warn", - "no-eval": "warn", - "no-async-promise-executor": "warn", - "no-extra-semi": "warn", - "no-new-wrappers": "warn", - "no-redeclare": "off", - "no-sparse-arrays": "warn", - "no-throw-literal": "warn", - "no-unsafe-finally": "warn", - "no-unused-labels": "warn", - "no-restricted-globals": [ - "warn", - "name", - "length", - "event", - "closed", - "external", - "status", - "origin", - "orientation", - "context" - ], // non-complete list of globals that are easy to access unintentionally - "no-var": "warn", - "jsdoc/no-types": "warn", - "semi": "off", - "@typescript-eslint/semi": "off", // <-- Void - "@typescript-eslint/member-delimiter-style": "warn", - "@typescript-eslint/naming-convention": [ - "warn", - { - "selector": "class", - "format": [ - "PascalCase" - ] - } - ], - "local/code-no-unused-expressions": [ - "warn", - { - "allowTernary": true - } - ], - "local/code-translation-remind": "warn", - "local/code-no-native-private": "warn", - "local/code-parameter-properties-must-have-explicit-accessibility": "warn", - "local/code-no-nls-in-standalone-editor": "warn", - "local/code-no-potentially-unsafe-disposables": "warn", - "local/code-no-dangerous-type-assertions": "off", - "local/code-no-standalone-editor": "warn", - "local/code-no-unexternalized-strings": "warn", - "local/code-must-use-super-dispose": "warn", - "local/code-declare-service-brand": "warn", - "local/code-layering": [ - "warn", - { - "common": [], - "node": [ - "common" - ], - "browser": [ - "common" - ], - "electron-sandbox": [ - "common", - "browser" - ], - "electron-utility": [ - "common", - "node" - ], - "electron-main": [ - "common", - "node", - "electron-utility" - ] - } - ] - // Void team commented this out: - // "header/header": [ - // 2, - // "block", - // [ - // "---------------------------------------------------------------------------------------------", - // " * Copyright (c) Microsoft Corporation. All rights reserved.", - // " * Licensed under the MIT License. See License.txt in the project root for license information.", - // " *--------------------------------------------------------------------------------------------" - // ] - // ] - }, - "overrides": [ - { - "files": [ - "*.js" - ], - "rules": { - "jsdoc/no-types": "off" - } - }, - { - "files": [ - "**/*.test.ts" - ], - "rules": { - "local/code-must-use-super-dispose": "off", - "local/code-no-test-only": "error", - "local/code-no-test-async-suite": "warn", - "local/code-no-unexternalized-strings": "off", - "local/code-must-use-result": [ - "warn", - [ - { - "message": "Expression must be awaited", - "functions": [ - "assertSnapshot", - "assertHeap" - ] - } - ] - ] - } - }, - { - "files": [ - "src/**/*.ts" - ], - "rules": { - "local/code-no-static-self-ref": "warn" - } - }, - { - "files": [ - "src/vs/**/*.test.ts" - ], - "rules": { - "local/code-ensure-no-disposables-leak-in-test": [ - "warn", - { - // 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" - ] - } - ] - } - }, - { - "files": [ - "**/vscode.d.ts", - "**/vscode.proposed.*.d.ts" - ], - "rules": { - "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-thenable": "warn", - "local/vscode-dts-region-comments": "warn", - "local/vscode-dts-vscode-in-comments": "warn", - "local/vscode-dts-provider-naming": [ - "warn", - { - "allowed": [ - "FileSystemProvider", - "TreeDataProvider", - "TestProvider", - "CustomEditorProvider", - "CustomReadonlyEditorProvider", - "TerminalLinkProvider", - "AuthenticationProvider", - "NotebookContentProvider" - ] - } - ], - "local/vscode-dts-event-naming": [ - "warn", - { - "allowed": [ - "onCancellationRequested", - "event" - ], - "verbs": [ - "accept", - "change", - "close", - "collapse", - "create", - "delete", - "discover", - "dispose", - "drop", - "edit", - "end", - "execute", - "expand", - "grant", - "hide", - "invalidate", - "open", - "override", - "perform", - "receive", - "register", - "remove", - "rename", - "save", - "send", - "start", - "terminate", - "trigger", - "unregister", - "write" - ] - } - ] - } - }, - { - "files": [ - "**/vscode.d.ts" - ], - "rules": { - "jsdoc/tag-lines": "off", - "jsdoc/valid-types": "off", - "jsdoc/no-multi-asterisks": [ - "warn", - { - "allowWhitespace": true - } - ], - "jsdoc/require-jsdoc": [ - "warn", - { - "enableFixer": false, - "contexts": [ - "TSInterfaceDeclaration", - "TSPropertySignature", - "TSMethodSignature", - "TSDeclareFunction", - "ClassDeclaration", - "MethodDefinition", - "PropertyDeclaration", - "TSEnumDeclaration", - "TSEnumMember", - "ExportNamedDeclaration" - ] - } - ], - "jsdoc/check-param-names": [ - "warn", - { - "enableFixer": false, - "checkDestructured": false - } - ], - "jsdoc/require-returns": "warn" - } - }, - { - "files": [ - "src/**/{common,browser}/**/*.ts" - ], - "rules": { - "local/code-amd-node-module": "warn" - } - }, - { - "files": [ - "src/**/{browser,electron-sandbox}/**/*.ts" - ], - "rules": { - "local/code-no-global-document-listener": "warn", - "no-restricted-syntax": [ - "warn", - { - "selector": "BinaryExpression[operator='instanceof'][right.name='MouseEvent']", - "message": "Use DOM.isMouseEvent() to support multi-window scenarios." - }, - { - "selector": "BinaryExpression[operator='instanceof'][right.name=/^HTML\\w+/]", - "message": "Use DOM.isHTMLElement() and related methods to support multi-window scenarios." - }, - { - "selector": "BinaryExpression[operator='instanceof'][right.name='KeyboardEvent']", - "message": "Use DOM.isKeyboardEvent() to support multi-window scenarios." - }, - { - "selector": "BinaryExpression[operator='instanceof'][right.name='PointerEvent']", - "message": "Use DOM.isPointerEvent() to support multi-window scenarios." - }, - { - "selector": "BinaryExpression[operator='instanceof'][right.name='DragEvent']", - "message": "Use DOM.isDragEvent() to support multi-window scenarios." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='activeElement']", - "message": "Use .document.activeElement to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='contains']", - "message": "Use .document.contains to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='styleSheets']", - "message": "Use .document.styleSheets to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='fullscreenElement']", - "message": "Use .document.fullscreenElement to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='body']", - "message": "Use .document.body to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='addEventListener']", - "message": "Use .document.addEventListener to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='removeEventListener']", - "message": "Use .document.removeEventListener to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='hasFocus']", - "message": "Use .document.hasFocus to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='head']", - "message": "Use .document.head to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='exitFullscreen']", - "message": "Use .document.exitFullscreen to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='getElementById']", - "message": "Use .document.getElementById to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='getElementsByClassName']", - "message": "Use .document.getElementsByClassName to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='getElementsByName']", - "message": "Use .document.getElementsByName to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='getElementsByTagName']", - "message": "Use .document.getElementsByTagName to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='getElementsByTagNameNS']", - "message": "Use .document.getElementsByTagNameNS to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='getSelection']", - "message": "Use .document.getSelection to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='open']", - "message": "Use .document.open to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='close']", - "message": "Use .document.close to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='documentElement']", - "message": "Use .document.documentElement to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='visibilityState']", - "message": "Use .document.visibilityState to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='querySelector']", - "message": "Use .document.querySelector to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='querySelectorAll']", - "message": "Use .document.querySelectorAll to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='elementFromPoint']", - "message": "Use .document.elementFromPoint to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='elementsFromPoint']", - "message": "Use .document.elementsFromPoint to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='onkeydown']", - "message": "Use .document.onkeydown to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='onkeyup']", - "message": "Use .document.onkeyup to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='onmousedown']", - "message": "Use .document.onmousedown to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='onmouseup']", - "message": "Use .document.onmouseup to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "selector": "MemberExpression[object.name='document'][property.name='execCommand']", - "message": "Use .document.execCommand to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - } - ], - "no-restricted-globals": [ - "warn", - "name", - "length", - "event", - "closed", - "external", - "status", - "origin", - "orientation", - "context", - { - "name": "setInterval", - "message": "Use .setInterval to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "clearInterval", - "message": "Use .clearInterval to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "requestAnimationFrame", - "message": "Use .requestAnimationFrame to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "cancelAnimationFrame", - "message": "Use .cancelAnimationFrame to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "requestIdleCallback", - "message": "Use .requestIdleCallback to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "cancelIdleCallback", - "message": "Use .cancelIdleCallback to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "window", - "message": "Use to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "addEventListener", - "message": "Use .addEventListener to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "removeEventListener", - "message": "Use .removeEventListener to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "getComputedStyle", - "message": "Use .getComputedStyle to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "focus", - "message": "Use .focus to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "blur", - "message": "Use .blur to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "close", - "message": "Use .close to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "dispatchEvent", - "message": "Use .dispatchEvent to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "getSelection", - "message": "Use .getSelection to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "matchMedia", - "message": "Use .matchMedia to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "open", - "message": "Use .open to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "parent", - "message": "Use .parent to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "postMessage", - "message": "Use .postMessage to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "devicePixelRatio", - "message": "Use .devicePixelRatio to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "frames", - "message": "Use .frames to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "frameElement", - "message": "Use .frameElement to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "innerHeight", - "message": "Use .innerHeight to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "innerWidth", - "message": "Use .innerWidth to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "outerHeight", - "message": "Use .outerHeight to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "outerWidth", - "message": "Use .outerWidth to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "opener", - "message": "Use .opener to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "origin", - "message": "Use .origin to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "screen", - "message": "Use .screen to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "screenLeft", - "message": "Use .screenLeft to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "screenTop", - "message": "Use .screenTop to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "screenX", - "message": "Use .screenX to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "screenY", - "message": "Use .screenY to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "scrollX", - "message": "Use .scrollX to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "scrollY", - "message": "Use .scrollY to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "top", - "message": "Use .top to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - }, - { - "name": "visualViewport", - "message": "Use .visualViewport to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant." - } - ] - } - }, - { - "files": [ - "src/**/electron-utility/**/*.ts" - ], - "rules": { - "no-restricted-imports": [ - "warn", - { - "paths": [ - { - "name": "electron", - "importNames": [ - "app", - "autoUpdater", - "BaseWindow", - "BrowserWindow", - "contentTracing", - "desktopCapturer", - "dialog", - "globalShortcut", - "inAppPurchase", - "ipcMain", - "Menu", - "MenuItem", - "MessageChannelMain", - "MessagePortMain", - "nativeTheme", - "netLog", - "Notification", - "powerMonitor", - "powerSaveBlocker", - "protocol", - "pushNotifications", - "safeStorage", - "screen", - "session", - "ShareMenu", - "TouchBar", - "Tray", - "utilityProcess", - "View", - "webContents", - "webFrameMain", - "webContentsView", - "default" - ], - "message": "Only net and system-preferences are allowed to be imported from electron" - } - ] - } - ] - } - }, - { - "files": [ - "src/**/*.ts" - ], - "rules": { - "local/code-import-patterns": [ - "warn", - { - // imports that are allowed in all files of layers: - // - browser - // - electron-sandbox - "when": "hasBrowser", - "allow": [ - "vs/css!./**/*" - ] - }, - { - // imports that are allowed in all files of layers: - // - node - // - electron-utility - // - electron-main - "when": "hasNode", - "allow": [ - "@parcel/watcher", - "@vscode/sqlite3", - "@vscode/vscode-languagedetection", - "@vscode/ripgrep", - "@vscode/iconv-lite-umd", - "@vscode/policy-watcher", - "@vscode/proxy-agent", - "@vscode/spdlog", - "@vscode/windows-process-tree", - "assert", - "child_process", - "console", - "cookie", - "crypto", - "dns", - "events", - "fs", - "fs/promises", - "http", - "https", - "minimist", - "node:module", - "native-keymap", - "native-watchdog", - "net", - "node-pty", - "os", - "path", - "perf_hooks", - "readline", - "stream", - "string_decoder", - "tas-client-umd", - "tls", - "url", - "util", - "v8-inspect-profiler", - "vscode-regexpp", - "vscode-textmate", - "worker_threads", - "@xterm/addon-clipboard", - "@xterm/addon-image", - "@xterm/addon-search", - "@xterm/addon-serialize", - "@xterm/addon-unicode11", - "@xterm/addon-webgl", - "@xterm/headless", - "@xterm/xterm", - "yauzl", - "yazl", - "zlib" - ] - }, - { - // imports that are allowed in all files of layers: - // - electron-utility - // - electron-main - "when": "hasElectron", - "allow": [ - "electron" - ] - }, - { - // imports that are allowed in all /test/ files - "when": "test", - "allow": [ - "vs/css.build", - "vs/css.build.js", - "assert", - "sinon", - "sinon-test" - ] - }, - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // !!! Do not relax these rules !!! - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // - // A path ending in /~ has a special meaning. It indicates a template position - // which will be substituted with one or more layers. - // - // When /~ is used in the target, the rule will be expanded to 14 distinct rules. - // e.g. "src/vs/base/~" will be expanded to: - // - src/vs/base/common - // - src/vs/base/worker - // - src/vs/base/browser - // - src/vs/base/electron-sandbox - // - src/vs/base/node - // - src/vs/base/electron-main - // - src/vs/base/test/common - // - src/vs/base/test/worker - // - src/vs/base/test/browser - // - src/vs/base/test/electron-sandbox - // - src/vs/base/test/node - // - src/vs/base/test/electron-main - // - // When /~ is used in the restrictions, it will be replaced with the correct - // layers that can be used e.g. "src/vs/base/electron-sandbox" will be able - // to import "{common,browser,electron-sanbox}", etc. - // - // It is possible to use /~ in the restrictions property even without using it in - // the target property by adding a layer property. - { - "target": "src/vs/base/~", - "restrictions": [ - "vs/base/~" - ] - }, - { - "target": "src/vs/base/parts/*/~", - "restrictions": [ - "vs/base/~", - "vs/base/parts/*/~" - ] - }, - { - "target": "src/vs/platform/*/~", - "restrictions": [ - "vs/base/~", - "vs/base/parts/*/~", - "vs/platform/*/~", - "tas-client-umd", // node module allowed even in /common/ - "@microsoft/1ds-core-js", // node module allowed even in /common/ - "@microsoft/1ds-post-js", // node module allowed even in /common/ - "@xterm/headless" // node module allowed even in /common/ - ] - }, - { - "target": "src/vs/editor/~", - "restrictions": [ - "vs/base/~", - "vs/base/parts/*/~", - "vs/platform/*/~", - "vs/editor/~", - "@vscode/tree-sitter-wasm" // node module allowed even in /common/ - ] - }, - { - "target": "src/vs/editor/contrib/*/~", - "restrictions": [ - "vs/base/~", - "vs/base/parts/*/~", - "vs/platform/*/~", - "vs/editor/~", - "vs/editor/contrib/*/~" - ] - }, - { - "target": "src/vs/editor/standalone/~", - "restrictions": [ - "vs/base/~", - "vs/base/parts/*/~", - "vs/platform/*/~", - "vs/editor/~", - "vs/editor/contrib/*/~", - "vs/editor/standalone/~", - "@vscode/tree-sitter-wasm" // type import - ] - }, - { - "target": "src/vs/editor/editor.all.ts", - "layer": "browser", - "restrictions": [ - "vs/base/~", - "vs/base/parts/*/~", - "vs/platform/*/~", - "vs/editor/~", - "vs/editor/contrib/*/~" - ] - }, - { - "target": "src/vs/editor/editor.worker.ts", - "layer": "worker", - "restrictions": [ - "vs/base/~", - "vs/base/parts/*/~", - "vs/platform/*/~", - "vs/editor/~" - ] - }, - { - "target": "src/vs/editor/{editor.api.ts,editor.main.ts}", - "layer": "browser", - "restrictions": [ - "vs/base/~", - "vs/base/parts/*/~", - "vs/editor/~", - "vs/editor/contrib/*/~", - "vs/editor/standalone/~", - "vs/editor/*" - ] - }, - { - "target": "src/vs/workbench/~", - "restrictions": [ - "vs/base/~", - "vs/base/parts/*/~", - "vs/platform/*/~", - "vs/editor/~", - "vs/editor/contrib/*/~", - "vs/workbench/~", - "vs/workbench/services/*/~", - "assert", - { - "when": "test", - "pattern": "vs/workbench/contrib/*/~" - } // TODO@layers - ] - }, - { - "target": "src/vs/workbench/api/~", - "restrictions": [ - "vscode", - "vs/base/~", - "vs/base/parts/*/~", - "vs/platform/*/~", - "vs/editor/~", - "vs/editor/contrib/*/~", - "vs/workbench/api/~", - "vs/workbench/~", - "vs/workbench/services/*/~", - "vs/workbench/contrib/*/~", - "vs/workbench/contrib/terminalContrib/*/~" - ] - }, - { - "target": "src/vs/workbench/services/*/~", - "restrictions": [ - "vs/base/~", - "vs/base/parts/*/~", - "vs/platform/*/~", - "vs/editor/~", - "vs/editor/contrib/*/~", - "vs/workbench/~", - "vs/workbench/services/*/~", - { - "when": "test", - "pattern": "vs/workbench/contrib/*/~" - }, // TODO@layers - "tas-client-umd", // node module allowed even in /common/ - "vscode-textmate", // node module allowed even in /common/ - "@vscode/vscode-languagedetection", // node module allowed even in /common/ - "@vscode/tree-sitter-wasm", // type import - { - "when": "hasBrowser", - "pattern": "@xterm/xterm" - } // node module allowed even in /browser/ - ] - }, - { - "target": "src/vs/workbench/contrib/*/~", - "restrictions": [ - "vs/base/~", - "vs/base/parts/*/~", - "vs/platform/*/~", - "vs/editor/~", - "vs/editor/contrib/*/~", - "vs/workbench/~", - "vs/workbench/services/*/~", - "vs/workbench/contrib/*/~", - "vs/workbench/contrib/terminal/terminalContribExports*", - "vscode-notebook-renderer", // Type only import - { - "when": "hasBrowser", - "pattern": "@xterm/xterm" - }, // node module allowed even in /browser/ - { - "when": "hasBrowser", - "pattern": "@xterm/addon-*" - }, // node module allowed even in /browser/ - { - "when": "hasBrowser", - "pattern": "vscode-textmate" - } // node module allowed even in /browser/ - ] - }, - { - "target": "src/vs/workbench/contrib/terminalContrib/*/~", - "restrictions": [ - "vs/base/~", - "vs/base/parts/*/~", - "vs/platform/*/~", - "vs/editor/~", - "vs/editor/contrib/*/~", - "vs/workbench/~", - "vs/workbench/services/*/~", - "vs/workbench/contrib/*/~", - // Only allow terminalContrib to import from itself, this works because - // terminalContrib is one extra folder deep - "vs/workbench/contrib/terminalContrib/*/~", - "vs/workbench/contrib/terminal/terminalContribExports*", - "vscode-notebook-renderer", // Type only import - { - "when": "hasBrowser", - "pattern": "@xterm/xterm" - }, // node module allowed even in /browser/ - { - "when": "hasBrowser", - "pattern": "@xterm/addon-*" - }, // node module allowed even in /browser/ - { - "when": "hasBrowser", - "pattern": "vscode-textmate" - }, // node module allowed even in /browser/ - "@xterm/headless" // node module allowed even in /common/ and /browser/ - ] - }, - { - "target": "src/vs/code/~", - "restrictions": [ - "vs/base/~", - "vs/base/parts/*/~", - "vs/platform/*/~", - "vs/editor/~", - "vs/editor/contrib/*/~", - "vs/code/~", - { - "when": "hasBrowser", - "pattern": "vs/workbench/workbench.web.main" - }, - { - "when": "hasBrowser", - "pattern": "vs/workbench/workbench.web.main.js" - }, - { - "when": "hasBrowser", - "pattern": "vs/workbench/workbench.web.main.internal" - }, - { - "when": "hasBrowser", - "pattern": "vs/workbench/workbench.web.main.internal.js" - }, - { - "when": "hasBrowser", - "pattern": "vs/workbench/~" - }, - { - "when": "hasBrowser", - "pattern": "vs/workbench/services/*/~" - } - ] - }, - { - "target": "src/vs/server/~", - "restrictions": [ - "vs/base/~", - "vs/base/parts/*/~", - "vs/platform/*/~", - "vs/workbench/~", - "vs/workbench/api/~", - "vs/workbench/services/*/~", - "vs/workbench/contrib/*/~", - "vs/server/~" - ] - }, - { - "target": "src/vs/workbench/contrib/terminal/terminal.all.ts", - "layer": "browser", - "restrictions": [ - "vs/workbench/contrib/**" - ] - }, - { - "target": "src/vs/workbench/contrib/terminal/terminalContribExports.ts", - "layer": "browser", - "restrictions": [ - "vs/workbench/contrib/terminalContrib/*/~" - ] - }, - { - "target": "src/vs/workbench/workbench.common.main.ts", - "layer": "browser", - "restrictions": [ - "vs/base/~", - "vs/base/parts/*/~", - "vs/platform/*/~", - "vs/editor/~", - "vs/editor/contrib/*/~", - "vs/editor/editor.all", - "vs/editor/editor.all.js", - "vs/workbench/~", - "vs/workbench/api/~", - "vs/workbench/services/*/~", - "vs/workbench/contrib/*/~", - "vs/workbench/contrib/terminal/terminal.all", - "vs/workbench/contrib/terminal/terminal.all.js" - ] - }, - { - "target": "src/vs/workbench/workbench.web.main.ts", - "layer": "browser", - "restrictions": [ - "vs/base/~", - "vs/base/parts/*/~", - "vs/platform/*/~", - "vs/editor/~", - "vs/editor/contrib/*/~", - "vs/editor/editor.all", - "vs/editor/editor.all.js", - "vs/workbench/~", - "vs/workbench/api/~", - "vs/workbench/services/*/~", - "vs/workbench/contrib/*/~", - "vs/workbench/workbench.common.main", - "vs/workbench/workbench.common.main.js" - ] - }, - { - "target": "src/vs/workbench/workbench.web.main.internal.ts", - "layer": "browser", - "restrictions": [ - "vs/base/~", - "vs/base/parts/*/~", - "vs/platform/*/~", - "vs/editor/~", - "vs/editor/contrib/*/~", - "vs/editor/editor.all", - "vs/editor/editor.all.js", - "vs/workbench/~", - "vs/workbench/api/~", - "vs/workbench/services/*/~", - "vs/workbench/contrib/*/~", - "vs/workbench/workbench.common.main", - "vs/workbench/workbench.common.main.js" - ] - }, - { - "target": "src/vs/workbench/workbench.desktop.main.ts", - "layer": "electron-sandbox", - "restrictions": [ - "vs/base/*/~", - "vs/base/parts/*/~", - "vs/platform/*/~", - "vs/editor/~", - "vs/editor/contrib/*/~", - "vs/editor/editor.all", - "vs/editor/editor.all.js", - "vs/workbench/~", - "vs/workbench/api/~", - "vs/workbench/services/*/~", - "vs/workbench/contrib/*/~", - "vs/workbench/workbench.common.main", - "vs/workbench/workbench.common.main.js" - ] - }, - { - "target": "src/vs/amdX.ts", - "restrictions": [ - "vs/base/common/*" - ] - }, - { - "target": "src/vs/{loader.d.ts,css.ts,css.build.ts,monaco.d.ts,nls.ts,nls.messages.ts}", - "restrictions": [] - }, - { - "target": "src/vscode-dts/**", - "restrictions": [] - }, - { - "target": "src/{bootstrap-amd.js,bootstrap-fork.js,bootstrap-node.js,bootstrap-window.js,cli.js,main.js,server-cli.js,server-main.js,bootstrap-cli.js,bootstrap-server.js}", - "restrictions": [] - } - ] - } - }, - { - "files": [ - "test/**/*.ts" - ], - "rules": { - "local/code-import-patterns": [ - "warn", - { - "target": "test/smoke/**", - "restrictions": [ - "test/automation", - "test/smoke/**", - "@vscode/*", - "@parcel/*", - "@playwright/*", - "*" // node modules - ] - }, - { - "target": "test/automation/**", - "restrictions": [ - "test/automation/**", - "@vscode/*", - "@parcel/*", - "playwright-core/**", - "@playwright/*", - "*" // node modules - ] - }, - { - "target": "test/integration/**", - "restrictions": [ - "test/integration/**", - "@vscode/*", - "@parcel/*", - "@playwright/*", - "*" // node modules - ] - }, - { - "target": "test/monaco/**", - "restrictions": [ - "test/monaco/**", - "@vscode/*", - "@parcel/*", - "@playwright/*", - "*" // node modules - ] - } - ] - } - }, - { - "files": [ - "src/vs/workbench/contrib/notebook/browser/view/renderers/*.ts" - ], - "rules": { - "local/code-no-runtime-import": [ - "error", - { - "src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts": [ - "**/*" - ] - } - ] - } - } - ] -} diff --git a/.gitignore b/.gitignore index 17d12e30b50..a40dd0d1336 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,5 @@ product.overrides.json .tmp/ .tmp2/ .tool-versions +src/vs/workbench/contrib/void/browser/react/out/** +src/vs/workbench/contrib/void/browser/react/src2/** diff --git a/.idx/dev.nix b/.idx/dev.nix index 83eb293f7c8..c20c14b405d 100644 --- a/.idx/dev.nix +++ b/.idx/dev.nix @@ -1,48 +1,48 @@ -# Created for Void -# To learn more about how to use Nix to configure your environment -# see: https://developers.google.com/idx/guides/customize-idx-env -{pkgs}: { - # Which nixpkgs channel to use. - channel = "stable-23.11"; # or "unstable" - # Use https://search.nixos.org/packages to find packages - packages = [ - pkgs.nodejs_20 - pkgs.yarn - pkgs.nodePackages.pnpm - pkgs.bun - pkgs.gh - ]; - # Sets environment variables in the workspace - env = {}; - idx = { - # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id" - extensions = [ - # "vscodevim.vim" - ]; - workspace = { - # Runs when a workspace is first created with this `dev.nix` file - onCreate = { - npm-install = "npm ci --no-audit --prefer-offline --no-progress --timing"; - # Open editors for the following files by default, if they exist: - default.openFiles = [ - # Cover all the variations of language, src-dir, router (app/pages) - "pages/index.tsx" "pages/index.jsx" - "src/pages/index.tsx" "src/pages/index.jsx" - "app/page.tsx" "app/page.jsx" - "src/app/page.tsx" "src/app/page.jsx" - ]; - }; - # To run something each time the workspace is (re)started, use the `onStart` hook - }; - # Enable previews and customize configuration - previews = { - enable = true; - previews = { - web = { - command = ["npm" "run" "dev" "--" "--port" "$PORT" "--hostname" "0.0.0.0"]; - manager = "web"; - }; - }; - }; - }; -} +# Created for Void +# To learn more about how to use Nix to configure your environment +# see: https://developers.google.com/idx/guides/customize-idx-env +{pkgs}: { + # Which nixpkgs channel to use. + channel = "stable-23.11"; # or "unstable" + # Use https://search.nixos.org/packages to find packages + packages = [ + pkgs.nodejs_20 + pkgs.yarn + pkgs.nodePackages.pnpm + pkgs.bun + pkgs.gh + ]; + # Sets environment variables in the workspace + env = {}; + idx = { + # Search for the extensions you want on https://open-vsx.org/ and use "publisher.id" + extensions = [ + # "vscodevim.vim" + ]; + workspace = { + # Runs when a workspace is first created with this `dev.nix` file + onCreate = { + npm-install = "npm ci --no-audit --prefer-offline --no-progress --timing"; + # Open editors for the following files by default, if they exist: + default.openFiles = [ + # Cover all the variations of language, src-dir, router (app/pages) + "pages/index.tsx" "pages/index.jsx" + "src/pages/index.tsx" "src/pages/index.jsx" + "app/page.tsx" "app/page.jsx" + "src/app/page.tsx" "src/app/page.jsx" + ]; + }; + # To run something each time the workspace is (re)started, use the `onStart` hook + }; + # Enable previews and customize configuration + previews = { + enable = true; + previews = { + web = { + command = ["npm" "run" "dev" "--" "--port" "$PORT" "--hostname" "0.0.0.0"]; + manager = "web"; + }; + }; + }; + }; +} diff --git a/.npmrc b/.npmrc index 87dc701a6a1..bfa6fba99e3 100644 --- a/.npmrc +++ b/.npmrc @@ -1,6 +1,6 @@ disturl="https://electronjs.org/headers" -target="30.5.1" -ms_build_id="10262041" +target="34.2.0" +ms_build_id="11044223" runtime="electron" build_from_source="true" legacy-peer-deps="true" diff --git a/.nvmrc b/.nvmrc index 8ce7030825b..0254b1e633c 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20.16.0 +20.18.2 diff --git a/.vscode-test.js b/.vscode-test.js index 89e1b855ee0..917413ede2a 100644 --- a/.vscode-test.js +++ b/.vscode-test.js @@ -5,9 +5,15 @@ //@ts-check -const path = require('path'); +import { createRequire } from 'node:module'; +import { fileURLToPath } from 'url'; +import * as path from 'path'; +import * as os from 'os'; + +const require = createRequire(import.meta.url); +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + const { defineConfig } = require('@vscode/test-cli'); -const os = require('os'); /** * A list of extension folders who have opted into tests, or configuration objects. @@ -36,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)}`), @@ -57,7 +73,7 @@ const defaultLaunchArgs = process.env.API_TESTS_EXTRA_ARGS?.split(' ') || [ '--disable-telemetry', '--skip-welcome', '--skip-release-notes', `--crash-reporter-directory=${__dirname}/.build/crashes`, `--logsPath=${__dirname}/.build/logs/integration-tests`, '--no-cached-data', '--disable-updates', '--use-inmemory-secretstorage', '--disable-extensions', '--disable-workspace-trust' ]; -module.exports = defineConfig(extensions.map(extension => { +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 } @@ -99,3 +115,5 @@ module.exports = defineConfig(extensions.map(extension => { return config; })); + +export default config; diff --git a/.vscode/extensions/vscode-selfhost-test-provider/package.json b/.vscode/extensions/vscode-selfhost-test-provider/package.json index 3548b00ba81..71b5ee73c60 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" diff --git a/.vscode/extensions/vscode-selfhost-test-provider/src/coverageProvider.ts b/.vscode/extensions/vscode-selfhost-test-provider/src/coverageProvider.ts index 3fff7c5b637..a1d1c162dbd 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 2732ef3b3f6..cbb8d50bf99 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 e04d4beede4..5bed5dd63e3 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 56d6859c3e3..e40cd7d7492 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 b2e25927a8a..afd57934304 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", @@ -257,10 +275,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", diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index 6b6a41f5135..09d6480e102 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:\"September 2024\"" + "value": "$REPO=repo:microsoft/vscode\n$MILESTONE=milestone:\"February 2025\"" }, { "kind": 1, diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index 9f6624a1323..ca93d503338 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 8bb9a246d5f..6d9deb71792 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 f17345ed7f6..0fd05ece485 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 5f54a2e5d91..c7674cef414 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:\"September 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 910e0becf78..59a78245c83 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -8,6 +8,7 @@ "**/.DS_Store": true, ".vscode-test": true, "cli/target": true, + "build/**/*.js.map": true, "build/**/*.js": { "when": "$(basename).ts" } @@ -41,8 +42,6 @@ "**/yarn.lock": true, "**/package-lock.json": true, "**/Cargo.lock": true, - "src/vs/workbench/workbench.web.main.css": true, - "src/vs/workbench/workbench.desktop.main.css": true, "build/**/*.js": true, "out/**": true, "out-build/**": true, @@ -52,8 +51,7 @@ "extensions/**/out/**": true, "test/smoke/out/**": true, "test/automation/out/**": true, - "test/integration/browser/out/**": true, - "src2/**": true, + "test/integration/browser/out/**": true }, "files.readonlyExclude": { "build/builtin/*.js": true, @@ -94,13 +92,12 @@ } ], "git.ignoreLimitWarning": true, - // Removing this for Void: - // "git.branchProtection": [ - // "main", - // "distro", - // "release/*" - // ], - // "git.branchProtectionPrompt": "alwaysCommitToNewBranch", + "git.branchProtection": [ + "main", + "distro", + "release/*" + ], + "git.branchProtectionPrompt": "alwaysCommitToNewBranch", "git.branchRandomName.enable": true, "git.pullBeforeCheckout": true, "git.mergeEditor": true, @@ -171,5 +168,13 @@ }, "css.format.spaceAroundSelectorSeparator": true, "typescript.enablePromptUseWorkspaceTsdk": true, - "inlineChat.experimental.onlyZoneWidget": 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/.vscode/shared.code-snippets b/.vscode/shared.code-snippets index ba36a9f5fcb..f473425b76f 100644 --- a/.vscode/shared.code-snippets +++ b/.vscode/shared.code-snippets @@ -36,23 +36,5 @@ "private readonly _onDid$1 = new Emitter<$2>();", "readonly onDid$1: Event<$2> = this._onDid$1.event;" ], - }, - "esm-comment": { - "scope": "typescript,javascript", - "prefix": "esm-comment", - "body": [ - "// ESM-comment-begin", - "$SELECTION$0", - "// ESM-comment-end", - ] - }, - "esm-uncomment": { - "scope": "typescript,javascript", - "prefix": "esm-uncomment", - "body": [ - "// ESM-uncomment-begin", - "// $SELECTION$0", - "// ESM-uncomment-end", - ] } } diff --git a/README.md b/README.md index 15249823133..609456cbfe9 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ /> -Void is the open-source Cursor alternative. +Void is the open-source Cursor alternative. This repo contains the full sourcecode for Void. We are currently in [open beta](https://voideditor.com/email) for Discord members (see the `announcements` channel), with a waitlist for our official release. If you're new, welcome! diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index da3989dd371..0f17c5ed11b 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 3c6029dc2f4..5a294f35396 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 3f573e06078..6b7f365730f 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/**/* @@ -163,7 +167,7 @@ typescript/lib/tsserverlibrary.js jschardet/index.js jschardet/src/** -# TODO@esm uncomment when we can use jschardet.min.js again jschardet/dist/jschardet.js +jschardet/dist/jschardet.js es6-promise/lib/** diff --git a/build/.webignore b/build/.webignore index 837366b67f7..31c366cc1fa 100644 --- a/build/.webignore +++ b/build/.webignore @@ -14,7 +14,7 @@ jschardet/index.js jschardet/src/** -# TODO@esm uncomment when we can use jschardet.min.js again jschardet/dist/jschardet.js +jschardet/dist/jschardet.js vscode-textmate/webpack.config.js @@ -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 07321ebcd97..145133481f2 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 077ec0ba11b..d6fe74a9d61 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)" @@ -73,6 +73,7 @@ steps: - script: | set -e + for i in {1..5}; do # try 5 times npm ci && break if [ $i -eq 5 ]; then diff --git a/build/azure-pipelines/cli/cli-compile.yml b/build/azure-pipelines/cli/cli-compile.yml index e77ba78a999..71cd3f71e78 100644 --- a/build/azure-pipelines/cli/cli-compile.yml +++ b/build/azure-pipelines/cli/cli-compile.yml @@ -108,6 +108,34 @@ 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 + + - task: CopyFiles@2 + inputs: + SourceFolder: $(Build.SourcesDirectory)/cli/target/${{ parameters.VSCODE_CLI_TARGET }}/release + Contents: 'code.*' + TargetFolder: $(Agent.TempDirectory)/binskim-cli + displayName: Copy files for BinSkim + + - task: BinSkim@4 + inputs: + InputType: Basic + Function: analyze + TargetPattern: guardianGlob + AnalyzeTargetGlob: $(Agent.TempDirectory)/binskim-cli/*.* + AnalyzeSymPath: $(Agent.TempDirectory)/binskim-cli + displayName: Run BinSkim + - 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 b6dc424d690..ba8150651a7 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 e39df13c998..bc711bec4a7 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/common/computeBuiltInDepsCacheKey.js b/build/azure-pipelines/common/computeBuiltInDepsCacheKey.js index 2d747f56cc7..10fa9087454 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 53d6c501ea9..8abaaccb654 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 976e096fad2..c09c13be9d4 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 0940c929b54..57b35dc78de 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 29c3448edc1..c605ed6218e 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 afc5f59003a..6afeb01e6cc 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/extract-telemetry.sh b/build/azure-pipelines/common/extract-telemetry.sh old mode 100644 new mode 100755 diff --git a/build/azure-pipelines/common/getPublishAuthTokens.js b/build/azure-pipelines/common/getPublishAuthTokens.js new file mode 100644 index 00000000000..9c22e9ad94b --- /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 00000000000..68e76de1a83 --- /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 aaa44c51a12..301b5f930b6 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 aca461f8b5f..fb85b25cfd1 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 aa185ed8369..444e005d3c6 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(); @@ -473,33 +483,102 @@ 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 isLegacy = artifact.name.includes('_legacy'); + const platform = getPlatform(product, os, arch, unprocessedType, isLegacy); + 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,7 +597,13 @@ 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'); } @@ -561,12 +646,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 652cd168335..f061d043d13 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 { @@ -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,77 @@ 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 isLegacy = artifact.name.includes('_legacy'); + const platform = getPlatform(product, os, arch, unprocessedType, isLegacy); + 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,7 +943,17 @@ 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'); } diff --git a/build/azure-pipelines/common/releaseBuild.js b/build/azure-pipelines/common/releaseBuild.js index c2aab2075d0..fa69cb4e258 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 2e8fff04fb4..b7762de7df6 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 da899cd3fc0..f4e3f27c1f2 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 76828b42e1e..ad88435b5a3 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 32996a7db03..fd87772b3b8 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 28fca31205e..19a288483c8 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/config/tsaoptions.json b/build/azure-pipelines/config/tsaoptions.json new file mode 100644 index 00000000000..e337b577c1c --- /dev/null +++ b/build/azure-pipelines/config/tsaoptions.json @@ -0,0 +1,12 @@ +{ + "codebaseName": "devdiv_microsoft_vscode", + "serviceTreeID": "79c048b2-322f-4ed5-a1ea-252a1250e4b3", + "instanceUrl": "https://devdiv.visualstudio.com/defaultcollection", + "projectName": "DevDiv", + "areaPath": "DevDiv\\VS Code (compliance tracking only)\\Visual Studio Code Client", + "notificationAliases": [ + "monacotools@microsoft.com" + ], + "validateToolOutput": "None", + "allTools": true +} 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 32615c58463..b3d01ca7ff1 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 82a1e89f2ab..dffb6665d99 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 937d9d70c3e..9e054574c81 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-test.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-test.yml @@ -7,9 +7,6 @@ parameters: type: boolean - name: VSCODE_RUN_SMOKE_TESTS type: boolean - - name: VSCODE_BUILD_AMD - type: boolean - default: false steps: - script: npm exec -- npm-run-all -lp "electron $(VSCODE_ARCH)" "playwright-install" @@ -20,56 +17,30 @@ steps: - ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}: - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: - - ${{ if eq(parameters.VSCODE_BUILD_AMD, true) }}: - - script: ./scripts/test-amd.sh --tfs "Unit Tests" - displayName: Run unit tests (Electron) [AMD] - timeoutInMinutes: 15 - - script: npm run test-node-amd - displayName: Run unit tests (node.js) [AMD] - timeoutInMinutes: 15 - - script: npm run test-browser-amd-no-install -- --sequential --browser chromium --browser webkit --tfs "Browser Unit Tests" - env: - DEBUG: "*browser*" - displayName: Run unit tests (Browser, Chromium & Webkit) [AMD] - timeoutInMinutes: 30 - - ${{ if eq(parameters.VSCODE_BUILD_AMD, false) }}: - - script: ./scripts/test.sh --tfs "Unit Tests" - displayName: Run unit tests (Electron) - timeoutInMinutes: 15 - - script: npm run test-node - displayName: Run unit tests (node.js) - timeoutInMinutes: 15 - - script: npm run test-browser-no-install -- --sequential --browser chromium --browser webkit --tfs "Browser Unit Tests" - env: - DEBUG: "*browser*" - displayName: Run unit tests (Browser, Chromium & Webkit) - timeoutInMinutes: 30 + - script: ./scripts/test.sh --tfs "Unit Tests" + displayName: Run unit tests (Electron) + timeoutInMinutes: 15 + - script: npm run test-node + displayName: Run unit tests (node.js) + timeoutInMinutes: 15 + - script: npm run test-browser-no-install -- --sequential --browser chromium --browser webkit --tfs "Browser Unit Tests" + env: + DEBUG: "*browser*" + displayName: Run unit tests (Browser, Chromium & Webkit) + timeoutInMinutes: 30 - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - ${{ if eq(parameters.VSCODE_BUILD_AMD, true) }}: - - script: ./scripts/test-amd.sh --build --tfs "Unit Tests" - displayName: Run unit tests (Electron) [AMD] - timeoutInMinutes: 15 - - script: npm run test-node-amd -- --build - displayName: Run unit tests (node.js) [AMD] - timeoutInMinutes: 15 - - script: npm run test-browser-amd-no-install -- --sequential --build --browser chromium --browser webkit --tfs "Browser Unit Tests" - env: - DEBUG: "*browser*" - displayName: Run unit tests (Browser, Chromium & Webkit) [AMD] - timeoutInMinutes: 30 - - ${{ if eq(parameters.VSCODE_BUILD_AMD, false) }}: - - script: ./scripts/test.sh --build --tfs "Unit Tests" - displayName: Run unit tests (Electron) - timeoutInMinutes: 15 - - script: npm run test-node -- --build - displayName: Run unit tests (node.js) - timeoutInMinutes: 15 - - script: npm run test-browser-no-install -- --sequential --build --browser chromium --browser webkit --tfs "Browser Unit Tests" - env: - DEBUG: "*browser*" - displayName: Run unit tests (Browser, Chromium & Webkit) - timeoutInMinutes: 30 + - script: ./scripts/test.sh --build --tfs "Unit Tests" + displayName: Run unit tests (Electron) + timeoutInMinutes: 15 + - script: npm run test-node -- --build + displayName: Run unit tests (node.js) + timeoutInMinutes: 15 + - script: npm run test-browser-no-install -- --sequential --build --browser chromium --browser webkit --tfs "Browser Unit Tests" + env: + DEBUG: "*browser*" + displayName: Run unit tests (Browser, Chromium & Webkit) + timeoutInMinutes: 30 - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: - script: | @@ -90,48 +61,29 @@ 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 - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: - - ${{ if eq(parameters.VSCODE_BUILD_AMD, true) }}: - - script: ./scripts/test-integration-amd.sh --tfs "Integration Tests" - displayName: Run integration tests (Electron) [AMD] - timeoutInMinutes: 20 - - ${{ if eq(parameters.VSCODE_BUILD_AMD, false) }}: - - script: ./scripts/test-integration --tfs "Integration Tests" - displayName: Run integration tests (Electron) - timeoutInMinutes: 20 + - script: ./scripts/test-integration --tfs "Integration Tests" + displayName: Run integration tests (Electron) + timeoutInMinutes: 20 - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - ${{ if eq(parameters.VSCODE_BUILD_AMD, true) }}: - - script: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - set -e - APP_ROOT="$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)" - APP_NAME="`ls $APP_ROOT | head -n 1`" - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ - ./scripts/test-integration-amd.sh --build --tfs "Integration Tests" - env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH) - displayName: Run integration tests (Electron) [AMD] - timeoutInMinutes: 20 - - ${{ if eq(parameters.VSCODE_BUILD_AMD, false) }}: - - script: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - set -e - APP_ROOT="$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)" - APP_NAME="`ls $APP_ROOT | head -n 1`" - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ - ./scripts/test-integration.sh --build --tfs "Integration Tests" - env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH) - displayName: Run integration tests (Electron) - timeoutInMinutes: 20 + - script: | + # Figure out the full absolute path of the product we just built + # including the remote server and configure the integration tests + # to run with these builds instead of running out of sources. + set -e + APP_ROOT="$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH)" + APP_NAME="`ls $APP_ROOT | head -n 1`" + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ + ./scripts/test-integration.sh --build --tfs "Integration Tests" + env: + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-darwin-$(VSCODE_ARCH) + displayName: Run integration tests (Electron) + timeoutInMinutes: 20 - script: ./scripts/test-web-integration.sh --browser webkit env: diff --git a/build/azure-pipelines/darwin/product-build-darwin-universal.yml b/build/azure-pipelines/darwin/product-build-darwin-universal.yml index 549e4107438..3bb62e15403 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-universal.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-universal.yml @@ -1,5 +1,3 @@ -# Void - this looks like the relevant file for us (product-build-darwin.yml is independent and maybe just used for testing) - steps: - task: NodeTool@0 inputs: @@ -12,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" @@ -48,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 @@ -61,8 +61,6 @@ steps: - script: node build/azure-pipelines/distro/mixin-quality displayName: Mixin distro quality - - ## Void - IMPORTANT - script: | set -e unzip $(Pipeline.Workspace)/unsigned_vscode_client_darwin_x64_archive/VSCode-darwin-x64.zip -d $(agent.builddirectory)/VSCode-darwin-x64 @@ -70,7 +68,13 @@ steps: DEBUG=* node build/darwin/create-universal-app.js $(agent.builddirectory) displayName: Create Universal App - ## Void - IMPORTANT + - script: | + set -e + APP_ROOT="$(Agent.BuildDirectory)/VSCode-darwin-$(VSCODE_ARCH)" + APP_NAME="`ls $APP_ROOT | head -n 1`" + APP_PATH="$APP_ROOT/$APP_NAME" node build/darwin/verify-macho.js universal + displayName: Verify arch of Mach-O objects + - script: | set -e security create-keychain -p pwd $(agent.tempdirectory)/buildagent.keychain diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index a76ab27b520..e77000d431b 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -9,9 +9,6 @@ parameters: type: boolean - name: VSCODE_RUN_SMOKE_TESTS type: boolean - - name: VSCODE_BUILD_AMD - type: boolean - default: false steps: - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: @@ -31,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" @@ -78,16 +75,10 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), ne(variables['NPM_REGISTRY'], 'none')) displayName: Setup NPM Authentication - - script: | - set -e - # Refs https://github.com/microsoft/vscode/issues/219893#issuecomment-2209313109 - sudo xcode-select --switch /Applications/Xcode_15.2.app - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - displayName: Switch to Xcode >= 15.1 - - script: | set -e c++ --version + xcode-select -print-path python3 -m pip install setuptools for i in {1..5}; do # try 5 times @@ -164,7 +155,7 @@ steps: displayName: Build server (web) - ${{ else }}: - - script: npm run gulp transpile-client-swc transpile-extensions + - script: npm run gulp transpile-client-esbuild transpile-extensions env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Transpile @@ -176,7 +167,6 @@ steps: VSCODE_RUN_UNIT_TESTS: ${{ parameters.VSCODE_RUN_UNIT_TESTS }} VSCODE_RUN_INTEGRATION_TESTS: ${{ parameters.VSCODE_RUN_INTEGRATION_TESTS }} VSCODE_RUN_SMOKE_TESTS: ${{ parameters.VSCODE_RUN_SMOKE_TESTS }} - VSCODE_BUILD_AMD: ${{ parameters.VSCODE_BUILD_AMD }} - ${{ elseif and(ne(parameters.VSCODE_CIBUILD, true), ne(parameters.VSCODE_QUALITY, 'oss')) }}: - task: DownloadPipelineArtifact@2 @@ -198,6 +188,14 @@ steps: chmod +x "$APP_PATH/Contents/Resources/app/bin/$CLI_APP_NAME" displayName: Make CLI executable + - script: | + set -e + APP_ROOT="$(Agent.BuildDirectory)/VSCode-darwin-$(VSCODE_ARCH)" + APP_NAME="`ls $APP_ROOT | head -n 1`" + APP_PATH="$APP_ROOT/$APP_NAME" node build/darwin/verify-macho.js $(VSCODE_ARCH) + APP_PATH="$(Agent.BuildDirectory)/vscode-server-darwin-$(VSCODE_ARCH)" node build/darwin/verify-macho.js $(VSCODE_ARCH) + displayName: Verify arch of Mach-O objects + # Setting hardened entitlements is a requirement for: # * Apple notarization # * Running tests on Big Sur (because Big Sur has additional security precautions) diff --git a/build/azure-pipelines/distro-build.yml b/build/azure-pipelines/distro-build.yml index ee5dd5d9919..ae11345bb6d 100644 --- a/build/azure-pipelines/distro-build.yml +++ b/build/azure-pipelines/distro-build.yml @@ -1,5 +1,6 @@ pool: - vmImage: "ubuntu-latest" + name: 1es-ubuntu-22.04-x64 + os: linux trigger: branches: diff --git a/build/azure-pipelines/distro/download-distro.yml b/build/azure-pipelines/distro/download-distro.yml index 92325762a60..5c9ed0e56cd 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 0c61bb3dcf4..87958a5d449 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 da5eb24ca28..6e32f10db50 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 6e011b5a1e9..335f63ca1fc 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 b9b3c4f6c42..29c90f00a65 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/apt-retry.sh b/build/azure-pipelines/linux/apt-retry.sh old mode 100644 new mode 100755 diff --git a/build/azure-pipelines/linux/cli-build-linux.yml b/build/azure-pipelines/linux/cli-build-linux.yml index 89bc8a39e24..dba949395de 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 index ce9d71a5290..4c26baf2f99 100644 --- a/build/azure-pipelines/linux/product-build-linux-legacy-server.yml +++ b/build/azure-pipelines/linux/product-build-linux-legacy-server.yml @@ -5,9 +5,6 @@ parameters: type: boolean - name: VSCODE_ARCH type: string - - name: VSCODE_BUILD_AMD - type: boolean - default: false steps: - task: NodeTool@0 @@ -21,7 +18,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" @@ -45,7 +42,9 @@ steps: libxkbfile-dev \ libkrb5-dev \ libgbm1 \ - rpm + rpm \ + gcc-10 \ + g++-10 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 @@ -80,7 +79,7 @@ steps: - task: Docker@1 displayName: "Pull Docker image" inputs: - azureSubscriptionEndpoint: "vscode-builds-subscription" + azureSubscriptionEndpoint: vscode azureContainerRegistry: vscodehub.azurecr.io command: "Run an image" imageName: vscode-linux-build-agent:centos7-devtoolset8-$(VSCODE_ARCH) @@ -98,13 +97,17 @@ steps: echo "Npm install failed $i, trying again..." done workingDirectory: build + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Install build dependencies - script: | set -e export VSCODE_SYSROOT_PREFIX='-glibc-2.17' - source ./build/azure-pipelines/linux/setup-env.sh --only-remote + export CC=$(which gcc-10) + export CXX=$(which g++-10) + source ./build/azure-pipelines/linux/setup-env.sh --skip-sysroot for i in {1..5}; do # try 5 times npm ci && break @@ -206,7 +209,6 @@ steps: VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: ${{ parameters.VSCODE_RUN_INTEGRATION_TESTS }} VSCODE_RUN_SMOKE_TESTS: false - VSCODE_BUILD_AMD: ${{ parameters.VSCODE_BUILD_AMD }} ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: PUBLISH_TASK_NAME: 1ES.PublishPipelineArtifact@1 diff --git a/build/azure-pipelines/linux/product-build-linux-test.yml b/build/azure-pipelines/linux/product-build-linux-test.yml index 2443e2b1299..6796339c738 100644 --- a/build/azure-pipelines/linux/product-build-linux-test.yml +++ b/build/azure-pipelines/linux/product-build-linux-test.yml @@ -10,9 +10,6 @@ parameters: - name: PUBLISH_TASK_NAME type: string default: PublishPipelineArtifact@0 - - name: VSCODE_BUILD_AMD - type: boolean - default: false steps: - script: npm exec -- npm-run-all -lp "electron $(VSCODE_ARCH)" "playwright-install" @@ -36,61 +33,33 @@ steps: - ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}: - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: - - ${{ if eq(parameters.VSCODE_BUILD_AMD, true) }}: - - script: ./scripts/test-amd.sh --tfs "Unit Tests" - env: - DISPLAY: ":10" - displayName: Run unit tests (Electron) [AMD] - timeoutInMinutes: 15 - - script: npm run test-node-amd - displayName: Run unit tests (node.js) [AMD] - timeoutInMinutes: 15 - - script: npm run test-browser-amd-no-install -- --browser chromium --tfs "Browser Unit Tests" - env: - DEBUG: "*browser*" - displayName: Run unit tests (Browser, Chromium) [AMD] - timeoutInMinutes: 15 - - ${{ if eq(parameters.VSCODE_BUILD_AMD, false) }}: - - script: ./scripts/test.sh --tfs "Unit Tests" - env: - DISPLAY: ":10" - displayName: Run unit tests (Electron) - timeoutInMinutes: 15 - - script: npm run test-node - displayName: Run unit tests (node.js) - timeoutInMinutes: 15 - - script: npm run test-browser-no-install -- --browser chromium --tfs "Browser Unit Tests" - env: - DEBUG: "*browser*" - displayName: Run unit tests (Browser, Chromium) - timeoutInMinutes: 15 + - script: ./scripts/test.sh --tfs "Unit Tests" + env: + DISPLAY: ":10" + displayName: Run unit tests (Electron) + timeoutInMinutes: 15 + - script: npm run test-node + displayName: Run unit tests (node.js) + timeoutInMinutes: 15 + - script: npm run test-browser-no-install -- --browser chromium --tfs "Browser Unit Tests" + env: + DEBUG: "*browser*" + displayName: Run unit tests (Browser, Chromium) + timeoutInMinutes: 15 - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - ${{ if eq(parameters.VSCODE_BUILD_AMD, true) }}: - - script: ./scripts/test-amd.sh --build --tfs "Unit Tests" - displayName: Run unit tests (Electron) [AMD] - timeoutInMinutes: 15 - - script: npm run test-node-amd -- --build - displayName: Run unit tests (node.js) [AMD] - timeoutInMinutes: 15 - - script: npm run test-browser-amd-no-install -- --build --browser chromium --tfs "Browser Unit Tests" - env: - DEBUG: "*browser*" - displayName: Run unit tests (Browser, Chromium) [AMD] - timeoutInMinutes: 15 - - ${{ if eq(parameters.VSCODE_BUILD_AMD, false) }}: - - script: ./scripts/test.sh --build --tfs "Unit Tests" - displayName: Run unit tests (Electron) - timeoutInMinutes: 15 - - script: npm run test-node -- --build - displayName: Run unit tests (node.js) - timeoutInMinutes: 15 - - script: npm run test-browser-no-install -- --build --browser chromium --tfs "Browser Unit Tests" - env: - DEBUG: "*browser*" - displayName: Run unit tests (Browser, Chromium) - timeoutInMinutes: 15 + - script: ./scripts/test.sh --build --tfs "Unit Tests" + displayName: Run unit tests (Electron) + timeoutInMinutes: 15 + - script: npm run test-node -- --build + displayName: Run unit tests (node.js) + timeoutInMinutes: 15 + - script: npm run test-browser-no-install -- --build --browser chromium --tfs "Browser Unit Tests" + env: + DEBUG: "*browser*" + displayName: Run unit tests (Browser, Chromium) + timeoutInMinutes: 15 - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: - script: | @@ -111,23 +80,17 @@ 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 - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: - - ${{ if eq(parameters.VSCODE_BUILD_AMD, true) }}: - - script: ./scripts/test-integration-amd.sh --tfs "Integration Tests" - env: - DISPLAY: ":10" - displayName: Run integration tests (Electron) [AMD] - timeoutInMinutes: 20 - - ${{ if eq(parameters.VSCODE_BUILD_AMD, false) }}: - - script: ./scripts/test-integration.sh --tfs "Integration Tests" - env: - DISPLAY: ":10" - displayName: Run integration tests (Electron) - timeoutInMinutes: 20 + - script: ./scripts/test-integration.sh --tfs "Integration Tests" + env: + DISPLAY: ":10" + displayName: Run integration tests (Electron) + timeoutInMinutes: 20 - script: ./scripts/test-web-integration.sh --browser chromium displayName: Run integration tests (Browser, Chromium) @@ -138,36 +101,20 @@ steps: timeoutInMinutes: 20 - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - ${{ if eq(parameters.VSCODE_BUILD_AMD, true) }}: - - script: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") - INTEGRATION_TEST_APP_NAME="$APP_NAME" \ - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ - ./scripts/test-integration-amd.sh --build --tfs "Integration Tests" - env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH) - displayName: Run integration tests (Electron) [AMD] - timeoutInMinutes: 20 - - ${{ if eq(parameters.VSCODE_BUILD_AMD, false) }}: - - script: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - set -e - APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") - INTEGRATION_TEST_APP_NAME="$APP_NAME" \ - INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ - ./scripts/test-integration.sh --build --tfs "Integration Tests" - env: - VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH) - displayName: Run integration tests (Electron) - timeoutInMinutes: 20 + - script: | + # Figure out the full absolute path of the product we just built + # including the remote server and configure the integration tests + # to run with these builds instead of running out of sources. + set -e + APP_ROOT=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + APP_NAME=$(node -p "require(\"$APP_ROOT/resources/app/product.json\").applicationName") + INTEGRATION_TEST_APP_NAME="$APP_NAME" \ + INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ + ./scripts/test-integration.sh --build --tfs "Integration Tests" + env: + VSCODE_REMOTE_SERVER_PATH: $(agent.builddirectory)/vscode-server-linux-$(VSCODE_ARCH) + displayName: Run integration tests (Electron) + timeoutInMinutes: 20 - script: ./scripts/test-web-integration.sh --browser chromium env: diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 3f53731b70a..b9300b1ccba 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -11,9 +11,6 @@ parameters: type: boolean - name: VSCODE_ARCH type: string - - name: VSCODE_BUILD_AMD - type: boolean - default: false steps: - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: @@ -33,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: @@ -114,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')) @@ -132,6 +131,28 @@ steps: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Download vscode sysroots + - ${{ 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 @@ -159,20 +180,6 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) displayName: Mixin distro node modules - - ${{ else }}: - # Ref https://github.com/microsoft/vscode/issues/189019 - # for the node-gyp rebuild step - - script: | - set -e - - cd node_modules/native-keymap && npx node-gyp@9.4.0 -y rebuild --debug - cd ../.. && ./.github/workflows/check-clean-git-state.sh - env: - npm_config_arch: $(NPM_ARCH) - GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Rebuild debug version of native modules (OSS) - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - - script: | set -e node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt @@ -251,8 +258,6 @@ steps: - script: | set -e - source ./build/azure-pipelines/linux/setup-env.sh - EXPECTED_GLIBC_VERSION="2.28" \ EXPECTED_GLIBCXX_VERSION="3.4.25" \ ./build/azure-pipelines/linux/verify-glibc-requirements.sh @@ -266,8 +271,6 @@ steps: - script: | set -e - source ./build/azure-pipelines/linux/setup-env.sh - EXPECTED_GLIBC_VERSION="2.28" \ EXPECTED_GLIBCXX_VERSION="3.4.26" \ ./build/azure-pipelines/linux/verify-glibc-requirements.sh @@ -278,7 +281,7 @@ steps: displayName: Check GLIBC and GLIBCXX dependencies in server archive - ${{ else }}: - - script: npm run gulp "transpile-client-swc" "transpile-extensions" + - script: npm run gulp "transpile-client-esbuild" "transpile-extensions" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Transpile client and extensions @@ -290,7 +293,6 @@ steps: VSCODE_RUN_UNIT_TESTS: ${{ parameters.VSCODE_RUN_UNIT_TESTS }} VSCODE_RUN_INTEGRATION_TESTS: ${{ parameters.VSCODE_RUN_INTEGRATION_TESTS }} VSCODE_RUN_SMOKE_TESTS: ${{ parameters.VSCODE_RUN_SMOKE_TESTS }} - VSCODE_BUILD_AMD: ${{ parameters.VSCODE_BUILD_AMD }} ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: PUBLISH_TASK_NAME: 1ES.PublishPipelineArtifact@1 @@ -305,6 +307,11 @@ steps: - script: | set -e npm run gulp "vscode-linux-$(VSCODE_ARCH)-build-deb" + file_output=$(file $(ls .build/linux/deb/*/deb/*.deb)) + if [[ "$file_output" != *"data compression xz"* ]]; then + echo "Error: unknown compression. $file_output" + exit 1 + fi echo "##vso[task.setvariable variable=DEB_PATH]$(ls .build/linux/deb/*/deb/*.deb)" displayName: Build deb package @@ -344,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 old mode 100644 new mode 100755 index 1ce3ba742c1..1f198441bc3 --- a/build/azure-pipelines/linux/setup-env.sh +++ b/build/azure-pipelines/linux/setup-env.sh @@ -16,9 +16,8 @@ else fi if [ "$npm_config_arch" == "x64" ]; then - if [ "$(echo "$@" | grep -c -- "--only-remote")" -eq 0 ]; then # Download clang based on chromium revision used by vscode - curl -s https://raw.githubusercontent.com/chromium/chromium/124.0.6367.243/tools/clang/scripts/update.py | python - --output-dir=$PWD/.build/CR_Clang --host-os=linux + curl -s https://raw.githubusercontent.com/chromium/chromium/132.0.6834.196/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 \ @@ -30,14 +29,15 @@ if [ "$npm_config_arch" == "x64" ]; then # Set compiler toolchain # Flags for the client build are based on - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/124.0.6367.243:build/config/arm.gni - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/124.0.6367.243:build/config/compiler/BUILD.gn - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/124.0.6367.243:build/config/c++/BUILD.gn + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/132.0.6834.196:build/config/arm.gni + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/132.0.6834.196:build/config/compiler/BUILD.gn + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/132.0.6834.196: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 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++ @@ -45,7 +45,7 @@ if [ "$npm_config_arch" == "x64" ]; then 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 elif [ "$npm_config_arch" == "arm64" ]; then - if [ "$(echo "$@" | grep -c -- "--only-remote")" -eq 0 ]; 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++ @@ -59,11 +59,13 @@ elif [ "$npm_config_arch" == "arm64" ]; then 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 elif [ "$npm_config_arch" == "arm" ]; 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" + 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 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 3582be07cca..4d0d26411c3 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/linux/verify-glibc-requirements.sh b/build/azure-pipelines/linux/verify-glibc-requirements.sh old mode 100644 new mode 100755 diff --git a/build/azure-pipelines/oss/product-build-pr-cache-linux.yml b/build/azure-pipelines/oss/product-build-pr-cache-linux.yml index 6609e80ed28..72cd33cdd75 100644 --- a/build/azure-pipelines/oss/product-build-pr-cache-linux.yml +++ b/build/azure-pipelines/oss/product-build-pr-cache-linux.yml @@ -43,8 +43,11 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), ne(variables['NPM_REGISTRY'], 'none')) displayName: Setup NPM Authentication - - script: sudo apt-get update && sudo apt-get install -y libkrb5-dev - displayName: Install build dependencies + - script: | + set -e + ./build/azure-pipelines/linux/apt-retry.sh sudo apt-get update + ./build/azure-pipelines/linux/apt-retry.sh sudo apt-get install -y libkrb5-dev + displayName: Setup system services condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - script: | diff --git a/build/azure-pipelines/oss/product-build-pr-cache-win32.yml b/build/azure-pipelines/oss/product-build-pr-cache-win32.yml index e1c8305bbb2..76944f69b14 100644 --- a/build/azure-pipelines/oss/product-build-pr-cache-win32.yml +++ b/build/azure-pipelines/oss/product-build-pr-cache-win32.yml @@ -49,15 +49,15 @@ steps: - powershell: | . build/azure-pipelines/win32/exec.ps1 - . build/azure-pipelines/win32/retry.ps1 $ErrorActionPreference = "Stop" - $env:npm_config_arch="$(VSCODE_ARCH)" - $env:CHILD_CONCURRENCY="1" - retry { exec { npm ci } } + exec { npm ci } env: + npm_config_arch: $(VSCODE_ARCH) + npm_config_foreground_scripts: "true" ELECTRON_SKIP_BINARY_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 GITHUB_TOKEN: "$(github-distro-mixin-password)" + retryCountOnTaskFailure: 5 displayName: Install dependencies condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) diff --git a/build/azure-pipelines/product-build-pr.yml b/build/azure-pipelines/product-build-pr.yml index 025a1eeac67..2d66ff3945d 100644 --- a/build/azure-pipelines/product-build-pr.yml +++ b/build/azure-pipelines/product-build-pr.yml @@ -21,8 +21,6 @@ variables: value: oss - name: VSCODE_STEP_ON_IT value: false - - name: VSCODE_BUILD_AMD - value: false jobs: - ${{ if ne(variables['VSCODE_CIBUILD'], true) }}: @@ -50,7 +48,6 @@ jobs: parameters: VSCODE_ARCH: x64 VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: true VSCODE_RUN_INTEGRATION_TESTS: false @@ -69,7 +66,6 @@ jobs: parameters: VSCODE_ARCH: x64 VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: true @@ -88,7 +84,6 @@ jobs: parameters: VSCODE_ARCH: x64 VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false @@ -112,7 +107,6 @@ jobs: - template: win32/product-build-win32.yml@self parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: true @@ -130,7 +124,6 @@ jobs: - template: win32/product-build-win32.yml@self parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index d4038dced08..0e5faf24296 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/*"] @@ -100,22 +101,19 @@ parameters: displayName: "Skip tests" type: boolean default: false - - name: VSCODE_BUILD_AMD # TODO@bpasero TODO@esm remove me once AMD is removed - displayName: "️❗ Build as AMD (!FOR EMERGENCY ONLY!) ️❗" - type: boolean - default: false 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 value: ${{ parameters.VSCODE_QUALITY }} - - name: VSCODE_BUILD_AMD - value: ${{ parameters.VSCODE_BUILD_AMD }} - name: VSCODE_BUILD_STAGE_WINDOWS value: ${{ or(eq(parameters.VSCODE_BUILD_WIN32, true), eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }} - name: VSCODE_BUILD_STAGE_LINUX @@ -134,22 +132,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 @@ -180,15 +182,7 @@ extends: sdl: tsa: enabled: true - config: - codebaseName: 'devdiv_$(Build.Repository.Name)' - serviceTreeID: '79c048b2-322f-4ed5-a1ea-252a1250e4b3' - instanceUrl: 'https://devdiv.visualstudio.com/defaultcollection' - projectName: 'DevDiv' - areaPath: "DevDiv\\VS Code (compliance tracking only)\\Visual Studio Code Client" - notificationAliases: ['monacotools@microsoft.com'] - validateToolOutput: None - allTools: true + configFile: $(Build.SourcesDirectory)/build/azure-pipelines/config/tsaoptions.json codeql: runSourceLanguagesInSourceAnalysis: true compiled: @@ -199,7 +193,7 @@ extends: eslint: enabled: true enableExclusions: true - exclusionsFilePath: $(Build.SourcesDirectory)/.eslintignore + exclusionsFilePath: $(Build.SourcesDirectory)/.eslint-ignore sourceAnalysisPool: 1es-windows-2022-x64 createAdoIssuesForJustificationsForDisablement: false containers: @@ -207,25 +201,20 @@ 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 stages: - stage: Compile jobs: - job: Compile timeoutInMinutes: 90 pool: - name: 1es-ubuntu-22.04-x64 - os: linux + name: AcesShared + os: macOS variables: - VSCODE_ARCH: x64 + VSCODE_ARCH: arm64 steps: - template: build/azure-pipelines/product-compile.yml@self parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} - ${{ 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)) }}: - stage: CompileCLI @@ -273,6 +262,11 @@ extends: os: linux hostArchitecture: arm64 container: ubuntu-2004-arm64 + templateContext: + authenticatedContainerRegistries: + - registry: onebranch.azurecr.io + tenant: AME + identity: 1ESPipelineIdentity steps: - template: build/azure-pipelines/alpine/cli-build-alpine.yml@self parameters: @@ -335,8 +329,6 @@ extends: os: windows jobs: - job: WindowsSDL - variables: - - group: 'API Scan' steps: - template: build/azure-pipelines/win32/sdl-scan-win32.yml@self parameters: @@ -363,7 +355,6 @@ extends: - template: build/azure-pipelines/win32/product-build-win32.yml@self parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: true @@ -378,7 +369,6 @@ extends: - template: build/azure-pipelines/win32/product-build-win32.yml@self parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false @@ -393,7 +383,6 @@ extends: - template: build/azure-pipelines/win32/product-build-win32.yml@self parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false @@ -409,7 +398,6 @@ extends: - template: build/azure-pipelines/win32/product-build-win32.yml@self parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_ARCH: x64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} @@ -421,6 +409,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 }} @@ -433,7 +422,6 @@ extends: - template: build/azure-pipelines/win32/product-build-win32.yml@self parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_ARCH: arm64 VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false @@ -462,7 +450,6 @@ extends: parameters: VSCODE_ARCH: x64 VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: true VSCODE_RUN_INTEGRATION_TESTS: false @@ -478,7 +465,6 @@ extends: parameters: VSCODE_ARCH: x64 VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: true @@ -494,7 +480,6 @@ extends: parameters: VSCODE_ARCH: x64 VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false @@ -512,7 +497,6 @@ extends: parameters: VSCODE_ARCH: x64 VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} @@ -525,6 +509,11 @@ extends: container: snapcraft variables: VSCODE_ARCH: x64 + templateContext: + authenticatedContainerRegistries: + - registry: onebranch.azurecr.io + tenant: AME + identity: 1ESPipelineIdentity steps: - template: build/azure-pipelines/linux/snap-build-linux.yml@self @@ -538,7 +527,6 @@ extends: parameters: VSCODE_ARCH: armhf VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false @@ -554,7 +542,6 @@ extends: parameters: VSCODE_ARCH: arm64 VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false @@ -579,7 +566,6 @@ extends: parameters: VSCODE_ARCH: x64 VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} - ${{ if eq(parameters.VSCODE_BUILD_LINUX_ARMHF_LEGACY_SERVER, true) }}: @@ -592,7 +578,6 @@ extends: parameters: VSCODE_ARCH: armhf VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_RUN_INTEGRATION_TESTS: false - ${{ if eq(parameters.VSCODE_BUILD_LINUX_ARM64_LEGACY_SERVER, true) }}: @@ -605,7 +590,6 @@ extends: parameters: VSCODE_ARCH: arm64 VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} 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)) }}: @@ -658,7 +642,6 @@ extends: - template: build/azure-pipelines/darwin/product-build-darwin.yml@self parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: true VSCODE_RUN_INTEGRATION_TESTS: false @@ -672,7 +655,6 @@ extends: - template: build/azure-pipelines/darwin/product-build-darwin.yml@self parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: true @@ -686,7 +668,6 @@ extends: - template: build/azure-pipelines/darwin/product-build-darwin.yml@self parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false @@ -701,7 +682,6 @@ extends: - template: build/azure-pipelines/darwin/product-build-darwin.yml@self parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false @@ -716,7 +696,6 @@ extends: - template: build/azure-pipelines/darwin/product-build-darwin.yml@self parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} @@ -736,6 +715,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 }} @@ -748,7 +728,6 @@ extends: - template: build/azure-pipelines/darwin/product-build-darwin.yml@self parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_AMD: ${{ variables.VSCODE_BUILD_AMD }} VSCODE_CIBUILD: ${{ variables.VSCODE_CIBUILD }} VSCODE_RUN_UNIT_TESTS: false VSCODE_RUN_INTEGRATION_TESTS: false @@ -821,16 +800,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 ca8b6a7b3ea..dcc1a62225d 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -1,9 +1,6 @@ parameters: - name: VSCODE_QUALITY type: string - - name: VSCODE_BUILD_AMD - type: boolean - default: false steps: - task: NodeTool@0 @@ -18,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" @@ -26,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 @@ -56,9 +53,10 @@ 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')) + - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: + - 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,10 +101,6 @@ steps: - script: node build/azure-pipelines/distro/mixin-quality displayName: Mixin distro quality - - ${{ if eq(parameters.VSCODE_BUILD_AMD, true) }}: - - script: node migrate.mjs --disable-watch --enable-esm-to-amd - displayName: Migrate ESM -> AMD - - template: common/install-builtin-extensions.yml@self - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: @@ -138,37 +132,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-npm-package-validate.yml b/build/azure-pipelines/product-npm-package-validate.yml new file mode 100644 index 00000000000..05f2cd8e00e --- /dev/null +++ b/build/azure-pipelines/product-npm-package-validate.yml @@ -0,0 +1,94 @@ +trigger: none + +pr: + branches: + include: ["main"] + paths: + include: ["package.json", "package-lock.json"] + +variables: + - name: NPM_REGISTRY + value: "https://pkgs.dev.azure.com/monacotools/Monaco/_packaging/vscode/npm/registry/" + - name: VSCODE_CIBUILD + value: ${{ in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI') }} + - name: VSCODE_QUALITY + value: oss + +jobs: + - ${{ if ne(variables['VSCODE_CIBUILD'], true) }}: + - job: ValidateNpmPackage + displayName: Valiate NPM package against Terrapin + pool: + name: 1es-ubuntu-22.04-x64 + os: linux + timeoutInMinutes: 40000 + continueOnError: true + variables: + VSCODE_ARCH: x64 + steps: + - task: NodeTool@0 + inputs: + versionSource: fromFile + versionFilePath: .nvmrc + nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download + + - 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 + echo "NPMRC Path: $(npm config get userconfig)" + echo "NPM Registry: $(npm config get registry)" + 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 + + - 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: succeeded() + + - script: | + set -e + + for attempt in {1..6}; do + if [ $attempt -gt 1 ]; then + echo "Attempt $attempt: Waiting for 1 hour before retrying..." + sleep 3600 + fi + + echo "Attempt $attempt: Running npm ci" + if npm i --ignore-scripts; then + if node build/npm/postinstall.js; then + echo "npm i succeeded on attempt $attempt" + exit 0 + else + echo "node build/npm/postinstall.js failed on attempt $attempt" + fi + else + echo "npm i failed on attempt $attempt" + fi + done + + echo "npm i failed after 6 attempts" + exit 1 + env: + npm_command: 'install --ignore-scripts' + ELECTRON_SKIP_BINARY_DOWNLOAD: 1 + PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 + GITHUB_TOKEN: "$(github-distro-mixin-password)" + displayName: Install dependencies with retries + timeoutInMinutes: 400 + + - script: .github/workflows/check-clean-git-state.sh + displayName: Check clean git state diff --git a/build/azure-pipelines/product-publish.yml b/build/azure-pipelines/product-publish.yml index 59012a938ac..8ecf5e6238e 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 diff --git a/build/azure-pipelines/product-release.yml b/build/azure-pipelines/product-release.yml index 8afdcf10053..87896f9340b 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 9e93a7fa4c9..5bd80a69bbf 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 35c5a511593..4496ed93af1 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 ed2deded3fc..29f9bfcf66e 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 a727647e64a..0f99b07cf9a 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 { diff --git a/build/azure-pipelines/upload-cdn.js b/build/azure-pipelines/upload-cdn.js index 62247de06bf..f8247450f25 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 81a4ac14eab..61d7cea523c 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 5b6cd3ed1fd..e89a6497d70 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 030cc8f0e5a..1a4f2665617 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 2a01ab79d6b..cac1ae3caf2 100644 --- a/build/azure-pipelines/upload-sourcemaps.js +++ b/build/azure-pipelines/upload-sourcemaps.js @@ -3,44 +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 }); -const path = require("path"); -const es = require("event-stream"); -const vfs = require("vinyl-fs"); -const util = require("../lib/util"); -const amd_1 = require("../lib/amd"); -// @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; })); } function main() { - if ((0, amd_1.isAMD)()) { - return Promise.resolve(); // in AMD we run into some issues, but we want to unblock the build for recovery - } const sources = []; // vscode client maps (default) 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 @@ -48,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 aed6446d7e5..0c51827fef4 100644 --- a/build/azure-pipelines/upload-sourcemaps.ts +++ b/build/azure-pipelines/upload-sourcemaps.ts @@ -3,20 +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'; -import { isAMD } from '../lib/amd'; -// @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; @@ -30,9 +28,6 @@ function src(base: string, maps = `${base}/**/*.map`) { } function main(): Promise { - if (isAMD()) { - return Promise.resolve(); // in AMD we run into some issues, but we want to unblock the build for recovery - } const sources: any[] = []; // vscode client maps (default) @@ -40,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}`))); @@ -65,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 75cf20bfc94..e0e91c1c589 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" @@ -57,12 +57,16 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), ne(variables['NPM_REGISTRY'], 'none')) displayName: Setup NPM Authentication - - script: sudo apt-get update && sudo apt-get install -y libkrb5-dev - displayName: Install build dependencies + - script: | + set -e + ./build/azure-pipelines/linux/apt-retry.sh sudo apt-get update + ./build/azure-pipelines/linux/apt-retry.sh sudo apt-get install -y libkrb5-dev + displayName: Setup system services condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - script: | set -e + for i in {1..5}; do # try 5 times npm ci && break if [ $i -eq 5 ]; then @@ -109,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 @@ -132,28 +136,25 @@ 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) + displayName: Upload sourcemaps (Web Main) - # 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_STORAGE_ACCOUNT="vscodeweb" \ 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) + 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) - 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 19409272ff0..d61f0e722f5 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 62b1b715af2..c7f4b0a0a12 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 @@ -36,11 +46,12 @@ steps: - powershell: | . azure-pipelines/win32/exec.ps1 - . azure-pipelines/win32/retry.ps1 $ErrorActionPreference = "Stop" - $env:CHILD_CONCURRENCY="1" - retry { exec { npm ci } } + exec { npm ci } workingDirectory: build + env: + GITHUB_TOKEN: "$(github-distro-mixin-password)" + retryCountOnTaskFailure: 5 displayName: Install build dependencies - template: ../cli/cli-win32-sign.yml@self diff --git a/build/azure-pipelines/win32/product-build-win32-test.yml b/build/azure-pipelines/win32/product-build-win32-test.yml index 6c6a0949d0d..09db30d1914 100644 --- a/build/azure-pipelines/win32/product-build-win32-test.yml +++ b/build/azure-pipelines/win32/product-build-win32-test.yml @@ -12,9 +12,6 @@ parameters: - name: PUBLISH_TASK_NAME type: string default: PublishPipelineArtifact@0 - - name: VSCODE_BUILD_AMD - type: boolean - default: false steps: - powershell: npm exec -- npm-run-all -lp "electron $(VSCODE_ARCH)" "playwright-install" @@ -25,48 +22,26 @@ steps: - ${{ if eq(parameters.VSCODE_RUN_UNIT_TESTS, true) }}: - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: - - ${{ if eq(parameters.VSCODE_BUILD_AMD, true) }}: - - powershell: .\scripts\test-amd.bat --tfs "Unit Tests" - displayName: Run unit tests (Electron) [AMD] - timeoutInMinutes: 15 - - powershell: npm run test-node-amd - displayName: Run unit tests (node.js) [AMD] - timeoutInMinutes: 15 - - powershell: node test/unit/browser/index.amd.js --sequential --browser chromium --tfs "Browser Unit Tests" - displayName: Run unit tests (Browser, Chromium) [AMD] - timeoutInMinutes: 20 - - ${{ if eq(parameters.VSCODE_BUILD_AMD, false) }}: - - powershell: .\scripts\test.bat --tfs "Unit Tests" - displayName: Run unit tests (Electron) - timeoutInMinutes: 15 - - powershell: npm run test-node - displayName: Run unit tests (node.js) - timeoutInMinutes: 15 - - powershell: node test/unit/browser/index.js --sequential --browser chromium --tfs "Browser Unit Tests" - displayName: Run unit tests (Browser, Chromium) - timeoutInMinutes: 20 + - powershell: .\scripts\test.bat --tfs "Unit Tests" + displayName: Run unit tests (Electron) + timeoutInMinutes: 15 + - powershell: npm run test-node + displayName: Run unit tests (node.js) + timeoutInMinutes: 15 + - powershell: node test/unit/browser/index.js --sequential --browser chromium --tfs "Browser Unit Tests" + displayName: Run unit tests (Browser, Chromium) + timeoutInMinutes: 20 - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - ${{ if eq(parameters.VSCODE_BUILD_AMD, true) }}: - - powershell: .\scripts\test-amd.bat --build --tfs "Unit Tests" - displayName: Run unit tests (Electron) [AMD] - timeoutInMinutes: 15 - - script: npm run test-node-amd -- --build - displayName: Run unit tests (node.js) [AMD] - timeoutInMinutes: 15 - - powershell: npm run test-browser-amd-no-install -- --sequential --build --browser chromium --tfs "Browser Unit Tests" - displayName: Run unit tests (Browser, Chromium) [AMD] - timeoutInMinutes: 20 - - ${{ if eq(parameters.VSCODE_BUILD_AMD, false) }}: - - powershell: .\scripts\test.bat --build --tfs "Unit Tests" - displayName: Run unit tests (Electron) - timeoutInMinutes: 15 - - powershell: npm run test-node -- --build - displayName: Run unit tests (node.js) - timeoutInMinutes: 15 - - powershell: npm run test-browser-no-install -- --sequential --build --browser chromium --tfs "Browser Unit Tests" - displayName: Run unit tests (Browser, Chromium) - timeoutInMinutes: 20 + - powershell: .\scripts\test.bat --build --tfs "Unit Tests" + displayName: Run unit tests (Electron) + timeoutInMinutes: 15 + - powershell: npm run test-node -- --build + displayName: Run unit tests (node.js) + timeoutInMinutes: 15 + - powershell: npm run test-browser-no-install -- --sequential --build --browser chromium --tfs "Browser Unit Tests" + displayName: Run unit tests (Browser, Chromium) + timeoutInMinutes: 20 - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: - powershell: | @@ -88,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 @@ -98,14 +74,9 @@ steps: condition: succeededOrFailed() - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: - - ${{ if eq(parameters.VSCODE_BUILD_AMD, true) }}: - - powershell: .\scripts\test-integration-amd.bat --tfs "Integration Tests" - displayName: Run integration tests (Electron) [AMD] - timeoutInMinutes: 20 - - ${{ if eq(parameters.VSCODE_BUILD_AMD, false) }}: - - powershell: .\scripts\test-integration.bat --tfs "Integration Tests" - displayName: Run integration tests (Electron) - timeoutInMinutes: 20 + - powershell: .\scripts\test-integration.bat --tfs "Integration Tests" + displayName: Run integration tests (Electron) + timeoutInMinutes: 20 - powershell: .\scripts\test-web-integration.bat --browser chromium displayName: Run integration tests (Browser, Chromium) @@ -116,36 +87,20 @@ steps: timeoutInMinutes: 20 - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - - ${{ if eq(parameters.VSCODE_BUILD_AMD, true) }}: - - powershell: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json - $AppNameShort = $AppProductJson.nameShort - $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe" - $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-server-win32-$(VSCODE_ARCH)" - exec { .\scripts\test-integration-amd.bat --build --tfs "Integration Tests" } - displayName: Run integration tests (Electron) [AMD] - timeoutInMinutes: 20 - - ${{ if eq(parameters.VSCODE_BUILD_AMD, false) }}: - - powershell: | - # Figure out the full absolute path of the product we just built - # including the remote server and configure the integration tests - # to run with these builds instead of running out of sources. - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json - $AppNameShort = $AppProductJson.nameShort - $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe" - $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-server-win32-$(VSCODE_ARCH)" - exec { .\scripts\test-integration.bat --build --tfs "Integration Tests" } - displayName: Run integration tests (Electron) - timeoutInMinutes: 20 + - powershell: | + # Figure out the full absolute path of the product we just built + # including the remote server and configure the integration tests + # to run with these builds instead of running out of sources. + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" + $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json + $AppNameShort = $AppProductJson.nameShort + $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe" + $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-server-win32-$(VSCODE_ARCH)" + exec { .\scripts\test-integration.bat --build --tfs "Integration Tests" } + displayName: Run integration tests (Electron) + timeoutInMinutes: 20 - powershell: | . build/azure-pipelines/win32/exec.ps1 diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index dddcb34a894..ed7f10048ca 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -11,9 +11,6 @@ parameters: type: boolean - name: VSCODE_RUN_SMOKE_TESTS type: boolean - - name: VSCODE_BUILD_AMD - type: boolean - default: false steps: - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: @@ -38,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: @@ -94,15 +91,15 @@ steps: - powershell: | . build/azure-pipelines/win32/exec.ps1 - . build/azure-pipelines/win32/retry.ps1 $ErrorActionPreference = "Stop" - retry { exec { npm ci } } + exec { npm ci } env: npm_config_arch: $(VSCODE_ARCH) - CHILD_CONCURRENCY: 1 + npm_config_foreground_scripts: "true" ELECTRON_SKIP_BINARY_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 GITHUB_TOKEN: "$(github-distro-mixin-password)" + retryCountOnTaskFailure: 5 displayName: Install dependencies condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) @@ -132,7 +129,7 @@ steps: retryCountOnTaskFailure: 3 - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: - - powershell: npm run gulp "transpile-client-swc" "transpile-extensions" + - powershell: npm run gulp "transpile-client-esbuild" "transpile-extensions" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Transpile client and extensions @@ -182,7 +179,6 @@ steps: VSCODE_RUN_UNIT_TESTS: ${{ parameters.VSCODE_RUN_UNIT_TESTS }} VSCODE_RUN_INTEGRATION_TESTS: ${{ parameters.VSCODE_RUN_INTEGRATION_TESTS }} VSCODE_RUN_SMOKE_TESTS: ${{ parameters.VSCODE_RUN_SMOKE_TESTS }} - VSCODE_BUILD_AMD: ${{ parameters.VSCODE_BUILD_AMD }} ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: PUBLISH_TASK_NAME: 1ES.PublishPipelineArtifact@1 @@ -210,29 +206,41 @@ steps: 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" @@ -272,25 +280,23 @@ steps: - 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 fc40b9a0e60..def3cb53dfc 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" @@ -80,15 +80,15 @@ steps: - powershell: | . build/azure-pipelines/win32/exec.ps1 - . build/azure-pipelines/win32/retry.ps1 $ErrorActionPreference = "Stop" - retry { exec { npm ci } } + exec { npm ci } env: npm_config_arch: ${{ parameters.VSCODE_ARCH }} - CHILD_CONCURRENCY: 1 + npm_config_foreground_scripts: "true" ELECTRON_SKIP_BINARY_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 GITHUB_TOKEN: "$(github-distro-mixin-password)" + retryCountOnTaskFailure: 5 displayName: Install dependencies - script: node build/azure-pipelines/distro/mixin-npm @@ -139,6 +139,19 @@ steps: flattenFolders: true condition: succeeded() + - task: PublishSymbols@2 + inputs: + IndexSources: false + SymbolsFolder: '$(Agent.BuildDirectory)\symbols' + SearchPattern: '**\*.pdb' + SymbolServerType: TeamServices + SymbolsProduct: 'code' + ArtifactServices.Symbol.AccountName: microsoft + ArtifactServices.Symbol.PAT: $(System.AccessToken) + ArtifactServices.Symbol.UseAAD: false + displayName: Publish Symbols + condition: succeeded() + - task: APIScan@2 inputs: softwareFolder: $(Agent.BuildDirectory)\scanbin @@ -151,7 +164,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 @@ -160,3 +173,11 @@ steps: ArtifactType: Container PublishProcessedResults: false AllTools: true + + # TSA Upload + - task: securedevelopmentteam.vss-secure-development-tools.build-task-uploadtotsa.TSAUpload@2 + displayName: TSA Upload + continueOnError: true + inputs: + GdnPublishTsaOnboard: true + GdnPublishTsaConfigFile: '$(Build.SourcesDirectory)/build/azure-pipelines/config/tsaoptions.json' diff --git a/build/buildfile.js b/build/buildfile.js index 0e323ac0faf..683e20fc46b 100644 --- a/build/buildfile.js +++ b/build/buildfile.js @@ -3,24 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -const { isAMD } = require('./lib/amd'); - /** * @param {string} name * @param {string[]=} exclude * @returns {import('./lib/bundle').IEntryPoint} */ function createModuleDescription(name, exclude) { - - let excludes = ['vs/css']; - if (Array.isArray(exclude) && exclude.length > 0) { - excludes = excludes.concat(exclude); - } - return { - name: name, - include: [], - exclude: excludes + name, + exclude }; } @@ -28,76 +19,27 @@ function createModuleDescription(name, exclude) { * @param {string} name */ function createEditorWorkerModuleDescription(name) { - const amdVariant = createModuleDescription(name, ['vs/base/common/worker/simpleWorker', 'vs/editor/common/services/editorSimpleWorker']); - amdVariant.target = 'amd'; - - const esmVariant = { ...amdVariant, dest: undefined }; - esmVariant.target = 'esm'; - esmVariant.name = `${esmVariant.name}.esm`; - - return [amdVariant, esmVariant]; + return createModuleDescription(name, ['vs/base/common/worker/simpleWorker', 'vs/editor/common/services/editorSimpleWorker']); } -// TODO@esm take the editor simple worker top level and rename away from "base" -exports.base = [ - { - name: 'vs/editor/common/services/editorSimpleWorker', - include: ['vs/base/common/worker/simpleWorker'], - exclude: [], - prepend: [ - { path: 'vs/loader.js' }, - { path: 'vs/base/worker/workerMain.js' } - ], - dest: 'vs/base/worker/workerMain.js', - target: 'amd' - }, - { - name: 'vs/editor/common/services/editorSimpleWorker.esm', - target: 'esm' - }, - { - name: 'vs/base/common/worker/simpleWorker', - exclude: [], - target: 'amd' - } -]; - -exports.workerExtensionHost = createEditorWorkerModuleDescription('vs/workbench/api/worker/extensionHostWorker'); -exports.workerNotebook = createEditorWorkerModuleDescription('vs/workbench/contrib/notebook/common/services/notebookSimpleWorker'); -exports.workerLanguageDetection = createEditorWorkerModuleDescription('vs/workbench/services/languageDetection/browser/languageDetectionSimpleWorker'); -exports.workerLocalFileSearch = createEditorWorkerModuleDescription('vs/workbench/services/search/worker/localFileSearch'); -exports.workerProfileAnalysis = createEditorWorkerModuleDescription('vs/platform/profiling/electron-sandbox/profileAnalysisWorker'); -exports.workerOutputLinks = createEditorWorkerModuleDescription('vs/workbench/contrib/output/common/outputLinkComputer'); -exports.workerBackgroundTokenization = createEditorWorkerModuleDescription('vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateTokenizationWorker.worker'); +exports.workerEditor = createEditorWorkerModuleDescription('vs/editor/common/services/editorSimpleWorkerMain'); +exports.workerExtensionHost = createEditorWorkerModuleDescription('vs/workbench/api/worker/extensionHostWorkerMain'); +exports.workerNotebook = createEditorWorkerModuleDescription('vs/workbench/contrib/notebook/common/services/notebookSimpleWorkerMain'); +exports.workerLanguageDetection = createEditorWorkerModuleDescription('vs/workbench/services/languageDetection/browser/languageDetectionSimpleWorkerMain'); +exports.workerLocalFileSearch = createEditorWorkerModuleDescription('vs/workbench/services/search/worker/localFileSearchMain'); +exports.workerProfileAnalysis = createEditorWorkerModuleDescription('vs/platform/profiling/electron-sandbox/profileAnalysisWorkerMain'); +exports.workerOutputLinks = createEditorWorkerModuleDescription('vs/workbench/contrib/output/common/outputLinkComputerMain'); +exports.workerBackgroundTokenization = createEditorWorkerModuleDescription('vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateTokenizationWorker.workerMain'); -exports.workbenchDesktop = function () { - return !isAMD() ? [ - createModuleDescription('vs/workbench/contrib/debug/node/telemetryApp'), - 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') - ] : [ - ...createEditorWorkerModuleDescription('vs/workbench/contrib/output/common/outputLinkComputer'), - ...createEditorWorkerModuleDescription('vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateTokenizationWorker.worker'), - createModuleDescription('vs/workbench/contrib/debug/node/telemetryApp'), - 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'), - ]; -}; +exports.workbenchDesktop = [ + createModuleDescription('vs/workbench/contrib/debug/node/telemetryApp'), + createModuleDescription('vs/platform/files/node/watcher/watcherMain'), + createModuleDescription('vs/platform/terminal/node/ptyHostMain'), + createModuleDescription('vs/workbench/api/node/extensionHostProcess'), + createModuleDescription('vs/workbench/workbench.desktop.main') +]; -exports.workbenchWeb = function () { - return !isAMD() ? [ - createModuleDescription('vs/workbench/workbench.web.main') - ] : [ - ...createEditorWorkerModuleDescription('vs/workbench/contrib/output/common/outputLinkComputer'), - ...createEditorWorkerModuleDescription('vs/workbench/services/textMate/browser/backgroundTokenization/worker/textMateTokenizationWorker.worker'), - createModuleDescription('vs/code/browser/workbench/workbench', ['vs/workbench/workbench.web.main.internal']) - ]; -}; +exports.workbenchWeb = createModuleDescription('vs/workbench/workbench.web.main'); exports.keyboardMaps = [ createModuleDescription('vs/workbench/services/keybinding/browser/keyboardLayouts/layout.contribution.linux'), @@ -106,15 +48,23 @@ exports.keyboardMaps = [ ]; exports.code = [ - createModuleDescription('vs/code/electron-main/main'), - createModuleDescription('vs/code/node/cli'), - createModuleDescription('vs/code/node/cliProcessMain', ['vs/code/node/cli']), + // 'vs/code/electron-main/main' is not included here because it comes in via ./src/main.js + // 'vs/code/node/cli' is not included here because it comes in via ./src/cli.js + createModuleDescription('vs/code/node/cliProcessMain'), createModuleDescription('vs/code/electron-utility/sharedProcess/sharedProcessMain'), - createModuleDescription('vs/code/electron-sandbox/processExplorer/processExplorerMain') + createModuleDescription('vs/code/electron-sandbox/processExplorer/processExplorerMain'), + createModuleDescription('vs/code/electron-sandbox/workbench/workbench'), + createModuleDescription('vs/code/electron-sandbox/processExplorer/processExplorer') ]; -exports.codeWeb = [ - createModuleDescription('vs/code/browser/workbench/workbench') +exports.codeWeb = createModuleDescription('vs/code/browser/workbench/workbench'); + +exports.codeServer = [ + // 'vs/server/node/server.main' is not included here because it gets inlined via ./src/server-main.js + // 'vs/server/node/server.cli' is not included here because it gets inlined via ./src/server-cli.js + createModuleDescription('vs/workbench/api/node/extensionHostProcess'), + createModuleDescription('vs/platform/files/node/watcher/watcherMain'), + createModuleDescription('vs/platform/terminal/node/ptyHostMain') ]; exports.entrypoint = createModuleDescription; diff --git a/build/checksums/electron.txt b/build/checksums/electron.txt index 53654213809..dcc7bf55c5c 100644 --- a/build/checksums/electron.txt +++ b/build/checksums/electron.txt @@ -1,75 +1,75 @@ -3de7da4462c7690f75680aecac8fddedc4b998d0da769136d8eff2932b36004e *chromedriver-v30.5.1-darwin-arm64.zip -d89b89f7c2ba45cb10df7fc23722bacf6f77e13002c42648762cd18ae3fa9182 *chromedriver-v30.5.1-darwin-x64.zip -3722d46929fd2c7b33c17d37464a08150e60d9269053eb67195795254fb5e947 *chromedriver-v30.5.1-linux-arm64.zip -a786d51f834c24b768bd415bf9a2fc5c1d9abdf9dc0b1a091bf9a8ff101becfe *chromedriver-v30.5.1-linux-armv7l.zip -688e4da8dbcb7dbfacab6f29341d96736e6d06e4c8029835b83ef30b69885b01 *chromedriver-v30.5.1-linux-x64.zip -786a7c2659ad97d5a09866b9aafd55edc015cb17a87bd8d72aa5925f2bfcf55a *chromedriver-v30.5.1-mas-arm64.zip -398759d1dc02fc4928d48ff0f8fbade8811347e1a51d1cedcb2ba9350fbed04d *chromedriver-v30.5.1-mas-x64.zip -65766f1270d1876e2a81acfb4b1130dc4a41eb7165842afa8f41ea438bf2fecb *chromedriver-v30.5.1-win32-arm64.zip -ca9fe0abd1032ebd51497049cb1bb2ae1dd9592697b278f6b1ee2a1a25148891 *chromedriver-v30.5.1-win32-ia32.zip -b6b6ea2202e0139ea436288add736d19078bfb190fc0b22937283927f3024bb9 *chromedriver-v30.5.1-win32-x64.zip -50e8b2d59916bc180873324fdbeb8227dbb8b2375cc936e58b7c9885fb23376d *electron-api.json -595db4fa3f755432bf59cbbaf591ee44b576e15952d014d83d3748646b2e338c *electron-v30.5.1-darwin-arm64-dsym-snapshot.zip -c1beb80553f3c9575e638625ce0ffbfdb87b6f8b23799eb132954b2bb74a9a2f *electron-v30.5.1-darwin-arm64-dsym.zip -cba315d6d6f607a2ee6cfc437b46f92da88daded86f0130d85129adb4742bc48 *electron-v30.5.1-darwin-arm64-symbols.zip -d312544ea29844cf328b44b9dbde12f4fdced90cb442dfca6df36c098dbb6e7a *electron-v30.5.1-darwin-arm64.zip -fd24d585d28909c082d703db3fcc5ffa0b55e1077ff320e25ed510f36e6a3761 *electron-v30.5.1-darwin-x64-dsym-snapshot.zip -4d1a2adea4b98c4d0b03c6561fca146aab102d636d359e48ce418c465df891ae *electron-v30.5.1-darwin-x64-dsym.zip -d9065eaf659f4c3e8a75f5453ceb65269763b2e57110bdcc01904b9a1e33a62c *electron-v30.5.1-darwin-x64-symbols.zip -faf9dcc20d525607ea205f2f6a1dfe3270f6268aa439bb0ba5646c7e4fbbd842 *electron-v30.5.1-darwin-x64.zip -b85dba1cdb49591542dcecff3e710f29b81285569c8c5db8c1181c3ed818ba44 *electron-v30.5.1-linux-arm64-debug.zip -5e97cc105282783d1c20d8e8dd4ca1134342839235288840cb50f314ca7a6ede *electron-v30.5.1-linux-arm64-symbols.zip -eb31470c0d7cd6e23e7ce0d89cc93a2356c9dac8bcc997e335353b8aa995afa0 *electron-v30.5.1-linux-arm64.zip -b85dba1cdb49591542dcecff3e710f29b81285569c8c5db8c1181c3ed818ba44 *electron-v30.5.1-linux-armv7l-debug.zip -137548cd73cd648107f6e01c777e411838173309848a492b42825857cec7d110 *electron-v30.5.1-linux-armv7l-symbols.zip -224bd46983e503101c756c72d10b195f14712a7a56438718acb126017dd04edf *electron-v30.5.1-linux-armv7l.zip -1f87fadebc444c9c0de43f52972a3f61af83ca0594c3de368f7579ce613fcb60 *electron-v30.5.1-linux-x64-debug.zip -aae78654f599a68310bdbdc0e3de8db644320ef44e58a5c6e5c693dff5cd1970 *electron-v30.5.1-linux-x64-symbols.zip -ec4707783d39e86005f42899e30ae59e50dd5d9c7f28531ed494eb43f2361403 *electron-v30.5.1-linux-x64.zip -c61f3121e52fd29987814b7805b597ef3fc78b2ce891eba5e3fc6bbe14128f23 *electron-v30.5.1-mas-arm64-dsym-snapshot.zip -f934e55ef6c986d3ec56626b2605fed16030efc45efcd8b05afa9322d625ec56 *electron-v30.5.1-mas-arm64-dsym.zip -3cc36e99f2cd59d7cb2f47b52d19609c2a2358f6ddde35fd832872abe241cc8d *electron-v30.5.1-mas-arm64-symbols.zip -c5085ab1fb74dfa4a4e463dcb688989bc63baf44007419fd96db4e7c974fb6db *electron-v30.5.1-mas-arm64.zip -47102b6dcd5892de734be2b48e40bbbdbc5e0a228bf5fec33071661d2724d946 *electron-v30.5.1-mas-x64-dsym-snapshot.zip -607091ddfd313ed27bb5a0acb6fa58b0d65cb87e6c6ce9b373949b4b152476d8 *electron-v30.5.1-mas-x64-dsym.zip -ac49568635d41e1075bca39c97aca1f2fcede702f721ddce5d80f18cc2dd1067 *electron-v30.5.1-mas-x64-symbols.zip -3746bc4ba32ab9c11398a393d54ba81733088729ae7cef4e5dccf1e64dc82b0a *electron-v30.5.1-mas-x64.zip -930ad941c8bc0aa5e4e3457b235f104846939974d71bde5b42a8980de2d4a35d *electron-v30.5.1-win32-arm64-pdb.zip -11c1252547b381ab6e40cc032ee60c5209dee9ea1102d1b47ad3fcd4da03049f *electron-v30.5.1-win32-arm64-symbols.zip -7351fa5cb892853a7d4b67d8858d0f9cc6506c554a2e42c9ad7e8d5e29ae2743 *electron-v30.5.1-win32-arm64-toolchain-profile.zip -f18baa98ba9c04b346fda9f40fc3150a57b539d0cd510971a073983f78e0a20c *electron-v30.5.1-win32-arm64.zip -b03c7297dc61aa234ab2273c419d9ae064e52c2c0c3dd56f09135f81964630b5 *electron-v30.5.1-win32-ia32-pdb.zip -8fc570c7f4d97eb6b745fb4af5932311d1da57058da8f39a3d97d6e099cd6982 *electron-v30.5.1-win32-ia32-symbols.zip -7351fa5cb892853a7d4b67d8858d0f9cc6506c554a2e42c9ad7e8d5e29ae2743 *electron-v30.5.1-win32-ia32-toolchain-profile.zip -20845163bf9d5a4ead03c5f0280c9bae71f0ab1fc03362f3406ed12620e4d9cd *electron-v30.5.1-win32-ia32.zip -019f3ae97a69dc837d89c44d25e0e5dadc88564b74202c5a3524fc6ab59490bc *electron-v30.5.1-win32-x64-pdb.zip -2eb4889fe275e5ea42cf6f51821bcceb833d5714aa1b45ad7915495afb447e94 *electron-v30.5.1-win32-x64-symbols.zip -7351fa5cb892853a7d4b67d8858d0f9cc6506c554a2e42c9ad7e8d5e29ae2743 *electron-v30.5.1-win32-x64-toolchain-profile.zip -443119bb559fc2ca297a57cf79f2bce532e853ada070c1e71460c9657c13b4b3 *electron-v30.5.1-win32-x64.zip -deecdc1f3f5ea9548ef73774f412c7af489afeb7dca1c394b5add10e929aff42 *electron.d.ts -14319b51118a5c94e108731f6b5c4e701c700212e790198ba22242eb9674a334 *ffmpeg-v30.5.1-darwin-arm64.zip -bfd5110457438fa8b113a8806db45f30b8975304d95e5efb1b71b0f5e46757fe *ffmpeg-v30.5.1-darwin-x64.zip -44ebf3185fc24404647da0c1f9e496effefd32ff3dbc2499c022164b919d06ed *ffmpeg-v30.5.1-linux-arm64.zip -4f7583513d48b48c44a2cbc4430cbc9a33d8f9728622166db688e3de61190821 *ffmpeg-v30.5.1-linux-armv7l.zip -154900e5db5810f63e38fd4551cfff3d88d6eac39226e06e12e1e43558d360a4 *ffmpeg-v30.5.1-linux-x64.zip -14319b51118a5c94e108731f6b5c4e701c700212e790198ba22242eb9674a334 *ffmpeg-v30.5.1-mas-arm64.zip -bfd5110457438fa8b113a8806db45f30b8975304d95e5efb1b71b0f5e46757fe *ffmpeg-v30.5.1-mas-x64.zip -f2169c4b6b9f073c20652953599f2453947936e851ff25c9f2719e7d889faea3 *ffmpeg-v30.5.1-win32-arm64.zip -90745f1422ae0431b295cb5d27ff03e69c890f3572ef6c4655774b892419e94f *ffmpeg-v30.5.1-win32-ia32.zip -567fe8952f17b224d4492980af741b470e86b31d58e7c6a9262258eeae490122 *ffmpeg-v30.5.1-win32-x64.zip -8bbbadb52a19f074d9f8bd533891e2bacbe4813101ae6c344cfc88e28b1429a3 *hunspell_dictionaries.zip -61137508aee6837c70a2ead2e31dd106d753ec5fac01b49e7e8701be0e09e7aa *libcxx-objects-v30.5.1-linux-arm64.zip -3c285f012f256f767e777550a1f18054cfcffc4e13ad6fa4e4e90dd58ec22069 *libcxx-objects-v30.5.1-linux-armv7l.zip -9b96b53c8dfbc70a0f617f78dde89a1e8510ae2261bcddfbf7c12c61eb5a3d73 *libcxx-objects-v30.5.1-linux-x64.zip -1b5cbe24d6356a388e166bff60d7452583019ea7dec85f593c552eb287346f43 *libcxx_headers.zip -eae200a0d1fd0d4b881358bdef600111891c75ecc4db679b680b0cbaf87ecfc3 *libcxxabi_headers.zip -ed1f7463b5869acf57dd388208918652182b049b2846f572d52ed47e7c091d54 *mksnapshot-v30.5.1-darwin-arm64.zip -bc80de1f29b869cdb2676d2dc08cbb4bd6228a6825b1b0bfc13acee0b070ac3e *mksnapshot-v30.5.1-darwin-x64.zip -5e526ca4f4250a9b9b040d6eb60f1d2b8db974ab68c27e46198f4bf8dc1ef347 *mksnapshot-v30.5.1-linux-arm64-x64.zip -c40ca2a45cc9ab4e1668f063860ba243baef8c192a253638e4c93aaccb0ef8da *mksnapshot-v30.5.1-linux-armv7l-x64.zip -a3134e0b0980547167dd3ca2f17dd2f40a7db697ab563ba6257534f9ad0d256d *mksnapshot-v30.5.1-linux-x64.zip -82776eafcb3e4d053e275ad50d84151f41a651986baacda20c6ed722854cb8fb *mksnapshot-v30.5.1-mas-arm64.zip -9d6d95d4cb121a29fd68d64557edd8c9859345ac37d0645de8aff518391755ed *mksnapshot-v30.5.1-mas-x64.zip -94b9ce6cb40ae001962b77525d12483c3458776066a69a4e714b327bab1decfc *mksnapshot-v30.5.1-win32-arm64-x64.zip -4d89d06c2e85b25d52bb64f48258ad5d05b98ac21697ae9c49ccd19bad9d7d94 *mksnapshot-v30.5.1-win32-ia32.zip -ccfd322bafde0485a0ac6b0acc3669c7b08f30003a1c2e044eebc1bf02289de3 *mksnapshot-v30.5.1-win32-x64.zip +c0a187acca68906c4a6387e8fabd052cb031ace6132d60a71001d9a0e891958e *chromedriver-v34.2.0-darwin-arm64.zip +fa5a46d752267d8497d375e19079e8b6a8df70c234a79b2d6b48f5862e1a0abc *chromedriver-v34.2.0-darwin-x64.zip +61e03d4fa570976d80f740637f56192b6448a05a73d1fba9717900b29f2b1b4d *chromedriver-v34.2.0-linux-arm64.zip +ec774d9b1a1b828a0db1502a1017fcab1dfed99b1b6b2fd2308dd600a1efa98a *chromedriver-v34.2.0-linux-armv7l.zip +cc15a6e6206485a2d96649ceb60509b9da04fa2811c4824b2e0eb43d1f4b1417 *chromedriver-v34.2.0-linux-x64.zip +9777122f6684180ef375b9b21dcabbc731d8a8befa300d1d47ad954a5b64c1c8 *chromedriver-v34.2.0-mas-arm64.zip +69451fa148b105fec9644646b22ca758a206499574c5816591354835c8056679 *chromedriver-v34.2.0-mas-x64.zip +eb7adc7e720f5e0f1d2c12ecbe886bdc01f2c9aaa3954bd6ebd313750bb18819 *chromedriver-v34.2.0-win32-arm64.zip +cb1973b0c2f5565974d5c2cb51816692f064b6cdc7897fa341d528ba7f9b14bf *chromedriver-v34.2.0-win32-ia32.zip +af5575b4727c3dbe7272016cbbaa8872043f843168a47d86748a50397efb4f77 *chromedriver-v34.2.0-win32-x64.zip +fcc718af2a28fb953290dc971e945818b4dbb293f297e6e25acb669d450cc0dd *electron-api.json +fa47e752e559a6472b87d8907f63296ed8cd53ecf862a4ae113018474d40aa8e *electron-v34.2.0-darwin-arm64-dsym-snapshot.zip +336c3374e721e2379901141be6345459f78d243b037c65b55bac4ae8cb14bfbe *electron-v34.2.0-darwin-arm64-dsym.zip +ac3b9d712d9f036f066d8eba42797117a513e2d250fcc117f0354300b7d5c1e5 *electron-v34.2.0-darwin-arm64-symbols.zip +ee447c17b2ac545e48083113d7e39a916821c1316f60f42cbcbee4fffe7c022a *electron-v34.2.0-darwin-arm64.zip +d7510bc038d06b26690ac9a78fbb256626502303ff7f5b1c2242f64a02832b1b *electron-v34.2.0-darwin-x64-dsym-snapshot.zip +0e95c2bbda00afe78e6229b824e3ffe0cc8612956dc11a5a30380224acdbecae *electron-v34.2.0-darwin-x64-dsym.zip +6d734ef8e8fd007071aae9a13534894dd801f11900f3e73e49cf5352b426e575 *electron-v34.2.0-darwin-x64-symbols.zip +8ef741819c8a5370dabc3b9df5e6ac217366477c5d5c656ed23c800bc984d887 *electron-v34.2.0-darwin-x64.zip +c2f448882a0392ebfd9810058a6a9580b087002c74fca3dcd3cf4ba5c3b27a7c *electron-v34.2.0-linux-arm64-debug.zip +51e887c382593021127593ceba89ad662d3a6de0f748b94fe3a0ce78a7393923 *electron-v34.2.0-linux-arm64-symbols.zip +818c91309da8ff948c43df58a996c05c0d27daa690e1d659355d9db01e351919 *electron-v34.2.0-linux-arm64.zip +c2f448882a0392ebfd9810058a6a9580b087002c74fca3dcd3cf4ba5c3b27a7c *electron-v34.2.0-linux-armv7l-debug.zip +71ec2b7473db766194bcf04648229c4affedce06e150e692745cc72dbc3749c5 *electron-v34.2.0-linux-armv7l-symbols.zip +0c75996c6bf2d37d0441d3e1021386a9348f8d21783d75571cdb10ede606424f *electron-v34.2.0-linux-armv7l.zip +d1f17be8df8ec6dc1a23afd8a7752f0b949c755a493b8a2c1e83f76c629258ca *electron-v34.2.0-linux-x64-debug.zip +d8fa79154b0b663dbd0054d69f53aec326997449ef21943d27225a2d6899660f *electron-v34.2.0-linux-x64-symbols.zip +f12a02d86cc657500978d263ec6d1841b6e2085cd3bd4d30a97cfe14685396f3 *electron-v34.2.0-linux-x64.zip +71ef8bfebb8513a405fd2beb44ad18f802bbac2248f81a5328ddaaa12906d70c *electron-v34.2.0-mas-arm64-dsym-snapshot.zip +437aca6cad3158f15fd852e5913462637052940f812b20ccc10fccc133d5a0d6 *electron-v34.2.0-mas-arm64-dsym.zip +7f253375a7b43d34b770c03153e47185be7a64bc0c10a783993a93144eb35492 *electron-v34.2.0-mas-arm64-symbols.zip +1f601c20430b036b485c7dc02a39a297f0d08905462a4568306c12f299375e2f *electron-v34.2.0-mas-arm64.zip +d32664181804a17f825bf1fbfd8f0cbe01e0b41b284937359e6d9754b3481ed8 *electron-v34.2.0-mas-x64-dsym-snapshot.zip +f6b394b89fb77dfeefdf525739fe8cd9695f8c2ac2251ed7c571a376cde059e9 *electron-v34.2.0-mas-x64-dsym.zip +539328d93e9bc122e6e34faeab6a42f4845c523f796c7a11bbdcfe5e856f6b98 *electron-v34.2.0-mas-x64-symbols.zip +749b6ced7a9d35a719ad98d4c3bf1a08c315c19ce97d2497235d4ac302cda665 *electron-v34.2.0-mas-x64.zip +e843ea4cb7a93686728d056c957c124760886167932ff619b518e45917edf38a *electron-v34.2.0-win32-arm64-pdb.zip +4908423be5f8ad1b5dd737edfd69ee0870460e16bb639bb963b0981bda2628e4 *electron-v34.2.0-win32-arm64-symbols.zip +9d13b2bd61416eec28f43faa399cc5c0dc9e36dec226314bbf397828f55d74de *electron-v34.2.0-win32-arm64-toolchain-profile.zip +1629cec7b5620e6ca3b5305c393ae147d1a3871a8f164d686b7bee3810fc1109 *electron-v34.2.0-win32-arm64.zip +e7182f1ef5ed13187fe12f35133cefb50c59e1d52ada758cbb72813dda575205 *electron-v34.2.0-win32-ia32-pdb.zip +a10f778f62bf060a7e38f5ea75ea472679b9ef4f12767ee0703e6d77effae78a *electron-v34.2.0-win32-ia32-symbols.zip +9d13b2bd61416eec28f43faa399cc5c0dc9e36dec226314bbf397828f55d74de *electron-v34.2.0-win32-ia32-toolchain-profile.zip +96396712a0240f04471f71246b3885a513b08e3f2d40154272d34e59419db7e6 *electron-v34.2.0-win32-ia32.zip +0c55ef5b1a6ea4604e3f0e9f8b5e946944abd1cfc3b974432d40e924a9996812 *electron-v34.2.0-win32-x64-pdb.zip +d6b67cf12edabcc62ae21e7c90ac6b1161dbefe4e6b765c69fc7040096b7d026 *electron-v34.2.0-win32-x64-symbols.zip +9d13b2bd61416eec28f43faa399cc5c0dc9e36dec226314bbf397828f55d74de *electron-v34.2.0-win32-x64-toolchain-profile.zip +a4fdf617dca787b7f1c6f8d0d1cb69c8adb37ee23f9553fe69803f9cad713360 *electron-v34.2.0-win32-x64.zip +5ca44a4ee9cc74a56c9556ce3f3774986dbb8b4f4953088c7f4577bfba4356a8 *electron.d.ts +86247a6815cc98f321374edcbfc0ab7ee79bcb13a5c25213f42151e4059ec690 *ffmpeg-v34.2.0-darwin-arm64.zip +e94f4707a91194f97d0451f9d5a27982c873a5c13d83d20926ce6fdb613f536c *ffmpeg-v34.2.0-darwin-x64.zip +947d7b7cb035eab94cc15b602dfa8a925cf238c1d9225c01732fe0c41f59c571 *ffmpeg-v34.2.0-linux-arm64.zip +64d74b6b94ac4fd755b97e0ec8d50af9cd73810fdb52f6c081b13a3337ace0ea *ffmpeg-v34.2.0-linux-armv7l.zip +a62cb20c5e5f2ba6f1df6f1bc406cc30f7ed44fe6380b506afa333d1b4ad7a87 *ffmpeg-v34.2.0-linux-x64.zip +715bbb3d193b1ca57d4b329edd05cd6b125dc188cdb73d1c66f220f62c8562e3 *ffmpeg-v34.2.0-mas-arm64.zip +866bf47106e8c3e50f046783eb8f5c756069c6de962a46f4edc7a5ee7e4593ee *ffmpeg-v34.2.0-mas-x64.zip +71a2a6c4cca15ccbcbf8912f5d73f855b0ca79804f941f68527ae808f8163957 *ffmpeg-v34.2.0-win32-arm64.zip +399f841cca166781078ca42c8ea060e3d5850ec6cb79716186d0806f3ce20842 *ffmpeg-v34.2.0-win32-ia32.zip +afad2a44f20a0c0c01fb1fea637f3f821842399593c9961c74d650ca12d32cbb *ffmpeg-v34.2.0-win32-x64.zip +c95fdc9dba05aa68aeccb69d4c34f0cb1fb98d7f5291d974d0b638488693655f *hunspell_dictionaries.zip +0abe74138afdb6e45a085d77407659f13c75ab96f694313d4e98bd662f9c6df2 *libcxx-objects-v34.2.0-linux-arm64.zip +3d0cbf6fb150b006428eab78235856d2204d5e93ca85f162e429b4c8bd9b0d3b *libcxx-objects-v34.2.0-linux-armv7l.zip +3a5491e32cec825499919be1b8bf0550d28fe5a31ff00a95572d49a58bb4820a *libcxx-objects-v34.2.0-linux-x64.zip +ff8753d52f759041b8e5462125ee2b96798fe8b5047f8fb8ae60cd07af2fb13d *libcxx_headers.zip +c98cce0091681bc367a48f66c5f4602961aa9cb6dd1a995d8969d6b39ce732f3 *libcxxabi_headers.zip +78a9606190fb227460ddcd276ad540873595d6a82fd1007f42900f53347e3eb1 *mksnapshot-v34.2.0-darwin-arm64.zip +6d4587a36f509356a908b6752de527cfe8a294a2d435b82ea3a98288c3a3a178 *mksnapshot-v34.2.0-darwin-x64.zip +17737bd34f7feefc5143b745f2c4f0e5a41678780e144eb43de41f8d4b9d7852 *mksnapshot-v34.2.0-linux-arm64-x64.zip +565aa9a84d913b7b5eb8d3b868cff151cb8a6c16548233ffd92b2f9bf3789227 *mksnapshot-v34.2.0-linux-armv7l-x64.zip +7332d7864ab4e5ee7fa8d00580d9357f30d0e69d3d1434d67c61f79f666314a4 *mksnapshot-v34.2.0-linux-x64.zip +cc81bfc9894378a9fc8a777429e04cc80860663b3dff8eba1f8cc72559a4f23d *mksnapshot-v34.2.0-mas-arm64.zip +c98230088698638159f6e7e0c5ddde3cb4dba9fa81d76e3c58fc86f96b63ef81 *mksnapshot-v34.2.0-mas-x64.zip +cc7b42943d998e1083ad8119dc2201dd27d709c15aa2a9b78f409485d2927592 *mksnapshot-v34.2.0-win32-arm64-x64.zip +ade9e3126f113318e226f9bdeba068b4383b30a98517a361386eff448459effa *mksnapshot-v34.2.0-win32-ia32.zip +344292ea318dc0e21f37bc7c82d57a57449f0fb278d9868f1b1b597bdcb1f36f *mksnapshot-v34.2.0-win32-x64.zip diff --git a/build/checksums/nodejs.txt b/build/checksums/nodejs.txt index 3436ff37cc5..d00d52bc25f 100644 --- a/build/checksums/nodejs.txt +++ b/build/checksums/nodejs.txt @@ -1,7 +1,7 @@ -fc7355e778b181575153b7dea4879e8021776eeb376c43c50f65893d2ea70aa3 node-v20.16.0-darwin-arm64.tar.gz -e18942cd706e4d69a4845ddacee2f1c17a72e853a229e3d2623d2edeb7efde72 node-v20.16.0-darwin-x64.tar.gz -551588f8f5ca05c04efb53f1b2bb7d9834603327bdc82d60a944d385569866e1 node-v20.16.0-linux-arm64.tar.gz -1c77c52ab507ddee479012f0b4bf523dd8400df4504447d623632353076e2e27 node-v20.16.0-linux-armv7l.tar.gz -b3f874ea84e440d69ed02ca92429d0eccd17737fde86db69c1c153d16ec654f2 node-v20.16.0-linux-x64.tar.gz -7e773fba3a19eac5ccbe85c1f87a05d7b112ecf41440076e6b6de1c7bffa0fdf win-arm64/node.exe -ba221658a3b68bd583e3068903eb675b5206d86a883c084ed95502e8f634b82a win-x64/node.exe +fa76d5b5340f14070ebaa88ef8faa28c1e9271502725e830cb52f0cf5b6493de node-v20.18.2-darwin-arm64.tar.gz +00a16bb0a82a2ad5d00d66b466ae1afa678482283747c27e9bce96668f334744 node-v20.18.2-darwin-x64.tar.gz +319789e8a055ff80793a05e633c8c5c9226050144a09da3747225b4ec56a2a99 node-v20.18.2-linux-arm64.tar.gz +65397a4a63960bda94718099698d2961623e9ef400f60f4c3a71add2268bccfb node-v20.18.2-linux-armv7l.tar.gz +eb5b031bdd728871c3b9a82655dbfa533bc262c0b6da1d09a86842430cef07d4 node-v20.18.2-linux-x64.tar.gz +83e7ad1b8c4d4d9c5e06849c3e8f3a5948a5eb6aa34c5bd973ba700e0386f42c win-arm64/node.exe +8487a277e92282904dfe0f860dbd5d229543e97a858a223fbe9c9b8670bbe170 win-x64/node.exe diff --git a/build/darwin/create-universal-app.js b/build/darwin/create-universal-app.js index e6a355d583d..535d46eb174 100644 --- a/build/darwin/create-universal-app.js +++ b/build/darwin/create-universal-app.js @@ -3,29 +3,32 @@ * 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 }); -// Void explanation - product-build-darwin-universal.yml runs this (create-universal-app.ts), then sign.ts -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 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)); 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', + // 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, @@ -37,24 +40,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')); - // Verify if native module architecture is correct - const findOutput = await (0, cross_spawn_promise_1.spawn)('find', [outAppPath, '-name', 'kerberos.node']); - const lipoOutput = await (0, cross_spawn_promise_1.spawn)('lipo', ['-archs', findOutput.replace(/\n$/, '')]); - if (lipoOutput.replace(/\n$/, '') !== 'x86_64 arm64') { - throw new Error(`Invalid arch, got : ${lipoOutput}`); - } + 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 e732bd6965c..9e013cdb10c 100644 --- a/build/darwin/create-universal-app.ts +++ b/build/darwin/create-universal-app.ts @@ -3,13 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// Void explanation - product-build-darwin-universal.yml runs this (create-universal-app.ts), then sign.ts - -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'; -import { spawn } from '@malept/cross-spawn-promise'; const root = path.dirname(path.dirname(__dirname)); @@ -31,6 +28,8 @@ async function main(buildDir?: string) { const filesToSkip = [ '**/CodeResources', '**/Credits.rtf', + // TODO: Should we consider expanding this to other files in this area? + '**/node_modules/@parcel/node-addon-api/nothing.target.mk' ]; await makeUniversalApp({ @@ -56,13 +55,6 @@ async function main(buildDir?: string) { darwinUniversalAssetId: 'darwin-universal' }); fs.writeFileSync(productJsonPath, JSON.stringify(productJson, null, '\t')); - - // Verify if native module architecture is correct - const findOutput = await spawn('find', [outAppPath, '-name', 'kerberos.node']); - const lipoOutput = await spawn('lipo', ['-archs', findOutput.replace(/\n$/, '')]); - if (lipoOutput.replace(/\n$/, '') !== 'x86_64 arm64') { - throw new Error(`Invalid arch, got : ${lipoOutput}`); - } } if (require.main === module) { diff --git a/build/darwin/sign.js b/build/darwin/sign.js index 90c2e825aa0..8cb34a084f8 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. @@ -81,28 +84,28 @@ async function main(buildDir) { '-replace', // Void changed this to replace 'NSAppleEventsUsageDescription', '-string', - 'An application in Void wants to use AppleScript.', + 'An application in Visual Studio Code wants to use AppleScript.', `${infoPlistPath}` ]); await (0, cross_spawn_promise_1.spawn)('plutil', [ '-replace', 'NSMicrophoneUsageDescription', '-string', - 'An application in Void wants to use the Microphone.', + 'An application in Visual Studio Code wants to use the Microphone.', `${infoPlistPath}` ]); await (0, cross_spawn_promise_1.spawn)('plutil', [ '-replace', 'NSCameraUsageDescription', '-string', - 'An application in Void wants to use the Camera.', + 'An application in Visual Studio Code wants to use the Camera.', `${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 a41ca30da08..c509dd014f3 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)); @@ -92,21 +92,21 @@ async function main(buildDir?: string): Promise { '-replace', // Void changed this to replace 'NSAppleEventsUsageDescription', '-string', - 'An application in Void wants to use AppleScript.', + 'An application in Visual Studio Code wants to use AppleScript.', `${infoPlistPath}` ]); await spawn('plutil', [ '-replace', 'NSMicrophoneUsageDescription', '-string', - 'An application in Void wants to use the Microphone.', + 'An application in Visual Studio Code wants to use the Microphone.', `${infoPlistPath}` ]); await spawn('plutil', [ '-replace', 'NSCameraUsageDescription', '-string', - 'An application in Void wants to use the Camera.', + 'An application in Visual Studio Code wants to use the Camera.', `${infoPlistPath}` ]); } diff --git a/build/darwin/verify-macho.js b/build/darwin/verify-macho.js new file mode 100644 index 00000000000..e7a4eb28d70 --- /dev/null +++ b/build/darwin/verify-macho.js @@ -0,0 +1,126 @@ +"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 __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", { value: true }); +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 '; +const MACHO_64_MAGIC_LE = 0xfeedfacf; +const MACHO_UNIVERSAL_MAGIC_LE = 0xbebafeca; +const MACHO_ARM64_CPU_TYPE = new Set([ + 0x0c000001, + 0x0100000c, +]); +const MACHO_X86_64_CPU_TYPE = new Set([ + 0x07000001, + 0x01000007, +]); +async function read(file, buf, offset, length, position) { + let filehandle; + try { + filehandle = await (0, promises_1.open)(file); + await filehandle.read(buf, offset, length, position); + } + finally { + await filehandle?.close(); + } +} +async function checkMachOFiles(appPath, arch) { + const visited = new Set(); + const invalidFiles = []; + const header = Buffer.alloc(8); + const file_header_entry_size = 20; + const checkx86_64Arch = (arch === 'x64'); + const checkArm64Arch = (arch === 'arm64'); + const checkUniversalArch = (arch === 'universal'); + const traverse = async (p) => { + p = await (0, promises_1.realpath)(p); + if (visited.has(p)) { + return; + } + visited.add(p); + const info = await (0, promises_1.stat)(p); + if (info.isSymbolicLink()) { + return; + } + if (info.isFile()) { + let fileOutput = ''; + try { + fileOutput = await (0, cross_spawn_promise_1.spawn)('file', ['--brief', '--no-pad', p]); + } + catch (e) { + if (e instanceof cross_spawn_promise_1.ExitCodeError) { + /* silently accept error codes from "file" */ + } + else { + throw e; + } + } + if (fileOutput.startsWith(MACHO_PREFIX)) { + console.log(`Verifying architecture of ${p}`); + read(p, header, 0, 8, 0).then(_ => { + const header_magic = header.readUInt32LE(); + if (header_magic === MACHO_64_MAGIC_LE) { + const cpu_type = header.readUInt32LE(4); + if (checkUniversalArch) { + invalidFiles.push(p); + } + else if (checkArm64Arch && !MACHO_ARM64_CPU_TYPE.has(cpu_type)) { + invalidFiles.push(p); + } + else if (checkx86_64Arch && !MACHO_X86_64_CPU_TYPE.has(cpu_type)) { + invalidFiles.push(p); + } + } + else if (header_magic === MACHO_UNIVERSAL_MAGIC_LE) { + const num_binaries = header.readUInt32BE(4); + 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(_ => { + for (let i = 0; i < num_binaries; i++) { + const cpu_type = file_entries.readUInt32LE(file_header_entry_size * i); + if (!MACHO_ARM64_CPU_TYPE.has(cpu_type) && !MACHO_X86_64_CPU_TYPE.has(cpu_type)) { + invalidFiles.push(p); + } + } + }); + } + }); + } + } + if (info.isDirectory()) { + for (const child of await (0, promises_1.readdir)(p)) { + await traverse(path_1.default.resolve(p, child)); + } + } + }; + await traverse(appPath); + return invalidFiles; +} +const archToCheck = process.argv[2]; +(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'); + for (const file of invalidFiles) { + console.error(`\x1b[31m${file}\x1b[0m`); + } + process.exit(1); + } + else { + console.log('\x1b[32mAll files are valid\x1b[0m'); + } +}).catch(err => { + console.error(err); + process.exit(1); +}); +//# sourceMappingURL=verify-macho.js.map \ No newline at end of file diff --git a/build/darwin/verify-macho.ts b/build/darwin/verify-macho.ts new file mode 100644 index 00000000000..61ebc71aba2 --- /dev/null +++ b/build/darwin/verify-macho.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 assert from 'assert'; +import path from 'path'; +import { open, stat, readdir, realpath } from 'fs/promises'; +import { spawn, ExitCodeError } from '@malept/cross-spawn-promise'; + +const MACHO_PREFIX = 'Mach-O '; +const MACHO_64_MAGIC_LE = 0xfeedfacf; +const MACHO_UNIVERSAL_MAGIC_LE = 0xbebafeca; +const MACHO_ARM64_CPU_TYPE = new Set([ + 0x0c000001, + 0x0100000c, +]); +const MACHO_X86_64_CPU_TYPE = new Set([ + 0x07000001, + 0x01000007, +]); + +async function read(file: string, buf: Buffer, offset: number, length: number, position: number) { + let filehandle; + try { + filehandle = await open(file); + await filehandle.read(buf, offset, length, position); + } finally { + await filehandle?.close(); + } +} + +async function checkMachOFiles(appPath: string, arch: string) { + const visited = new Set(); + const invalidFiles: string[] = []; + const header = Buffer.alloc(8); + const file_header_entry_size = 20; + const checkx86_64Arch = (arch === 'x64'); + const checkArm64Arch = (arch === 'arm64'); + const checkUniversalArch = (arch === 'universal'); + const traverse = async (p: string) => { + p = await realpath(p); + if (visited.has(p)) { + return; + } + visited.add(p); + + const info = await stat(p); + if (info.isSymbolicLink()) { + return; + } + if (info.isFile()) { + let fileOutput = ''; + try { + fileOutput = await spawn('file', ['--brief', '--no-pad', p]); + } catch (e) { + if (e instanceof ExitCodeError) { + /* silently accept error codes from "file" */ + } else { + throw e; + } + } + if (fileOutput.startsWith(MACHO_PREFIX)) { + console.log(`Verifying architecture of ${p}`); + read(p, header, 0, 8, 0).then(_ => { + const header_magic = header.readUInt32LE(); + if (header_magic === MACHO_64_MAGIC_LE) { + const cpu_type = header.readUInt32LE(4); + if (checkUniversalArch) { + invalidFiles.push(p); + } else if (checkArm64Arch && !MACHO_ARM64_CPU_TYPE.has(cpu_type)) { + invalidFiles.push(p); + } else if (checkx86_64Arch && !MACHO_X86_64_CPU_TYPE.has(cpu_type)) { + invalidFiles.push(p); + } + } else if (header_magic === MACHO_UNIVERSAL_MAGIC_LE) { + const num_binaries = header.readUInt32BE(4); + assert.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(_ => { + for (let i = 0; i < num_binaries; i++) { + const cpu_type = file_entries.readUInt32LE(file_header_entry_size * i); + if (!MACHO_ARM64_CPU_TYPE.has(cpu_type) && !MACHO_X86_64_CPU_TYPE.has(cpu_type)) { + invalidFiles.push(p); + } + } + }); + } + }); + } + } + + if (info.isDirectory()) { + for (const child of await readdir(p)) { + await traverse(path.resolve(p, child)); + } + } + }; + await traverse(appPath); + 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`); +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'); + for (const file of invalidFiles) { + console.error(`\x1b[31m${file}\x1b[0m`); + } + process.exit(1); + } else { + console.log('\x1b[32mAll files are valid\x1b[0m'); + } +}).catch(err => { + console.error(err); + process.exit(1); +}); diff --git a/build/eslint.js b/build/eslint.js index 288917b35bf..55e11b277d9 100644 --- a/build/eslint.js +++ b/build/eslint.js @@ -8,17 +8,11 @@ const vfs = require('vinyl-fs'); const { eslintFilter } = require('./filters'); function eslint() { - const gulpeslint = require('gulp-eslint'); + const eslint = require('./gulp-eslint'); return vfs .src(eslintFilter, { base: '.', follow: true, allowEmpty: true }) .pipe( - gulpeslint({ - configFile: '.eslintrc.json' - }) - ) - .pipe(gulpeslint.formatEach('compact')) - .pipe( - gulpeslint.results((results) => { + eslint((results) => { if (results.warningCount > 0 || results.errorCount > 0) { throw new Error('eslint failed with warnings and/or errors'); } diff --git a/build/filters.js b/build/filters.js index a065d2edde0..f972d9c1c7a 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/**', @@ -79,7 +80,6 @@ module.exports.indentationFilter = [ '!src/vs/base/node/cpuUsage.sh', '!src/vs/editor/common/languages/highlights/*.scm', '!test/unit/assert.js', - '!test/unit/assert-esm.js', '!resources/linux/snap/electron-launch', '!build/ext.js', '!build/npm/gyp/patches/gyp_spectre_mitigation_support.patch', @@ -89,6 +89,8 @@ module.exports.indentationFilter = [ '!test/automation/out/**', '!test/monaco/out/**', '!test/smoke/out/**', + '!extensions/terminal-suggest/src/shell/zshBuiltinsCache.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/**', @@ -134,8 +136,9 @@ module.exports.indentationFilter = [ '!extensions/markdown-math/notebook-out/*.js', '!extensions/ipynb/notebook-out/**', '!extensions/notebook-renderers/renderer-out/*.js', - '!extensions/open-remote-ssh/out/*.js', '!extensions/simple-browser/media/*.js', + + '!extensions/open-remote-ssh/out/*.js', // Void added this ]; module.exports.copyrightFilter = [ @@ -171,10 +174,11 @@ module.exports.copyrightFilter = [ '!extensions/markdown-language-features/media/highlight.css', '!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 = [ @@ -192,15 +196,18 @@ 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', ]; module.exports.eslintFilter = [ '**/*.js', + '**/*.cjs', + '**/*.mjs', '**/*.ts', - ...readFileSync(join(__dirname, '../.eslintignore')) - .toString().split(/\r\n|\n/) - .filter(line => !line.startsWith('#')) - .filter(line => !!line) + ...readFileSync(join(__dirname, '..', '.eslint-ignore')) + .toString() + .split(/\r\n|\n/) + .filter(line => line && !line.startsWith('#')) .map(line => line.startsWith('!') ? line.slice(1) : `!${line}`) ]; diff --git a/build/gulp-eslint.js b/build/gulp-eslint.js new file mode 100644 index 00000000000..902c7b47003 --- /dev/null +++ b/build/gulp-eslint.js @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +const { ESLint } = require('eslint'); +const { Transform } = require('stream'); +const { relative } = require('path'); +const fancyLog = require('fancy-log'); + +/** + * @param {Function} action - A function to handle all ESLint results + * @returns {stream} gulp file stream + */ +function eslint(action) { + const linter = new ESLint({}); + const formatter = linter.loadFormatter('compact'); + + const results = []; + results.errorCount = 0; + results.warningCount = 0; + + return transform( + async (file, enc, cb) => { + const filePath = relative(process.cwd(), file.path); + + if (file.isNull()) { + cb(null, file); + return; + } + + if (file.isStream()) { + cb(new Error('vinyl files with Stream contents are not supported')); + return; + } + + try { + // TODO: Should this be checked? + if (await linter.isPathIgnored(filePath)) { + cb(null, file); + return; + } + + const result = (await linter.lintText(file.contents.toString(), { filePath }))[0]; + results.push(result); + results.errorCount += result.errorCount; + results.warningCount += result.warningCount; + + const message = (await formatter).format([result]); + if (message) { + fancyLog(message); + } + cb(null, file); + } catch (error) { + cb(error); + } + }, + (done) => { + try { + action(results); + done(); + } catch (error) { + done(error); + } + }); +} + +function transform(transform, flush) { + return new Transform({ + objectMode: true, + transform, + flush + }); +} + +module.exports = eslint; diff --git a/build/gulpfile.compile.js b/build/gulpfile.compile.js index a8d8fb094b2..e40b05f8d39 100644 --- a/build/gulpfile.compile.js +++ b/build/gulpfile.compile.js @@ -9,12 +9,8 @@ const gulp = require('gulp'); const util = require('./lib/util'); const date = require('./lib/date'); -const amd = require('./lib/amd'); const task = require('./lib/task'); const compilation = require('./lib/compilation'); -const optimize = require('./lib/optimize'); - -const isAMDBuild = typeof process.env.VSCODE_BUILD_AMD === 'string' && process.env.VSCODE_BUILD_AMD.toLowerCase() === 'true'; /** * @param {boolean} disableMangle @@ -22,12 +18,9 @@ const isAMDBuild = typeof process.env.VSCODE_BUILD_AMD === 'string' && process.e function makeCompileBuildTask(disableMangle) { return task.series( util.rimraf('out-build'), - util.buildWebNodePaths('out-build'), date.writeISODate('out-build'), - amd.setAMD(isAMDBuild), compilation.compileApiProposalNamesTask, - compilation.compileTask(isAMDBuild ? 'src2' : 'src', 'out-build', true, { disableMangle }), - optimize.optimizeLoaderTask('out-build', 'out-build', true) + compilation.compileTask('src', 'out-build', true, { disableMangle }) ); } diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 0d55982a8aa..9f7ea57465b 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', } }); }); @@ -89,23 +89,13 @@ const extractEditorSrcTask = task.define('extract-editor-src', () => { // Disable NLS task to remove english strings to preserve backwards compatibility when we removed the `vs/nls!` AMD plugin. const compileEditorAMDTask = task.define('compile-editor-amd', compilation.compileTask('out-editor-src', 'out-editor-build', true, { disableMangle: true, preserveEnglish: true })); -const optimizeEditorAMDTask = task.define('optimize-editor-amd', optimize.optimizeTask( +const bundleEditorAMDTask = task.define('bundle-editor-amd', optimize.bundleTask( { out: 'out-editor', - amd: { + esm: { src: 'out-editor-build', entryPoints: editorEntryPoints, - resources: editorResources, - loaderConfig: { - paths: { - 'vs': 'out-editor-build/vs', - 'vs/css': 'out-editor-build/vs/css.build', - 'vscode': 'empty:' - } - }, - header: BUNDLED_FILE_HEADER, - bundleInfo: true, - languages + resources: editorResources } } )); @@ -369,7 +359,7 @@ gulp.task('editor-distro', task.parallel( task.series( compileEditorAMDTask, - optimizeEditorAMDTask, + bundleEditorAMDTask, minifyEditorAMDTask ), task.series( @@ -421,7 +411,6 @@ function createTscCompileTask(watch) { /** @type {NodeJS.ReadWriteStream | undefined} */ let report; - // eslint-disable-next-line no-control-regex const magic = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g; // https://stackoverflow.com/questions/25245716/remove-all-ansi-colors-styles-from-strings child.stdout.on('data', data => { diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 65ea5daf231..b7f5b7d1257 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -52,10 +52,10 @@ 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', - 'extensions/open-remote-ssh/tsconfig.json', 'extensions/php-language-features/tsconfig.json', 'extensions/references-view/tsconfig.json', 'extensions/search-result/tsconfig.json', @@ -66,10 +66,14 @@ 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', '.vscode/extensions/vscode-selfhost-import-aid/tsconfig.json', + + 'extensions/open-remote-ssh/tsconfig.json', // Void added this + ]; const getBaseUrl = out => `https://main.vscode-cdn.net/sourcemaps/${commit}/${out}`; @@ -230,27 +234,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.js b/build/gulpfile.js index 5f7b71a40f2..7894398c2ea 100644 --- a/build/gulpfile.js +++ b/build/gulpfile.js @@ -20,29 +20,25 @@ gulp.task(compileApiProposalNamesTask); gulp.task(watchApiProposalNamesTask); // SWC Client Transpile -const transpileClientSWCTask = task.define('transpile-client-swc', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), transpileTask('src', 'out', true))); +const transpileClientSWCTask = task.define('transpile-client-esbuild', task.series(util.rimraf('out'), transpileTask('src', 'out', true))); gulp.task(transpileClientSWCTask); // Transpile only -const transpileClientTask = task.define('transpile-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), transpileTask('src', 'out'))); +const transpileClientTask = task.define('transpile-client', task.series(util.rimraf('out'), transpileTask('src', 'out'))); gulp.task(transpileClientTask); // Fast compile for development time -const compileClientTask = task.define('compile-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), compileApiProposalNamesTask, compileTask('src', 'out', false))); +const compileClientTask = task.define('compile-client', task.series(util.rimraf('out'), compileApiProposalNamesTask, compileTask('src', 'out', false))); gulp.task(compileClientTask); -const watchClientTask = task.define('watch-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), task.parallel(watchTask('out', false), watchApiProposalNamesTask))); +const watchClientTask = task.define('watch-client', task.series(util.rimraf('out'), task.parallel(watchTask('out', false), watchApiProposalNamesTask))); gulp.task(watchClientTask); -const watchClientAMDTask = task.define('watch-client-amd', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), task.parallel(watchTask('out', false, 'src2'), watchApiProposalNamesTask))); -gulp.task(watchClientAMDTask); - // All const _compileTask = task.define('compile', task.parallel(monacoTypecheckTask, compileClientTask, compileExtensionsTask, compileExtensionMediaTask)); gulp.task(_compileTask); gulp.task(task.define('watch', task.parallel(/* monacoTypecheckWatchTask, */ watchClientTask, watchExtensionsTask))); -gulp.task(task.define('watch-amd', task.parallel(/* monacoTypecheckWatchTask, */ watchClientAMDTask, watchExtensionsTask))); // Default gulp.task('default', _compileTask); diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index 4c6841ce6c2..e0df76f1c8f 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -27,11 +27,10 @@ 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'); -const { isAMD } = require('./lib/amd'); const buildfile = require('./buildfile'); const REPO_ROOT = path.dirname(__dirname); @@ -64,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', @@ -73,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', ]; @@ -89,51 +91,25 @@ const serverResources = [ ...serverResourceExcludes ]; -const serverWithWebResourceIncludes = !isAMD() ? [ +const serverWithWebResourceIncludes = [ ...serverResourceIncludes, 'out-build/vs/code/browser/workbench/*.html', ...vscodeWebResourceIncludes -] : [ - ...serverResourceIncludes, - ...vscodeWebResourceIncludes ]; const serverWithWebResourceExcludes = [ ...serverResourceExcludes, - '!out-build/vs/code/**/*-dev.html', - '!out-build/vs/code/**/*-dev.esm.html', + '!out-build/vs/code/**/*-dev.html' ]; const serverWithWebResources = [ ...serverWithWebResourceIncludes, ...serverWithWebResourceExcludes ]; +const serverEntryPoints = buildfile.codeServer; -const serverEntryPoints = [ - { - name: 'vs/server/node/server.main', - exclude: ['vs/css'] - }, - { - name: 'vs/server/node/server.cli', - exclude: ['vs/css'] - }, - { - name: 'vs/workbench/api/node/extensionHostProcess', - exclude: ['vs/css'] - }, - { - name: 'vs/platform/files/node/watcher/watcherMain', - exclude: ['vs/css'] - }, - { - name: 'vs/platform/terminal/node/ptyHostMain', - exclude: ['vs/css'] - } -]; - -const webEntryPoints = !isAMD() ? [ - buildfile.base, +const webEntryPoints = [ + buildfile.workerEditor, buildfile.workerExtensionHost, buildfile.workerNotebook, buildfile.workerLanguageDetection, @@ -142,15 +118,6 @@ const webEntryPoints = !isAMD() ? [ buildfile.workerBackgroundTokenization, buildfile.keyboardMaps, buildfile.codeWeb -].flat() : [ - buildfile.entrypoint('vs/workbench/workbench.web.main.internal'), - buildfile.base, - buildfile.workerExtensionHost, - buildfile.workerNotebook, - buildfile.workerLanguageDetection, - buildfile.workerLocalFileSearch, - buildfile.keyboardMaps, - buildfile.workbenchWeb() ].flat(); const serverWithWebEntryPoints = [ @@ -162,10 +129,10 @@ const serverWithWebEntryPoints = [ ...webEntryPoints, ].flat(); -const commonJSEntryPoints = [ +const bootstrapEntryPoints = [ 'out-build/server-main.js', 'out-build/server-cli.js', - 'out-build/bootstrap-fork.js', + 'out-build/bootstrap-fork.js' ]; function getNodeVersion() { @@ -344,7 +311,7 @@ function packageTask(type, platform, arch, sourceFolderName, destinationFolderNa let packageJsonContents; const packageJsonStream = gulp.src(['remote/package.json'], { base: 'remote' }) - .pipe(json({ name, version, dependencies: undefined, optionalDependencies: undefined, ...(!isAMD() ? { type: 'module' } : {}) })) // TODO@esm this should be configured in the top level package.json + .pipe(json({ name, version, dependencies: undefined, optionalDependencies: undefined, type: 'module' })) .pipe(es.through(function (file) { packageJsonContents = file.contents.toString(); this.emit('data', file); @@ -450,7 +417,7 @@ function packageTask(type, platform, arch, sourceFolderName, destinationFolderNa } result = inlineMeta(result, { - targetPaths: commonJSEntryPoints, + targetPaths: bootstrapEntryPoints, packageJsonFn: () => packageJsonContents, productJsonFn: () => productJsonContents }); @@ -469,41 +436,26 @@ function tweakProductForServerWeb(product) { } ['reh', 'reh-web'].forEach(type => { - const optimizeTask = task.define(`optimize-vscode-${type}`, task.series( + const bundleTask = task.define(`bundle-vscode-${type}`, task.series( util.rimraf(`out-vscode-${type}`), - optimize.optimizeTask( + optimize.bundleTask( { out: `out-vscode-${type}`, - amd: { + esm: { src: 'out-build', - entryPoints: (type === 'reh' ? serverEntryPoints : serverWithWebEntryPoints).flat(), - otherSources: [], + entryPoints: [ + ...(type === 'reh' ? serverEntryPoints : serverWithWebEntryPoints), + ...bootstrapEntryPoints + ], resources: type === 'reh' ? serverResources : serverWithWebResources, - loaderConfig: optimize.loaderConfig(), - inlineAmdImages: true, - bundleInfo: undefined, fileContentMapper: createVSCodeWebFileContentMapper('.build/extensions', type === 'reh-web' ? tweakProductForServerWeb(product) : product) - }, - commonJS: { - src: 'out-build', - entryPoints: commonJSEntryPoints, - platform: 'node', - external: [ - 'minimist', - // We cannot inline `product.json` from here because - // it is being changed during build time at a later - // point in time (such as `checksums`) - // We have a manual step to inline these later. - '../product.json', - '../package.json' - ] } } ) )); const minifyTask = task.define(`minify-vscode-${type}`, task.series( - optimizeTask, + bundleTask, util.rimraf(`out-vscode-${type}-min`), optimize.minifyTask(`out-vscode-${type}`, `https://main.vscode-cdn.net/sourcemaps/${commit}/core`) )); @@ -516,9 +468,10 @@ function tweakProductForServerWeb(product) { ['', 'min'].forEach(minified => { const sourceFolderName = `out-vscode-${type}${dashed(minified)}`; - const destinationFolderName = `void-${type}${dashed(platform)}${dashed(arch)}`; + 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) @@ -527,9 +480,10 @@ function tweakProductForServerWeb(product) { const serverTask = task.define(`vscode-${type}${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series( compileBuildTask, - compileExtensionsBuildTask, + cleanExtensionsBuildTask, + compileNonNativeExtensionsBuildTask, compileExtensionMediaBuildTask, - minified ? minifyTask : optimizeTask, + minified ? minifyTask : bundleTask, serverTaskCI )); gulp.task(serverTask); diff --git a/build/gulpfile.scan.js b/build/gulpfile.scan.js index 582481bf1eb..cbcdddb74bc 100644 --- a/build/gulpfile.scan.js +++ b/build/gulpfile.scan.js @@ -71,12 +71,15 @@ BUILD_TARGETS.forEach(buildTarget => { gulp.task(setupSymbolsTask); }); -function nodeModules(destinationExe, destinationPdb, platform) { +function getProductionDependencySources() { const productionDependencies = deps.getProductionDependencies(root); - const dependenciesSrc = productionDependencies.map(d => path.relative(root, d)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`]).flat(); + return productionDependencies.map(d => path.relative(root, d)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`]).flat(); +} + +function nodeModules(destinationExe, destinationPdb, platform) { const exe = () => { - return gulp.src(dependenciesSrc, { base: '.', dot: true }) + return gulp.src(getProductionDependencySources(), { base: '.', dot: true }) .pipe(filter([ '**/*.node', // Exclude these paths. @@ -89,7 +92,7 @@ function nodeModules(destinationExe, destinationPdb, platform) { if (platform === 'win32') { const pdb = () => { - return gulp.src(dependenciesSrc, { base: '.', dot: true }) + return gulp.src(getProductionDependencySources(), { base: '.', dot: true }) .pipe(filter(['**/*.pdb'])) .pipe(gulp.dest(destinationPdb)); }; @@ -99,7 +102,7 @@ function nodeModules(destinationExe, destinationPdb, platform) { if (platform === 'linux') { const pdb = () => { - return gulp.src(dependenciesSrc, { base: '.', dot: true }) + return gulp.src(getProductionDependencySources(), { base: '.', dot: true }) .pipe(filter(['**/*.sym'])) .pipe(gulp.dest(destinationPdb)); }; diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 4d90766e541..99db93aced3 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -31,15 +31,14 @@ 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 { isAMD } = require('./lib/amd'); const glob = promisify(require('glob')); const rcedit = promisify(require('rcedit')); // Build -const vscodeEntryPoints = !isAMD() ? [ - buildfile.base, +const vscodeEntryPoints = [ + buildfile.workerEditor, buildfile.workerExtensionHost, buildfile.workerNotebook, buildfile.workerLanguageDetection, @@ -47,28 +46,18 @@ const vscodeEntryPoints = !isAMD() ? [ buildfile.workerProfileAnalysis, buildfile.workerOutputLinks, buildfile.workerBackgroundTokenization, - buildfile.workbenchDesktop(), - buildfile.code -].flat() : [ - buildfile.entrypoint('vs/workbench/workbench.desktop.main'), - buildfile.base, - buildfile.workerExtensionHost, - buildfile.workerNotebook, - buildfile.workerLanguageDetection, - buildfile.workerLocalFileSearch, - buildfile.workerProfileAnalysis, - buildfile.workbenchDesktop(), + buildfile.workbenchDesktop, buildfile.code ].flat(); -const vscodeResourceIncludes = !isAMD() ? [ +const vscodeResourceIncludes = [ // NLS 'out-build/nls.messages.json', 'out-build/nls.keys.json', // Workbench - 'out-build/vs/code/electron-sandbox/workbench/workbench.esm.html', + 'out-build/vs/code/electron-sandbox/workbench/workbench.html', // Electron Preload 'out-build/vs/base/parts/sandbox/electron-sandbox/preload.js', @@ -85,7 +74,7 @@ const vscodeResourceIncludes = !isAMD() ? [ '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', @@ -105,43 +94,13 @@ const vscodeResourceIncludes = !isAMD() ? [ 'out-build/vs/workbench/contrib/webview/browser/pre/*.{js,html}', // Extension Host Worker - 'out-build/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.esm.html', + 'out-build/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html', // Process Explorer - 'out-build/vs/code/electron-sandbox/processExplorer/processExplorer.esm.html', + 'out-build/vs/code/electron-sandbox/processExplorer/processExplorer.html', // Tree Sitter highlights 'out-build/vs/editor/common/languages/highlights/*.scm', - - // Issue Reporter - 'out-build/vs/workbench/contrib/issue/electron-sandbox/issueReporter.esm.html' -] : [ - 'out-build/nls.messages.json', - 'out-build/nls.keys.json', - 'out-build/vs/**/*.{svg,png,html,jpg,mp3}', - '!out-build/vs/code/browser/**/*.html', - '!out-build/vs/code/**/*-dev.html', - '!out-build/vs/code/**/*-dev.esm.html', - '!out-build/vs/editor/standalone/**/*.svg', - 'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}', - 'out-build/vs/base/browser/ui/codicons/codicon/**', - 'out-build/vs/base/parts/sandbox/electron-sandbox/preload.js', - 'out-build/vs/base/parts/sandbox/electron-sandbox/preload-aux.js', - 'out-build/vs/workbench/browser/media/*-theme.css', - 'out-build/vs/workbench/contrib/debug/**/*.json', - 'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt', - 'out-build/vs/workbench/contrib/terminal/common/scripts/fish_xdg_data/fish/vendor_conf.d/*.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', - 'out-build/vs/workbench/contrib/terminal/common/scripts/*.zsh', - 'out-build/vs/workbench/contrib/webview/browser/pre/*.js', - '!out-build/vs/workbench/contrib/issue/**/*-dev.html', - '!out-build/vs/workbench/contrib/issue/**/*-dev.esm.html', - 'out-build/vs/editor/common/languages/highlights/*.scm', - 'out-build/vs/**/markdown.css', - 'out-build/vs/workbench/contrib/tasks/**/*.json', - '!**/test/**' ]; const vscodeResources = [ @@ -153,73 +112,55 @@ const vscodeResources = [ '!out-build/vs/code/browser/**', '!out-build/vs/editor/standalone/**', '!out-build/vs/code/**/*-dev.html', - '!out-build/vs/code/**/*-dev.esm.html', '!out-build/vs/workbench/contrib/issue/**/*-dev.html', - '!out-build/vs/workbench/contrib/issue/**/*-dev.esm.html', '!**/test/**' ]; -// Do not change the order of these files! They will -// be inlined into the target window file in this order -// and they depend on each other in this way. -const windowBootstrapFiles = []; -if (isAMD()) { - windowBootstrapFiles.push('out-build/vs/loader.js'); -} -windowBootstrapFiles.push('out-build/bootstrap-window.js'); - -const commonJSEntryPoints = [ +const bootstrapEntryPoints = [ 'out-build/main.js', 'out-build/cli.js', 'out-build/bootstrap-fork.js' ]; -const optimizeVSCodeTask = task.define('optimize-vscode', task.series( +const bundleVSCodeTask = task.define('bundle-vscode', task.series( util.rimraf('out-vscode'), // Optimize: bundles source files automatically based on - // AMD and CommonJS import statements based on the passed - // in entry points. In addition, concat window related - // bootstrap files into a single file. - optimize.optimizeTask( + // import statements based on the passed in entry points. + // In addition, concat window related bootstrap files into + // a single file. + optimize.bundleTask( { out: 'out-vscode', - amd: { + esm: { src: 'out-build', - entryPoints: vscodeEntryPoints, + entryPoints: [ + ...vscodeEntryPoints, + ...bootstrapEntryPoints + ], resources: vscodeResources, - loaderConfig: optimize.loaderConfig(), - bundleInfo: undefined - }, - commonJS: { - src: 'out-build', - entryPoints: commonJSEntryPoints, - platform: 'node', - external: [ - 'electron', - 'minimist', - 'original-fs', - // We cannot inline `product.json` from here because - // it is being changed during build time at a later - // point in time (such as `checksums`) - // We have a manual step to inline these later. - '../product.json', - '../package.json', - ] - }, - manual: [ - { src: [...windowBootstrapFiles, 'out-build/vs/code/electron-sandbox/workbench/workbench.js'], out: '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. - { src: [...windowBootstrapFiles, 'out-build/vs/workbench/contrib/issue/electron-sandbox/issueReporter.js'], out: 'vs/workbench/contrib/issue/electron-sandbox/issueReporter.js' }, - { src: [...windowBootstrapFiles, 'out-build/vs/code/electron-sandbox/processExplorer/processExplorer.js'], out: 'vs/code/electron-sandbox/processExplorer/processExplorer.js' } - ] + fileContentMapper: filePath => { + if ( + filePath.endsWith('vs/code/electron-sandbox/workbench/workbench.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'); + return `${bootstrapWindowContent}\n${content}`; // prepend bootstrap-window.js content to entry points that are Electron windows + }; + } + return undefined; + }, + skipTSBoilerplateRemoval: entryPoint => + entryPoint === 'vs/code/electron-sandbox/workbench/workbench' || + entryPoint === 'vs/code/electron-sandbox/processExplorer/processExplorer', + } } ) )); -gulp.task(optimizeVSCodeTask); +gulp.task(bundleVSCodeTask); const sourceMappingURLBase = `https://main.vscode-cdn.net/sourcemaps/${commit}`; const minifyVSCodeTask = task.define('minify-vscode', task.series( - optimizeVSCodeTask, + bundleVSCodeTask, util.rimraf('out-vscode-min'), optimize.minifyTask('out-vscode', `${sourceMappingURLBase}/core`) )); @@ -296,7 +237,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op 'vs/workbench/workbench.desktop.main.js', 'vs/workbench/workbench.desktop.main.css', 'vs/workbench/api/node/extensionHostProcess.js', - !isAMD() ? 'vs/code/electron-sandbox/workbench/workbench.esm.html' : 'vs/code/electron-sandbox/workbench/workbench.html', + 'vs/code/electron-sandbox/workbench/workbench.html', 'vs/code/electron-sandbox/workbench/workbench.js' ]); @@ -326,11 +267,10 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op } const name = product.nameShort; - const packageJsonUpdates = { name, version, ...(!isAMD() ? { type: 'module', main: 'out/main.js' } : {}) }; // TODO@esm this should be configured in the top level package.json + 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; @@ -341,7 +281,6 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op this.emit('data', file); })); - // Void - this is important, creates the product.json in .app let productJsonContents; const productJsonStream = gulp.src(['product.json'], { base: '.' }) @@ -361,7 +300,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'])) @@ -379,12 +318,10 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op '**/node-pty/lib/shared/conout.js', '**/*.wasm', '**/@vscode/vsce-sign/bin/*', - ], isAMD() ? [ - '**/*.mk', - ] : [ + ], [ '**/*.mk', '!node_modules/vsda/**' // stay compatible with extensions that depend on us shipping `vsda` into ASAR - ], isAMD() ? [] : [ + ], [ 'node_modules/vsda/**' // retain copy of `vsda` in node_modules for internal use ], 'node_modules.asar')); @@ -427,15 +364,15 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op 'resources/win32/vue.ico', 'resources/win32/xml.ico', 'resources/win32/yaml.ico', - 'resources/win32/code_70x70.png', // <-- Void icon - 'resources/win32/code_150x150.png' // <-- Void icon + 'resources/win32/code_70x70.png', + 'resources/win32/code_150x150.png' ], { base: '.' })); } else if (platform === 'linux') { - all = es.merge(all, gulp.src('resources/linux/code.png', { base: '.' })); // <-- Void icon + all = es.merge(all, gulp.src('resources/linux/code.png', { base: '.' })); } else if (platform === 'darwin') { const shortcut = gulp.src('resources/darwin/bin/code.sh') .pipe(replace('@@APPNAME@@', product.applicationName)) - .pipe(rename('bin/code')); // <-- Void icon + .pipe(rename('bin/code')); all = es.merge(all, shortcut); } @@ -491,7 +428,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op } result = inlineMeta(result, { - targetPaths: commonJSEntryPoints, + targetPaths: bootstrapEntryPoints, packageJsonFn: () => packageJsonContents, productJsonFn: () => productJsonContents }); @@ -551,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) ]; @@ -564,9 +502,10 @@ BUILD_TARGETS.forEach(buildTarget => { const vscodeTask = task.define(`vscode${dashed(platform)}${dashed(arch)}${dashed(minified)}`, task.series( compileBuildTask, - compileExtensionsBuildTask, + cleanExtensionsBuildTask, + compileNonNativeExtensionsBuildTask, compileExtensionMediaBuildTask, - minified ? minifyVSCodeTask : optimizeVSCodeTask, + minified ? minifyVSCodeTask : bundleVSCodeTask, vscodeTaskCI )); gulp.task(vscodeTask); @@ -601,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.linux.js b/build/gulpfile.vscode.linux.js index 5fa5a5d136e..fb0e5a463dc 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -128,7 +128,7 @@ function buildDebPackage(arch) { return async () => { await exec(`chmod 755 ${product.applicationName}-${debArch}/DEBIAN/postinst ${product.applicationName}-${debArch}/DEBIAN/prerm ${product.applicationName}-${debArch}/DEBIAN/postrm`, { cwd }); await exec('mkdir -p deb', { cwd }); - await exec(`fakeroot dpkg-deb -b ${product.applicationName}-${debArch} deb`, { cwd }); + await exec(`fakeroot dpkg-deb -Zxz -b ${product.applicationName}-${debArch} deb`, { cwd }); }; } diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index 80b7fc559a4..02b17022fa8 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -21,7 +21,6 @@ const vfs = require('vinyl-fs'); const packageJson = require('../package.json'); const { compileBuildTask } = require('./gulpfile.compile'); const extensions = require('./lib/extensions'); -const { isAMD } = require('./lib/amd'); const VinylFile = require('vinyl'); const REPO_ROOT = path.dirname(__dirname); @@ -32,7 +31,7 @@ const commit = getVersion(REPO_ROOT); const quality = product.quality; const version = (quality && quality !== 'stable') ? `${packageJson.version}-${quality}` : packageJson.version; -const vscodeWebResourceIncludes = !isAMD() ? [ +const vscodeWebResourceIncludes = [ // NLS 'out-build/nls.messages.js', @@ -54,30 +53,7 @@ const vscodeWebResourceIncludes = !isAMD() ? [ 'out-build/vs/editor/common/languages/highlights/*.scm', // Extension Host Worker - 'out-build/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.esm.html', -] : [ - - // Workbench - 'out-build/vs/{base,platform,editor,workbench}/**/*.{svg,png,jpg,mp3}', - 'out-build/vs/code/browser/workbench/*.html', - 'out-build/vs/base/browser/ui/codicons/codicon/**/*.ttf', - 'out-build/vs/**/markdown.css', - - // NLS - 'out-build/nls.messages.js', - - // Webview - 'out-build/vs/workbench/contrib/webview/browser/pre/*.js', - 'out-build/vs/workbench/contrib/webview/browser/pre/*.html', - - // Extension Worker 'out-build/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html', - - // Tree Sitter highlights - 'out-build/vs/editor/common/languages/highlights/*.scm', - - // Web node paths (needed for integration tests) - 'out-build/vs/webPackagePaths.js', ]; exports.vscodeWebResourceIncludes = vscodeWebResourceIncludes; @@ -91,14 +67,13 @@ const vscodeWebResources = [ '!out-build/vs/editor/standalone/**', '!out-build/vs/workbench/**/*-tb.png', '!out-build/vs/code/**/*-dev.html', - '!out-build/vs/code/**/*-dev.esm.html', '!**/test/**' ]; const buildfile = require('./buildfile'); -const vscodeWebEntryPoints = !isAMD() ? [ - buildfile.base, +const vscodeWebEntryPoints = [ + buildfile.workerEditor, buildfile.workerExtensionHost, buildfile.workerNotebook, buildfile.workerLanguageDetection, @@ -106,107 +81,47 @@ const vscodeWebEntryPoints = !isAMD() ? [ buildfile.workerOutputLinks, buildfile.workerBackgroundTokenization, buildfile.keyboardMaps, - buildfile.workbenchWeb(), + buildfile.workbenchWeb, buildfile.entrypoint('vs/workbench/workbench.web.main.internal') // TODO@esm remove line when we stop supporting web-amd-esm-bridge -].flat() : [ - buildfile.entrypoint('vs/workbench/workbench.web.main.internal'), - buildfile.base, - buildfile.workerExtensionHost, - buildfile.workerNotebook, - buildfile.workerLanguageDetection, - buildfile.workerLocalFileSearch, - buildfile.keyboardMaps, - buildfile.workbenchWeb() ].flat(); /** + * @param extensionsRoot {string} The location where extension will be read from * @param {object} product The parsed product.json file contents */ -const createVSCodeWebProductConfigurationPatcher = (product) => { - /** - * @param content {string} The contents of the file - * @param path {string} The absolute file path, always using `/`, even on Windows - */ - const result = (content, path) => { - // (1) Patch product configuration +const createVSCodeWebFileContentMapper = (extensionsRoot, product) => { + return path => { if (path.endsWith('vs/platform/product/common/product.js')) { - const productConfiguration = JSON.stringify({ - ...product, - version, - commit, - date: readISODate('out-build') - }); - return content.replace('/*BUILD->INSERT_PRODUCT_CONFIGURATION*/', () => productConfiguration.substr(1, productConfiguration.length - 2) /* without { and }*/); + return content => { + const productConfiguration = JSON.stringify({ + ...product, + version, + commit, + date: readISODate('out-build') + }); + return content.replace('/*BUILD->INSERT_PRODUCT_CONFIGURATION*/', () => productConfiguration.substr(1, productConfiguration.length - 2) /* without { and }*/); + }; + } else if (path.endsWith('vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.js')) { + return content => { + const builtinExtensions = JSON.stringify(extensions.scanBuiltinExtensions(extensionsRoot)); + return content.replace('/*BUILD->INSERT_BUILTIN_EXTENSIONS*/', () => builtinExtensions.substr(1, builtinExtensions.length - 2) /* without [ and ]*/); + }; } - return content; + return undefined; }; - return result; -}; - -/** - * @param extensionsRoot {string} The location where extension will be read from - */ -const createVSCodeWebBuiltinExtensionsPatcher = (extensionsRoot) => { - /** - * @param content {string} The contents of the file - * @param path {string} The absolute file path, always using `/`, even on Windows - */ - const result = (content, path) => { - // (2) Patch builtin extensions - if (path.endsWith('vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.js')) { - const builtinExtensions = JSON.stringify(extensions.scanBuiltinExtensions(extensionsRoot)); - return content.replace('/*BUILD->INSERT_BUILTIN_EXTENSIONS*/', () => builtinExtensions.substr(1, builtinExtensions.length - 2) /* without [ and ]*/); - } - - return content; - }; - return result; -}; - -/** - * @param patchers {((content:string, path: string)=>string)[]} - */ -const combineContentPatchers = (...patchers) => { - /** - * @param content {string} The contents of the file - * @param path {string} The absolute file path, always using `/`, even on Windows - */ - const result = (content, path) => { - for (const patcher of patchers) { - content = patcher(content, path); - } - return content; - }; - return result; -}; - -/** - * @param extensionsRoot {string} The location where extension will be read from - * @param {object} product The parsed product.json file contents - */ -const createVSCodeWebFileContentMapper = (extensionsRoot, product) => { - return combineContentPatchers( - createVSCodeWebProductConfigurationPatcher(product), - createVSCodeWebBuiltinExtensionsPatcher(extensionsRoot) - ); }; exports.createVSCodeWebFileContentMapper = createVSCodeWebFileContentMapper; -const optimizeVSCodeWebTask = task.define('optimize-vscode-web', task.series( +const bundleVSCodeWebTask = task.define('bundle-vscode-web', task.series( util.rimraf('out-vscode-web'), - optimize.optimizeTask( + optimize.bundleTask( { out: 'out-vscode-web', - amd: { + esm: { src: 'out-build', - entryPoints: vscodeWebEntryPoints.flat(), - otherSources: [], + entryPoints: vscodeWebEntryPoints, resources: vscodeWebResources, - loaderConfig: optimize.loaderConfig(), - externalLoaderInfo: util.createExternalLoaderConfig(product.webEndpointUrl, commit, quality), - inlineAmdImages: true, - bundleInfo: undefined, fileContentMapper: createVSCodeWebFileContentMapper('.build/web/extensions', product) } } @@ -214,7 +129,7 @@ const optimizeVSCodeWebTask = task.define('optimize-vscode-web', task.series( )); const minifyVSCodeWebTask = task.define('minify-vscode-web', task.series( - optimizeVSCodeWebTask, + bundleVSCodeWebTask, util.rimraf('out-vscode-web-min'), optimize.minifyTask('out-vscode-web', `https://main.vscode-cdn.net/sourcemaps/${commit}/core`) )); @@ -233,7 +148,7 @@ function packageTask(sourceFolderName, destinationFolderName) { const loader = gulp.src('build/loader.min', { base: 'build', dot: true }).pipe(rename('out/vs/loader.js')); // TODO@esm remove line when we stop supporting web-amd-esm-bridge - const sources = es.merge(...(!isAMD() ? [src, extensions, loader] : [src, extensions])) + const sources = es.merge(src, extensions, loader) .pipe(filter(['**', '!**/*.js.map'], { dot: true })) // TODO@esm remove me once we stop supporting our web-esm-bridge .pipe(es.through(function (file) { @@ -287,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')), )); @@ -301,7 +216,7 @@ const dashed = (/** @type {string} */ str) => (str ? `-${str}` : ``); const vscodeWebTaskCI = task.define(`vscode-web${dashed(minified)}-ci`, task.series( compileWebExtensionsBuildTask, - minified ? minifyVSCodeWebTask : optimizeVSCodeWebTask, + minified ? minifyVSCodeWebTask : bundleVSCodeWebTask, util.rimraf(path.join(BUILD_ROOT, destinationFolderName)), packageTask(sourceFolderName, destinationFolderName) )); diff --git a/build/hygiene.js b/build/hygiene.js index 5dfe1bf763f..84982259068 100644 --- a/build/hygiene.js +++ b/build/hygiene.js @@ -21,7 +21,7 @@ const copyrightHeaderLines = [ ]; function hygiene(some, linting = true) { - const gulpeslint = require('gulp-eslint'); + const eslint = require('./gulp-eslint'); const gulpstylelint = require('./stylelint'); const formatter = require('./lib/formatter'); @@ -62,6 +62,7 @@ function hygiene(some, linting = true) { } } // Please do not add symbols that resemble ASCII letters! + // eslint-disable-next-line no-misleading-character-class const m = /([^\t\n\r\x20-\x7E⊃⊇✔︎✓🎯⚠️🛑🔴🚗🚙🚕🎉✨❗⇧⌥⌘×÷¦⋯…↑↓→→←↔⟷·•●◆▼⟪⟫┌└├⏎↩√φ]+)/g.exec(line); if (m) { console.error( @@ -172,13 +173,7 @@ function hygiene(some, linting = true) { result .pipe(filter(eslintFilter)) .pipe( - gulpeslint({ - configFile: '.eslintrc.json' - }) - ) - .pipe(gulpeslint.formatEach('compact')) - .pipe( - gulpeslint.results((results) => { + eslint((results) => { errorCount += results.warningCount; errorCount += results.errorCount; }) @@ -265,8 +260,8 @@ function createGitIndexVinyls(paths) { return pall(fns, { concurrency: 4 }).then((r) => r.filter((p) => !!p)); } -// NO PRE COMMIT HOOKS!!!! for now... - Void team +// Void - NO PRE COMMIT HOOKS!!!! for now... - Void team // // this allows us to run hygiene as a git pre-commit hook // if (require.main === module) { // const cp = require('child_process'); diff --git a/build/lib/amd.js b/build/lib/amd.js deleted file mode 100644 index 8d4e428f830..00000000000 --- a/build/lib/amd.js +++ /dev/null @@ -1,41 +0,0 @@ -"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.setAMD = setAMD; -exports.isAMD = isAMD; -const path = require("path"); -const fs = require("fs"); -// TODO@esm remove this -const outDirectory = path.join(__dirname, '..', '..', 'out-build'); -const amdMarkerFile = path.join(outDirectory, 'amd'); -function setAMD(enabled) { - const result = () => new Promise((resolve, _) => { - if (enabled) { - fs.mkdirSync(outDirectory, { recursive: true }); - fs.writeFileSync(amdMarkerFile, 'true', 'utf8'); - console.warn(`Setting build to AMD: true`); - } - else { - console.warn(`Setting build to AMD: false`); - } - resolve(); - }); - result.taskName = 'set-amd'; - return result; -} -function isAMD(logWarning) { - try { - const res = (typeof process.env.VSCODE_BUILD_AMD === 'string' && process.env.VSCODE_BUILD_AMD.toLowerCase() === 'true') || (fs.readFileSync(amdMarkerFile, 'utf8') === 'true'); - if (res && logWarning) { - console.warn(`[amd] ${logWarning}`); - } - return res; - } - catch (error) { - return false; - } -} -//# sourceMappingURL=amd.js.map \ No newline at end of file diff --git a/build/lib/amd.ts b/build/lib/amd.ts deleted file mode 100644 index 5373024c6a0..00000000000 --- a/build/lib/amd.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 path from 'path'; -import * as fs from 'fs'; - -// TODO@esm remove this - -const outDirectory = path.join(__dirname, '..', '..', 'out-build'); -const amdMarkerFile = path.join(outDirectory, 'amd'); - -export function setAMD(enabled: boolean) { - const result = () => new Promise((resolve, _) => { - if (enabled) { - fs.mkdirSync(outDirectory, { recursive: true }); - fs.writeFileSync(amdMarkerFile, 'true', 'utf8'); - console.warn(`Setting build to AMD: true`); - } else { - console.warn(`Setting build to AMD: false`); - } - - resolve(); - }); - result.taskName = 'set-amd'; - return result; -} - -export function isAMD(logWarning?: string): boolean { - try { - const res = (typeof process.env.VSCODE_BUILD_AMD === 'string' && process.env.VSCODE_BUILD_AMD.toLowerCase() === 'true') || (fs.readFileSync(amdMarkerFile, 'utf8') === 'true'); - if (res && logWarning) { - console.warn(`[amd] ${logWarning}`); - } - return res; - } catch (error) { - return false; - } -} diff --git a/build/lib/asar.js b/build/lib/asar.js index 19285ef7100..20c982a6621 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 0b225ab1624..5f2df925bde 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 ac784c03506..249777c4458 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 8b831d42d44..e9a1180ce35 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 6a1e5ea539e..3dc0ae27f0a 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 9d11dea3dca..4628b365a2e 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 627b9966700..f1490f4ad4b 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 6e3f96a5062..68182e6b85d 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; @@ -51,9 +51,9 @@ export interface IEntryPoint { name: string; include?: string[]; exclude?: string[]; + /** @deprecated unsupported by ESM */ prepend?: IExtraFile[]; dest?: string; - target?: 'amd' | 'esm'; } interface IEntryPointMap { @@ -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 e6fe4b592a6..841dbe13ecf 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -3,32 +3,68 @@ * 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 ts = require("typescript"); -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"); 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; @@ -52,7 +88,7 @@ function createCompile(src, { build, emitError, transpileOnly, preserveEnglish } const compilation = tsb.create(projectPath, overrideOptions, { verbose: false, transpileOnly: Boolean(transpileOnly), - transpileWithSwc: typeof transpileOnly !== 'boolean' && transpileOnly.swc + transpileWithSwc: typeof transpileOnly !== 'boolean' && transpileOnly.esbuild }, err => reporter(err)); function pipeline(token) { const bom = require('gulp-bom'); @@ -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 }); @@ -88,34 +124,34 @@ function createCompile(src, { build, emitError, transpileOnly, preserveEnglish } pipeline.projectPath = projectPath; return pipeline; } -function transpileTask(src, out, swc) { +function transpileTask(src, out, esbuild) { const task = () => { - const transpile = createCompile(src, { build: false, emitError: true, transpileOnly: { swc }, preserveEnglish: false }); - const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); + const transpile = createCompile(src, { build: false, emitError: true, transpileOnly: { esbuild }, preserveEnglish: false }); + 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 978fb15df9e..6e1fcab5186 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -3,23 +3,23 @@ * 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 ts = require('typescript'); -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'; import { gulpPostcss } from './postcss'; +import ts = require('typescript'); const watch = require('./watch'); @@ -45,7 +45,7 @@ function getTypeScriptCompilerOptions(src: string): ts.CompilerOptions { interface ICompileTaskOptions { readonly build: boolean; readonly emitError: boolean; - readonly transpileOnly: boolean | { swc: boolean }; + readonly transpileOnly: boolean | { esbuild: boolean }; readonly preserveEnglish: boolean; } @@ -63,7 +63,7 @@ function createCompile(src: string, { build, emitError, transpileOnly, preserveE const compilation = tsb.create(projectPath, overrideOptions, { verbose: false, transpileOnly: Boolean(transpileOnly), - transpileWithSwc: typeof transpileOnly !== 'boolean' && transpileOnly.swc + transpileWithSwc: typeof transpileOnly !== 'boolean' && transpileOnly.esbuild }, err => reporter(err)); function pipeline(token?: util.ICancellationToken) { @@ -105,11 +105,11 @@ function createCompile(src: string, { build, emitError, transpileOnly, preserveE return pipeline; } -export function transpileTask(src: string, out: string, swc: boolean): task.StreamTask { +export function transpileTask(src: string, out: string, esbuild: boolean): task.StreamTask { const task = () => { - const transpile = createCompile(src, { build: false, emitError: true, transpileOnly: { swc }, preserveEnglish: false }); + const transpile = createCompile(src, { build: false, emitError: true, transpileOnly: { esbuild }, preserveEnglish: false }); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); return srcPipe diff --git a/build/lib/date.js b/build/lib/date.js index 77fff0e5073..1ed884fb7ee 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 998e89f8e6a..8a933178952 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 9bcd1204eab..04a09f98708 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 45368ffd26d..a5bc70088a7 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 3abbbd7d998..f7814d5d27d 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 47985a95408..9cdf58ddbba 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'; @@ -93,7 +93,7 @@ function darwinBundleDocumentTypes(types: { [name: string]: string | string[] }, ostypes: ['TEXT', 'utxt', 'TUTX', '****'], extensions: Array.isArray(extensions) ? extensions : [extensions], iconFile: 'resources/darwin/' + icon + '.icns' - } as DarwinDocumentType; + }; }); } @@ -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 fbf11ee1ee4..79a9507f66c 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 628cf90c4c9..908480b6077 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 b7da65f4af2..078706cdd00 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 0c44b8e567f..47a65b88fb5 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 29f265c8289..1085ea8f488 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 0d9035b3d87..993722e5f92 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 b50ead538a2..7606c17ab14 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 798a408bdb9..30de97ed6e3 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 dbb424f21df..a3c23d8c29b 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 6964616291b..9483d319a50 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 cce1c944475..2b510757855 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -18,10 +18,6 @@ } ], "workbench": [ - { - "name": "vs/workbench/contrib/void", - "project": "vscode-workbench" - }, { "name": "vs/code", "project": "vscode-workbench" @@ -62,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 cd7e522ad36..d2904ccf0fb 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 f1dbfa83a7e..3b473ae091e 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 ef3987fc32e..2a0db13d06e 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 98241a368b5..5cf5c58402c 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"); @@ -23,7 +26,6 @@ const minimatch_1 = require("minimatch"); // Types we assume are present in all implementations of JS VMs (node.js, browsers) // Feel free to add more core types as you see needed if present in node.js and browsers const CORE_TYPES = [ - 'require', // from our AMD loader 'setTimeout', 'clearTimeout', 'setInterval', @@ -68,11 +70,18 @@ const CORE_TYPES = [ 'fetch', 'RequestInit', 'Headers', + 'Request', 'Response', + 'Body', + '__type', '__global', + 'Performance', 'PerformanceMark', 'PerformanceObserver', - 'ImportMeta' + 'ImportMeta', + // webcrypto has been available since Node.js 19, but still live in dom.d.ts + 'Crypto', + 'SubtleCrypto' ]; // Types that are defined in a common layer but are known to be only // available in native environments should not be allowed in browser @@ -120,6 +129,22 @@ const RULES = [ '@types/node' // no node.js ] }, + // Common: vs/base/common/performance.ts + { + target: '**/vs/base/common/performance.ts', + allowedTypes: [ + ...CORE_TYPES, + // Safe access to Performance + 'Performance', + 'PerformanceEntry', + 'PerformanceTiming' + ], + disallowedTypes: NATIVE_TYPES, + disallowedDefinitions: [ + 'lib.dom.d.ts', // no DOM + '@types/node' // no node.js + ] + }, // Common: vs/platform/environment/common/* { target: '**/vs/platform/environment/common/*.ts', @@ -174,9 +199,9 @@ const RULES = [ '@types/node' // no node.js ] }, - // Common: vs/base/parts/sandbox/electron-sandbox/preload.js + // Common: vs/base/parts/sandbox/electron-sandbox/preload.ts { - target: '**/vs/base/parts/sandbox/electron-sandbox/preload.js', + target: '**/vs/base/parts/sandbox/electron-sandbox/preload.ts', allowedTypes: [ ...CORE_TYPES, // Safe access to a very small subset of node.js @@ -273,8 +298,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); @@ -330,11 +355,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 26a18a48da7..63377328928 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'; @@ -24,7 +24,6 @@ import { match } from 'minimatch'; // Types we assume are present in all implementations of JS VMs (node.js, browsers) // Feel free to add more core types as you see needed if present in node.js and browsers const CORE_TYPES = [ - 'require', // from our AMD loader 'setTimeout', 'clearTimeout', 'setInterval', @@ -69,11 +68,19 @@ const CORE_TYPES = [ 'fetch', 'RequestInit', 'Headers', + 'Request', 'Response', + 'Body', + '__type', '__global', + 'Performance', 'PerformanceMark', 'PerformanceObserver', - 'ImportMeta' + 'ImportMeta', + + // webcrypto has been available since Node.js 19, but still live in dom.d.ts + 'Crypto', + 'SubtleCrypto' ]; // Types that are defined in a common layer but are known to be only @@ -129,6 +136,24 @@ const RULES: IRule[] = [ ] }, + // Common: vs/base/common/performance.ts + { + target: '**/vs/base/common/performance.ts', + allowedTypes: [ + ...CORE_TYPES, + + // Safe access to Performance + 'Performance', + 'PerformanceEntry', + 'PerformanceTiming' + ], + disallowedTypes: NATIVE_TYPES, + disallowedDefinitions: [ + 'lib.dom.d.ts', // no DOM + '@types/node' // no node.js + ] + }, + // Common: vs/platform/environment/common/* { target: '**/vs/platform/environment/common/*.ts', @@ -189,9 +214,9 @@ const RULES: IRule[] = [ ] }, - // Common: vs/base/parts/sandbox/electron-sandbox/preload.js + // Common: vs/base/parts/sandbox/electron-sandbox/preload.ts { - target: '**/vs/base/parts/sandbox/electron-sandbox/preload.js', + target: '**/vs/base/parts/sandbox/electron-sandbox/preload.ts', allowedTypes: [ ...CORE_TYPES, diff --git a/build/lib/mangle/index.js b/build/lib/mangle/index.js index f429712f100..ce744642551 100644 --- a/build/lib/mangle/index.js +++ b/build/lib/mangle/index.js @@ -3,18 +3,20 @@ * 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 amd_1 = require("../amd"); const buildfile = require('../../buildfile'); class ShortIdent { prefix; @@ -67,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); } } @@ -110,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; } @@ -121,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 { @@ -248,51 +250,36 @@ function isNameTakenInFile(node, name) { } return false; } -const skippedExportMangledFiles = function () { - return [ - // Build - 'css.build', - // Monaco - 'editorCommon', - 'editorOptions', - 'editorZoom', - 'standaloneEditor', - 'standaloneEnums', - 'standaloneLanguages', - // Generated - 'extensionsApiProposals', - // Module passed around as type - 'pfs', - // entry points - ...!(0, amd_1.isAMD)() ? [ - buildfile.entrypoint('vs/server/node/server.main'), - buildfile.base, - buildfile.workerExtensionHost, - buildfile.workerNotebook, - buildfile.workerLanguageDetection, - buildfile.workerLocalFileSearch, - buildfile.workerProfileAnalysis, - buildfile.workerOutputLinks, - buildfile.workerBackgroundTokenization, - buildfile.workbenchDesktop(), - buildfile.workbenchWeb(), - buildfile.code, - buildfile.codeWeb - ].flat().map(x => x.name) : [ - buildfile.entrypoint('vs/server/node/server.main'), - buildfile.entrypoint('vs/workbench/workbench.desktop.main'), - buildfile.base, - buildfile.workerExtensionHost, - buildfile.workerNotebook, - buildfile.workerLanguageDetection, - buildfile.workerLocalFileSearch, - buildfile.workerProfileAnalysis, - buildfile.workbenchDesktop(), - buildfile.workbenchWeb(), - buildfile.code - ].flat().map(x => x.name), - ]; -}; +const skippedExportMangledFiles = [ + // Build + 'css.build', + // Monaco + 'editorCommon', + 'editorOptions', + 'editorZoom', + 'standaloneEditor', + 'standaloneEnums', + 'standaloneLanguages', + // Generated + 'extensionsApiProposals', + // Module passed around as type + 'pfs', + // entry points + ...[ + buildfile.workerEditor, + buildfile.workerExtensionHost, + buildfile.workerNotebook, + buildfile.workerLanguageDetection, + buildfile.workerLocalFileSearch, + buildfile.workerProfileAnalysis, + buildfile.workerOutputLinks, + buildfile.workerBackgroundTokenization, + buildfile.workbenchDesktop, + buildfile.workbenchWeb, + buildfile.code, + buildfile.codeWeb + ].flat().map(x => x.name), +]; const skippedExportMangledProjects = [ // Test projects 'vscode-api-tests', @@ -318,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) { @@ -366,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)) { @@ -392,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. /* || ( @@ -422,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; @@ -513,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) { @@ -536,7 +523,7 @@ class Mangler { for (const data of this.allExportedSymbols.values()) { if (data.fileName.endsWith('.d.ts') || skippedExportMangledProjects.some(proj => data.fileName.includes(proj)) - || skippedExportMangledFiles().some(file => data.fileName.endsWith(file + '.ts'))) { + || skippedExportMangledFiles.some(file => data.fileName.endsWith(file + '.ts'))) { continue; } if (!data.shouldMangle(data.replacementName)) { @@ -561,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; @@ -573,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); @@ -612,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; @@ -630,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; } } @@ -652,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 ecede4cc108..4cbbd3cdadd 100644 --- a/build/lib/mangle/index.ts +++ b/build/lib/mangle/index.ts @@ -3,16 +3,15 @@ * 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'; -import { isAMD } from '../amd'; const buildfile = require('../../buildfile'); class ShortIdent { @@ -280,55 +279,40 @@ function isNameTakenInFile(node: ts.Node, name: string): boolean { return false; } -const skippedExportMangledFiles = function () { // using a function() to ensure late isAMD() check - return [ - // Build - 'css.build', - - // Monaco - 'editorCommon', - 'editorOptions', - 'editorZoom', - 'standaloneEditor', - 'standaloneEnums', - 'standaloneLanguages', - - // Generated - 'extensionsApiProposals', - - // Module passed around as type - 'pfs', - - // entry points - ...!isAMD() ? [ - buildfile.entrypoint('vs/server/node/server.main'), - buildfile.base, - buildfile.workerExtensionHost, - buildfile.workerNotebook, - buildfile.workerLanguageDetection, - buildfile.workerLocalFileSearch, - buildfile.workerProfileAnalysis, - buildfile.workerOutputLinks, - buildfile.workerBackgroundTokenization, - buildfile.workbenchDesktop(), - buildfile.workbenchWeb(), - buildfile.code, - buildfile.codeWeb - ].flat().map(x => x.name) : [ - buildfile.entrypoint('vs/server/node/server.main'), - buildfile.entrypoint('vs/workbench/workbench.desktop.main'), - buildfile.base, - buildfile.workerExtensionHost, - buildfile.workerNotebook, - buildfile.workerLanguageDetection, - buildfile.workerLocalFileSearch, - buildfile.workerProfileAnalysis, - buildfile.workbenchDesktop(), - buildfile.workbenchWeb(), - buildfile.code - ].flat().map(x => x.name), - ]; -}; +const skippedExportMangledFiles = [ + // Build + 'css.build', + + // Monaco + 'editorCommon', + 'editorOptions', + 'editorZoom', + 'standaloneEditor', + 'standaloneEnums', + 'standaloneLanguages', + + // Generated + 'extensionsApiProposals', + + // Module passed around as type + 'pfs', + + // entry points + ...[ + buildfile.workerEditor, + buildfile.workerExtensionHost, + buildfile.workerNotebook, + buildfile.workerLanguageDetection, + buildfile.workerLocalFileSearch, + buildfile.workerProfileAnalysis, + buildfile.workerOutputLinks, + buildfile.workerBackgroundTokenization, + buildfile.workbenchDesktop, + buildfile.workbenchWeb, + buildfile.code, + buildfile.codeWeb + ].flat().map(x => x.name), +]; const skippedExportMangledProjects = [ // Test projects @@ -423,7 +407,7 @@ export class Mangler { ) { this.renameWorkerPool = workerpool.pool(path.join(__dirname, 'renameWorker.js'), { - maxWorkers: 1, + maxWorkers: 4, minWorkers: 'max' }); } @@ -625,7 +609,7 @@ export class Mangler { for (const data of this.allExportedSymbols.values()) { if (data.fileName.endsWith('.d.ts') || skippedExportMangledProjects.some(proj => data.fileName.includes(proj)) - || skippedExportMangledFiles().some(file => data.fileName.endsWith(file + '.ts')) + || skippedExportMangledFiles.some(file => data.fileName.endsWith(file + '.ts')) ) { continue; } diff --git a/build/lib/mangle/renameWorker.js b/build/lib/mangle/renameWorker.js index 6cd429b8c9a..8bd59a4e2d5 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 29b34e8c514..0cce5677593 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 1f338f0e61c..7777888dd06 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 c2793342ce3..b41b4e52133 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 2052806c46b..84cc556cb62 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 288bec0f858..5dc9a04266c 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 00b9c9262fa..12e60a36ec9 100644 --- a/build/lib/nls.js +++ b/build/lib/nls.js @@ -3,15 +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 amd_1 = require("./amd"); +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"; @@ -47,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.`)); @@ -58,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) { @@ -68,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, @@ -76,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. *--------------------------------------------------------*/ @@ -112,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, @@ -164,29 +166,19 @@ 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) .map(n => n) .filter(d => d.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) - .filter(d => { - if (!(0, amd_1.isAMD)()) { - return d.moduleReference.expression.getText().endsWith(`/nls.js'`); - } - return d.moduleReference.expression.getText().endsWith(`/nls'`); - }); + .filter(d => d.moduleReference.expression.getText().endsWith(`/nls.js'`)); // import ... from 'vs/nls'; const importDeclarations = imports .filter(n => n.kind === ts.SyntaxKind.ImportDeclaration) .map(n => n) .filter(d => d.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral) - .filter(d => { - if (!(0, amd_1.isAMD)()) { - return d.moduleSpecifier.getText().endsWith(`/nls.js'`); - } - return d.moduleSpecifier.getText().endsWith(`/nls'`); - }) + .filter(d => d.moduleSpecifier.getText().endsWith(`/nls.js'`)) .filter(d => !!d.importClause && !!d.importClause.namedBindings); // `nls.localize(...)` calls const nlsLocalizeCallExpressions = importDeclarations @@ -199,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 @@ -225,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 @@ -292,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 }); @@ -328,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)); } @@ -352,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) => { @@ -360,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") ] : [ @@ -369,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 a861122b1bb..ef2afc5d7c8 100644 --- a/build/lib/nls.ts +++ b/build/lib/nls.ts @@ -4,13 +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 { isAMD } from './amd'; +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; @@ -43,7 +42,7 @@ function collect(ts: typeof import('typescript'), node: ts.Node, fn: (node: ts.N } function clone(object: T): T { - const result = {}; + const result = {} as any as T; for (const id in object) { result[id] = object[id]; } @@ -232,24 +231,14 @@ module _nls { .filter(n => n.kind === ts.SyntaxKind.ImportEqualsDeclaration) .map(n => n) .filter(d => d.moduleReference.kind === ts.SyntaxKind.ExternalModuleReference) - .filter(d => { - if (!isAMD()) { - return (d.moduleReference).expression.getText().endsWith(`/nls.js'`); - } - return (d.moduleReference).expression.getText().endsWith(`/nls'`); - }); + .filter(d => (d.moduleReference).expression.getText().endsWith(`/nls.js'`)); // import ... from 'vs/nls'; const importDeclarations = imports .filter(n => n.kind === ts.SyntaxKind.ImportDeclaration) .map(n => n) .filter(d => d.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral) - .filter(d => { - if (!isAMD()) { - return d.moduleSpecifier.getText().endsWith(`/nls.js'`); - } - return d.moduleSpecifier.getText().endsWith(`/nls'`); - }) + .filter(d => d.moduleSpecifier.getText().endsWith(`/nls.js'`)) .filter(d => !!d.importClause && !!d.importClause.namedBindings); // `nls.localize(...)` calls diff --git a/build/lib/node.js b/build/lib/node.js index 74a54a3c170..01a381183ff 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 4beb13ae91b..a2fdc361aa1 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 1d6d48afd64..d75a2978697 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -3,251 +3,136 @@ * 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.loaderConfig = loaderConfig; -exports.optimizeLoaderTask = optimizeLoaderTask; -exports.optimizeTask = optimizeTask; +exports.bundleTask = bundleTask; exports.minifyTask = minifyTask; -const es = require("event-stream"); -const gulp = require("gulp"); -const concat = require("gulp-concat"); -const filter = require("gulp-filter"); -const fancyLog = require("fancy-log"); -const ansiColors = require("ansi-colors"); -const path = require("path"); -const fs = require("fs"); -const pump = require("pump"); -const VinylFile = require("vinyl"); -const bundle = require("./bundle"); -const i18n_1 = require("./i18n"); -const stats_1 = require("./stats"); -const util = require("./util"); +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 amd_1 = require("./amd"); -const REPO_ROOT_PATH = path.join(__dirname, '../..'); -function log(prefix, message) { - fancyLog(ansiColors.cyan('[' + prefix + ']'), message); -} -function loaderConfig() { - const result = { - paths: { - 'vs': 'out-build/vs', - 'vscode': 'empty:' - }, - amdModulesPattern: /^vs\// - }; - result['vs/css'] = { inlineResources: true }; - return result; -} -const IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i; -function loaderPlugin(src, base, amdModuleId) { - return (gulp - .src(src, { base }) - .pipe(es.through(function (data) { - if (amdModuleId) { - let contents = data.contents.toString('utf8'); - contents = contents.replace(/^define\(/m, `define("${amdModuleId}",`); - data.contents = Buffer.from(contents); - } - this.emit('data', data); - }))); -} -function loader(src, bundledFileHeader, bundleLoader, externalLoaderInfo) { - let loaderStream = gulp.src(`${src}/vs/loader.js`, { base: `${src}` }); - if (bundleLoader) { - loaderStream = es.merge(loaderStream, loaderPlugin(`${src}/vs/css.js`, `${src}`, 'vs/css')); - } - const files = []; - const order = (f) => { - if (f.path.endsWith('loader.js')) { - return 0; - } - if (f.path.endsWith('css.js')) { - return 1; - } - return 2; - }; - return (loaderStream - .pipe(es.through(function (data) { - files.push(data); - }, function () { - files.sort((a, b) => { - return order(a) - order(b); - }); - files.unshift(new VinylFile({ - path: 'fake', - base: '.', - contents: Buffer.from(bundledFileHeader) - })); - if (externalLoaderInfo !== undefined) { - files.push(new VinylFile({ - path: 'fake2', - base: '.', - contents: Buffer.from(emitExternalLoaderInfo(externalLoaderInfo)) - })); - } - for (const file of files) { - this.emit('data', file); - } - this.emit('end'); - })) - .pipe(concat('vs/loader.js'))); -} -function emitExternalLoaderInfo(externalLoaderInfo) { - const externalBaseUrl = externalLoaderInfo.baseUrl; - externalLoaderInfo.baseUrl = '$BASE_URL'; - // If defined, use the runtime configured baseUrl. - const code = ` -(function() { - const baseUrl = require.getConfig().baseUrl || ${JSON.stringify(externalBaseUrl)}; - require.config(${JSON.stringify(externalLoaderInfo, undefined, 2)}); -})();`; - return code.replace('"$BASE_URL"', 'baseUrl'); -} -function toConcatStream(src, bundledFileHeader, sources, dest, fileContentMapper) { - const useSourcemaps = /\.js$/.test(dest) && !/\.nls\.js$/.test(dest); - // If a bundle ends up including in any of the sources our copyright, then - // insert a fake source at the beginning of each bundle with our copyright - let containsOurCopyright = false; - for (let i = 0, len = sources.length; i < len; i++) { - const fileContents = sources[i].contents; - if (IS_OUR_COPYRIGHT_REGEXP.test(fileContents)) { - containsOurCopyright = true; - break; - } - } - if (containsOurCopyright) { - sources.unshift({ - path: null, - contents: bundledFileHeader - }); - } - const treatedSources = sources.map(function (source) { - const root = source.path ? REPO_ROOT_PATH.replace(/\\/g, '/') : ''; - const base = source.path ? root + `/${src}` : '.'; - const path = source.path ? root + '/' + source.path.replace(/\\/g, '/') : 'fake'; - const contents = source.path ? fileContentMapper(source.contents, path) : source.contents; - return new VinylFile({ - path: path, - base: base, - contents: Buffer.from(contents) - }); - }); - return es.readArray(treatedSources) - .pipe(useSourcemaps ? util.loadSourcemaps() : es.through()) - .pipe(concat(dest)) - .pipe((0, stats_1.createStatsStream)(dest)); -} -function toBundleStream(src, bundledFileHeader, bundles, fileContentMapper) { - return es.merge(bundles.map(function (bundle) { - return toConcatStream(src, bundledFileHeader, bundle.sources, bundle.dest, fileContentMapper); - })); -} +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 optimizeAMDTask(opts) { - const src = opts.src; - const entryPoints = opts.entryPoints.filter(d => d.target !== 'esm'); - const resources = opts.resources; - const loaderConfig = opts.loaderConfig; - const bundledFileHeader = opts.header || DEFAULT_FILE_HEADER; - const fileContentMapper = opts.fileContentMapper || ((contents, _path) => contents); - const bundlesStream = es.through(); // this stream will contain the bundled files - const resourcesStream = es.through(); // this stream will contain the resources - const bundleInfoStream = es.through(); // this stream will contain bundleInfo.json - bundle.bundle(entryPoints, loaderConfig, function (err, result) { - if (err || !result) { - return bundlesStream.emit('error', JSON.stringify(err)); - } - toBundleStream(src, bundledFileHeader, result.files, fileContentMapper).pipe(bundlesStream); - // Remove css inlined resources - const filteredResources = resources.slice(); - result.cssInlinedResources.forEach(function (resource) { - if (process.env['VSCODE_BUILD_VERBOSE']) { - log('optimizer', 'excluding inlined: ' + resource); - } - filteredResources.push('!' + resource); - }); - gulp.src(filteredResources, { base: `${src}`, allowEmpty: true }).pipe(resourcesStream); - const bundleInfoArray = []; - if (opts.bundleInfo) { - bundleInfoArray.push(new VinylFile({ - path: 'bundleInfo.json', - base: '.', - contents: Buffer.from(JSON.stringify(result.bundleData, null, '\t')) - })); +function bundleESMTask(opts) { + 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_1.default.parse(entryPoint).name }; } - es.readArray(bundleInfoArray).pipe(bundleInfoStream); + return entryPoint; }); - const result = es.merge(loader(src, bundledFileHeader, false, opts.externalLoaderInfo), bundlesStream, resourcesStream, bundleInfoStream); - return result - .pipe(sourcemaps.write('./', { - sourceRoot: undefined, - addComment: true, - includeContent: true - })) - .pipe(opts.languages && opts.languages.length ? (0, i18n_1.processNlsFiles)({ - out: opts.src, - fileHeader: bundledFileHeader, - languages: opts.languages - }) : es.through()); -} -function optimizeESMTask(opts, cjsOpts) { - const resourcesStream = es.through(); // this stream will contain the resources - const bundlesStream = es.through(); // this stream will contain the bundled files - const entryPoints = opts.entryPoints.filter(d => d.target !== 'amd'); - if (cjsOpts) { - cjsOpts.entryPoints.forEach(entryPoint => entryPoints.push({ name: path.parse(entryPoint).name })); - } const allMentionedModules = new Set(); for (const entryPoint of entryPoints) { allMentionedModules.add(entryPoint.name); entryPoint.include?.forEach(allMentionedModules.add, allMentionedModules); entryPoint.exclude?.forEach(allMentionedModules.add, allMentionedModules); } - allMentionedModules.delete('vs/css'); // TODO@esm remove this when vs/css is removed const bundleAsync = async () => { const files = []; const tasks = []; for (const entryPoint of entryPoints) { - console.log(`[bundle] '${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; - // boilerplate massage - const banner = { js: '' }; - const tslibPath = path.join(require.resolve('tslib'), '../tslib.es6.js'); - banner.js += await fs.promises.readFile(tslibPath, 'utf-8'); - const boilerplateTrimmer = { - name: 'boilerplate-trimmer', + // banner contents + const banner = { + js: DEFAULT_FILE_HEADER, + css: DEFAULT_FILE_HEADER + }; + // TS Boilerplate + if (!opts.skipTSBoilerplateRemoval?.(entryPoint.name)) { + 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 (args) => { - const contents = await fs.promises.readFile(args.path, 'utf-8'); - const newContents = bundle.removeAllTSBoilerplate(contents); + build.onLoad({ filter: /\.js$/ }, async ({ path }) => { + const contents = await fs_1.default.promises.readFile(path, 'utf-8'); + // TS Boilerplate + let newContents; + if (!opts.skipTSBoilerplateRemoval?.(entryPoint.name)) { + newContents = bundle.removeAllTSBoilerplate(contents); + } + else { + newContents = contents; + } + // File Content Mapper + const mapper = opts.fileContentMapper?.(path.replace(/\\/g, '/')); + if (mapper) { + newContents = await mapper(newContents); + } return { contents: newContents }; }); } }; - // support for 'preprend' via the esbuild#banner - if (entryPoint.prepend?.length) { - for (const item of entryPoint.prepend) { - const fullpath = path.join(REPO_ROOT_PATH, opts.src, item.path); - const source = await fs.promises.readFile(fullpath, 'utf8'); - banner.js += source + '\n'; - } - } - const task = esbuild.build({ + const externalOverride = { + name: 'external-override', + setup(build) { + // 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_1.default.join(REPO_ROOT_PATH, 'node_modules', 'minimist', 'index.js'), external: false }; + }); + }, + }; + const task = esbuild_1.default.build({ bundle: true, external: entryPoint.exclude, packages: 'external', // "external all the things", see https://esbuild.github.io/api/#packages platform: 'neutral', // makes esm format: 'esm', sourcemap: 'external', - plugins: [boilerplateTrimmer], + plugins: [contentsMapper, externalOverride], target: ['es2022'], loader: { '.ttf': 'file', @@ -259,40 +144,29 @@ function optimizeESMTask(opts, cjsOpts) { 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 }).then(res => { for (const file of res.outputFiles) { - let contents = file.contents; let sourceMapFile = undefined; if (file.path.endsWith('.js')) { - if (opts.fileContentMapper) { - // UGLY the fileContentMapper is per file but at this point we have all files - // bundled already. So, we call the mapper for the same contents but each file - // that has been included in the bundle... - let newText = file.text; - for (const input of Object.keys(res.metafile.inputs)) { - newText = opts.fileContentMapper(newText, input); - } - contents = Buffer.from(newText); - } sourceMapFile = res.outputFiles.find(f => f.path === `${file.path}.map`); } const fileProps = { - contents: Buffer.from(contents), + 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)); } }); - // await task; // FORCE serial bundling (makes debugging easier) tasks.push(task); } await Promise.all(tasks); @@ -300,68 +174,21 @@ function optimizeESMTask(opts, cjsOpts) { }; 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 - })) - .pipe(opts.languages && opts.languages.length ? (0, i18n_1.processNlsFiles)({ - out: opts.src, - fileHeader: opts.header || DEFAULT_FILE_HEADER, - languages: opts.languages - }) : es.through()); -} -function optimizeCommonJSTask(opts) { - const src = opts.src; - const entryPoints = opts.entryPoints; - return gulp.src(entryPoints, { base: `${src}`, allowEmpty: true }) - .pipe(es.map((f, cb) => { - esbuild.build({ - entryPoints: [f.path], - bundle: true, - platform: opts.platform, - write: false, - external: opts.external - }).then(res => { - const jsFile = res.outputFiles[0]; - f.contents = Buffer.from(jsFile.contents); - cb(undefined, f); - }); })); } -function optimizeManualTask(options) { - const concatenations = options.map(opt => { - return gulp - .src(opt.src) - .pipe(concat(opt.out)); - }); - return es.merge(...concatenations); -} -function optimizeLoaderTask(src, out, bundleLoader, bundledFileHeader = '', externalLoaderInfo) { - return () => loader(src, bundledFileHeader, bundleLoader, externalLoaderInfo).pipe(gulp.dest(out)); -} -function optimizeTask(opts) { +function bundleTask(opts) { return function () { - const optimizers = []; - if (!(0, amd_1.isAMD)()) { - optimizers.push(optimizeESMTask(opts.amd, opts.commonJS)); - } - else { - optimizers.push(optimizeAMDTask(opts.amd)); - if (opts.commonJS) { - optimizers.push(optimizeCommonJSTask(opts.commonJS)); - } - } - if (opts.manual) { - optimizers.push(optimizeManualTask(opts.manual)); - } - return es.merge(...optimizers).pipe(gulp.dest(opts.out)); + return bundleESMTask(opts.esm).pipe(gulp_1.default.dest(opts.out)); }; } function minifyTask(src, sourceMapBaseUrl) { @@ -369,16 +196,17 @@ 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', outdir: '.', - platform: 'node', + packages: 'external', // "external all the things", see https://esbuild.github.io/api/#packages + platform: 'neutral', // makes esm target: ['es2022'], write: false }).then(res => { @@ -395,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 a5b4efe2823..e12c85c3fa0 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -3,210 +3,45 @@ * 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 concat from 'gulp-concat'; -import * as filter from 'gulp-filter'; -import * as fancyLog from 'fancy-log'; -import * as ansiColors from 'ansi-colors'; -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 { Language, processNlsFiles } from './i18n'; -import { createStatsStream } from './stats'; -import * as util from './util'; import { gulpPostcss } from './postcss'; -import * as esbuild from 'esbuild'; -import * as sourcemaps from 'gulp-sourcemaps'; -import { isAMD } from './amd'; +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, '../..'); -function log(prefix: string, message: string): void { - fancyLog(ansiColors.cyan('[' + prefix + ']'), message); -} - -export function loaderConfig() { - const result: any = { - paths: { - 'vs': 'out-build/vs', - 'vscode': 'empty:' - }, - amdModulesPattern: /^vs\// - }; - - result['vs/css'] = { inlineResources: true }; - - return result; -} - -const IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i; - -function loaderPlugin(src: string, base: string, amdModuleId: string | undefined): NodeJS.ReadWriteStream { - return ( - gulp - .src(src, { base }) - .pipe(es.through(function (data: VinylFile) { - if (amdModuleId) { - let contents = data.contents.toString('utf8'); - contents = contents.replace(/^define\(/m, `define("${amdModuleId}",`); - data.contents = Buffer.from(contents); - } - this.emit('data', data); - })) - ); -} - -function loader(src: string, bundledFileHeader: string, bundleLoader: boolean, externalLoaderInfo?: util.IExternalLoaderInfo): NodeJS.ReadWriteStream { - let loaderStream = gulp.src(`${src}/vs/loader.js`, { base: `${src}` }); - if (bundleLoader) { - loaderStream = es.merge( - loaderStream, - loaderPlugin(`${src}/vs/css.js`, `${src}`, 'vs/css') - ); - } - - const files: VinylFile[] = []; - const order = (f: VinylFile) => { - if (f.path.endsWith('loader.js')) { - return 0; - } - if (f.path.endsWith('css.js')) { - return 1; - } - return 2; - }; - - return ( - loaderStream - .pipe(es.through(function (data) { - files.push(data); - }, function () { - files.sort((a, b) => { - return order(a) - order(b); - }); - files.unshift(new VinylFile({ - path: 'fake', - base: '.', - contents: Buffer.from(bundledFileHeader) - })); - if (externalLoaderInfo !== undefined) { - files.push(new VinylFile({ - path: 'fake2', - base: '.', - contents: Buffer.from(emitExternalLoaderInfo(externalLoaderInfo)) - })); - } - for (const file of files) { - this.emit('data', file); - } - this.emit('end'); - })) - .pipe(concat('vs/loader.js')) - ); -} - -function emitExternalLoaderInfo(externalLoaderInfo: util.IExternalLoaderInfo): string { - const externalBaseUrl = externalLoaderInfo.baseUrl; - externalLoaderInfo.baseUrl = '$BASE_URL'; - - // If defined, use the runtime configured baseUrl. - const code = ` -(function() { - const baseUrl = require.getConfig().baseUrl || ${JSON.stringify(externalBaseUrl)}; - require.config(${JSON.stringify(externalLoaderInfo, undefined, 2)}); -})();`; - return code.replace('"$BASE_URL"', 'baseUrl'); -} - -function toConcatStream(src: string, bundledFileHeader: string, sources: bundle.IFile[], dest: string, fileContentMapper: (contents: string, path: string) => string): NodeJS.ReadWriteStream { - const useSourcemaps = /\.js$/.test(dest) && !/\.nls\.js$/.test(dest); - - // If a bundle ends up including in any of the sources our copyright, then - // insert a fake source at the beginning of each bundle with our copyright - let containsOurCopyright = false; - for (let i = 0, len = sources.length; i < len; i++) { - const fileContents = sources[i].contents; - if (IS_OUR_COPYRIGHT_REGEXP.test(fileContents)) { - containsOurCopyright = true; - break; - } - } - - if (containsOurCopyright) { - sources.unshift({ - path: null, - contents: bundledFileHeader - }); - } - - const treatedSources = sources.map(function (source) { - const root = source.path ? REPO_ROOT_PATH.replace(/\\/g, '/') : ''; - const base = source.path ? root + `/${src}` : '.'; - const path = source.path ? root + '/' + source.path.replace(/\\/g, '/') : 'fake'; - const contents = source.path ? fileContentMapper(source.contents, path) : source.contents; - - return new VinylFile({ - path: path, - base: base, - contents: Buffer.from(contents) - }); - }); - - return es.readArray(treatedSources) - .pipe(useSourcemaps ? util.loadSourcemaps() : es.through()) - .pipe(concat(dest)) - .pipe(createStatsStream(dest)); -} - -function toBundleStream(src: string, bundledFileHeader: string, bundles: bundle.IConcatFile[], fileContentMapper: (contents: string, path: string) => string): NodeJS.ReadWriteStream { - return es.merge(bundles.map(function (bundle) { - return toConcatStream(src, bundledFileHeader, bundle.sources, bundle.dest, fileContentMapper); - })); -} - -export interface IOptimizeAMDTaskOpts { +export interface IBundleESMTaskOpts { /** * The folder to read files from. */ src: string; /** - * (for AMD files, will get bundled and get Copyright treatment) - */ - entryPoints: bundle.IEntryPoint[]; - /** - * (svg, etc.) - */ - resources: string[]; - loaderConfig: any; - /** - * Additional info we append to the end of the loader - */ - externalLoaderInfo?: util.IExternalLoaderInfo; - /** - * (true by default - append css and nls to loader) + * The entry points to bundle. */ - bundleLoader?: boolean; + entryPoints: Array; /** - * (basically the Copyright treatment) + * Other resources to consider (svg, etc.) */ - header?: string; + resources?: string[]; /** - * (emit bundleInfo.json file) + * File contents interceptor for a given path. */ - bundleInfo: boolean; + fileContentMapper?: (path: string) => ((contents: string) => Promise | string) | undefined; /** - * Language configuration. + * Allows to skip the removal of TS boilerplate. Use this when + * the entry point is small and the overhead of removing the + * boilerplate makes the file larger in the end. */ - languages?: Language[]; - /** - * File contents interceptor - * @param contents The contents of the file - * @param path The absolute file path, always using `/`, even on Windows - */ - fileContentMapper?: (contents: string, path: string) => string; + skipTSBoilerplateRemoval?: (entryPointName: string) => boolean; } const DEFAULT_FILE_HEADER = [ @@ -215,72 +50,17 @@ const DEFAULT_FILE_HEADER = [ ' *--------------------------------------------------------*/' ].join('\n'); -function optimizeAMDTask(opts: IOptimizeAMDTaskOpts): NodeJS.ReadWriteStream { - const src = opts.src; - const entryPoints = opts.entryPoints.filter(d => d.target !== 'esm'); - const resources = opts.resources; - const loaderConfig = opts.loaderConfig; - const bundledFileHeader = opts.header || DEFAULT_FILE_HEADER; - const fileContentMapper = opts.fileContentMapper || ((contents: string, _path: string) => contents); - - const bundlesStream = es.through(); // this stream will contain the bundled files +function bundleESMTask(opts: IBundleESMTaskOpts): NodeJS.ReadWriteStream { const resourcesStream = es.through(); // this stream will contain the resources - const bundleInfoStream = es.through(); // this stream will contain bundleInfo.json - - bundle.bundle(entryPoints, loaderConfig, function (err, result) { - if (err || !result) { return bundlesStream.emit('error', JSON.stringify(err)); } - - toBundleStream(src, bundledFileHeader, result.files, fileContentMapper).pipe(bundlesStream); + const bundlesStream = es.through(); // this stream will contain the bundled files - // Remove css inlined resources - const filteredResources = resources.slice(); - result.cssInlinedResources.forEach(function (resource) { - if (process.env['VSCODE_BUILD_VERBOSE']) { - log('optimizer', 'excluding inlined: ' + resource); - } - filteredResources.push('!' + resource); - }); - gulp.src(filteredResources, { base: `${src}`, allowEmpty: true }).pipe(resourcesStream); - - const bundleInfoArray: VinylFile[] = []; - if (opts.bundleInfo) { - bundleInfoArray.push(new VinylFile({ - path: 'bundleInfo.json', - base: '.', - contents: Buffer.from(JSON.stringify(result.bundleData, null, '\t')) - })); + const entryPoints = opts.entryPoints.map(entryPoint => { + if (typeof entryPoint === 'string') { + return { name: path.parse(entryPoint).name }; } - es.readArray(bundleInfoArray).pipe(bundleInfoStream); - }); - - const result = es.merge( - loader(src, bundledFileHeader, false, opts.externalLoaderInfo), - bundlesStream, - resourcesStream, - bundleInfoStream - ); - - return result - .pipe(sourcemaps.write('./', { - sourceRoot: undefined, - addComment: true, - includeContent: true - })) - .pipe(opts.languages && opts.languages.length ? processNlsFiles({ - out: opts.src, - fileHeader: bundledFileHeader, - languages: opts.languages - }) : es.through()); -} -function optimizeESMTask(opts: IOptimizeAMDTaskOpts, cjsOpts?: IOptimizeCommonJSTaskOpts): NodeJS.ReadWriteStream { - const resourcesStream = es.through(); // this stream will contain the resources - const bundlesStream = es.through(); // this stream will contain the bundled files - - const entryPoints = opts.entryPoints.filter(d => d.target !== 'amd'); - if (cjsOpts) { - cjsOpts.entryPoints.forEach(entryPoint => entryPoints.push({ name: path.parse(entryPoint).name })); - } + return entryPoint; + }); const allMentionedModules = new Set(); for (const entryPoint of entryPoints) { @@ -289,44 +69,63 @@ function optimizeESMTask(opts: IOptimizeAMDTaskOpts, cjsOpts?: IOptimizeCommonJS entryPoint.exclude?.forEach(allMentionedModules.add, allMentionedModules); } - allMentionedModules.delete('vs/css'); // TODO@esm remove this when vs/css is removed - const bundleAsync = async () => { - const files: VinylFile[] = []; const tasks: Promise[] = []; for (const entryPoint of entryPoints) { - - console.log(`[bundle] '${entryPoint.name}'`); + fancyLog(`Bundled entry point: ${ansiColors.yellow(entryPoint.name)}...`); // support for 'dest' via esbuild#in/out const dest = entryPoint.dest?.replace(/\.[^/.]+$/, '') ?? entryPoint.name; - // boilerplate massage - const banner = { js: '' }; - const tslibPath = path.join(require.resolve('tslib'), '../tslib.es6.js'); - banner.js += await fs.promises.readFile(tslibPath, 'utf-8'); + // banner contents + const banner = { + js: DEFAULT_FILE_HEADER, + css: DEFAULT_FILE_HEADER + }; - const boilerplateTrimmer: esbuild.Plugin = { - name: 'boilerplate-trimmer', + // 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 contentsMapper: esbuild.Plugin = { + name: 'contents-mapper', setup(build) { - build.onLoad({ filter: /\.js$/ }, async args => { - const contents = await fs.promises.readFile(args.path, 'utf-8'); - const newContents = bundle.removeAllTSBoilerplate(contents); + build.onLoad({ filter: /\.js$/ }, async ({ path }) => { + const contents = await fs.promises.readFile(path, 'utf-8'); + + // TS Boilerplate + let newContents: string; + if (!opts.skipTSBoilerplateRemoval?.(entryPoint.name)) { + newContents = bundle.removeAllTSBoilerplate(contents); + } else { + newContents = contents; + } + + // File Content Mapper + const mapper = opts.fileContentMapper?.(path.replace(/\\/g, '/')); + if (mapper) { + newContents = await mapper(newContents); + } + return { contents: newContents }; }); } }; - // support for 'preprend' via the esbuild#banner - if (entryPoint.prepend?.length) { - for (const item of entryPoint.prepend) { - const fullpath = path.join(REPO_ROOT_PATH, opts.src, item.path); - const source = await fs.promises.readFile(fullpath, 'utf8'); - banner.js += source + '\n'; - } - } + const externalOverride: esbuild.Plugin = { + name: 'external-override', + setup(build) { + // 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 }; + }); + }, + }; const task = esbuild.build({ bundle: true, @@ -335,7 +134,7 @@ function optimizeESMTask(opts: IOptimizeAMDTaskOpts, cjsOpts?: IOptimizeCommonJS platform: 'neutral', // makes esm format: 'esm', sourcemap: 'external', - plugins: [boilerplateTrimmer], + plugins: [contentsMapper, externalOverride], target: ['es2022'], loader: { '.ttf': 'file', @@ -354,31 +153,16 @@ function optimizeESMTask(opts: IOptimizeAMDTaskOpts, cjsOpts?: IOptimizeCommonJS outdir: path.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 }).then(res => { for (const file of res.outputFiles) { - - let contents = file.contents; let sourceMapFile: esbuild.OutputFile | undefined = undefined; - if (file.path.endsWith('.js')) { - - if (opts.fileContentMapper) { - // UGLY the fileContentMapper is per file but at this point we have all files - // bundled already. So, we call the mapper for the same contents but each file - // that has been included in the bundle... - let newText = file.text; - for (const input of Object.keys(res.metafile.inputs)) { - newText = opts.fileContentMapper(newText, input); - } - contents = Buffer.from(newText); - } - sourceMapFile = res.outputFiles.find(f => f.path === `${file.path}.map`); } const fileProps = { - contents: Buffer.from(contents), + 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) @@ -387,7 +171,6 @@ function optimizeESMTask(opts: IOptimizeAMDTaskOpts, cjsOpts?: IOptimizeCommonJS } }); - // await task; // FORCE serial bundling (makes debugging easier) tasks.push(task); } @@ -401,7 +184,7 @@ function optimizeESMTask(opts: IOptimizeAMDTaskOpts, cjsOpts?: IOptimizeCommonJS es.readArray(output.files).pipe(bundlesStream); // forward all resources - gulp.src(opts.resources, { base: `${opts.src}`, allowEmpty: true }).pipe(resourcesStream); + gulp.src(opts.resources ?? [], { base: `${opts.src}`, allowEmpty: true }).pipe(resourcesStream); }); const result = es.merge( @@ -414,117 +197,23 @@ function optimizeESMTask(opts: IOptimizeAMDTaskOpts, cjsOpts?: IOptimizeCommonJS sourceRoot: undefined, addComment: true, includeContent: true - })) - .pipe(opts.languages && opts.languages.length ? processNlsFiles({ - out: opts.src, - fileHeader: opts.header || DEFAULT_FILE_HEADER, - languages: opts.languages - }) : es.through()); -} - -export interface IOptimizeCommonJSTaskOpts { - /** - * The paths to consider for optimizing. - */ - entryPoints: string[]; - /** - * The folder to read files from. - */ - src: string; - /** - * ESBuild `platform` option: https://esbuild.github.io/api/#platform - */ - platform: 'browser' | 'node' | 'neutral'; - /** - * ESBuild `external` option: https://esbuild.github.io/api/#external - */ - external: string[]; -} - -function optimizeCommonJSTask(opts: IOptimizeCommonJSTaskOpts): NodeJS.ReadWriteStream { - const src = opts.src; - const entryPoints = opts.entryPoints; - - return gulp.src(entryPoints, { base: `${src}`, allowEmpty: true }) - .pipe(es.map((f: any, cb) => { - esbuild.build({ - entryPoints: [f.path], - bundle: true, - platform: opts.platform, - write: false, - external: opts.external - }).then(res => { - const jsFile = res.outputFiles[0]; - f.contents = Buffer.from(jsFile.contents); - - cb(undefined, f); - }); })); } -export interface IOptimizeManualTaskOpts { - /** - * The paths to consider for concatenation. The entries - * will be concatenated in the order they are provided. - */ - src: string[]; - /** - * Destination target to concatenate the entryPoints into. - */ - out: string; -} - -function optimizeManualTask(options: IOptimizeManualTaskOpts[]): NodeJS.ReadWriteStream { - const concatenations = options.map(opt => { - return gulp - .src(opt.src) - .pipe(concat(opt.out)); - }); - - return es.merge(...concatenations); -} - -export function optimizeLoaderTask(src: string, out: string, bundleLoader: boolean, bundledFileHeader = '', externalLoaderInfo?: util.IExternalLoaderInfo): () => NodeJS.ReadWriteStream { - return () => loader(src, bundledFileHeader, bundleLoader, externalLoaderInfo).pipe(gulp.dest(out)); -} - -export interface IOptimizeTaskOpts { +export interface IBundleESMTaskOpts { /** - * Destination folder for the optimized files. + * Destination folder for the bundled files. */ out: string; /** - * Optimize AMD modules (using our AMD loader). - */ - amd: IOptimizeAMDTaskOpts; - /** - * Optimize CommonJS modules (using esbuild). - */ - commonJS?: IOptimizeCommonJSTaskOpts; - /** - * Optimize manually by concatenating files. - */ - manual?: IOptimizeManualTaskOpts[]; + * Bundle ESM modules (using esbuild). + */ + esm: IBundleESMTaskOpts; } -export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStream { +export function bundleTask(opts: IBundleESMTaskOpts): () => NodeJS.ReadWriteStream { return function () { - const optimizers: NodeJS.ReadWriteStream[] = []; - if (!isAMD()) { - optimizers.push(optimizeESMTask(opts.amd, opts.commonJS)); - } else { - optimizers.push(optimizeAMDTask(opts.amd)); - - if (opts.commonJS) { - optimizers.push(optimizeCommonJSTask(opts.commonJS)); - } - } - - if (opts.manual) { - optimizers.push(optimizeManualTask(opts.manual)); - } - - return es.merge(...optimizers).pipe(gulp.dest(opts.out)); + return bundleESMTask(opts.esm).pipe(gulp.dest(opts.out)); }; } @@ -549,7 +238,8 @@ export function minifyTask(src: string, sourceMapBaseUrl?: string): (cb: any) => minify: true, sourcemap: 'external', outdir: '.', - platform: 'node', + packages: 'external', // "external all the things", see https://esbuild.github.io/api/#packages + platform: 'neutral', // makes esm target: ['es2022'], write: false }).then(res => { diff --git a/build/lib/policies.js b/build/lib/policies.js index 466295b8ad5..b76d9ffe00a 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'); @@ -37,7 +40,7 @@ function renderADMLString(prefix, moduleName, nlsString, translations) { if (!value) { value = nlsString.value; } - return `${value}`; + return `${value}`; } class BasePolicy { policyType; @@ -59,7 +62,7 @@ class BasePolicy { } renderADMX(regKey) { return [ - ``, + ``, ` `, ` `, ` `, @@ -145,6 +148,24 @@ class StringPolicy extends BasePolicy { return ``; } } +class ObjectPolicy extends BasePolicy { + static from(name, category, minimumVersion, description, moduleName, settingNode) { + const type = getStringProperty(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.StringEnum, name, category, minimumVersion, description, moduleName); + } + renderADMXElements() { + return [``]; + } + renderADMLPresentationContents() { + return ``; + } +} class StringEnumPolicy extends BasePolicy { enum_; enumDescriptions; @@ -240,7 +261,7 @@ const StringArrayQ = { } }; function getProperty(qtype, node, key) { - const query = new Parser.Query(typescript, `( + const query = new tree_sitter_1.default.Query(typescript, `( (pair key: [(property_identifier)(string)] @key value: ${qtype.Q} @@ -264,6 +285,7 @@ const PolicyTypes = [ IntPolicy, StringEnumPolicy, StringPolicy, + ObjectPolicy ]; function getPolicy(moduleName, configurationNode, settingNode, policyNode, categories) { const name = getStringProperty(policyNode, 'name'); @@ -312,14 +334,14 @@ function getPolicy(moduleName, configurationNode, settingNode, policyNode, categ 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) 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) value: (object) @policy @@ -341,7 +363,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)); @@ -475,13 +497,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)); @@ -510,11 +532,11 @@ async function main() { const root = '.build/policies/win32'; 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')); } } if (require.main === module) { diff --git a/build/lib/policies.ts b/build/lib/policies.ts index 68f6989f27a..2488920ce26 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'); @@ -59,7 +59,7 @@ function renderADMLString(prefix: string, moduleName: string, nlsString: NlsStri value = nlsString.value; } - return `${value}`; + return `${value}`; } abstract class BasePolicy implements Policy { @@ -78,7 +78,7 @@ abstract class BasePolicy implements Policy { renderADMX(regKey: string) { return [ - ``, + ``, ` `, ` `, ` `, @@ -232,6 +232,44 @@ class StringPolicy extends BasePolicy { } } +class ObjectPolicy extends BasePolicy { + + static from( + name: string, + category: Category, + minimumVersion: string, + description: NlsString, + moduleName: string, + settingNode: Parser.SyntaxNode + ): ObjectPolicy | undefined { + const type = getStringProperty(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.StringEnum, name, category, minimumVersion, description, moduleName); + } + + protected renderADMXElements(): string[] { + return [``]; + } + + renderADMLPresentationContents() { + return ``; + } +} + class StringEnumPolicy extends BasePolicy { static from( @@ -402,6 +440,7 @@ const PolicyTypes = [ IntPolicy, StringEnumPolicy, StringPolicy, + ObjectPolicy ]; function getPolicy( @@ -474,7 +513,7 @@ function getPolicies(moduleName: string, node: Parser.SyntaxNode): Policy[] { arguments: (arguments (object (pair key: [(property_identifier)(string)] @propertiesKey (#eq? @propertiesKey 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) value: (object) @policy diff --git a/build/lib/postcss.js b/build/lib/postcss.js index 356015ab159..210a184e5f5 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 cf3121e221e..9ec2188d13a 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 4791514fdfe..75207fe50c0 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 e0ea274458a..0c178afcb59 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/reporter.js b/build/lib/reporter.js index 9d4a1b4fd79..16bb44ec539 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 382e0c78546..c21fd841c0d 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 0e58ceedffa..7d9b3f154f1 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 c3d66dba7e1..3cb2191144d 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 b724a009e8a..0e7a9ecc782 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); } } @@ -103,25 +139,22 @@ function extractEditor(options) { delete tsConfig.compilerOptions.moduleResolution; writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); [ - 'vs/css.build.ts', - 'vs/css.ts', - 'vs/loader.js', - 'vs/loader.d.ts' + 'vs/loader.js' ].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) { @@ -129,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}`); @@ -151,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 { @@ -209,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); @@ -220,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 9563cd6670b..b2ae02f1007 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, '../../'); @@ -115,10 +115,7 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); [ - 'vs/css.build.ts', - 'vs/css.ts', - 'vs/loader.js', - 'vs/loader.d.ts' + 'vs/loader.js' ].forEach(copyFile); } diff --git a/build/lib/stats.js b/build/lib/stats.js index e089cb0c1b4..3f6d953ae40 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 fe4b22453b5..8db55d3e777 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 6a50d1d6894..b0e064e7b56 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 6d9fa8a7cef..b28aed13f4b 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 56aa02ebfb7..9a5b08022b6 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -17,6 +17,10 @@ "--vscode-activityBarTop-dropBorder", "--vscode-activityBarTop-foreground", "--vscode-activityBarTop-inactiveForeground", + "--vscode-activityErrorBadge-background", + "--vscode-activityErrorBadge-foreground", + "--vscode-activityWarningBadge-background", + "--vscode-activityWarningBadge-foreground", "--vscode-badge-background", "--vscode-badge-foreground", "--vscode-banner-background", @@ -35,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", @@ -45,6 +52,7 @@ "--vscode-charts-yellow", "--vscode-chat-avatarBackground", "--vscode-chat-avatarForeground", + "--vscode-chat-editedFileForeground", "--vscode-chat-requestBackground", "--vscode-chat-requestBorder", "--vscode-chat-slashCommandBackground", @@ -128,6 +136,7 @@ "--vscode-dropdown-foreground", "--vscode-dropdown-listBackground", "--vscode-editor-background", + "--vscode-editor-compositionBorder", "--vscode-editor-findMatchBackground", "--vscode-editor-findMatchBorder", "--vscode-editor-findMatchForeground", @@ -270,6 +279,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", @@ -340,6 +350,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", @@ -351,6 +368,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", @@ -435,6 +472,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", @@ -502,7 +540,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", @@ -546,14 +587,16 @@ "--vscode-scmGraph-foreground1", "--vscode-scmGraph-foreground2", "--vscode-scmGraph-foreground3", + "--vscode-scmGraph-foreground4", + "--vscode-scmGraph-foreground5", "--vscode-scmGraph-historyItemBaseRefColor", - "--vscode-scmGraph-historyItemRefColor", - "--vscode-scmGraph-historyItemRemoteRefColor", "--vscode-scmGraph-historyItemHoverAdditionsForeground", "--vscode-scmGraph-historyItemHoverDefaultLabelBackground", "--vscode-scmGraph-historyItemHoverDefaultLabelForeground", "--vscode-scmGraph-historyItemHoverDeletionsForeground", "--vscode-scmGraph-historyItemHoverLabelForeground", + "--vscode-scmGraph-historyItemRefColor", + "--vscode-scmGraph-historyItemRemoteRefColor", "--vscode-scrollbar-shadow", "--vscode-scrollbarSlider-activeBackground", "--vscode-scrollbarSlider-background", @@ -596,6 +639,7 @@ "--vscode-sideBarStickyScroll-border", "--vscode-sideBarStickyScroll-shadow", "--vscode-sideBarTitle-background", + "--vscode-sideBarTitle-border", "--vscode-sideBarTitle-foreground", "--vscode-sideBySideEditor-horizontalBorder", "--vscode-sideBySideEditor-verticalBorder", @@ -738,6 +782,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", @@ -755,7 +801,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", @@ -804,8 +852,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", @@ -816,6 +868,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", @@ -879,6 +934,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 597b2a0d397..6887714681a 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 7d2a4dee016..6af23983178 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 b8f4a2bedef..41aa8a7f668 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 b8a68323dd7..4e4545548b8 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 af06f4e3ec5..d51eee91f1e 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 cd17c5f0278..ac71bb205da 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 cee2758e896..84308191361 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 70c71591a6e..7a1b0e0cbb4 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 8b8116d5a49..843b76c823f 100644 --- a/build/lib/tsb/index.js +++ b/build/lib/tsb/index.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.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 colors = require("ansi-colors"); +const fancy_log_1 = __importDefault(require("fancy-log")); const transpiler_1 = require("./transpiler"); +const colors = require("ansi-colors"); class EmptyDuplex extends stream_1.Duplex { _write(_chunk, _encoding, callback) { callback(); } _read() { this.push(null); } @@ -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'); @@ -97,7 +133,7 @@ function create(projectPath, existingOptions, config, onError = _defaultOnError) if (config.transpileOnly) { const transpiler = !config.transpileWithSwc ? new transpiler_1.TscTranspiler(logFn, printDiagnostic, projectPath, cmdLine) - : new transpiler_1.SwcTranspiler(logFn, printDiagnostic, projectPath, cmdLine); + : new transpiler_1.ESBuildTranspiler(logFn, printDiagnostic, projectPath, cmdLine); result = (() => createTranspileStream(transpiler)); } else { @@ -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 670ac6542e7..e577d386cd9 100644 --- a/build/lib/tsb/index.ts +++ b/build/lib/tsb/index.ts @@ -3,17 +3,17 @@ * 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'); -import { ITranspiler, SwcTranspiler, TscTranspiler } from './transpiler'; export interface IncrementalCompiler { (token?: any): Readable & Writable; @@ -130,7 +130,7 @@ export function create( if (config.transpileOnly) { const transpiler = !config.transpileWithSwc ? new TscTranspiler(logFn, printDiagnostic, projectPath, cmdLine) - : new SwcTranspiler(logFn, printDiagnostic, projectPath, cmdLine); + : new ESBuildTranspiler(logFn, printDiagnostic, projectPath, cmdLine); result = (() => createTranspileStream(transpiler)); } else { const _builder = builder.createTypeScriptBuilder({ logFn }, projectPath, cmdLine); diff --git a/build/lib/tsb/transpiler.js b/build/lib/tsb/transpiler.js index 5dcc4ca1ed3..adccb104416 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.SwcTranspiler = exports.TscTranspiler = void 0; -const swc = require("@swc/core"); -const ts = require("typescript"); -const threads = require("node:worker_threads"); -const Vinyl = require("vinyl"); +exports.ESBuildTranspiler = exports.TscTranspiler = void 0; +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(); } @@ -62,7 +65,7 @@ class OutputFileNameOracle { catch (err) { console.error(file, cmdLine.fileNames); console.error(err); - throw new err; + throw err; } }; } @@ -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), @@ -224,25 +227,41 @@ class TscTranspiler { } } exports.TscTranspiler = TscTranspiler; -function _isDefaultEmpty(src) { - return src - .replace('"use strict";', '') - .replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1') - .trim().length === 0; -} -class SwcTranspiler { +class ESBuildTranspiler { _logFn; _onError; _cmdLine; - onOutfile; _outputFileNames; _jobs = []; + onOutfile; + _transformOpts; constructor(_logFn, _onError, configFilePath, _cmdLine) { this._logFn = _logFn; this._onError = _onError; this._cmdLine = _cmdLine; - _logFn('Transpile', `will use SWC to transpile source files`); + _logFn('Transpile', `will use ESBuild to transpile source files`); this._outputFileNames = new OutputFileNameOracle(_cmdLine, configFilePath); + const isExtension = configFilePath.includes('extensions'); + this._transformOpts = { + target: ['es2022'], + format: isExtension ? 'cjs' : 'esm', + platform: isExtension ? 'node' : undefined, + loader: 'ts', + sourcemap: 'inline', + tsconfigRaw: JSON.stringify({ + compilerOptions: { + ...this._cmdLine.options, + ...{ + module: isExtension ? typescript_1.default.ModuleKind.CommonJS : undefined + } + } + }), + supported: { + 'class-static-blocks': false, // SEE https://github.com/evanw/esbuild/issues/3823, + 'dynamic-import': !isExtension, // see https://github.com/evanw/esbuild/issues/1281 + 'class-field': !isExtension + } + }; } async join() { const jobs = this._jobs.slice(); @@ -250,78 +269,38 @@ class SwcTranspiler { await Promise.allSettled(jobs); } transpile(file) { - if (this._cmdLine.options.noEmit) { - // not doing ANYTHING here - return; + if (!(file.contents instanceof Buffer)) { + throw Error('file.contents must be a Buffer'); } - const tsSrc = String(file.contents); const t1 = Date.now(); - let options = SwcTranspiler._swcrcEsm; - if (this._cmdLine.options.module === ts.ModuleKind.AMD) { - const isAmd = /\n(import|export)/m.test(tsSrc); - if (isAmd) { - options = SwcTranspiler._swcrcAmd; - } - } - else if (this._cmdLine.options.module === ts.ModuleKind.CommonJS) { - options = SwcTranspiler._swcrcCommonJS; - } - this._jobs.push(swc.transform(tsSrc, options).then(output => { + this._jobs.push(esbuild_1.default.transform(file.contents, { + ...this._transformOpts, + sourcefile: file.path, + }).then(result => { // check if output of a DTS-files isn't just "empty" and iff so // skip this file - if (file.path.endsWith('.d.ts') && _isDefaultEmpty(output.code)) { + if (file.path.endsWith('.d.ts') && _isDefaultEmpty(result.code)) { return; } 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(output.code), + contents: Buffer.from(result.code), })); - this._logFn('Transpile', `swc took ${Date.now() - t1}ms for ${file.path}`); + this._logFn('Transpile', `esbuild took ${Date.now() - t1}ms for ${file.path}`); }).catch(err => { this._onError(err); })); } - // --- .swcrc - static _swcrcAmd = { - exclude: '\.js$', - jsc: { - parser: { - syntax: 'typescript', - tsx: false, - decorators: true - }, - target: 'es2022', - loose: false, - minify: { - compress: false, - mangle: false - }, - transform: { - useDefineForClassFields: false, - }, - }, - module: { - type: 'amd', - noInterop: false - }, - minify: false, - }; - static _swcrcCommonJS = { - ...this._swcrcAmd, - module: { - type: 'commonjs', - importInterop: 'swc' - } - }; - static _swcrcEsm = { - ...this._swcrcAmd, - module: { - type: 'es6' - } - }; } -exports.SwcTranspiler = SwcTranspiler; +exports.ESBuildTranspiler = ESBuildTranspiler; +function _isDefaultEmpty(src) { + return src + .replace('"use strict";', '') + .replace(/\/\/# sourceMappingURL.*^/, '') + .replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1') + .trim().length === 0; +} //# sourceMappingURL=transpiler.js.map \ No newline at end of file diff --git a/build/lib/tsb/transpiler.ts b/build/lib/tsb/transpiler.ts index cbc3d9e8eee..16a3b347538 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 swc from '@swc/core'; -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 { @@ -84,7 +84,7 @@ class OutputFileNameOracle { } catch (err) { console.error(file, cmdLine.fileNames); console.error(err); - throw new err; + throw err; } }; } @@ -291,20 +291,14 @@ export class TscTranspiler implements ITranspiler { } } -function _isDefaultEmpty(src: string): boolean { - return src - .replace('"use strict";', '') - .replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1') - .trim().length === 0; -} - +export class ESBuildTranspiler implements ITranspiler { -export class SwcTranspiler implements ITranspiler { + private readonly _outputFileNames: OutputFileNameOracle; + private _jobs: Promise[] = []; onOutfile?: ((file: Vinyl) => void) | undefined; - private readonly _outputFileNames: OutputFileNameOracle; - private _jobs: Promise[] = []; + private readonly _transformOpts: esbuild.TransformOptions; constructor( private readonly _logFn: (topic: string, message: string) => void, @@ -312,8 +306,31 @@ export class SwcTranspiler implements ITranspiler { configFilePath: string, private readonly _cmdLine: ts.ParsedCommandLine ) { - _logFn('Transpile', `will use SWC to transpile source files`); + _logFn('Transpile', `will use ESBuild to transpile source files`); this._outputFileNames = new OutputFileNameOracle(_cmdLine, configFilePath); + + const isExtension = configFilePath.includes('extensions'); + + this._transformOpts = { + target: ['es2022'], + format: isExtension ? 'cjs' : 'esm', + platform: isExtension ? 'node' : undefined, + loader: 'ts', + sourcemap: 'inline', + tsconfigRaw: JSON.stringify({ + compilerOptions: { + ...this._cmdLine.options, + ...{ + module: isExtension ? ts.ModuleKind.CommonJS : undefined + } satisfies ts.CompilerOptions + } + }), + supported: { + 'class-static-blocks': false, // SEE https://github.com/evanw/esbuild/issues/3823, + 'dynamic-import': !isExtension, // see https://github.com/evanw/esbuild/issues/1281 + 'class-field': !isExtension + } + }; } async join(): Promise { @@ -323,29 +340,18 @@ export class SwcTranspiler implements ITranspiler { } transpile(file: Vinyl): void { - if (this._cmdLine.options.noEmit) { - // not doing ANYTHING here - return; + if (!(file.contents instanceof Buffer)) { + throw Error('file.contents must be a Buffer'); } - - const tsSrc = String(file.contents); const t1 = Date.now(); - - let options: swc.Options = SwcTranspiler._swcrcEsm; - if (this._cmdLine.options.module === ts.ModuleKind.AMD) { - const isAmd = /\n(import|export)/m.test(tsSrc); - if (isAmd) { - options = SwcTranspiler._swcrcAmd; - } - } else if (this._cmdLine.options.module === ts.ModuleKind.CommonJS) { - options = SwcTranspiler._swcrcCommonJS; - } - - this._jobs.push(swc.transform(tsSrc, options).then(output => { + this._jobs.push(esbuild.transform(file.contents, { + ...this._transformOpts, + sourcefile: file.path, + }).then(result => { // check if output of a DTS-files isn't just "empty" and iff so // skip this file - if (file.path.endsWith('.d.ts') && _isDefaultEmpty(output.code)) { + if (file.path.endsWith('.d.ts') && _isDefaultEmpty(result.code)) { return; } @@ -355,56 +361,21 @@ export class SwcTranspiler implements ITranspiler { this.onOutfile!(new Vinyl({ path: outPath, base: outBase, - contents: Buffer.from(output.code), + contents: Buffer.from(result.code), })); - this._logFn('Transpile', `swc took ${Date.now() - t1}ms for ${file.path}`); + this._logFn('Transpile', `esbuild took ${Date.now() - t1}ms for ${file.path}`); }).catch(err => { this._onError(err); })); } +} - // --- .swcrc - - - private static readonly _swcrcAmd: swc.Options = { - exclude: '\.js$', - jsc: { - parser: { - syntax: 'typescript', - tsx: false, - decorators: true - }, - target: 'es2022', - loose: false, - minify: { - compress: false, - mangle: false - }, - transform: { - useDefineForClassFields: false, - }, - }, - module: { - type: 'amd', - noInterop: false - }, - minify: false, - }; - - private static readonly _swcrcCommonJS: swc.Options = { - ...this._swcrcAmd, - module: { - type: 'commonjs', - importInterop: 'swc' - } - }; - - private static readonly _swcrcEsm: swc.Options = { - ...this._swcrcAmd, - module: { - type: 'es6' - } - }; +function _isDefaultEmpty(src: string): boolean { + return src + .replace('"use strict";', '') + .replace(/\/\/# sourceMappingURL.*^/, '') + .replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1') + .trim().length === 0; } diff --git a/build/lib/tsb/utils.js b/build/lib/tsb/utils.js index 6ea66221b1b..29cccf0f833 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 3b003e3a6e1..59d1cab36d3 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 260051be52e..2b021ef258e 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 09df0e69e3f..8b6f0396281 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; @@ -21,26 +24,22 @@ exports.rreddir = rreddir; exports.ensureDir = ensureDir; exports.rebase = rebase; exports.filter = filter; -exports.versionStringToNumber = versionStringToNumber; exports.streamToPromise = streamToPromise; exports.getElectronVersion = getElectronVersion; -exports.acquireWebNodePaths = acquireWebNodePaths; -exports.createExternalLoaderConfig = createExternalLoaderConfig; -exports.buildWebNodePaths = buildWebNodePaths; -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 }; @@ -49,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(); })) @@ -58,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; @@ -73,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) { @@ -92,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(); @@ -101,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; } @@ -115,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; } }; } @@ -125,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); @@ -141,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; @@ -189,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); } @@ -197,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(); } @@ -256,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}`); @@ -276,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); } @@ -297,17 +296,9 @@ function filter(fn) { result.restore.push(data); } }); - result.restore = es.through(); + result.restore = event_stream_1.default.through(); return result; } -function versionStringToNumber(versionStr) { - const semverRegex = /(\d+)\.(\d+)\.(\d+)/; - const match = versionStr.match(semverRegex); - if (!match) { - throw new Error('Version string is not properly formatted: ' + versionStr); - } - return parseInt(match[1], 10) * 1e4 + parseInt(match[2], 10) * 1e2 + parseInt(match[3], 10); -} function streamToPromise(stream) { return new Promise((c, e) => { stream.on('error', err => e(err)); @@ -315,93 +306,9 @@ 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 }; } -function acquireWebNodePaths() { - const root = path.join(__dirname, '..', '..'); - const webPackageJSON = path.join(root, '/remote/web', 'package.json'); - const webPackages = JSON.parse(fs.readFileSync(webPackageJSON, 'utf8')).dependencies; - const distroWebPackageJson = path.join(root, '.build/distro/npm/remote/web/package.json'); - if (fs.existsSync(distroWebPackageJson)) { - const distroWebPackages = JSON.parse(fs.readFileSync(distroWebPackageJson, 'utf8')).dependencies; - Object.assign(webPackages, distroWebPackages); - } - const nodePaths = {}; - for (const key of Object.keys(webPackages)) { - const packageJSON = path.join(root, 'node_modules', key, 'package.json'); - const packageData = JSON.parse(fs.readFileSync(packageJSON, 'utf8')); - // Only cases where the browser is a string are handled - let entryPoint = typeof packageData.browser === 'string' ? packageData.browser : packageData.main; - // On rare cases a package doesn't have an entrypoint so we assume it has a dist folder with a min.js - if (!entryPoint) { - // TODO @lramos15 remove this when jschardet adds an entrypoint so we can warn on all packages w/out entrypoint - if (key !== 'jschardet') { - console.warn(`No entry point for ${key} assuming dist/${key}.min.js`); - } - entryPoint = `dist/${key}.min.js`; - } - // Remove any starting path information so it's all relative info - if (entryPoint.startsWith('./')) { - entryPoint = entryPoint.substring(2); - } - else if (entryPoint.startsWith('/')) { - entryPoint = entryPoint.substring(1); - } - // Search for a minified entrypoint as well - if (/(? new Promise((resolve, _) => { - const root = path.join(__dirname, '..', '..'); - const nodePaths = acquireWebNodePaths(); - // Now we write the node paths to out/vs - const outDirectory = path.join(root, outDir, 'vs'); - fs.mkdirSync(outDirectory, { recursive: true }); - const headerWithGeneratedFileWarning = `/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - - // This file is generated by build/npm/postinstall.js. Do not edit.`; - const fileContents = `${headerWithGeneratedFileWarning}\nself.webPackagePaths = ${JSON.stringify(nodePaths, null, 2)};`; - fs.writeFileSync(path.join(outDirectory, 'webPackagePaths.js'), fileContents, 'utf8'); - resolve(); - }); - result.taskName = 'build-web-node-paths'; - return result; -} //# sourceMappingURL=util.js.map \ No newline at end of file diff --git a/build/lib/util.ts b/build/lib/util.ts index 4cbe8f552ce..ad81730b3de 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)); @@ -367,16 +367,6 @@ export function filter(fn: (data: any) => boolean): FilterStream { return result; } -export function versionStringToNumber(versionStr: string) { - const semverRegex = /(\d+)\.(\d+)\.(\d+)/; - const match = versionStr.match(semverRegex); - if (!match) { - throw new Error('Version string is not properly formatted: ' + versionStr); - } - - return parseInt(match[1], 10) * 1e4 + parseInt(match[2], 10) * 1e2 + parseInt(match[3], 10); -} - export function streamToPromise(stream: NodeJS.ReadWriteStream): Promise { return new Promise((c, e) => { stream.on('error', err => e(err)); @@ -390,104 +380,3 @@ export function getElectronVersion(): Record { const msBuildId = /^ms_build_id="(.*)"$/m.exec(npmrc)![1]; return { electronVersion, msBuildId }; } - -export function acquireWebNodePaths() { - const root = path.join(__dirname, '..', '..'); - const webPackageJSON = path.join(root, '/remote/web', 'package.json'); - const webPackages = JSON.parse(fs.readFileSync(webPackageJSON, 'utf8')).dependencies; - - const distroWebPackageJson = path.join(root, '.build/distro/npm/remote/web/package.json'); - if (fs.existsSync(distroWebPackageJson)) { - const distroWebPackages = JSON.parse(fs.readFileSync(distroWebPackageJson, 'utf8')).dependencies; - Object.assign(webPackages, distroWebPackages); - } - - const nodePaths: { [key: string]: string } = {}; - for (const key of Object.keys(webPackages)) { - const packageJSON = path.join(root, 'node_modules', key, 'package.json'); - const packageData = JSON.parse(fs.readFileSync(packageJSON, 'utf8')); - // Only cases where the browser is a string are handled - let entryPoint: string = typeof packageData.browser === 'string' ? packageData.browser : packageData.main; - - // On rare cases a package doesn't have an entrypoint so we assume it has a dist folder with a min.js - if (!entryPoint) { - // TODO @lramos15 remove this when jschardet adds an entrypoint so we can warn on all packages w/out entrypoint - if (key !== 'jschardet') { - console.warn(`No entry point for ${key} assuming dist/${key}.min.js`); - } - - entryPoint = `dist/${key}.min.js`; - } - - // Remove any starting path information so it's all relative info - if (entryPoint.startsWith('./')) { - entryPoint = entryPoint.substring(2); - } else if (entryPoint.startsWith('/')) { - entryPoint = entryPoint.substring(1); - } - - // Search for a minified entrypoint as well - if (/(? new Promise((resolve, _) => { - const root = path.join(__dirname, '..', '..'); - const nodePaths = acquireWebNodePaths(); - // Now we write the node paths to out/vs - const outDirectory = path.join(root, outDir, 'vs'); - fs.mkdirSync(outDirectory, { recursive: true }); - const headerWithGeneratedFileWarning = `/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - - // This file is generated by build/npm/postinstall.js. Do not edit.`; - const fileContents = `${headerWithGeneratedFileWarning}\nself.webPackagePaths = ${JSON.stringify(nodePaths, null, 2)};`; - fs.writeFileSync(path.join(outDirectory, 'webPackagePaths.js'), fileContents, 'utf8'); - resolve(); - }); - result.taskName = 'build-web-node-paths'; - return result; -} diff --git a/build/lib/watch/index.js b/build/lib/watch/index.js index 86d2611febf..69eca78fd70 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 934d8e8110f..7b77981d620 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 afde6a79f22..bbfde6afba9 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 bbcb6bfc3de..34276ce7705 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 92f8065f262..addc38696a8 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 3bb58fb1215..8ac57b94d7f 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 e3d78d1139a..df119e8b485 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 77ff92151e8..16d8d01468f 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://msftelectron.blob.core.windows.net'; +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,20 +32,19 @@ 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 }; } function getSha(filename) { - // CodeQL [SM04514] Hash logic cannot be changed due to external dependency, also the code is only used during build. - const hash = (0, crypto_1.createHash)('sha1'); + 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; } @@ -50,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) { @@ -87,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); @@ -140,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,25 +170,25 @@ async function getChromiumSysroot(arch) { const sysrootArch = `bullseye_${arch}`; const sysrootDict = sysrootInfo[sysrootArch]; const tarballFilename = sysrootDict['Tarball']; - const tarballSha = sysrootDict['Sha1Sum']; - const sysroot = path.join((0, os_1.tmpdir)(), sysrootDict['SysrootDir']); - const url = [URL_PREFIX, URL_PATH, tarballSha, tarballFilename].join('/'); - const stamp = path.join(sysroot, '.stamp'); - if (fs.existsSync(stamp) && fs.readFileSync(stamp).toString() === url) { + const tarballSha = sysrootDict['Sha256Sum']; + const sysroot = path_1.default.join((0, os_1.tmpdir)(), sysrootDict['SysrootDir']); + const url = [URL_PREFIX, URL_PATH, tarballSha].join('/'); + 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; @@ -199,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); @@ -210,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 76a31d63cdb..aa10e39f95f 100644 --- a/build/linux/debian/install-sysroot.ts +++ b/build/linux/debian/install-sysroot.ts @@ -5,15 +5,15 @@ 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://msftelectron.blob.core.windows.net'; +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))); @@ -45,8 +45,7 @@ function getElectronVersion(): Record { } function getSha(filename: fs.PathLike): string { - // CodeQL [SM04514] Hash logic cannot be changed due to external dependency, also the code is only used during build. - const hash = createHash('sha1'); + const hash = createHash('sha256'); // Read file 1 MB at a time const fd = fs.openSync(filename, 'r'); const buffer = Buffer.alloc(1024 * 1024); @@ -129,7 +128,7 @@ async function fetchUrl(options: IFetchOptions, retries = 10, retryDelay = 1000) } type SysrootDictEntry = { - Sha1Sum: string; + Sha256Sum: string; SysrootDir: string; Tarball: string; }; @@ -186,9 +185,9 @@ export async function getChromiumSysroot(arch: DebianArchString): Promise;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 e3c8cdd0916..a3369eb25a7 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 d06a1ec1f58..ed0b97d72f0 100644 --- a/build/npm/dirs.js +++ b/build/npm/dirs.js @@ -36,7 +36,6 @@ const dirs = [ 'extensions/microsoft-authentication', 'extensions/notebook-renderers', 'extensions/npm', - 'extensions/open-remote-ssh', 'extensions/php-language-features', 'extensions/references-view', 'extensions/search-result', @@ -45,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', @@ -54,10 +54,7 @@ const dirs = [ 'test/smoke', '.vscode/extensions/vscode-selfhost-import-aid', '.vscode/extensions/vscode-selfhost-test-provider', - - // Void added these: - // 'extensions/void', - // 'void-imports', + 'extensions/open-remote-ssh', // Void added this ]; diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index 10d5d95bc52..c1f22aa5002 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -60,8 +60,9 @@ function npmInstall(dir, opts) { run('sudo', ['chown', '-R', `${userinfo.uid}:${userinfo.gid}`, `${path.resolve(root, dir)}`], opts); } else { log(dir, 'Installing dependencies...'); - run(npm, [command], opts); + 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' && 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; @@ -131,7 +162,27 @@ for (let dir of dirs) { if (process.env['VSCODE_REMOTE_LDFLAGS']) { opts.env['LDFLAGS'] = process.env['VSCODE_REMOTE_LDFLAGS']; } if (process.env['VSCODE_REMOTE_NODE_GYP']) { opts.env['npm_config_node_gyp'] = process.env['VSCODE_REMOTE_NODE_GYP']; } + const globalGypPath = path.join(os.homedir(), '.gyp'); + const globalInclude = path.join(globalGypPath, 'include.gypi'); + const tempGlobalInclude = path.join(globalGypPath, 'include.gypi.bak'); + if (process.platform === 'linux' && + (process.env['CI'] || process.env['BUILD_ARTIFACTSTAGINGDIRECTORY'])) { + // Following include file rename should be removed + // when `Override gnu target for arm64 and arm` step + // is removed from the product build pipeline. + if (fs.existsSync(globalInclude)) { + fs.renameSync(globalInclude, tempGlobalInclude); + } + } setNpmrcConfig('remote', opts.env); + npmInstall(dir, opts); + if (process.platform === 'linux' && + (process.env['CI'] || process.env['BUILD_ARTIFACTSTAGINGDIRECTORY'])) { + if (fs.existsSync(tempGlobalInclude)) { + fs.renameSync(tempGlobalInclude, globalInclude); + } + } + continue; } npmInstall(dir, opts); @@ -139,21 +190,3 @@ for (let dir of dirs) { cp.execSync('git config pull.rebase merges'); cp.execSync('git config blame.ignoreRevsFile .git-blame-ignore-revs'); - - -// // Void added this (inject void-imports into project): -// const buildVoidImports = () => { -// console.log('\n\nVoid is injecting void-imports...') -// cp.execSync(`npm install`, { // this goes here, not in postinstall, because we need to -// env: process.env, -// cwd: path.join(__dirname, '..', '..', '/void-imports'), -// stdio: 'inherit' -// }); -// cp.execSync(`node build-index.mjs`, { -// env: process.env, -// cwd: path.join(__dirname, '..', '..', '/void-imports'), -// stdio: 'inherit' -// }); -// console.log('Done injecting void-imports.') -// } -// buildVoidImports() diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js index b98f22977bf..41a17b01676 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 4499f7cf541..aa939e40375 100644 --- a/build/package-lock.json +++ b/build/package-lock.json @@ -9,9 +9,11 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { + "@azure/core-auth": "^1.9.0", "@azure/cosmos": "^3", "@azure/identity": "^4.2.1", - "@azure/storage-blob": "^12.17.0", + "@azure/msal-node": "^2.16.1", + "@azure/storage-blob": "^12.25.0", "@electron/get": "^2.0.0", "@types/ansi-colors": "^3.2.0", "@types/byline": "^4.2.32", @@ -21,13 +23,13 @@ "@types/fs-extra": "^9.0.12", "@types/glob": "^7.1.1", "@types/gulp": "^4.0.17", - "@types/gulp-concat": "^0.0.32", "@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", "@types/gulp-sort": "^2.0.4", "@types/gulp-sourcemaps": "^0.0.32", + "@types/jws": "^3.2.10", "@types/mime": "0.0.29", "@types/minimatch": "^3.0.3", "@types/minimist": "^1.2.1", @@ -37,32 +39,30 @@ "@types/rimraf": "^2.0.4", "@types/through": "^0.0.29", "@types/through2": "^2.0.36", - "@types/tmp": "^0.2.1", "@types/workerpool": "^6.4.0", "@types/xml2js": "0.0.33", "@vscode/iconv-lite-umd": "0.7.0", + "@vscode/ripgrep": "^1.15.10", "@vscode/vsce": "2.20.1", "byline": "^5.0.0", - "colors": "^1.4.0", - "commander": "^7.0.0", "debug": "^4.3.2", "electron-osx-sign": "^0.4.16", - "esbuild": "0.23.0", + "esbuild": "0.25.0", "extract-zip": "^2.0.1", "gulp-merge-json": "^2.1.1", "gulp-sort": "^2.0.0", "jsonc-parser": "^2.3.0", + "jws": "^4.0.0", "mime": "^1.4.1", "source-map": "0.6.1", "ternary-stream": "^3.0.0", "through2": "^4.0.2", - "tmp": "^0.2.1", + "tree-sitter": "^0.20.5", "vscode-universal-bundler": "^0.1.3", "workerpool": "^6.4.0", "yauzl": "^2.10.0" }, "optionalDependencies": { - "tree-sitter": "^0.20.5", "tree-sitter-typescript": "^0.20.5", "vscode-gulp-watch": "^5.0.3" } @@ -79,186 +79,188 @@ "node": ">=8.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/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.9.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.9.0.tgz", + "integrity": "sha512-FPwHpZywuyasDSLMqJ6fhbOK3TqUdviZNF8OqRGA4W5Ewib2lEEZ+pBsYcBa88B2NGO/SEnYPGhyBqNlE8ilSw==", "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.0.0", + "@azure/core-util": "^1.11.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" + } + }, + "node_modules/@azure/core-auth/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-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.9.2", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz", + "integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/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/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.4.0", + "@azure/core-rest-pipeline": "^1.9.1", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.6.1", "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.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-client/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": { - "@opentelemetry/api": "^1.0.1", - "tslib": "^2.2.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@azure/core-http": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@azure/core-http/-/core-http-3.0.4.tgz", - "integrity": "sha512-Fok9VVhMdxAFOtqiiAtg74fL0UJkt0z3D+ouUUxcRLzZNBioPRAMJFVxiWoJljYpXsRi4GDQHzQHDc9AiYaIUQ==", - "deprecated": "deprecating as we migrated to core v2", + "node_modules/@azure/core-http-compat": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.1.2.tgz", + "integrity": "sha512-5MnV1yqzZwgNLLjlizsU3QqOeQChkIXw781Fwh1xdAqJR5AA32IUaq6xv1BICJvfbHoa+JYcaij2HFkhLbNTJQ==", "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/core-util": "^1.1.1", - "@azure/logger": "^1.0.0", - "@types/node-fetch": "^2.5.0", - "@types/tunnel": "^0.0.3", - "form-data": "^4.0.0", - "node-fetch": "^2.6.7", - "process": "^0.11.10", - "tslib": "^2.2.0", - "tunnel": "^0.0.6", - "uuid": "^8.3.0", - "xml2js": "^0.5.0" + "@azure/abort-controller": "^2.0.0", + "@azure/core-client": "^1.3.0", + "@azure/core-rest-pipeline": "^1.3.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" } }, - "node_modules/@azure/core-http/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/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": { - "@opentelemetry/api": "^1.0.1", - "tslib": "^2.2.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" } }, "node_modules/@azure/core-lro": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.2.1.tgz", - "integrity": "sha512-HE6PBl+mlKa0eBsLwusHqAqjLc5n9ByxeDo3Hz4kF3B1hqHvRkBr4oMgoT6tX7Hc3q97KfDctDUon7EhvoeHPA==", + "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-tracing": "1.0.0-preview.13", + "@azure/abort-controller": "^2.0.0", + "@azure/core-util": "^1.2.0", "@azure/logger": "^1.0.0", - "tslib": "^2.2.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" } }, - "node_modules/@azure/core-lro/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-lro/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": { - "@opentelemetry/api": "^1.0.1", - "tslib": "^2.2.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" } }, "node_modules/@azure/core-paging": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.1.3.tgz", - "integrity": "sha512-his7Ah40ThEYORSpIAwuh6B8wkGwO/zG7gqVtmSE4WAJ46e36zUDXTKReUCLBDc6HmjjApQQxxcRFy5FruG79A==", + "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": { - "@azure/core-asynciterator-polyfill": "^1.0.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=8.0.0" + "node": ">=18.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==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.18.0.tgz", + "integrity": "sha512-QSoGUp4Eq/gohEFNJaUOwTN7BCc2nHTjjbm75JT0aD7W65PWM1H/tItz0GsABn22uaKyGxiMhWQLt2r+FGU89Q==", "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-auth": "^1.8.0", + "@azure/core-tracing": "^1.0.1", + "@azure/core-util": "^1.11.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" + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.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-rest-pipeline/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": { - "@opentelemetry/api": "^1.0.1", - "tslib": "^2.2.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.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.2.0", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.2.0.tgz", + "integrity": "sha512-UKTiEJPkWcESPYJz3X5uKRYyOcJD+4nYph+KpfdPRnQJVrZfk0KJgdnaAWKfhsBBtAf/D58Az4AvCJEmWgIBAg==", "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.2.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.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.11.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.11.0.tgz", + "integrity": "sha512-DxOSLua+NdpWoSqULhjDyAZTXFdP/LKkqtYuxxz1SCN289zk3OG8UOpnCQAz/tygyACBtWp/BoO72ptK7msY8g==", "dev": true, + "license": "MIT", "dependencies": { "@azure/abort-controller": "^2.0.0", "tslib": "^2.6.2" @@ -279,6 +281,20 @@ "node": ">=18.0.0" } }, + "node_modules/@azure/core-xml": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@azure/core-xml/-/core-xml-1.4.4.tgz", + "integrity": "sha512-J4FYAqakGXcbfeZjwjMzjNcpcH4E+JtEBv+xcV1yL0Ydn/6wbQfeFKTCHh9wttAi0lmajHw7yBbHPRG+YHckZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-xml-parser": "^4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@azure/cosmos": { "version": "3.17.3", "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-3.17.3.tgz", @@ -362,12 +378,13 @@ } }, "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": "2.16.1", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.16.1.tgz", + "integrity": "sha512-1NEFpTmMMT2A7RnZuvRl/hUmJU+GLPjh+ShyIqPktG2PvSd2yvPnzGd/BxIBAAvJG5nr9lH4oYcQXepDbaE7fg==", "dev": true, + "license": "MIT", "dependencies": { - "@azure/msal-common": "14.12.0", + "@azure/msal-common": "14.16.0", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" }, @@ -375,36 +392,52 @@ "node": ">=16" } }, + "node_modules/@azure/msal-node/node_modules/@azure/msal-common": { + "version": "14.16.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.0.tgz", + "integrity": "sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/@azure/storage-blob": { - "version": "12.17.0", - "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.17.0.tgz", - "integrity": "sha512-sM4vpsCpcCApagRW5UIjQNlNylo02my2opgp0Emi8x888hZUvJ3dN69Oq20cEGXkMUWnoCrBaB0zyS3yeB87sQ==", + "version": "12.25.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.25.0.tgz", + "integrity": "sha512-oodouhA3nCCIh843tMMbxty3WqfNT+Vgzj3Xo5jqR9UPnzq3d7mzLjlHAYz7lW+b4km3SIgz+NAgztvhm7Z6kQ==", "dev": true, + "license": "MIT", "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-http": "^3.0.0", + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.4.0", + "@azure/core-client": "^1.6.2", + "@azure/core-http-compat": "^2.0.0", "@azure/core-lro": "^2.2.0", "@azure/core-paging": "^1.1.1", - "@azure/core-tracing": "1.0.0-preview.13", + "@azure/core-rest-pipeline": "^1.10.1", + "@azure/core-tracing": "^1.1.2", + "@azure/core-util": "^1.6.1", + "@azure/core-xml": "^1.4.3", "@azure/logger": "^1.0.0", "events": "^3.0.0", "tslib": "^2.2.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" } }, - "node_modules/@azure/storage-blob/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/storage-blob/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": { - "@opentelemetry/api": "^1.0.1", - "tslib": "^2.2.0" + "tslib": "^2.6.2" }, "engines": { - "node": ">=12.0.0" + "node": ">=18.0.0" } }, "node_modules/@electron/asar": { @@ -455,13 +488,14 @@ } }, "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" @@ -471,13 +505,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -487,13 +522,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -503,13 +539,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -519,13 +556,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -535,13 +573,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -551,13 +590,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -567,13 +607,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -583,13 +624,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -599,13 +641,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -615,13 +658,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -631,13 +675,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -647,13 +692,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -663,13 +709,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -679,13 +726,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -695,13 +743,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -711,13 +760,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -726,14 +776,32 @@ "node": ">=18" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -743,13 +811,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -759,13 +828,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -775,13 +845,14 @@ } }, "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" @@ -791,13 +862,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -807,13 +879,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -823,13 +896,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -860,15 +934,6 @@ "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==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", @@ -893,15 +958,6 @@ "node": ">=10" } }, - "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==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/@types/ansi-colors": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@types/ansi-colors/-/ansi-colors-3.2.0.tgz", @@ -1004,15 +1060,6 @@ "chokidar": "^3.3.1" } }, - "node_modules/@types/gulp-concat": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@types/gulp-concat/-/gulp-concat-0.0.32.tgz", - "integrity": "sha512-CUCFADlITzzBfBa2bdGzhKtvBr4eFh+evb+4igVbvPoO5RyPfHifmyQlZl6lM7q19+OKncRlFXDU7B4X9Ayo2g==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/gulp-filter": { "version": "3.0.32", "resolved": "https://registry.npmjs.org/@types/gulp-filter/-/gulp-filter-3.0.32.tgz", @@ -1095,6 +1142,16 @@ "integrity": "sha512-/siF86XrwDKLuHe8l7h6NhrAWgLdgqbxmjZv9NvGWmgYRZoTipkjKiWb0SQHy/jcR+ee0GvbG6uGd+LEBMGNvA==", "dev": true }, + "node_modules/@types/jws": { + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/@types/jws/-/jws-3.2.10.tgz", + "integrity": "sha512-cOevhttJmssERB88/+XvZXvsq5m9JLKZNUiGfgjUb5lcPRdV2ZQciU6dU76D/qXXFYpSqkP3PrSg4hMTiafTZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/keyv": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", @@ -1143,30 +1200,6 @@ "undici-types": "~5.26.4" } }, - "node_modules/@types/node-fetch": { - "version": "2.5.8", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.8.tgz", - "integrity": "sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "form-data": "^3.0.0" - } - }, - "node_modules/@types/node-fetch/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==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/@types/pump": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/pump/-/pump-1.0.1.tgz", @@ -1213,21 +1246,6 @@ "@types/node": "*" } }, - "node_modules/@types/tmp": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.1.tgz", - "integrity": "sha512-7cTXwKP/HLOPVgjg+YhBdQ7bMiobGMuoBmrGmqwIWJv8elC6t1DfVc/mn4fD9UE1IjhwmhaQ5pGVXkmXbH0rhg==", - "dev": true - }, - "node_modules/@types/tunnel": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/@types/tunnel/-/tunnel-0.0.3.tgz", - "integrity": "sha512-sOUTGn6h1SfQ+gbgqC364jLFBw2lnFqkgF3q0WovEHRLMrVD1sd5aufqi/aJObLekJO+Aq5z646U4Oxy6shXMA==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/undertaker": { "version": "1.2.11", "resolved": "https://registry.npmjs.org/@types/undertaker/-/undertaker-1.2.11.tgz", @@ -1298,6 +1316,19 @@ "integrity": "sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg==", "dev": true }, + "node_modules/@vscode/ripgrep": { + "version": "1.15.10", + "resolved": "https://registry.npmjs.org/@vscode/ripgrep/-/ripgrep-1.15.10.tgz", + "integrity": "sha512-83Q6qFrELpFgf88bPOcwSWDegfY2r/cb6bIfdLTSZvN73Dg1wviSfO+1v6lTFMd0mAvUYYcTUu+Mn5xMroZMxA==", + "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/vsce": { "version": "2.20.1", "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-2.20.1.tgz", @@ -1362,6 +1393,16 @@ "node": ">=10" } }, + "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, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3" + } + }, "node_modules/@xmldom/xmldom": { "version": "0.8.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", @@ -1372,15 +1413,16 @@ } }, "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/ansi-colors": { @@ -1489,12 +1531,6 @@ "node": ">= 0.10" } }, - "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 - }, "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", @@ -1544,7 +1580,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "optional": true, + "devOptional": true, "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -1596,6 +1632,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "devOptional": true, "funding": [ { "type": "github", @@ -1610,7 +1647,6 @@ "url": "https://feross.org/support" } ], - "optional": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -1785,7 +1821,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "optional": true + "devOptional": true }, "node_modules/clone": { "version": "2.1.2", @@ -1870,36 +1906,6 @@ "color-support": "bin.js" } }, - "node_modules/colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true, - "engines": { - "node": ">=0.1.90" - } - }, - "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, - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.0.0.tgz", - "integrity": "sha512-ovx/7NkTrnPuIV8sqk/GjUIIM1+iUQeqA3ye2VNpq9sVoiZsooObWlQy+OPWGI17GDaEoybuAGJm6U8yC077BA==", - "dev": true, - "engines": { - "node": ">= 10" - } - }, "node_modules/compare-version": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", @@ -1922,10 +1928,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", @@ -2011,7 +2018,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "optional": true, + "devOptional": true, "engines": { "node": ">=4.0.0" } @@ -2064,20 +2071,11 @@ "node": ">= 0.4" } }, - "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==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/detect-libc": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", - "optional": true, + "devOptional": true, "engines": { "node": ">=8" } @@ -2271,11 +2269,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.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -2283,30 +2282,31 @@ "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.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" } }, "node_modules/escape-string-regexp": { @@ -2331,7 +2331,7 @@ "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==", - "optional": true, + "devOptional": true, "engines": { "node": ">=6" } @@ -2390,6 +2390,29 @@ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, + "node_modules/fast-xml-parser": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", + "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "license": "MIT", + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -2444,25 +2467,11 @@ "integrity": "sha512-Pqq5NnT78ehvUnAk/We/Jr22vSvanRlFTpAmQ88xBY/M1TlHe+P0ILuEyXS595ysdGfaj22634LBkGMA2GTcpA==", "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/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==", - "optional": true + "devOptional": true }, "node_modules/fs-extra": { "version": "8.1.0", @@ -2544,7 +2553,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==", - "optional": true + "devOptional": true }, "node_modules/glob": { "version": "7.2.3", @@ -2814,17 +2823,17 @@ "dev": true }, "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": { @@ -2841,22 +2850,24 @@ } }, "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==", + "devOptional": true, "funding": [ { "type": "github", @@ -2870,8 +2881,7 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "optional": true + ] }, "node_modules/inflight": { "version": "1.0.6", @@ -2894,7 +2904,7 @@ "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "optional": true + "devOptional": true }, "node_modules/is-binary-path": { "version": "2.1.0", @@ -3142,6 +3152,7 @@ "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", "dev": true, + "license": "MIT", "dependencies": { "jwa": "^2.0.0", "safe-buffer": "^5.0.1" @@ -3294,27 +3305,6 @@ "node": ">=4" } }, - "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==", - "dev": true, - "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==", - "dev": true, - "dependencies": { - "mime-db": "1.45.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", @@ -3346,7 +3336,7 @@ "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "optional": true + "devOptional": true }, "node_modules/ms": { "version": "2.1.2", @@ -3364,19 +3354,19 @@ "version": "2.19.0", "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", - "optional": true + "devOptional": 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==", - "optional": true + "devOptional": 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==", - "optional": true, + "devOptional": true, "dependencies": { "semver": "^7.3.5" }, @@ -3388,7 +3378,7 @@ "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, + "devOptional": true, "dependencies": { "lru-cache": "^6.0.0" }, @@ -3412,26 +3402,6 @@ "dev": true, "optional": true }, - "node_modules/node-fetch": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.9.tgz", - "integrity": "sha512-DJm/CJkZkRjKKj4Zi4BsKVZh3ValV5IR5s7LVZnW+6YMh0W1BfNA8XSs6DLMGYlId5F3KnA70uu2qepcR08Qqg==", - "dev": true, - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -3676,7 +3646,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", - "optional": true, + "devOptional": true, "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", @@ -3704,15 +3674,6 @@ "integrity": "sha1-LuTyPCVgkT4IwHzlzN1t498sWvg= sha512-lg++21mreCEOuGWTbO5DnJKAdxfjrdN0S9ysoW9SzdSJvbkWpkaDdpG/cdsPCsEnoLUwmd9m3WcZhngW7yKA2g==", "dev": true }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI= sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "dev": true, - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -3728,6 +3689,13 @@ "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", @@ -3769,7 +3737,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "optional": true, + "devOptional": true, "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -3982,6 +3950,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "devOptional": true, "funding": [ { "type": "github", @@ -3995,13 +3964,13 @@ "type": "consulting", "url": "https://feross.org/support" } - ], - "optional": true + ] }, "node_modules/simple-get": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "devOptional": true, "funding": [ { "type": "github", @@ -4016,7 +3985,6 @@ "url": "https://feross.org/support" } ], - "optional": true, "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", @@ -4111,11 +4079,18 @@ "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==", - "optional": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "dev": true, + "license": "MIT" + }, "node_modules/sumchecker": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", @@ -4144,7 +4119,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "optional": true, + "devOptional": true, "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", @@ -4156,7 +4131,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "optional": true, + "devOptional": true, "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", @@ -4235,18 +4210,12 @@ "node": ">=8.0" } }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true - }, "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==", + "devOptional": true, "hasInstallScript": true, - "optional": true, "dependencies": { "nan": "^2.18.0", "prebuild-install": "^7.1.1" @@ -4282,7 +4251,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==", - "optional": true, + "devOptional": true, "dependencies": { "safe-buffer": "^5.0.1" }, @@ -4506,22 +4475,6 @@ "node": ">= 10.0.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==", - "dev": true - }, - "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==", - "dev": true, - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4605,15 +4558,6 @@ "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" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/build/package.json b/build/package.json index 7017b9144e0..3a435a8326b 100644 --- a/build/package.json +++ b/build/package.json @@ -3,9 +3,11 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { + "@azure/core-auth": "^1.9.0", "@azure/cosmos": "^3", "@azure/identity": "^4.2.1", - "@azure/storage-blob": "^12.17.0", + "@azure/msal-node": "^2.16.1", + "@azure/storage-blob": "^12.25.0", "@electron/get": "^2.0.0", "@types/ansi-colors": "^3.2.0", "@types/byline": "^4.2.32", @@ -15,13 +17,13 @@ "@types/fs-extra": "^9.0.12", "@types/glob": "^7.1.1", "@types/gulp": "^4.0.17", - "@types/gulp-concat": "^0.0.32", "@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", "@types/gulp-sort": "^2.0.4", "@types/gulp-sourcemaps": "^0.0.32", + "@types/jws": "^3.2.10", "@types/mime": "0.0.29", "@types/minimatch": "^3.0.3", "@types/minimist": "^1.2.1", @@ -31,26 +33,25 @@ "@types/rimraf": "^2.0.4", "@types/through": "^0.0.29", "@types/through2": "^2.0.36", - "@types/tmp": "^0.2.1", "@types/workerpool": "^6.4.0", "@types/xml2js": "0.0.33", "@vscode/iconv-lite-umd": "0.7.0", + "@vscode/ripgrep": "^1.15.10", "@vscode/vsce": "2.20.1", "byline": "^5.0.0", - "colors": "^1.4.0", - "commander": "^7.0.0", "debug": "^4.3.2", "electron-osx-sign": "^0.4.16", - "esbuild": "0.23.0", + "esbuild": "0.25.0", "extract-zip": "^2.0.1", "gulp-merge-json": "^2.1.1", "gulp-sort": "^2.0.0", "jsonc-parser": "^2.3.0", + "jws": "^4.0.0", "mime": "^1.4.1", "source-map": "0.6.1", "ternary-stream": "^3.0.0", "through2": "^4.0.2", - "tmp": "^0.2.1", + "tree-sitter": "^0.20.5", "vscode-universal-bundler": "^0.1.3", "workerpool": "^6.4.0", "yauzl": "^2.10.0" @@ -62,7 +63,6 @@ "npmCheckJs": "../node_modules/.bin/tsc --noEmit" }, "optionalDependencies": { - "tree-sitter": "^0.20.5", "tree-sitter-typescript": "^0.20.5", "vscode-gulp-watch": "^5.0.3" } diff --git a/build/tsconfig.json b/build/tsconfig.json index ce7a493a7aa..f3ad981d62f 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 8d317f7cab0..4c169ba0f97 100644 --- a/build/win32/Cargo.lock +++ b/build/win32/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "bitflags" @@ -95,7 +95,7 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "inno_updater" -version = "0.11.1" +version = "0.12.1" dependencies = [ "byteorder", "crc", diff --git a/build/win32/Cargo.toml b/build/win32/Cargo.toml index 9dd72608934..e2130dd2bfa 100644 --- a/build/win32/Cargo.toml +++ b/build/win32/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "inno_updater" -version = "0.11.1" +version = "0.12.1" authors = ["Microsoft "] build = "build.rs" diff --git a/build/win32/code.iss b/build/win32/code.iss index 0bd3d09cc87..1d45a25b508 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -4,7 +4,6 @@ ? ('; LicenseFile: "' + RepoDir + '\licenses\LICENSE-' + Language + '.rtf"') \ : '; LicenseFile: "' + RepoDir + '\' + RootLicenseFileName + '"' - [Setup] AppId={#AppId} AppName={#NameLong} @@ -21,10 +20,10 @@ Compression=lzma SolidCompression=yes AppMutex={code:GetAppMutex} SetupMutex={#AppMutex}setup -; this is a Void icon comment. Old: WizardImageFile="{#RepoDir}\resources\win32\inno-big-100.bmp,{#RepoDir}\resources\win32\inno-big-125.bmp,{#RepoDir}\resources\win32\inno-big-150.bmp,{#RepoDir}\resources\win32\inno-big-175.bmp,{#RepoDir}\resources\win32\inno-big-200.bmp,{#RepoDir}\resources\win32\inno-big-225.bmp,{#RepoDir}\resources\win32\inno-big-250.bmp" -; this is a Void icon comment. Old: WizardSmallImageFile="{#RepoDir}\resources\win32\inno-small-100.bmp,{#RepoDir}\resources\win32\inno-small-125.bmp,{#RepoDir}\resources\win32\inno-small-150.bmp,{#RepoDir}\resources\win32\inno-small-175.bmp,{#RepoDir}\resources\win32\inno-small-200.bmp,{#RepoDir}\resources\win32\inno-small-225.bmp,{#RepoDir}\resources\win32\inno-small-250.bmp" -; WizardImageFile="{#RepoDir}\resources\win32\inno-void.bmp" -WizardSmallImageFile="{#RepoDir}\resources\win32\inno-void.bmp" +; this is a // Void icon comment. Old: WizardImageFile="{#RepoDir}\resources\win32\inno-big-100.bmp,{#RepoDir}\resources\win32\inno-big-125.bmp,{#RepoDir}\resources\win32\inno-big-150.bmp,{#RepoDir}\resources\win32\inno-big-175.bmp,{#RepoDir}\resources\win32\inno-big-200.bmp,{#RepoDir}\resources\win32\inno-big-225.bmp,{#RepoDir}\resources\win32\inno-big-250.bmp" +; this is a // Void icon comment. Old: WizardSmallImageFile="{#RepoDir}\resources\win32\inno-small-100.bmp,{#RepoDir}\resources\win32\inno-small-125.bmp,{#RepoDir}\resources\win32\inno-small-150.bmp,{#RepoDir}\resources\win32\inno-small-175.bmp,{#RepoDir}\resources\win32\inno-small-200.bmp,{#RepoDir}\resources\win32\inno-small-225.bmp,{#RepoDir}\resources\win32\inno-small-250.bmp" +; COMMENTED OUT WizardImageFile="" +WizardSmallImageFile="{#RepoDir}\resources\win32\inno-void.bmp,{#RepoDir}\resources\win32\inno-void.bmp,{#RepoDir}\resources\win32\inno-void.bmp,{#RepoDir}\resources\win32\inno-void.bmp,{#RepoDir}\resources\win32\inno-void.bmp,{#RepoDir}\resources\win32\inno-void.bmp,{#RepoDir}\resources\win32\inno-void.bmp" SetupIconFile={#RepoDir}\resources\win32\code.ico UninstallDisplayIcon={app}\{#ExeBasename}.exe ChangesEnvironment=true @@ -38,6 +37,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 @@ -914,7 +918,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 @@ -1118,14 +1122,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 554b449d872..78d2317147e 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 89fbb57c064..95121cd6503 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 5f969cc326c..ef7cb934eb6 100644 Binary files a/build/win32/inno_updater.exe and b/build/win32/inno_updater.exe differ diff --git a/.yarnrc b/cglicenses.json similarity index 96% rename from .yarnrc rename to cglicenses.json index 0b4e03e502b..8b8710fb8f5 100644 --- a/.yarnrc +++ 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 d81b79c0e0c..24c00b3731d 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "7fa4f6e14e0707c0e604cf7c1da33566e78169ce" + "commitHash": "46a5bd1e987735e0cdc41cf48a7db4988ae73d16" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "124.0.6367.243" + "version": "132.0.6834.196" }, { "component": { @@ -48,7 +48,7 @@ "git": { "name": "ffmpeg", "repositoryUrl": "https://chromium.googlesource.com/chromium/third_party/ffmpeg", - "commitHash": "52d8ef3799b2f16b66351dd0972bb0bcee1648ac" + "commitHash": "092f84b6141055bfab609b6b2666b724eee2e130" } }, "isOnlyProductionDependency": true, @@ -516,11 +516,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "1968ef32415607643770efb320d7d4e941baaa25" + "commitHash": "53a57efd83a18efe7a84bf1b460acc789139939b" } }, "isOnlyProductionDependency": true, - "version": "20.16.0" + "version": "20.18.2" }, { "component": { @@ -528,12 +528,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "a07a70e8db21718dd76644d99892d3e15e0ed440" + "commitHash": "ddc7afd3f006dab49a3b6bf73bf3222b017556d5" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "30.5.1" + "version": "34.2.0" }, { "component": { diff --git a/cli/.cargo/config.toml b/cli/.cargo/config.toml new file mode 100644 index 00000000000..ad9374d4a90 --- /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 d119f1ac98a..4809fccd080 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 27fe79896a2..ff45765a0c1 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -649,6 +649,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[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.65", +] + [[package]] name = "encode_unicode" version = "0.3.6" @@ -1148,14 +1159,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.65", +] + [[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]] @@ -1336,6 +1476,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[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" @@ -1571,9 +1717,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.66" +version = "0.10.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" dependencies = [ "bitflags 2.5.0", "cfg-if", @@ -1603,9 +1749,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.103" +version = "0.9.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" dependencies = [ "cc", "libc", @@ -2358,6 +2504,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[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" @@ -2404,6 +2556,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[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.65", +] + [[package]] name = "sysinfo" version = "0.29.11" @@ -2502,20 +2665,15 @@ 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" @@ -2721,27 +2879,12 @@ 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" @@ -2756,9 +2899,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[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 +2920,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" @@ -3119,6 +3274,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[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,6 +3317,30 @@ 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.65", + "synstructure", +] + [[package]] name = "zbus" version = "3.15.2" @@ -3211,12 +3402,55 @@ dependencies = [ "zvariant", ] +[[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.65", + "synstructure", +] + [[package]] name = "zeroize" 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.65", +] + [[package]] name = "zip" version = "0.6.6" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 2907ff3d7e7..2c87d662e07 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -41,7 +41,7 @@ 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" +url = "2.5.4" async-trait = "0.1.68" log = "0.4.18" const_format = "0.2.31" diff --git a/cli/ThirdPartyNotices.txt b/cli/ThirdPartyNotices.txt index bd58eb0e454..cc255c04cfd 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) @@ -3259,6 +3241,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [`cmac`]: ./cmac [`hmac`]: ./hmac [`pmac`]: ./pmac +[`retail-mac`]: ./retail-mac [//]: # (footnotes) @@ -3271,6 +3254,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [CMAC]: https://en.wikipedia.org/wiki/One-key_MAC [HMAC]: https://en.wikipedia.org/wiki/HMAC [PMAC]: https://en.wikipedia.org/wiki/PMAC_(cryptography) +[Retail MAC]: https://en.wikipedia.org/wiki/ISO/IEC_9797-1#MAC_algorithm_3 --------------------------------------------------------- --------------------------------------------------------- @@ -3312,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 @@ -3344,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 @@ -3398,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 @@ -3511,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 @@ -3626,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 @@ -3915,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 @@ -4099,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 @@ -4668,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 @@ -4812,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. @@ -4891,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) @@ -6144,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 @@ -6858,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 @@ -7711,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 @@ -7997,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) @@ -8092,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) @@ -8361,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 @@ -8669,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 @@ -8937,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. --------------------------------------------------------- --------------------------------------------------------- @@ -9345,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 @@ -9460,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 @@ -9492,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 @@ -9522,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 @@ -9554,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 @@ -9586,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 @@ -9618,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 @@ -9650,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 @@ -10626,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 @@ -10704,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/ @@ -10806,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 @@ -10828,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 @@ -10860,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 16078e34044..64a05e88a04 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 51942c96c75..9116e48339f 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 @@ -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 0bd92c92fd3..b61611a8057 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 b104976b9ab..b73d0aa885b 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 7a8cf1f4f62..52020627ca5 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 4acb9a18c73..2ddefe13083 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 dc4206e8316..06d8dc84226 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 30c918f7e85..c53d9e30218 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 f2a093b5c77..f9f307365c8 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 cd02b02d75a..87ca1924a79 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 1180f2c82c2..538827ed124 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() } @@ -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 e37a718a49a..7d152c0e1ec 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 d48d777a5dc..911cb4ccac0 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 936c6627ac0..45d661e5af9 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 534c1556763..5bc655ef2c1 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 465f6e24230..cf00bc42835 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 dfb5e381179..74fd247cfcd 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 a964b446384..0168ee60d89 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 818910fee8e..0559c9abff8 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 a0cd43cd83c..3d7d718a3cd 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 dba68f3b614..66bdf7a8e63 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 80599ba3c32..0a3e2df6ea2 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 9914b33bb92..1193426abd1 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 6189c06a493..7fbd00d04d3 100644 --- a/cli/src/tunnels/singleton_server.rs +++ b/cli/src/tunnels/singleton_server.rs @@ -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 fb9fb4f91d5..19c17b749b2 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 67519d5437e..b7ed029bb98 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 93a7efbfdd9..c5816ae55ad 100644 --- a/cli/src/util/io.rs +++ b/cli/src/util/io.rs @@ -313,7 +313,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 0f49ab20887..f8eff34ec39 100644 --- a/cli/src/util/prereqs.rs +++ b/cli/src/util/prereqs.rs @@ -15,7 +15,7 @@ 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(); @@ -121,7 +121,7 @@ impl PreReqChecker { let bullets = errors .iter() - .map(|e| format!(" - {}", e)) + .map(|e| format!(" - {e}")) .collect::>() .join("\n"); @@ -142,8 +142,7 @@ 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" )); } @@ -231,8 +230,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()), @@ -403,5 +401,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 5521fa42c54..f7b7ab9fe5a 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 new file mode 100644 index 00000000000..be583f8bc83 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,1432 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +// @ts-check +import fs from 'fs'; +import path from 'path'; +import tseslint from 'typescript-eslint'; +import { fileURLToPath } from 'url'; + +import stylisticTs from '@stylistic/eslint-plugin-ts'; +import pluginLocal from 'eslint-plugin-local'; +import pluginJsdoc from 'eslint-plugin-jsdoc'; +// import pluginReact from 'eslint-plugin-react'; // Void + +import pluginHeader from 'eslint-plugin-header'; +pluginHeader.rules.header.meta.schema = false; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ignores = fs.readFileSync(path.join(__dirname, '.eslint-ignore'), 'utf8') + .toString() + .split(/\r\n|\n/) + .filter(line => line && !line.startsWith('#')); + +export default tseslint.config( + // Global ignores + { + ignores, + }, + // All files (JS and TS) + { + languageOptions: { + parser: tseslint.parser, + }, + plugins: { + 'local': pluginLocal, + 'header': pluginHeader, + }, + rules: { + 'constructor-super': 'warn', + 'curly': 'off', // <-- Void + 'eqeqeq': 'warn', + 'prefer-const': [ + 'off', // <-- Void + { + 'destructuring': 'all' + } + ], + 'no-buffer-constructor': 'warn', + 'no-caller': 'warn', + 'no-case-declarations': 'warn', + 'no-debugger': 'warn', + 'no-duplicate-case': 'warn', + 'no-duplicate-imports': 'warn', + 'no-eval': 'warn', + 'no-async-promise-executor': 'warn', + 'no-extra-semi': 'warn', + 'no-new-wrappers': 'warn', + 'no-redeclare': 'off', + 'no-sparse-arrays': 'warn', + 'no-throw-literal': 'warn', + 'no-unsafe-finally': 'warn', + 'no-unused-labels': 'warn', + 'no-misleading-character-class': 'warn', + 'no-restricted-globals': [ + 'warn', + 'name', + 'length', + 'event', + 'closed', + 'external', + 'status', + 'origin', + 'orientation', + 'context' + ], // non-complete list of globals that are easy to access unintentionally + 'no-var': 'warn', + 'semi': 'off', + 'local/code-translation-remind': 'warn', + 'local/code-no-native-private': 'warn', + 'local/code-parameter-properties-must-have-explicit-accessibility': 'warn', + 'local/code-no-nls-in-standalone-editor': 'warn', + 'local/code-no-potentially-unsafe-disposables': 'warn', + 'local/code-no-dangerous-type-assertions': 'off', + 'local/code-no-standalone-editor': 'warn', + 'local/code-no-unexternalized-strings': 'warn', + 'local/code-must-use-super-dispose': 'warn', + 'local/code-declare-service-brand': 'warn', + 'local/code-layering': [ + 'warn', + { + 'common': [], + 'node': [ + 'common' + ], + 'browser': [ + 'common' + ], + 'electron-sandbox': [ + 'common', + 'browser' + ], + 'electron-utility': [ + 'common', + 'node' + ], + 'electron-main': [ + 'common', + 'node', + 'electron-utility' + ] + } + ], + // Void - this should only apply to workbench/void/ + // 'header/header': [ + // 2, + // 'block', + // [ + // '/*--------------------------------------------------------------------------------------', + // '* Copyright 2025 Glass Devtools, Inc. All rights reserved.', + // '* Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.', + // '*--------------------------------------------------------------------------------------*/', + // ] + // ] + }, + }, + // TS + { + files: [ + '**/*.ts', + ], + languageOptions: { + parser: tseslint.parser, + }, + plugins: { + '@stylistic/ts': stylisticTs, + '@typescript-eslint': tseslint.plugin, + 'local': pluginLocal, + 'jsdoc': pluginJsdoc, + }, + rules: { + '@stylistic/ts/semi': 'off', // <-- Void + '@stylistic/ts/member-delimiter-style': 'off', // <-- Void + 'local/code-no-unused-expressions': [ + 'warn', + { + 'allowTernary': true + } + ], + 'jsdoc/no-types': 'warn', + 'local/code-no-static-self-ref': 'off' // <-- Void + } + }, + // vscode TS + { + files: [ + 'src/**/*.ts', + ], + languageOptions: { + parser: tseslint.parser, + }, + plugins: { + '@typescript-eslint': tseslint.plugin, + }, + rules: { + '@typescript-eslint/naming-convention': [ + 'warn', + { + 'selector': 'class', + 'format': [ + 'PascalCase' + ] + } + ] + } + }, + // Tests + { + files: [ + '**/*.test.ts' + ], + languageOptions: { + parser: tseslint.parser, + }, + plugins: { + 'local': pluginLocal, + }, + rules: { + 'local/code-must-use-super-dispose': 'off', + 'local/code-no-test-only': 'error', + 'local/code-no-test-async-suite': 'warn', + 'local/code-no-unexternalized-strings': 'off', + 'local/code-must-use-result': [ + 'warn', + [ + { + 'message': 'Expression must be awaited', + 'functions': [ + 'assertSnapshot', + 'assertHeap' + ] + } + ] + ] + } + }, + // vscode tests specific rules + { + files: [ + 'src/vs/**/*.test.ts' + ], + languageOptions: { + parser: tseslint.parser, + }, + plugins: { + 'local': pluginLocal, + }, + rules: { + 'local/code-ensure-no-disposables-leak-in-test': [ + 'warn', + { + // Files should (only) be removed from the list they adopt the leak detector + 'exclude': [ + '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/contrib/bulkEdit/test/browser/bulkCellEdits.test.ts', + 'src/vs/workbench/contrib/chat/test/common/chatWordCounter.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/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' + ] + } + ] + } + }, + // vscode API + { + files: [ + '**/vscode.d.ts', + '**/vscode.proposed.*.d.ts' + ], + languageOptions: { + parser: tseslint.parser, + }, + plugins: { + '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-vscode-in-comments': 'warn', + 'local/vscode-dts-provider-naming': [ + 'warn', + { + 'allowed': [ + 'FileSystemProvider', + 'TreeDataProvider', + 'TestProvider', + 'CustomEditorProvider', + 'CustomReadonlyEditorProvider', + 'TerminalLinkProvider', + 'AuthenticationProvider', + 'NotebookContentProvider' + ] + } + ], + 'local/vscode-dts-event-naming': [ + 'warn', + { + 'allowed': [ + 'onCancellationRequested', + 'event' + ], + 'verbs': [ + 'accept', + 'change', + 'close', + 'collapse', + 'create', + 'delete', + 'discover', + 'dispose', + 'drop', + 'edit', + 'end', + 'execute', + 'expand', + 'grant', + 'hide', + 'invalidate', + 'open', + 'override', + 'perform', + 'receive', + 'register', + 'remove', + 'rename', + 'save', + 'send', + 'start', + 'terminate', + 'trigger', + 'unregister', + 'write' + ] + } + ] + } + }, + // vscode.d.ts + { + files: [ + '**/vscode.d.ts' + ], + languageOptions: { + parser: tseslint.parser, + }, + rules: { + 'jsdoc/tag-lines': 'off', + 'jsdoc/valid-types': 'off', + 'jsdoc/no-multi-asterisks': [ + 'warn', + { + 'allowWhitespace': true + } + ], + 'jsdoc/require-jsdoc': [ + 'warn', + { + 'enableFixer': false, + 'contexts': [ + 'TSInterfaceDeclaration', + 'TSPropertySignature', + 'TSMethodSignature', + 'TSDeclareFunction', + 'ClassDeclaration', + 'MethodDefinition', + 'PropertyDeclaration', + 'TSEnumDeclaration', + 'TSEnumMember', + 'ExportNamedDeclaration' + ] + } + ], + 'jsdoc/check-param-names': [ + 'warn', + { + 'enableFixer': false, + 'checkDestructured': false + } + ], + 'jsdoc/require-returns': 'warn' + } + }, + // common/browser layer + { + files: [ + 'src/**/{common,browser}/**/*.ts' + ], + languageOptions: { + parser: tseslint.parser, + }, + plugins: { + 'local': pluginLocal, + }, + rules: { + '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' + ], + languageOptions: { + parser: tseslint.parser, + }, + plugins: { + 'local': pluginLocal, + }, + rules: { + 'local/code-no-global-document-listener': 'warn', + 'no-restricted-syntax': [ + 'warn', + { + 'selector': `BinaryExpression[operator='instanceof'][right.name='MouseEvent']`, + 'message': 'Use DOM.isMouseEvent() to support multi-window scenarios.' + }, + { + 'selector': `BinaryExpression[operator='instanceof'][right.name=/^HTML\\w+/]`, + 'message': 'Use DOM.isHTMLElement() and related methods to support multi-window scenarios.' + }, + { + 'selector': `BinaryExpression[operator='instanceof'][right.name=/^SVG\\w+/]`, + 'message': 'Use DOM.isSVGElement() and related methods to support multi-window scenarios.' + }, + { + 'selector': `BinaryExpression[operator='instanceof'][right.name='KeyboardEvent']`, + 'message': 'Use DOM.isKeyboardEvent() to support multi-window scenarios.' + }, + { + 'selector': `BinaryExpression[operator='instanceof'][right.name='PointerEvent']`, + 'message': 'Use DOM.isPointerEvent() to support multi-window scenarios.' + }, + { + 'selector': `BinaryExpression[operator='instanceof'][right.name='DragEvent']`, + 'message': 'Use DOM.isDragEvent() to support multi-window scenarios.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='activeElement']`, + 'message': 'Use .document.activeElement to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='contains']`, + 'message': 'Use .document.contains to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='styleSheets']`, + 'message': 'Use .document.styleSheets to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='fullscreenElement']`, + 'message': 'Use .document.fullscreenElement to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='body']`, + 'message': 'Use .document.body to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='addEventListener']`, + 'message': 'Use .document.addEventListener to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='removeEventListener']`, + 'message': 'Use .document.removeEventListener to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='hasFocus']`, + 'message': 'Use .document.hasFocus to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='head']`, + 'message': 'Use .document.head to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='exitFullscreen']`, + 'message': 'Use .document.exitFullscreen to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='getElementById']`, + 'message': 'Use .document.getElementById to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='getElementsByClassName']`, + 'message': 'Use .document.getElementsByClassName to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='getElementsByName']`, + 'message': 'Use .document.getElementsByName to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='getElementsByTagName']`, + 'message': 'Use .document.getElementsByTagName to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='getElementsByTagNameNS']`, + 'message': 'Use .document.getElementsByTagNameNS to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='getSelection']`, + 'message': 'Use .document.getSelection to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='open']`, + 'message': 'Use .document.open to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='close']`, + 'message': 'Use .document.close to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='documentElement']`, + 'message': 'Use .document.documentElement to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='visibilityState']`, + 'message': 'Use .document.visibilityState to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='querySelector']`, + 'message': 'Use .document.querySelector to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='querySelectorAll']`, + 'message': 'Use .document.querySelectorAll to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='elementFromPoint']`, + 'message': 'Use .document.elementFromPoint to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='elementsFromPoint']`, + 'message': 'Use .document.elementsFromPoint to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='onkeydown']`, + 'message': 'Use .document.onkeydown to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='onkeyup']`, + 'message': 'Use .document.onkeyup to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='onmousedown']`, + 'message': 'Use .document.onmousedown to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='onmouseup']`, + 'message': 'Use .document.onmouseup to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'selector': `MemberExpression[object.name='document'][property.name='execCommand']`, + 'message': 'Use .document.execCommand to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + } + ], + 'no-restricted-globals': [ + 'warn', + 'name', + 'length', + 'event', + 'closed', + 'external', + 'status', + 'origin', + 'orientation', + 'context', + { + 'name': 'setInterval', + 'message': 'Use .setInterval to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'clearInterval', + 'message': 'Use .clearInterval to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'requestAnimationFrame', + 'message': 'Use .requestAnimationFrame to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'cancelAnimationFrame', + 'message': 'Use .cancelAnimationFrame to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'requestIdleCallback', + 'message': 'Use .requestIdleCallback to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'cancelIdleCallback', + 'message': 'Use .cancelIdleCallback to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'window', + 'message': 'Use to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'addEventListener', + 'message': 'Use .addEventListener to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'removeEventListener', + 'message': 'Use .removeEventListener to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'getComputedStyle', + 'message': 'Use .getComputedStyle to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'focus', + 'message': 'Use .focus to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'blur', + 'message': 'Use .blur to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'close', + 'message': 'Use .close to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'dispatchEvent', + 'message': 'Use .dispatchEvent to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'getSelection', + 'message': 'Use .getSelection to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'matchMedia', + 'message': 'Use .matchMedia to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'open', + 'message': 'Use .open to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'parent', + 'message': 'Use .parent to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'postMessage', + 'message': 'Use .postMessage to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'devicePixelRatio', + 'message': 'Use .devicePixelRatio to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'frames', + 'message': 'Use .frames to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'frameElement', + 'message': 'Use .frameElement to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'innerHeight', + 'message': 'Use .innerHeight to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'innerWidth', + 'message': 'Use .innerWidth to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'outerHeight', + 'message': 'Use .outerHeight to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'outerWidth', + 'message': 'Use .outerWidth to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'opener', + 'message': 'Use .opener to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'origin', + 'message': 'Use .origin to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'screen', + 'message': 'Use .screen to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'screenLeft', + 'message': 'Use .screenLeft to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'screenTop', + 'message': 'Use .screenTop to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'screenX', + 'message': 'Use .screenX to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'screenY', + 'message': 'Use .screenY to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'scrollX', + 'message': 'Use .scrollX to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'scrollY', + 'message': 'Use .scrollY to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'top', + 'message': 'Use .top to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + }, + { + 'name': 'visualViewport', + 'message': 'Use .visualViewport to support multi-window scenarios. Resolve targetWindow with DOM.getWindow(element) or DOM.getActiveWindow() or use the predefined mainWindow constant.' + } + ] + } + }, + // electron-utility layer + { + files: [ + 'src/**/electron-utility/**/*.ts' + ], + languageOptions: { + parser: tseslint.parser, + }, + rules: { + 'no-restricted-imports': [ + 'warn', + { + 'paths': [ + { + 'name': 'electron', + 'allowImportNames': [ + 'net', + 'system-preferences', + ], + 'message': 'Only net and system-preferences are allowed to be imported from electron' + } + ] + } + ] + } + }, + { + files: [ + 'src/**/*.ts' + ], + languageOptions: { + parser: tseslint.parser, + }, + plugins: { + 'local': pluginLocal, + }, + rules: { + 'local/code-import-patterns': [ + 'warn', + { + // imports that are allowed in all files of layers: + // - browser + // - electron-sandbox + 'when': 'hasBrowser', + 'allow': [] + }, + { + // imports that are allowed in all files of layers: + // - node + // - electron-utility + // - electron-main + 'when': 'hasNode', + 'allow': [ + '@parcel/watcher', + '@vscode/sqlite3', + '@vscode/vscode-languagedetection', + '@vscode/ripgrep', + '@vscode/iconv-lite-umd', + '@vscode/policy-watcher', + '@vscode/proxy-agent', + '@vscode/spdlog', + '@vscode/windows-process-tree', + 'assert', + 'child_process', + 'console', + 'cookie', + 'crypto', + 'dns', + 'events', + 'fs', + 'fs/promises', + 'http', + 'https', + 'minimist', + 'node:module', + 'native-keymap', + 'native-watchdog', + 'net', + 'node-pty', + 'os', + 'path', + 'perf_hooks', + 'readline', + 'stream', + 'string_decoder', + 'tas-client-umd', + 'tls', + 'undici-types', + 'url', + 'util', + 'v8-inspect-profiler', + 'vscode-regexpp', + 'vscode-textmate', + 'worker_threads', + '@xterm/addon-clipboard', + '@xterm/addon-image', + '@xterm/addon-ligatures', + '@xterm/addon-search', + '@xterm/addon-serialize', + '@xterm/addon-unicode11', + '@xterm/addon-webgl', + '@xterm/headless', + '@xterm/xterm', + 'yauzl', + 'yazl', + 'zlib' + ] + }, + { + // imports that are allowed in all files of layers: + // - electron-utility + // - electron-main + 'when': 'hasElectron', + 'allow': [ + 'electron' + ] + }, + { + // imports that are allowed in all /test/ files + 'when': 'test', + 'allow': [ + 'assert', + 'sinon', + 'sinon-test' + ] + }, + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // !!! Do not relax these rules !!! + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // + // A path ending in /~ has a special meaning. It indicates a template position + // which will be substituted with one or more layers. + // + // When /~ is used in the target, the rule will be expanded to 14 distinct rules. + // e.g. 'src/vs/base/~' will be expanded to: + // - src/vs/base/common + // - src/vs/base/worker + // - src/vs/base/browser + // - src/vs/base/electron-sandbox + // - src/vs/base/node + // - src/vs/base/electron-main + // - src/vs/base/test/common + // - src/vs/base/test/worker + // - src/vs/base/test/browser + // - src/vs/base/test/electron-sandbox + // - src/vs/base/test/node + // - src/vs/base/test/electron-main + // + // When /~ is used in the restrictions, it will be replaced with the correct + // layers that can be used e.g. 'src/vs/base/electron-sandbox' will be able + // to import '{common,browser,electron-sanbox}', etc. + // + // It is possible to use /~ in the restrictions property even without using it in + // the target property by adding a layer property. + { + 'target': 'src/vs/base/~', + 'restrictions': [ + 'vs/base/~' + ] + }, + { + 'target': 'src/vs/base/parts/*/~', + 'restrictions': [ + 'vs/base/~', + 'vs/base/parts/*/~' + ] + }, + { + 'target': 'src/vs/platform/*/~', + 'restrictions': [ + 'vs/base/~', + 'vs/base/parts/*/~', + 'vs/platform/*/~', + 'tas-client-umd', // node module allowed even in /common/ + '@microsoft/1ds-core-js', // node module allowed even in /common/ + '@microsoft/1ds-post-js', // node module allowed even in /common/ + '@xterm/headless' // node module allowed even in /common/ + ] + }, + { + 'target': 'src/vs/editor/~', + 'restrictions': [ + 'vs/base/~', + 'vs/base/parts/*/~', + 'vs/platform/*/~', + 'vs/editor/~', + '@vscode/tree-sitter-wasm' // node module allowed even in /common/ + ] + }, + { + 'target': 'src/vs/editor/contrib/*/~', + 'restrictions': [ + 'vs/base/~', + 'vs/base/parts/*/~', + 'vs/platform/*/~', + 'vs/editor/~', + 'vs/editor/contrib/*/~' + ] + }, + { + 'target': 'src/vs/editor/standalone/~', + 'restrictions': [ + 'vs/base/~', + 'vs/base/parts/*/~', + 'vs/platform/*/~', + 'vs/editor/~', + 'vs/editor/contrib/*/~', + 'vs/editor/standalone/~', + '@vscode/tree-sitter-wasm' // type import + ] + }, + { + 'target': 'src/vs/editor/editor.all.ts', + 'layer': 'browser', + 'restrictions': [ + 'vs/base/~', + 'vs/base/parts/*/~', + 'vs/platform/*/~', + 'vs/editor/~', + 'vs/editor/contrib/*/~' + ] + }, + { + 'target': 'src/vs/editor/editor.worker.ts', + 'layer': 'worker', + 'restrictions': [ + 'vs/base/~', + 'vs/base/parts/*/~', + 'vs/platform/*/~', + 'vs/editor/~' + ] + }, + { + 'target': 'src/vs/editor/{editor.api.ts,editor.main.ts}', + 'layer': 'browser', + 'restrictions': [ + 'vs/base/~', + 'vs/base/parts/*/~', + 'vs/editor/~', + 'vs/editor/contrib/*/~', + 'vs/editor/standalone/~', + 'vs/editor/*' + ] + }, + { + 'target': 'src/vs/workbench/~', + 'restrictions': [ + 'vs/base/~', + 'vs/base/parts/*/~', + 'vs/platform/*/~', + 'vs/editor/~', + 'vs/editor/contrib/*/~', + 'vs/workbench/~', + 'vs/workbench/services/*/~', + 'assert', + { + 'when': 'test', + 'pattern': 'vs/workbench/contrib/*/~' + } // TODO@layers + ] + }, + { + 'target': 'src/vs/workbench/api/~', + 'restrictions': [ + 'vscode', + 'vs/base/~', + 'vs/base/parts/*/~', + 'vs/platform/*/~', + 'vs/editor/~', + 'vs/editor/contrib/*/~', + 'vs/workbench/api/~', + 'vs/workbench/~', + 'vs/workbench/services/*/~', + 'vs/workbench/contrib/*/~', + 'vs/workbench/contrib/terminalContrib/*/~' + ] + }, + { + 'target': 'src/vs/workbench/services/*/~', + 'restrictions': [ + 'vs/base/~', + 'vs/base/parts/*/~', + 'vs/platform/*/~', + 'vs/editor/~', + 'vs/editor/contrib/*/~', + 'vs/workbench/~', + 'vs/workbench/services/*/~', + { + 'when': 'test', + 'pattern': 'vs/workbench/contrib/*/~' + }, // TODO@layers + 'tas-client-umd', // node module allowed even in /common/ + 'vscode-textmate', // node module allowed even in /common/ + '@vscode/vscode-languagedetection', // node module allowed even in /common/ + '@vscode/tree-sitter-wasm', // type import + { + 'when': 'hasBrowser', + 'pattern': '@xterm/xterm' + } // node module allowed even in /browser/ + ] + }, + { + 'target': 'src/vs/workbench/contrib/*/~', + 'restrictions': [ + 'vs/base/~', + 'vs/base/parts/*/~', + 'vs/platform/*/~', + 'vs/editor/~', + 'vs/editor/contrib/*/~', + 'vs/workbench/~', + 'vs/workbench/services/*/~', + 'vs/workbench/contrib/*/~', + 'vs/workbench/contrib/terminal/terminalContribChatExports*', + 'vs/workbench/contrib/terminal/terminalContribExports*', + 'vscode-notebook-renderer', // Type only import + '@vscode/tree-sitter-wasm', // type import + { + 'when': 'hasBrowser', + 'pattern': '@xterm/xterm' + }, // node module allowed even in /browser/ + { + 'when': 'hasBrowser', + 'pattern': '@xterm/addon-*' + }, // node module allowed even in /browser/ + { + 'when': 'hasBrowser', + 'pattern': 'vscode-textmate' + } // node module allowed even in /browser/ + ] + }, + { + 'target': 'src/vs/workbench/contrib/terminalContrib/*/~', + 'restrictions': [ + 'vs/base/~', + 'vs/base/parts/*/~', + 'vs/platform/*/~', + 'vs/editor/~', + 'vs/editor/contrib/*/~', + 'vs/workbench/~', + 'vs/workbench/services/*/~', + 'vs/workbench/contrib/*/~', + // Only allow terminalContrib to import from itself, this works because + // terminalContrib is one extra folder deep + 'vs/workbench/contrib/terminalContrib/*/~', + 'vscode-notebook-renderer', // Type only import + { + 'when': 'hasBrowser', + 'pattern': '@xterm/xterm' + }, // node module allowed even in /browser/ + { + 'when': 'hasBrowser', + 'pattern': '@xterm/addon-*' + }, // node module allowed even in /browser/ + { + 'when': 'hasBrowser', + 'pattern': 'vscode-textmate' + }, // node module allowed even in /browser/ + '@xterm/headless' // node module allowed even in /common/ and /browser/ + ] + }, + { + 'target': 'src/vs/code/~', + 'restrictions': [ + 'vs/base/~', + 'vs/base/parts/*/~', + 'vs/platform/*/~', + 'vs/editor/~', + 'vs/editor/contrib/*/~', + 'vs/code/~', + { + 'when': 'hasBrowser', + 'pattern': 'vs/workbench/workbench.web.main.js' + }, + { + 'when': 'hasBrowser', + 'pattern': 'vs/workbench/workbench.web.main.internal.js' + }, + { + 'when': 'hasBrowser', + 'pattern': 'vs/workbench/~' + }, + { + 'when': 'hasBrowser', + 'pattern': 'vs/workbench/services/*/~' + } + ] + }, + { + 'target': 'src/vs/server/~', + 'restrictions': [ + 'vs/base/~', + 'vs/base/parts/*/~', + 'vs/platform/*/~', + 'vs/workbench/~', + 'vs/workbench/api/~', + 'vs/workbench/services/*/~', + 'vs/workbench/contrib/*/~', + 'vs/server/~' + ] + }, + { + 'target': 'src/vs/workbench/contrib/terminal/terminal.all.ts', + 'layer': 'browser', + 'restrictions': [ + 'vs/workbench/contrib/**' + ] + }, + { + 'target': 'src/vs/workbench/contrib/terminal/terminalContribChatExports.ts', + 'layer': 'browser', + 'restrictions': [ + 'vs/workbench/contrib/terminalContrib/*/~' + ] + }, + { + 'target': 'src/vs/workbench/contrib/terminal/terminalContribExports.ts', + 'layer': 'browser', + 'restrictions': [ + 'vs/platform/*/~', + 'vs/workbench/contrib/terminalContrib/*/~' + ] + }, + { + 'target': 'src/vs/workbench/workbench.common.main.ts', + 'layer': 'browser', + 'restrictions': [ + 'vs/base/~', + 'vs/base/parts/*/~', + 'vs/platform/*/~', + 'vs/editor/~', + 'vs/editor/contrib/*/~', + 'vs/editor/editor.all.js', + 'vs/workbench/~', + 'vs/workbench/api/~', + 'vs/workbench/services/*/~', + 'vs/workbench/contrib/*/~', + 'vs/workbench/contrib/terminal/terminal.all.js' + ] + }, + { + 'target': 'src/vs/workbench/workbench.web.main.ts', + 'layer': 'browser', + 'restrictions': [ + 'vs/base/~', + 'vs/base/parts/*/~', + 'vs/platform/*/~', + 'vs/editor/~', + 'vs/editor/contrib/*/~', + 'vs/editor/editor.all.js', + 'vs/workbench/~', + 'vs/workbench/api/~', + 'vs/workbench/services/*/~', + 'vs/workbench/contrib/*/~', + 'vs/workbench/workbench.common.main.js' + ] + }, + { + 'target': 'src/vs/workbench/workbench.web.main.internal.ts', + 'layer': 'browser', + 'restrictions': [ + 'vs/base/~', + 'vs/base/parts/*/~', + 'vs/platform/*/~', + 'vs/editor/~', + 'vs/editor/contrib/*/~', + 'vs/editor/editor.all.js', + 'vs/workbench/~', + 'vs/workbench/api/~', + 'vs/workbench/services/*/~', + 'vs/workbench/contrib/*/~', + 'vs/workbench/workbench.common.main.js' + ] + }, + { + 'target': 'src/vs/workbench/workbench.desktop.main.ts', + 'layer': 'electron-sandbox', + 'restrictions': [ + 'vs/base/*/~', + 'vs/base/parts/*/~', + 'vs/platform/*/~', + 'vs/editor/~', + 'vs/editor/contrib/*/~', + 'vs/editor/editor.all.js', + 'vs/workbench/~', + 'vs/workbench/api/~', + 'vs/workbench/services/*/~', + 'vs/workbench/contrib/*/~', + 'vs/workbench/workbench.common.main.js' + ] + }, + { + 'target': 'src/vs/amdX.ts', + 'restrictions': [ + 'vs/base/common/*' + ] + }, + { + 'target': 'src/vs/{loader.d.ts,monaco.d.ts,nls.ts,nls.messages.ts}', + 'restrictions': [] + }, + { + 'target': 'src/vscode-dts/**', + 'restrictions': [] + }, + { + 'target': 'src/bootstrap-window.ts', + 'restrictions': [] + }, + { + 'target': 'src/vs/nls.ts', + 'restrictions': [ + 'vs/*' + ] + }, + { + 'target': 'src/{bootstrap-cli.ts,bootstrap-esm.ts,bootstrap-fork.ts,bootstrap-import.ts,bootstrap-meta.ts,bootstrap-node.ts,bootstrap-server.ts,cli.ts,main.ts,server-cli.ts,server-main.ts}', + 'restrictions': [ + 'vs/**/common/*', + 'vs/**/node/*', + 'vs/nls.js', + 'src/*.js', + '*' // node.js + ] + } + ] + } + }, + { + files: [ + 'test/**/*.ts' + ], + languageOptions: { + parser: tseslint.parser, + }, + plugins: { + 'local': pluginLocal, + }, + rules: { + 'local/code-import-patterns': [ + 'warn', + { + 'target': 'test/smoke/**', + 'restrictions': [ + 'test/automation', + 'test/smoke/**', + '@vscode/*', + '@parcel/*', + '@playwright/*', + '*' // node modules + ] + }, + { + 'target': 'test/automation/**', + 'restrictions': [ + 'test/automation/**', + '@vscode/*', + '@parcel/*', + 'playwright-core/**', + '@playwright/*', + '*' // node modules + ] + }, + { + 'target': 'test/integration/**', + 'restrictions': [ + 'test/integration/**', + '@vscode/*', + '@parcel/*', + '@playwright/*', + '*' // node modules + ] + }, + { + 'target': 'test/monaco/**', + 'restrictions': [ + 'test/monaco/**', + '@vscode/*', + '@parcel/*', + '@playwright/*', + '*' // node modules + ] + } + ] + } + }, + { + files: [ + 'src/vs/workbench/contrib/notebook/browser/view/renderers/*.ts' + ], + languageOptions: { + parser: tseslint.parser, + }, + plugins: { + 'local': pluginLocal, + }, + rules: { + 'local/code-no-runtime-import': [ + 'error', + { + 'src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts': [ + '**/*' + ] + } + ], + 'local/code-limited-top-functions': [ + 'error', + { + 'src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts': [ + 'webviewPreloads', + 'preloadsScriptStr' + ] + } + ] + } + }, + // Terminal + { + files: [ + 'src/vs/workbench/contrib/terminal/**/*.ts', + 'src/vs/workbench/contrib/terminalContrib/**/*.ts', + ], + languageOptions: { + parser: tseslint.parser, + }, + rules: { + '@typescript-eslint/naming-convention': [ + 'warn', + // variableLike + { 'selector': 'variable', 'format': ['camelCase', 'UPPER_CASE', 'PascalCase'] }, + { 'selector': 'variable', 'filter': '^I.+Service$', 'format': ['PascalCase'], 'prefix': ['I'] }, + // memberLike + { 'selector': 'memberLike', 'modifiers': ['private'], 'format': ['camelCase'], 'leadingUnderscore': 'require' }, + { 'selector': 'memberLike', 'modifiers': ['protected'], 'format': ['camelCase'], 'leadingUnderscore': 'require' }, + { 'selector': 'enumMember', 'format': ['PascalCase'] }, + // memberLike - Allow enum-like objects to use UPPER_CASE + { 'selector': 'method', 'modifiers': ['public'], 'format': ['camelCase', 'UPPER_CASE'] }, + // typeLike + { 'selector': 'typeLike', 'format': ['PascalCase'] }, + { 'selector': 'interface', 'format': ['PascalCase'] } + ], + 'comma-dangle': ['warn', 'only-multiline'] + } + }, + // markdown-language-features + { + files: [ + 'extensions/markdown-language-features/**/*.ts', + ], + languageOptions: { + parser: tseslint.parser, + }, + plugins: { + '@typescript-eslint': tseslint.plugin, + }, + rules: { + '@typescript-eslint/naming-convention': [ + 'warn', + { + 'selector': 'default', + 'modifiers': ['private'], + 'format': null, + 'leadingUnderscore': 'require' + }, + { + 'selector': 'default', + 'modifiers': ['public'], + 'format': null, + 'leadingUnderscore': 'forbid' + } + ] + } + }, + // typescript-language-features + { + files: [ + 'extensions/typescript-language-features/**/*.ts', + ], + languageOptions: { + parser: tseslint.parser, + parserOptions: { + project: [ + 'extensions/typescript-language-features/tsconfig.json', + 'extensions/typescript-language-features/web/tsconfig.json' + ], + } + }, + plugins: { + '@typescript-eslint': tseslint.plugin, + }, + rules: { + '@typescript-eslint/prefer-optional-chain': 'warn', + '@typescript-eslint/prefer-readonly': 'warn', + } + } +); diff --git a/extensions/.npmrc b/extensions/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/coffeescript/package.json b/extensions/coffeescript/package.json index 44f423c7dfc..615138a280a 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/.npmrc b/extensions/configuration-editing/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/configuration-editing/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/configuration-editing/package-lock.json b/extensions/configuration-editing/package-lock.json index 199dcf7f755..cba3a0fece6 100644 --- a/extensions/configuration-editing/package-lock.json +++ b/extensions/configuration-editing/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@octokit/rest": "19.0.4", + "@octokit/rest": "^21.1.1", "jsonc-parser": "^3.2.0", "tunnel": "^0.0.6" }, @@ -21,173 +21,157 @@ } }, "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": "5.1.2", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz", + "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==", + "license": "MIT", "engines": { - "node": ">= 14" + "node": ">= 18" } }, "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": "6.1.4", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.4.tgz", + "integrity": "sha512-lAS9k7d6I0MPN+gb9bKDt7X8SdxknYqAMh44S5L+lNqIN2NuV8nvv3g8rPp7MuRxcOpxpUIATWprO0C34a8Qmg==", + "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": "^5.0.0", + "@octokit/graphql": "^8.1.2", + "@octokit/request": "^9.2.1", + "@octokit/request-error": "^6.1.7", + "@octokit/types": "^13.6.2", + "before-after-hook": "^3.0.2", + "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "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": "10.1.3", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.3.tgz", + "integrity": "sha512-nBRBMpKPhQUxCsQQeW+rCJ/OPSMcj3g0nfHn01zGYZXuNDvvXudF/TYY6APj5THlurerpFN4a/dQAIAaM6BYhA==", + "license": "MIT", "dependencies": { - "@octokit/types": "^9.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" + "@octokit/types": "^13.6.2", + "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "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": "8.2.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.1.tgz", + "integrity": "sha512-n57hXtOoHrhwTWdvhVkdJHdhTv0JstjDbDRhJfwIRNfFqmSo1DaK/mD2syoNUoLCyqSjBpGAKOG0BuwF392slw==", + "license": "MIT", "dependencies": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "universal-user-agent": "^6.0.0" + "@octokit/request": "^9.2.2", + "@octokit/types": "^13.8.0", + "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "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": "23.0.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-23.0.1.tgz", + "integrity": "sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==", + "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": "11.4.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.4.2.tgz", + "integrity": "sha512-BXJ7XPCTDXFF+wxcg/zscfgw2O/iDPtNSkwwR1W1W5c4Mb3zav/M2XvxQ23nVmKj7jpweB4g8viMeCQdm7LMVA==", + "license": "MIT", "dependencies": { - "@octokit/types": "^7.5.0" + "@octokit/types": "^13.7.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" }, "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": "5.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-5.3.1.tgz", + "integrity": "sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, "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": "13.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.3.1.tgz", + "integrity": "sha512-o8uOBdsyR+WR8MK9Cco8dCgvG13H1RlM1nWnK/W7TEACQBFux/vPREgKucxUfuDQ5yi1T3hGf4C5ZmZXAERgwQ==", + "license": "MIT", "dependencies": { - "@octokit/types": "^8.1.1", - "deprecation": "^2.3.1" + "@octokit/types": "^13.8.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" }, "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": "9.2.2", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.2.tgz", + "integrity": "sha512-dZl0ZHx6gOQGcffgm1/Sf6JfEpmh34v3Af2Uci02vzUYz6qEN6zepoRtmybWXIGXFIK8K9ylE3b+duCWqhArtg==", + "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": "^10.1.3", + "@octokit/request-error": "^6.1.7", + "@octokit/types": "^13.6.2", + "fast-content-type-parse": "^2.0.0", + "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "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": "6.1.7", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.7.tgz", + "integrity": "sha512-69NIppAwaauwZv6aOzb+VVLwt+0havz9GT5YplkeJv7fG7a40qpLt/yZKyiDxAhgz0EtgNdNcb96Z0u+Zyuy2g==", + "license": "MIT", "dependencies": { - "@octokit/types": "^9.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" + "@octokit/types": "^13.6.2" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "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": "21.1.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.1.1.tgz", + "integrity": "sha512-sTQV7va0IUVZcntzy1q3QqPm/r8rWtDCqpRAmb8eXXnKkjoQEtFe3Nt5GTVsHft+R6jJoHeSiVLcgcvhtue/rg==", + "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": "^6.1.4", + "@octokit/plugin-paginate-rest": "^11.4.2", + "@octokit/plugin-request-log": "^5.3.1", + "@octokit/plugin-rest-endpoint-methods": "^13.3.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "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": "13.8.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.8.0.tgz", + "integrity": "sha512-x7DjTIbEpEWXK99DMd01QfWy0hd5h4EN+Q7shkdKds3otGQP+oWE/y0A76i1OvH9fygo4ddvNf7ZvF0t78P98A==", + "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^18.0.0" + "@octokit/openapi-types": "^23.0.1" } }, "node_modules/@types/node": { @@ -200,60 +184,32 @@ } }, "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" - } + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", + "license": "Apache-2.0" + }, + "node_modules/fast-content-type-parse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", + "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" }, "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 - } - } - }, - "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/tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", @@ -269,28 +225,10 @@ "dev": true }, "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.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/configuration-editing/package.json b/extensions/configuration-editing/package.json index f6bfd751895..224ea33c02a 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -25,8 +25,8 @@ "watch": "gulp watch-extension:configuration-editing" }, "dependencies": { + "@octokit/rest": "^21.1.1", "jsonc-parser": "^3.2.0", - "@octokit/rest": "19.0.4", "tunnel": "^0.0.6" }, "capabilities": { @@ -56,7 +56,8 @@ "devcontainer.json", ".devcontainer.json" ] - }, { + }, + { "id": "json", "extensions": [ ".code-profile" diff --git a/extensions/configuration-editing/src/extensionsProposals.ts b/extensions/configuration-editing/src/extensionsProposals.ts index 6438281dc0b..ba1c33e7845 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 6135df5315a..12b50f31759 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 0bf8df9dc01..cb1fb733b99 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 58a7408dbbe..58ae5ece50a 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 d8698b46c09..60814ae02f4 100644 --- a/extensions/csharp/language-configuration.json +++ b/extensions/csharp/language-configuration.json @@ -1,32 +1,100 @@ { "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 + { + "beforeText": { + "pattern": "\/\/.*" + }, + "afterText": { + "pattern": "^(?!\\s*$).+" + }, + "action": { + "indent": "none", + "appendText": "// " + } + }, + ] } diff --git a/extensions/csharp/package.json b/extensions/csharp/package.json index c8af5a25f1f..d3d4f262d9f 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 4a2497a064a..1afcc3053b6 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/.npmrc b/extensions/css-language-features/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/css-language-features/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/css-language-features/client/src/dropOrPaste/dropOrPasteResource.ts b/extensions/css-language-features/client/src/dropOrPaste/dropOrPasteResource.ts index 6a4c38d2417..aa057878a01 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 17bf7e962a8..5284e093858 100644 --- a/extensions/css-language-features/client/tsconfig.json +++ b/extensions/css-language-features/client/tsconfig.json @@ -4,11 +4,11 @@ "outDir": "./out", "lib": [ "webworker" - ] + ], + "module": "Node16", }, "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 e13184cae57..a645c2ab11a 100644 --- a/extensions/css-language-features/package-lock.json +++ b/extensions/css-language-features/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "vscode-languageclient": "10.0.0-next.8", + "vscode-languageclient": "^10.0.0-next.13", "vscode-uri": "^3.0.8" }, "devDependencies": { @@ -87,39 +87,40 @@ "dev": true }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.4", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.4.tgz", - "integrity": "sha512-zSVIr58lJSMYKIsZ5P7GtBbv1eEx25eNyOf0NmEzxmn1GhUNJAVAb5hkA1poKUwj1FRMwN6CeyWxZypmr8SsQQ==", + "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==", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageclient": { - "version": "10.0.0-next.8", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.8.tgz", - "integrity": "sha512-D9inIHgqKayO9Tv0MeLb3XIL76yTuWmKdHqcGZKzjtQrMGJgASJDYWTapu+yAjEpDp0gmVOaCYyIlLB86ncDoQ==", + "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==", + "license": "MIT", "dependencies": { "minimatch": "^9.0.3", "semver": "^7.6.0", - "vscode-languageserver-protocol": "3.17.6-next.6" + "vscode-languageserver-protocol": "3.17.6-next.11" }, "engines": { - "vscode": "^1.89.0" + "vscode": "^1.91.0" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.6", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.6.tgz", - "integrity": "sha512-naxM9kc/phpl0kAFNVPejMUWUtzFXdPYY/BtQTYtfbBbHf8sceHOrKkmf6yynZRu1A4oFtRZNqV3wyFRTWqUHw==", + "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==", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.4", - "vscode-languageserver-types": "3.17.6-next.4" + "vscode-jsonrpc": "9.0.0-next.6", + "vscode-languageserver-types": "3.17.6-next.5" } }, "node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.4", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.4.tgz", - "integrity": "sha512-SeJTpH/S14EbxOAVaOUoGVqPToqpRTld5QO5Ghig3AlbFJTFF9Wu7srHMfa85L0SX1RYAuuCSFKJVVCxDIk1/Q==" + "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/vscode-uri": { "version": "3.0.8", diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index 802e2fbf113..0533881380c 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,7 +994,7 @@ ] }, "dependencies": { - "vscode-languageclient": "10.0.0-next.8", + "vscode-languageclient": "^10.0.0-next.13", "vscode-uri": "^3.0.8" }, "devDependencies": { diff --git a/extensions/css-language-features/package.nls.json b/extensions/css-language-features/package.nls.json index d6e25a57a43..057ec214bc2 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/.npmrc b/extensions/css-language-features/server/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/css-language-features/server/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/css-language-features/server/package-lock.json b/extensions/css-language-features/server/package-lock.json index 2f7cb932b0c..b9c936d84bf 100644 --- a/extensions/css-language-features/server/package-lock.json +++ b/extensions/css-language-features/server/package-lock.json @@ -10,8 +10,8 @@ "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", - "vscode-css-languageservice": "^6.3.1", - "vscode-languageserver": "10.0.0-next.6", + "vscode-css-languageservice": "^6.3.2", + "vscode-languageserver": "^10.0.0-next.11", "vscode-uri": "^3.0.8" }, "devDependencies": { @@ -49,9 +49,10 @@ "dev": true }, "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.2", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.2.tgz", + "integrity": "sha512-GEpPxrUTAeXWdZWHev1OJU9lz2Q2/PPBxQ2TIRmLGvQiH3WZbqaNoute0n0ewxlgtjzTW3AKZT+NHySk5Rf4Eg==", + "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", @@ -60,37 +61,38 @@ } }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.4", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.4.tgz", - "integrity": "sha512-zSVIr58lJSMYKIsZ5P7GtBbv1eEx25eNyOf0NmEzxmn1GhUNJAVAb5hkA1poKUwj1FRMwN6CeyWxZypmr8SsQQ==", + "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==", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageserver": { - "version": "10.0.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.6.tgz", - "integrity": "sha512-0Lh1nhQfSxo5Ob+ayYO1QTIsDix2/Lc72Urm1KZrCFxK5zIFYaEh3QFeM9oZih4Rzs0ZkQPXXnoHtpvs5GT+Zw==", + "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==", + "license": "MIT", "dependencies": { - "vscode-languageserver-protocol": "3.17.6-next.6" + "vscode-languageserver-protocol": "3.17.6-next.11" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.6", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.6.tgz", - "integrity": "sha512-naxM9kc/phpl0kAFNVPejMUWUtzFXdPYY/BtQTYtfbBbHf8sceHOrKkmf6yynZRu1A4oFtRZNqV3wyFRTWqUHw==", + "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==", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.4", - "vscode-languageserver-types": "3.17.6-next.4" + "vscode-jsonrpc": "9.0.0-next.6", + "vscode-languageserver-types": "3.17.6-next.5" } }, "node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.4", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.4.tgz", - "integrity": "sha512-SeJTpH/S14EbxOAVaOUoGVqPToqpRTld5QO5Ghig3AlbFJTFF9Wu7srHMfa85L0SX1RYAuuCSFKJVVCxDIk1/Q==" + "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/vscode-languageserver-textdocument": { "version": "1.0.12", diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 4d8b9bd41ac..9b6a8b47d6b 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -11,8 +11,8 @@ "browser": "./dist/browser/cssServerMain", "dependencies": { "@vscode/l10n": "^0.0.18", - "vscode-css-languageservice": "^6.3.1", - "vscode-languageserver": "10.0.0-next.6", + "vscode-css-languageservice": "^6.3.2", + "vscode-languageserver": "^10.0.0-next.11", "vscode-uri": "^3.0.8" }, "devDependencies": { diff --git a/extensions/css-language-features/server/src/browser/cssServerWorkerMain.ts b/extensions/css-language-features/server/src/browser/cssServerWorkerMain.ts index bc645de8170..4f2ae207c16 100644 --- a/extensions/css-language-features/server/src/browser/cssServerWorkerMain.ts +++ b/extensions/css-language-features/server/src/browser/cssServerWorkerMain.ts @@ -22,7 +22,7 @@ const messageHandler = async (e: any) => { } else { l10nLog.push(`l10n: No bundle configured.`); } - await import('./cssServerMain'); + await import('./cssServerMain.js'); if (self.onmessage !== messageHandler) { pendingMessages.forEach(msg => self.onmessage?.(msg)); pendingMessages.length = 0; diff --git a/extensions/css-language-features/server/src/node/cssServerNodeMain.ts b/extensions/css-language-features/server/src/node/cssServerNodeMain.ts index 3d566c754ed..67d8439fd9b 100644 --- a/extensions/css-language-features/server/src/node/cssServerNodeMain.ts +++ b/extensions/css-language-features/server/src/node/cssServerNodeMain.ts @@ -15,7 +15,7 @@ async function setupMain() { l10nLog.push(`l10n: Problems loading ${i10lLocation.toString()} : ${e}`); } } - await import('./cssServerMain'); + await import('./cssServerMain.js'); l10nLog.forEach(console.log); } setupMain(); diff --git a/extensions/css-language-features/server/tsconfig.json b/extensions/css-language-features/server/tsconfig.json index ae02026a740..e21408bda97 100644 --- a/extensions/css-language-features/server/tsconfig.json +++ b/extensions/css-language-features/server/tsconfig.json @@ -5,7 +5,8 @@ "lib": [ "ES2020", "WebWorker" - ] + ], + "module": "Node16", }, "include": [ "src/**/*" diff --git a/extensions/css/cgmanifest.json b/extensions/css/cgmanifest.json index 0705aca66c4..7b85089b6b9 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 2096e16e920..5ba8bc90b73 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) (? { }); // 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/.npmrc b/extensions/extension-editing/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/extension-editing/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/extension-editing/src/extensionLinter.ts b/extensions/extension-editing/src/extensionLinter.ts index b69dac0e2dd..be7eea1a49b 100644 --- a/extensions/extension-editing/src/extensionLinter.ts +++ b/extensions/extension-editing/src/extensionLinter.ts @@ -290,7 +290,7 @@ export class ExtensionLinter { const text = document.getText(); if (!this.markdownIt) { - this.markdownIt = new (await import('markdown-it')); + this.markdownIt = new ((await import('markdown-it')).default); } const tokens = this.markdownIt.parse(text, {}); const tokensAndPositions: TokenAndPosition[] = (function toTokensAndPositions(this: ExtensionLinter, tokens: MarkdownItType.Token[], begin = 0, end = text.length): TokenAndPosition[] { diff --git a/extensions/fsharp/cgmanifest.json b/extensions/fsharp/cgmanifest.json index ba75f69541e..75e24896d8e 100644 --- a/extensions/fsharp/cgmanifest.json +++ b/extensions/fsharp/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "ionide/ionide-fsgrammar", "repositoryUrl": "https://github.com/ionide/ionide-fsgrammar", - "commitHash": "b38420f8569aa662b4862beb407a02e527e866c1" + "commitHash": "c62c78404d0b2c14816aae61ac0688663a5990a3" } }, "license": "MIT", diff --git a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json index 9594d1b6ce8..8ba555b7772 100644 --- a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json +++ b/extensions/fsharp/syntaxes/fsharp.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/ionide/ionide-fsgrammar/commit/b38420f8569aa662b4862beb407a02e527e866c1", + "version": "https://github.com/ionide/ionide-fsgrammar/commit/c62c78404d0b2c14816aae61ac0688663a5990a3", "name": "fsharp", "scopeName": "source.fsharp", "patterns": [ @@ -635,7 +635,7 @@ }, "abstract_definition": { "name": "abstract.definition.fsharp", - "begin": "\\b(static)?\\s+(abstract)\\s+(member)?(\\s+\\[\\<.*\\>\\])?\\s*([_[:alpha:]0-9,\\._`\\s]+)(<)?", + "begin": "\\b(static\\s+)?(abstract)\\s+(member)?(\\s+\\[\\<.*\\>\\])?\\s*([_[:alpha:]0-9,\\._`\\s]+)(<)?", "end": "\\s*(with)\\b|=|$", "beginCaptures": { "1": { diff --git a/extensions/git-base/.npmrc b/extensions/git-base/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/git-base/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/git-base/src/api/git-base.d.ts b/extensions/git-base/src/api/git-base.d.ts index 53cac4d5c70..d4ec49df47d 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/.npmrc b/extensions/git/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/git/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/git/package-lock.json b/extensions/git/package-lock.json index ef76d2719da..7a9cb2aded8 100644 --- a/extensions/git/package-lock.json +++ b/extensions/git/package-lock.json @@ -9,12 +9,11 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@joaomoreno/unique-names-generator": "^5.1.0", - "@vscode/extension-telemetry": "^0.9.0", + "@joaomoreno/unique-names-generator": "^5.2.0", + "@vscode/extension-telemetry": "^0.9.8", "@vscode/iconv-lite-umd": "0.7.0", "byline": "^5.0.0", "file-type": "16.5.4", - "jschardet": "3.1.3", "picomatch": "2.3.1", "vscode-uri": "^2.0.0", "which": "4.0.0" @@ -31,126 +30,137 @@ } }, "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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "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.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@tokenizer/token": { "version": "0.3.0", @@ -194,13 +204,14 @@ "dev": true }, "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": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "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.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" @@ -267,14 +278,6 @@ "node": ">=16" } }, - "node_modules/jschardet": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-3.1.3.tgz", - "integrity": "sha512-Q1PKVMK/uu+yjdlobgWIYkUOCR1SqUmW9m/eUJNNj4zI2N12i25v8fYpVf+zCakQeaTdBdhnZTFbVIAVZIVVOg==", - "engines": { - "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", diff --git a/extensions/git/package.json b/extensions/git/package.json index 57e332a8d78..23feced6ea1 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -32,8 +32,11 @@ "scmSelectedProvider", "scmTextDocument", "scmValidation", + "statusBarItemTooltip", "tabInputMultiDiff", "tabInputTextMerge", + "textDocumentEncoding", + "textEditorDiffInformation", "timeline" ], "categories": [ @@ -446,6 +449,12 @@ "category": "Git", "enablement": "!operationInProgress" }, + { + "command": "git.graph.checkout", + "title": "%command.graphCheckout%", + "category": "Git", + "enablement": "!operationInProgress" + }, { "command": "git.checkoutDetached", "title": "%command.checkoutDetached%", @@ -453,8 +462,8 @@ "enablement": "!operationInProgress" }, { - "command": "git.checkoutRefDetached", - "title": "%command.checkoutRefDetached%", + "command": "git.graph.checkoutDetached", + "title": "%command.graphCheckoutDetached%", "category": "Git", "enablement": "!operationInProgress" }, @@ -476,6 +485,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 +533,12 @@ "category": "Git", "enablement": "!operationInProgress" }, + { + "command": "git.graph.deleteTag", + "title": "%command.graphDeleteTag%", + "category": "Git", + "enablement": "!operationInProgress" + }, { "command": "git.deleteRemoteTag", "title": "%command.deleteRemoteTag%", @@ -567,7 +594,7 @@ "title": "%command.pull%", "icon": "$(repo-pull)", "category": "Git", - "enablement": "!operationInProgress" + "enablement": "!operationInProgress && scmCurrentHistoryItemRefInFilter && scmCurrentHistoryItemRefHasRemote" }, { "command": "git.push", @@ -616,7 +643,7 @@ "title": "%command.push%", "icon": "$(repo-push)", "category": "Git", - "enablement": "!operationInProgress" + "enablement": "!operationInProgress && scmCurrentHistoryItemRefInFilter && scmCurrentHistoryItemRefHasRemote" }, { "command": "git.cherryPick", @@ -625,8 +652,14 @@ "enablement": "!operationInProgress" }, { - "command": "git.cherryPickRef", - "title": "%command.cherryPickRef%", + "command": "git.graph.cherryPick", + "title": "%command.graphCherryPick%", + "category": "Git", + "enablement": "!operationInProgress" + }, + { + "command": "git.cherryPickAbort", + "title": "%command.cherryPickAbort%", "category": "Git", "enablement": "!operationInProgress" }, @@ -899,13 +932,6 @@ "category": "Git", "enablement": "!operationInProgress" }, - { - "command": "git.viewAllChanges", - "title": "%command.viewAllChanges%", - "icon": "$(diff-multiple)", - "category": "Git", - "enablement": "!operationInProgress" - }, { "command": "git.copyCommitId", "title": "%command.timelineCopyCommitId%", @@ -915,6 +941,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": [ @@ -930,19 +966,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": { @@ -1013,7 +1049,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", @@ -1021,7 +1057,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", @@ -1041,7 +1077,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", @@ -1195,6 +1231,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" @@ -1203,6 +1243,10 @@ "command": "git.cherryPick", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" }, + { + "command": "git.cherryPickAbort", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && gitCherryPickInProgress" + }, { "command": "git.pull", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" @@ -1427,10 +1471,6 @@ "command": "git.viewCommit", "when": "false" }, - { - "command": "git.viewAllChanges", - "when": "false" - }, { "command": "git.stageFile", "when": "false" @@ -1460,12 +1500,32 @@ "when": "false" }, { - "command": "git.checkoutRefDetached", + "command": "git.graph.checkout", + "when": "false" + }, + { + "command": "git.graph.checkoutDetached", "when": "false" }, { - "command": "git.cherryPickRef", + "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": [ @@ -1958,30 +2018,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", @@ -1994,6 +2069,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", @@ -2007,7 +2099,7 @@ }, { "command": "git.openChange", - "group": "navigation", + "group": "navigation@2", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && !isInDiffEditor && !isMergeEditor && resourceScheme == file && scmActiveResourceHasChanges" }, { @@ -2021,47 +2113,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": [ @@ -2336,6 +2448,10 @@ "command": "git.deleteBranch", "group": "3_modify@2" }, + { + "command": "git.deleteRemoteBranch", + "group": "3_modify@3" + }, { "command": "git.publish", "group": "4_publish@1" @@ -3154,6 +3270,63 @@ "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%" } } }, @@ -3257,6 +3430,16 @@ "highContrast": "#8db9e2", "highContrastLight": "#1258a7" } + }, + { + "id": "git.blame.editorDecorationForeground", + "description": "%colors.blameEditorDecoration%", + "defaults": { + "dark": "editorCodeLens.foreground", + "light": "editorCodeLens.foreground", + "highContrast": "editorCodeLens.foreground", + "highContrastLight": "editorCodeLens.foreground" + } } ], "configurationDefaults": { @@ -3281,22 +3464,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", @@ -3379,12 +3562,11 @@ ] }, "dependencies": { - "@joaomoreno/unique-names-generator": "^5.1.0", - "@vscode/extension-telemetry": "^0.9.0", + "@joaomoreno/unique-names-generator": "^5.2.0", + "@vscode/extension-telemetry": "^0.9.8", "@vscode/iconv-lite-umd": "0.7.0", "byline": "^5.0.0", "file-type": "16.5.4", - "jschardet": "3.1.3", "picomatch": "2.3.1", "vscode-uri": "^2.0.0", "which": "4.0.0" diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index bad361bcea7..101618a8e45 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -62,13 +62,13 @@ "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", "command.rebase": "Rebase Branch...", @@ -120,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", @@ -275,6 +281,14 @@ "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.", "submenu.explorer": "Git", "submenu.commit": "Commit", "submenu.commit.amend": "Amend", @@ -299,10 +313,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" ] @@ -311,6 +328,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" ] @@ -319,11 +338,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": [ @@ -333,7 +360,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 494972276ac..63eefb1de02 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 []; @@ -211,12 +244,12 @@ export class ActionButton { command: { command: 'git.sync', title: l10n.t('{0} Sync Changes{1}{2}', icon, behind, ahead), + shortTitle: `${icon}${behind}${ahead}`, tooltip: this.state.isSyncInProgress ? l10n.t('Synchronizing Changes...') : this.repository.syncTooltip, arguments: [this.repository.sourceControl], }, - description: `${icon}${behind}${ahead}`, enabled: !this.state.isCheckoutInProgress && !this.state.isSyncInProgress }; } @@ -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 63139af2447..d715c535a50 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 3c5aad1c4a0..5bbb4b03a9c 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 { @@ -200,10 +200,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 +268,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 +327,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 +371,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 { @@ -409,5 +433,7 @@ export const enum GitErrorCodes { EmptyCommitMessage = 'EmptyCommitMessage', BranchFastForwardRejected = 'BranchFastForwardRejected', BranchNotYetBorn = 'BranchNotYetBorn', - TagConflict = 'TagConflict' + TagConflict = 'TagConflict', + CherryPickEmpty = 'CherryPickEmpty', + CherryPickConflict = 'CherryPickConflict' } diff --git a/extensions/git/src/askpass-empty.sh b/extensions/git/src/askpass-empty.sh old mode 100644 new mode 100755 diff --git a/extensions/git/src/askpass.sh b/extensions/git/src/askpass.sh old mode 100644 new mode 100755 diff --git a/extensions/git/src/blame.ts b/extensions/git/src/blame.ts new file mode 100644 index 00000000000..f29212c51a8 --- /dev/null +++ b/extensions/git/src/blame.ts @@ -0,0 +1,727 @@ +/*--------------------------------------------------------------------------------------------- + * 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); +} + +type BlameInformationTemplateTokens = { + readonly hash: string; + readonly hashShort: string; + readonly subject: string; + readonly authorName: string; + readonly authorEmail: string; + readonly authorDate: string; + readonly authorDateAgo: string; +}; + +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: LineBlameInformation[] | undefined; + get textEditorBlameInformation(): readonly LineBlameInformation[] | undefined { + return this._textEditorBlameInformation; + } + private set textEditorBlameInformation(blameInformation: LineBlameInformation[] | undefined) { + 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, true), 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, showBlameInformationForPositionZero = false): 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 (!showBlameInformationForPositionZero && 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 + if (diffInformationWorkingTreeAndIndex && diffInformationWorkingTreeAndIndex.isStale) { + 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 + if (!diffInformationWorkingTree || diffInformationWorkingTree.isStale) { + return; + } + + // Working tree + index diff information + const diffInformationWorkingTreeAndIndex = getWorkingTreeAndIndexDiffInformation(textEditor); + + // Working tree + index diff information is present and it is stale + if (diffInformationWorkingTreeAndIndex && diffInformationWorkingTreeAndIndex.isStale) { + 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)) { + 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 = 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; + 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; + if (!blameInformation) { + 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; + 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 00000000000..df0c0df5561 --- /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 38c1db541bb..fa566adbead 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, 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, 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, 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, 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; @@ -155,8 +155,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 +181,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); } } } @@ -309,7 +312,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)); @@ -614,6 +617,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[]; @@ -1341,6 +1425,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,30 +1613,44 @@ 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; + let modifiedDocument: TextDocument | undefined; + if (!modifiedUri) { const textEditor = window.activeTextEditor; if (!textEditor) { return; } - const modifiedDocument = textEditor.document; + + modifiedDocument = textEditor.document; modifiedUri = modifiedDocument.uri; } + if (modifiedUri.scheme !== 'file') { return; } + + if (!modifiedDocument) { + modifiedDocument = await workspace.openTextDocument(modifiedUri); + } + const result = changes.originalWithModifiedChanges; - await this.runByRepository(modifiedUri, async (repository, resource) => await repository.stage(resource, result)); + await this.runByRepository(modifiedUri, async (repository, resource) => + await repository.stage(resource, result, modifiedDocument.encoding)); } @command('git.stageSelectedRanges', { diff: true }) @@ -1559,12 +1661,25 @@ export class CommandCenter { return; } + const workingTreeDiffInformation = getWorkingTreeDiffInformation(textEditor); + if (!workingTreeDiffInformation) { + return; + } + + const workingTreeLineChanges = toLineChanges(workingTreeDiffInformation); + + this.logger.trace(`[CommandCenter][stageSelectedChanges] changes: ${JSON.stringify(changes)}`); + 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; @@ -1712,7 +1827,8 @@ export class CommandCenter { const originalDocument = await workspace.openTextDocument(originalUri); const result = applyLineChanges(originalDocument, modifiedDocument, changes); - await this.runByRepository(modifiedUri, async (repository, resource) => await repository.stage(resource, result)); + await this.runByRepository(modifiedUri, async (repository, resource) => + await repository.stage(resource, result, modifiedDocument.encoding)); } @command('git.revertChange') @@ -1741,18 +1857,31 @@ export class CommandCenter { return; } + const workingTreeDiffInformation = getWorkingTreeDiffInformation(textEditor); + if (!workingTreeDiffInformation) { + return; + } + + const workingTreeLineChanges = toLineChanges(workingTreeDiffInformation); + + this.logger.trace(`[CommandCenter][revertSelectedRanges] changes: ${JSON.stringify(changes)}`); + 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; @@ -1811,7 +1940,7 @@ export class CommandCenter { } @command('git.unstageSelectedRanges', { diff: true }) - async unstageSelectedRanges(diffs: LineChange[]): Promise { + async unstageSelectedRanges(changes: LineChange[]): Promise { const textEditor = window.activeTextEditor; if (!textEditor) { @@ -1831,12 +1960,34 @@ 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] changes: ${JSON.stringify(changes)}`); + 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 +1995,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, modifiedDocument.encoding); } @command('git.unstageFile') @@ -1906,49 +2060,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 +2101,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 +2127,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); - - if (resources.length === 0) { - return; - } - - await this._cleanTrackedChanges(repository, resources); - } - - @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); + 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); - if (resources.length === 1) { - await this._cleanUntrackedChange(repository, resources[0]); - } else { - await this._cleanUntrackedChanges(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); - 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 +2327,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 +2376,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 +2658,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; } @@ -2603,7 +2790,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) { @@ -2807,19 +2994,87 @@ 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); + + 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 }); + + const refsToExclude: string[] = []; + if (options.remote) { + refsToExclude.push('origin/HEAD'); + + 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 !== currentHead).map(ref => new BranchDeleteItem(ref)); + 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 +3085,7 @@ export class CommandCenter { } try { - await run(force); + await run(options.force); } catch (err) { if (err.gitErrorCode !== GitErrorCodes.BranchNotFullyMerged) { throw err; @@ -2946,12 +3201,21 @@ export class CommandCenter { 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,14 +3574,20 @@ 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); } + @command('git.cherryPickAbort', { repository: true }) + async cherryPickAbort(repository: Repository): Promise { + await repository.cherryPickAbort(); + } + @command('git.pushTo', { repository: true }) async pushTo(repository: Repository, remote?: string, refspec?: string, setUpstream?: boolean): Promise { await this._push(repository, { pushType: PushType.PushTo, pushTo: { remote: remote, refspec: refspec, setUpstream: setUpstream } }); @@ -3969,16 +4239,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 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 title = `${item.shortRef} - ${commit.message}`; - const multiDiffSourceUri = toGitUri(Uri.file(repository.root), commit.hash, { scheme: 'git-commit' }); - - 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', @@ -4213,77 +4479,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 multiDiffSourceUri = toGitUri(Uri.file(repository.root), historyItem.id, { scheme: 'git-changes' }); + 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(); - 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 { @@ -4372,15 +4628,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: { @@ -4403,6 +4661,18 @@ export class CommandCenter { type = 'information'; options.modal = false; break; + case GitErrorCodes.CherryPickEmpty: + message = l10n.t('The changes are already present in the current branch.'); + choices.clear(); + type = 'information'; + options.modal = false; + break; + case GitErrorCodes.CherryPickConflict: + message = l10n.t('There were merge conflicts while cherry picking the changes. Resolve the conflicts before committing them.'); + type = 'warning'; + choices.set(l10n.t('Show Changes'), () => commands.executeCommand('workbench.view.scm')); + options.modal = false; + break; default: { const hint = (err.stderr || err.message || String(err)) .replace(/^error: /mi, '') diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index 548f3aa2a1c..2e72a1e4114 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/encoding.ts b/extensions/git/src/encoding.ts deleted file mode 100644 index c80fb6ee6d5..00000000000 --- a/extensions/git/src/encoding.ts +++ /dev/null @@ -1,100 +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 jschardet from 'jschardet'; - -function detectEncodingByBOM(buffer: Buffer): string | null { - if (!buffer || buffer.length < 2) { - return null; - } - - const b0 = buffer.readUInt8(0); - const b1 = buffer.readUInt8(1); - - // UTF-16 BE - if (b0 === 0xFE && b1 === 0xFF) { - return 'utf16be'; - } - - // UTF-16 LE - if (b0 === 0xFF && b1 === 0xFE) { - return 'utf16le'; - } - - if (buffer.length < 3) { - return null; - } - - const b2 = buffer.readUInt8(2); - - // UTF-8 - if (b0 === 0xEF && b1 === 0xBB && b2 === 0xBF) { - return 'utf8'; - } - - return null; -} - -const IGNORE_ENCODINGS = [ - 'ascii', - 'utf-8', - 'utf-16', - 'utf-32' -]; - -const JSCHARDET_TO_ICONV_ENCODINGS: { [name: string]: string } = { - 'ibm866': 'cp866', - 'big5': 'cp950' -}; - -const MAP_CANDIDATE_GUESS_ENCODING_TO_JSCHARDET: { [key: string]: string } = { - utf8: 'UTF-8', - utf16le: 'UTF-16LE', - utf16be: 'UTF-16BE', - windows1252: 'windows-1252', - windows1250: 'windows-1250', - iso88592: 'ISO-8859-2', - windows1251: 'windows-1251', - cp866: 'IBM866', - iso88595: 'ISO-8859-5', - koi8r: 'KOI8-R', - windows1253: 'windows-1253', - iso88597: 'ISO-8859-7', - windows1255: 'windows-1255', - iso88598: 'ISO-8859-8', - cp950: 'Big5', - shiftjis: 'SHIFT_JIS', - eucjp: 'EUC-JP', - euckr: 'EUC-KR', - gb2312: 'GB2312' -}; - -export function detectEncoding(buffer: Buffer, candidateGuessEncodings: string[]): string | null { - const result = detectEncodingByBOM(buffer); - - if (result) { - return result; - } - - candidateGuessEncodings = candidateGuessEncodings.map(e => MAP_CANDIDATE_GUESS_ENCODING_TO_JSCHARDET[e]).filter(e => !!e); - - const detected = jschardet.detect(buffer, candidateGuessEncodings.length > 0 ? { detectEncodings: candidateGuessEncodings } : undefined); - if (!detected || !detected.encoding) { - return null; - } - - const encoding = detected.encoding; - - // Ignore encodings that cannot guess correctly - // (http://chardet.readthedocs.io/en/latest/supported-encodings.html) - if (0 <= IGNORE_ENCODINGS.indexOf(encoding.toLowerCase())) { - return null; - } - - const normalizedEncodingName = encoding.replace(/[^a-zA-Z0-9]/g, '').toLowerCase(); - const mapped = JSCHARDET_TO_ICONV_ENCODINGS[normalizedEncodingName]; - - return mapped || normalizedEncodingName; -} diff --git a/extensions/git/src/fileSystemProvider.ts b/extensions/git/src/fileSystemProvider.ts index af80924ae13..19928863832 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 6cef535cfa5..437a126c9f3 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-editor-empty.sh b/extensions/git/src/git-editor-empty.sh old mode 100644 new mode 100755 diff --git a/extensions/git/src/git-editor.sh b/extensions/git/src/git-editor.sh old mode 100644 new mode 100755 diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index dea09733420..29fc38abfed 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -8,13 +8,12 @@ import * as path from 'path'; import * as os from 'os'; import * as cp from 'child_process'; import { fileURLToPath } from 'url'; -import * as which from 'which'; +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 { 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 * as byline from 'byline'; import { StringDecoder } from 'string_decoder'; @@ -62,6 +61,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 +315,7 @@ export interface IGitOptions { gitPath: string; userAgent: string; version: string; - env?: any; + env?: { [key: string]: string }; } function getGitErrorCode(stderr: string): string | undefined { @@ -369,7 +369,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(); @@ -484,6 +485,17 @@ export class Git { // Keep trailing spaces which are part of the directory name const repositoryRootPath = path.normalize(result.stdout.trimStart().replace(/[\r\n]+$/, '')); + // Handle symbolic links and UNC paths + // Git 2.31 added the `--path-format` flag to rev-parse which + // allows us to get the relative path of the repository root + if (!pathEquals(pathInsidePossibleRepository, repositoryRootPath) && + !isDescendant(repositoryRootPath, pathInsidePossibleRepository) && + !isDescendant(pathInsidePossibleRepository, repositoryRootPath) && + this.compareGitVersionTo('2.31.0') !== -1) { + const relativePathResult = await this.exec(pathInsidePossibleRepository, ['rev-parse', '--path-format=relative', '--show-toplevel',]); + return path.resolve(pathInsidePossibleRepository, relativePathResult.stdout.trimStart().replace(/[\r\n]+$/, '')); + } + if (isWindows) { // On Git 2.25+ if you call `rev-parse --show-toplevel` on a mapped drive, instead of getting the mapped // drive path back, you get the UNC path for the mapped drive. So we will try to normalize it back to the @@ -491,7 +503,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; @@ -520,17 +532,6 @@ export class Git { } } - // Handle symbolic links - // Git 2.31 added the `--path-format` flag to rev-parse which - // allows us to get the relative path of the repository root - if (!pathEquals(pathInsidePossibleRepository, repositoryRootPath) && - !isDescendant(repositoryRootPath, pathInsidePossibleRepository) && - !isDescendant(pathInsidePossibleRepository, repositoryRootPath) && - this.compareGitVersionTo('2.31.0') !== -1) { - const relativePathResult = await this.exec(pathInsidePossibleRepository, ['rev-parse', '--path-format=relative', '--show-toplevel',]); - return path.resolve(pathInsidePossibleRepository, relativePathResult.stdout.trimStart().replace(/[\r\n]+$/, '')); - } - return repositoryRootPath; } @@ -1054,9 +1055,80 @@ 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()); +} + export interface PullOptions { - unshallow?: boolean; - tags?: boolean; + readonly unshallow?: boolean; + readonly tags?: boolean; + readonly autoStash?: boolean; readonly cancellationToken?: CancellationToken; } @@ -1095,11 +1167,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 +1286,10 @@ export class Repository { } } + if (options?.shortStats) { + args.push('--shortstat'); + } + if (options?.sortByAuthorDate) { args.push('--author-date-order'); } @@ -1253,18 +1329,6 @@ export class Repository { .filter(entry => !!entry); } - async bufferString(object: string, encoding: string = 'utf8', autoGuessEncoding = false, candidateGuessEncodings: string[] = []): Promise { - const stdout = await this.buffer(object); - - if (autoGuessEncoding) { - encoding = detectEncoding(stdout, candidateGuessEncodings) || encoding; - } - - encoding = iconv.encodingExists(encoding) ? encoding : 'utf8'; - - return iconv.decode(stdout, encoding); - } - async buffer(object: string): Promise { const child = this.stream(['show', '--textconv', object]); @@ -1291,14 +1355,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,7 +1379,7 @@ 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 { @@ -1516,8 +1583,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); @@ -1587,9 +1660,9 @@ export class Repository { await this.exec(args); } - async stage(path: string, data: string): Promise { + async stage(path: string, data: string, encoding: string): Promise { const child = this.stream(['hash-object', '--stdin', '-w', '--path', sanitizePath(path)], { stdio: [null, null, null] }); - child.stdin!.end(data, 'utf8'); + child.stdin!.end(iconv.encode(data, encoding)); const { exitCode, stdout } = await exec(child); const hash = stdout.toString('utf8'); @@ -1831,8 +1904,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); } @@ -1992,6 +2071,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'); } @@ -2102,8 +2186,25 @@ export class Repository { } async cherryPick(commitHash: string): Promise { - const args = ['cherry-pick', commitHash]; - await this.exec(args); + try { + await this.exec(['cherry-pick', commitHash]); + } catch (err) { + if (/The previous cherry-pick is now empty, possibly due to conflict resolution./.test(err.stderr ?? '')) { + // Abort cherry-pick + await this.cherryPickAbort(); + + err.gitErrorCode = GitErrorCodes.CherryPickEmpty; + } else { + // Conflict during cherry-pick + err.gitErrorCode = GitErrorCodes.CherryPickConflict; + } + + throw err; + } + } + + async cherryPickAbort(): Promise { + await this.exec(['cherry-pick', '--abort']); } async blame(path: string): Promise { @@ -2120,6 +2221,25 @@ export class Repository { } } + async blame2(path: string, ref?: string): Promise { + try { + const args = ['blame', '--root', '--incremental']; + + if (ref) { + args.push(ref); + } + + args.push('--', sanitizePath(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']; @@ -2323,7 +2443,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 } }; } @@ -2410,7 +2532,7 @@ export class Repository { return result.stdout.trim().split('\n') .map(line => line.trim().split('\0')) .filter(([_, upstream]) => upstream === upstreamBranch) - .map(([ref]) => ({ name: ref, type: RefType.Head } as Branch)); + .map(([ref]): Branch => ({ name: ref, type: RefType.Head })); } async getRefs(query: RefQuery, cancellationToken?: CancellationToken): Promise { @@ -2614,7 +2736,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, @@ -2657,8 +2779,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'); } @@ -2726,6 +2848,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'); diff --git a/extensions/git/src/gitEditor.ts b/extensions/git/src/gitEditor.ts index f5701642084..6291e5152a7 100644 --- a/extensions/git/src/gitEditor.ts +++ b/extensions/git/src/gitEditor.ts @@ -3,10 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as path from 'path'; -import { TabInputText, Uri, window, workspace } from 'vscode'; +import { CancellationToken, DocumentLink, DocumentLinkProvider, l10n, Range, TabInputText, TextDocument, Uri, window, workspace } from 'vscode'; import { IIPCHandler, IIPCServer } from './ipc/ipcServer'; import { ITerminalEnvironmentProvider } from './terminal'; import { EmptyDisposable, IDisposable } from './util'; +import { Model } from './model'; +import { Repository } from './repository'; interface GitEditorRequest { commitMessagePath?: string; @@ -63,3 +65,51 @@ export class GitEditor implements IIPCHandler, ITerminalEnvironmentProvider { this.disposable.dispose(); } } + +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) { } + + provideDocumentLinks(document: TextDocument, token: CancellationToken): DocumentLink[] { + if (token.isCancellationRequested) { + return []; + } + + const repository = this._model.getRepository(document.uri); + if (!repository) { + return []; + } + + const links: DocumentLink[] = []; + for (const match of document.getText().matchAll(this._regex)) { + if (!match.groups) { + continue; + } + + const { file1, file2 } = match.groups; + + if (file1) { + links.push(this._createDocumentLink(repository, document, match, file1)); + } + if (file2) { + links.push(this._createDocumentLink(repository, document, match, file2)); + } + } + + return links; + } + + private _createDocumentLink(repository: Repository, document: TextDocument, match: RegExpExecArray, file: string): DocumentLink { + const startIndex = match[0].indexOf(file); + const startPosition = document.positionAt(match.index + startIndex); + const endPosition = document.positionAt(match.index + startIndex + file.length); + + const documentLink = new DocumentLink( + new Range(startPosition, endPosition), + Uri.file(path.join(repository.root, file))); + documentLink.tooltip = l10n.t('Open File'); + + return documentLink; + } +} diff --git a/extensions/git/src/historyItemDetailsProvider.ts b/extensions/git/src/historyItemDetailsProvider.ts new file mode 100644 index 00000000000..be0e2b337f8 --- /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 e652ca6dd20..db316c81d0b 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') @@ -45,6 +48,29 @@ function toSourceControlHistoryItemRef(ref: Ref): SourceControlHistoryItemRef { } } +function compareSourceControlHistoryItemRef(ref1: SourceControlHistoryItemRef, ref2: SourceControlHistoryItemRef): number { + const getOrder = (ref: SourceControlHistoryItemRef): number => { + if (ref.id.startsWith('refs/heads/')) { + return 1; + } else if (ref.id.startsWith('refs/remotes/')) { + return 2; + } else if (ref.id.startsWith('refs/tags/')) { + return 3; + } + + return 99; + }; + + const ref1Order = getOrder(ref1); + const ref2Order = getOrder(ref2); + + if (ref1Order !== ref2Order) { + return ref1Order - ref2Order; + } + + return ref1.name.localeCompare(ref2.name); +} + export class GitHistoryProvider implements SourceControlHistoryProvider, FileDecorationProvider, IDisposable { private readonly _onDidChangeDecorations = new EventEmitter(); readonly onDidChangeFileDecorations: Event = this._onDidChangeDecorations.event; @@ -65,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)); @@ -87,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 = ''; @@ -98,15 +136,31 @@ 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 && @@ -117,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 @@ -153,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), @@ -184,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; } } @@ -225,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 []; @@ -263,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); @@ -325,28 +408,26 @@ 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; - case ref.startsWith('tag: refs/tags/'): - references.push({ - id: ref.substring('tag: '.length), - name: ref.substring('tag: refs/tags/'.length), - revision: commit.hash, - icon: new ThemeIcon('tag') - }); - break; case ref.startsWith('refs/heads/'): references.push({ id: ref, name: ref.substring('refs/heads/'.length), revision: commit.hash, + category: l10n.t('branches'), icon: new ThemeIcon('git-branch') }); break; @@ -355,13 +436,23 @@ 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; + case ref.startsWith('tag: refs/tags/'): + references.push({ + id: ref.substring('tag: '.length), + name: ref.substring('tag: refs/tags/'.length), + revision: commit.hash, + category: l10n.t('tags'), + icon: new ThemeIcon('tag') + }); + break; } } - return references; + return references.sort(compareSourceControlHistoryItemRef); } private async resolveHEADMergeBase(): Promise { diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index aa4d98adc8b..8205d8de69f 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { env, ExtensionContext, workspace, window, Disposable, commands, Uri, version as vscodeVersion, WorkspaceFolder, LogOutputChannel, l10n, LogLevel } from 'vscode'; +import { env, ExtensionContext, workspace, window, Disposable, commands, Uri, version as vscodeVersion, WorkspaceFolder, LogOutputChannel, l10n, LogLevel, languages } from 'vscode'; import { findGit, Git, IGit } from './git'; import { Model } from './model'; import { CommandCenter } from './commands'; @@ -22,10 +22,12 @@ import { GitTimelineProvider } from './timelineProvider'; import { registerAPICommands } from './api/api1'; import { TerminalEnvironmentManager, TerminalShellExecutionManager } from './terminal'; import { createIPCServer, IPCServer } from './ipc/ipcServer'; -import { GitEditor } from './gitEditor'; +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); @@ -126,6 +130,9 @@ async function createModel(context: ExtensionContext, logger: LogOutputChannel, const codeActionsProvider = new GitCommitInputBoxCodeActionsProvider(diagnosticsManager); disposables.push(codeActionsProvider); + const gitEditorDocumentLinkProvider = languages.registerDocumentLinkProvider('git-commit', new GitEditorDocumentLinkProvider(model)); + disposables.push(gitEditorDocumentLinkProvider); + checkGitVersion(info); commands.executeCommand('setContext', 'gitVersion2.35', git.compareGitVersionTo('2.35') >= 0); diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 48e66d5e9ff..d17d118de1c 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 d960cedfdbb..d8b2773f1c2 100644 --- a/extensions/git/src/operation.ts +++ b/extensions/git/src/operation.ts @@ -2,6 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-no-dangerous-type-assertions */ import { LogOutputChannel } from 'vscode'; @@ -19,7 +20,7 @@ export const enum OperationKind { Config = 'Config', DeleteBranch = 'DeleteBranch', DeleteRef = 'DeleteRef', - DeleteRemoteTag = 'DeleteRemoteTag', + DeleteRemoteRef = 'DeleteRemoteRef', DeleteTag = 'DeleteTag', Diff = 'Diff', Fetch = 'Fetch', @@ -65,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 | @@ -87,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 }; @@ -131,9 +132,9 @@ export type SyncOperation = BaseOperation & { kind: OperationKind.Sync }; export type TagOperation = BaseOperation & { kind: OperationKind.Tag }; export const Operation = { - Add: (showProgress: boolean) => ({ kind: OperationKind.Add, blocking: false, readOnly: false, remote: false, retry: false, showProgress } as AddOperation), + 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, @@ -144,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 d4e227b6db7..69a18114a41 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 4fdd6f06c1d..eb63e5db81f 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 5788d7ee2a7..25445d2668c 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -6,15 +6,15 @@ import TelemetryReporter from '@vscode/extension-telemetry'; import * as fs from 'fs'; import * as path from 'path'; -import * as 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 picomatch from 'picomatch'; +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 +22,9 @@ 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 { ISourceControlHistoryItemDetailsProviderRegistry } from './historyItemDetailsProvider'; const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); @@ -558,13 +559,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: @@ -790,6 +789,21 @@ export class Repository implements Disposable { return this._mergeInProgress; } + private _cherryPickInProgress: boolean = false; + + set cherryPickInProgress(value: boolean) { + if (this._cherryPickInProgress === value) { + return; + } + + this._cherryPickInProgress = value; + commands.executeCommand('setContext', 'gitCherryPickInProgress', value); + } + + get cherryPickInProgress() { + return this._cherryPickInProgress; + } + private _operations = new OperationManager(this.logger); get operations(): OperationManager { return this._operations; } @@ -826,6 +840,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); @@ -839,6 +854,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 @@ -877,7 +893,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); @@ -969,9 +985,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); @@ -1006,7 +1022,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 { @@ -1016,11 +1032,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; } @@ -1042,15 +1081,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 { @@ -1121,7 +1164,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 { @@ -1174,10 +1220,10 @@ export class Repository implements Disposable { await this.run(Operation.Remove, () => this.repository.rm(resources.map(r => r.fsPath))); } - async stage(resource: Uri, contents: string): Promise { - const path = relativePath(this.repository.root, resource.fsPath).replace(/\\/g, '/'); + async stage(resource: Uri, contents: string, encoding: string): Promise { await this.run(Operation.Stage, async () => { - await this.repository.stage(path, contents); + const path = relativePath(this.repository.root, resource.fsPath).replace(/\\/g, '/'); + await this.repository.stage(path, contents, encoding); this._onDidChangeOriginalResource.fire(resource); this.closeDiffEditors([], [...resource.fsPath]); @@ -1302,6 +1348,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 () => { @@ -1339,7 +1388,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) { @@ -1402,7 +1458,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 { @@ -1434,6 +1493,10 @@ export class Repository implements Disposable { await this.run(Operation.CherryPick, () => this.repository.cherryPick(commitHash)); } + async cherryPickAbort(): Promise { + await this.run(Operation.CherryPick, () => this.repository.cherryPickAbort()); + } + async move(from: string, to: string): Promise { await this.run(Operation.Move, () => this.repository.move(from, to)); } @@ -1445,7 +1508,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)); }); } @@ -1520,8 +1583,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) { @@ -1584,12 +1652,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 () => { @@ -1607,7 +1675,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 })); } @@ -1636,6 +1704,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)); } @@ -1707,6 +1783,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'); @@ -1716,7 +1793,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 }); } }); }); @@ -1763,8 +1840,18 @@ export class Repository implements Disposable { await this.run(Operation.Push, () => this._push(remote, undefined, false, false, forcePushMode, true)); } - async blame(path: string): Promise { - return await this.run(Operation.Blame, () => this.repository.blame(path)); + async blame(filePath: string): Promise { + return await this.run(Operation.Blame(true), () => { + const path = relativePath(this.repository.root, filePath).replace(/\\/g, '/'); + return this.repository.blame(path); + }); + } + + async blame2(filePath: string, ref?: string): Promise { + return await this.run(Operation.Blame(false), () => { + const path = relativePath(this.repository.root, filePath).replace(/\\/g, '/'); + return this.repository.blame2(path, ref); + }); } @throttle @@ -1786,6 +1873,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'); @@ -1798,7 +1886,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 }); } }; @@ -1880,17 +1968,15 @@ 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); + const content = await this.repository.buffer(`${ref}:${path}`); + return await workspace.decode(content, Uri.file(filePath)); } 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 content = await this.repository.buffer(`${ref}:${gitRelativePath}`); + return await workspace.decode(content, Uri.file(filePath)); } throw err; @@ -1910,7 +1996,10 @@ export class Repository implements Disposable { } getObjectDetails(ref: string, filePath: string): Promise<{ mode: string; object: string; size: number }> { - return this.run(Operation.GetObjectDetails, () => this.repository.getObjectDetails(ref, filePath)); + return this.run(Operation.GetObjectDetails, () => { + const path = relativePath(this.repository.root, filePath).replace(/\\/g, '/'); + return this.repository.getObjectDetails(ref, path); + }); } detectObjectType(object: string): Promise<{ mimetype: string; encoding?: string }> { @@ -2171,20 +2260,33 @@ export class Repository implements Disposable { this._updateResourceGroupsState(optimisticResourcesGroups); } - const [HEAD, remotes, submodules, rebaseCommit, mergeInProgress, commitTemplate] = + const [HEAD, remotes, submodules, rebaseCommit, mergeInProgress, cherryPickInProgress, commitTemplate] = await Promise.all([ this.repository.getHEADRef(), this.repository.getRemotes(), this.repository.getSubmodules(), this.getRebaseCommit(), this.isMergeInProgress(), + 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!; this.rebaseCommit = rebaseCommit; this.mergeInProgress = mergeInProgress; + this.cherryPickInProgress = cherryPickInProgress; this._sourceControl.commitTemplate = commitTemplate; @@ -2286,24 +2388,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; + }); } } @@ -2412,20 +2516,30 @@ export class Repository implements Disposable { return new Promise(resolve => fs.exists(mergeHeadPath, resolve)); } + private isCherryPickInProgress(): Promise { + const cherryPickHeadPath = path.join(this.repository.root, '.git', 'CHERRY_PICK_HEAD'); + return new Promise(resolve => fs.exists(cherryPickHeadPath, resolve)); + } + 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 { @@ -2491,7 +2605,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 { @@ -2646,7 +2760,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/ssh-askpass-empty.sh b/extensions/git/src/ssh-askpass-empty.sh old mode 100644 new mode 100755 diff --git a/extensions/git/src/ssh-askpass.sh b/extensions/git/src/ssh-askpass.sh old mode 100644 new mode 100755 diff --git a/extensions/git/src/staging.ts b/extensions/git/src/staging.ts index 2dcc6d54487..14e4e379e47 100644 --- a/extensions/git/src/staging.ts +++ b/extensions/git/src/staging.ts @@ -3,7 +3,8 @@ * 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, LineChange, Selection, Uri, TextEditor, TextEditorDiffInformation } from 'vscode'; +import { fromGitUri, isGitUri } from './uri'; export function applyLineChanges(original: TextDocument, modified: TextDocument, diffs: LineChange[]): string { const result: string[] = []; @@ -143,6 +144,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 1b2c8c259ba..b9a3ddfd063 100644 --- a/extensions/git/src/test/smoke.test.ts +++ b/extensions/git/src/test/smoke.test.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import 'mocha'; -import * as assert from 'assert'; -import { workspace, commands, window, Uri, WorkspaceEdit, Range, TextDocument, extensions } from 'vscode'; +import assert from 'assert'; +import { workspace, commands, window, Uri, WorkspaceEdit, Range, TextDocument, extensions, TabInputTextDiff } from 'vscode'; import * as cp from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; @@ -73,17 +73,22 @@ suite('git smoke test', function () { await type(appjs, ' world'); await appjs.save(); await repository.status(); + assert.strictEqual(repository.state.workingTreeChanges.length, 1); - repository.state.workingTreeChanges.some(r => r.uri.path === appjs.uri.path && r.status === Status.MODIFIED); + assert.strictEqual(repository.state.workingTreeChanges[0].uri.path, appjs.uri.path); + assert.strictEqual(repository.state.workingTreeChanges[0].status, Status.MODIFIED); fs.writeFileSync(file('newfile.txt'), ''); const newfile = await open('newfile.txt'); await type(newfile, 'hey there'); await newfile.save(); await repository.status(); + assert.strictEqual(repository.state.workingTreeChanges.length, 2); - repository.state.workingTreeChanges.some(r => r.uri.path === appjs.uri.path && r.status === Status.MODIFIED); - repository.state.workingTreeChanges.some(r => r.uri.path === newfile.uri.path && r.status === Status.UNTRACKED); + assert.strictEqual(repository.state.workingTreeChanges[0].uri.path, appjs.uri.path); + assert.strictEqual(repository.state.workingTreeChanges[0].status, Status.MODIFIED); + assert.strictEqual(repository.state.workingTreeChanges[1].uri.path, newfile.uri.path); + assert.strictEqual(repository.state.workingTreeChanges[1].status, Status.UNTRACKED); }); test('opens diff editor', async function () { @@ -93,37 +98,50 @@ suite('git smoke test', function () { assert(window.activeTextEditor); assert.strictEqual(window.activeTextEditor!.document.uri.path, appjs.path); - // TODO: how do we really know this is a diff editor? + assert(window.tabGroups.activeTabGroup.activeTab); + assert(window.tabGroups.activeTabGroup.activeTab!.input instanceof TabInputTextDiff); }); test('stages correctly', async function () { const appjs = uri('app.js'); const newfile = uri('newfile.txt'); - await commands.executeCommand('git.stage', appjs); - assert.strictEqual(repository.state.workingTreeChanges.length, 1); - repository.state.workingTreeChanges.some(r => r.uri.path === newfile.path && r.status === Status.UNTRACKED); + await repository.add([appjs.fsPath]); + assert.strictEqual(repository.state.indexChanges.length, 1); - repository.state.indexChanges.some(r => r.uri.path === appjs.path && r.status === Status.INDEX_MODIFIED); + assert.strictEqual(repository.state.indexChanges[0].uri.path, appjs.path); + assert.strictEqual(repository.state.indexChanges[0].status, Status.INDEX_MODIFIED); + + assert.strictEqual(repository.state.workingTreeChanges.length, 1); + assert.strictEqual(repository.state.workingTreeChanges[0].uri.path, newfile.path); + assert.strictEqual(repository.state.workingTreeChanges[0].status, Status.UNTRACKED); + + await repository.revert([appjs.fsPath]); + + assert.strictEqual(repository.state.indexChanges.length, 0); - await commands.executeCommand('git.unstage', appjs); assert.strictEqual(repository.state.workingTreeChanges.length, 2); - repository.state.workingTreeChanges.some(r => r.uri.path === appjs.path && r.status === Status.MODIFIED); - repository.state.workingTreeChanges.some(r => r.uri.path === newfile.path && r.status === Status.UNTRACKED); + assert.strictEqual(repository.state.workingTreeChanges[0].uri.path, appjs.path); + assert.strictEqual(repository.state.workingTreeChanges[0].status, Status.MODIFIED); + assert.strictEqual(repository.state.workingTreeChanges[1].uri.path, newfile.path); + assert.strictEqual(repository.state.workingTreeChanges[1].status, Status.UNTRACKED); }); test('stages, commits changes and verifies outgoing change', async function () { const appjs = uri('app.js'); const newfile = uri('newfile.txt'); - await commands.executeCommand('git.stage', appjs); + await repository.add([appjs.fsPath]); await repository.commit('second commit'); + assert.strictEqual(repository.state.workingTreeChanges.length, 1); - repository.state.workingTreeChanges.some(r => r.uri.path === newfile.path && r.status === Status.UNTRACKED); + assert.strictEqual(repository.state.workingTreeChanges[0].uri.path, newfile.path); + assert.strictEqual(repository.state.workingTreeChanges[0].status, Status.UNTRACKED); + assert.strictEqual(repository.state.indexChanges.length, 0); - await commands.executeCommand('git.stageAll', appjs); - await repository.commit('third commit'); + await repository.commit('third commit', { all: true }); + assert.strictEqual(repository.state.workingTreeChanges.length, 0); assert.strictEqual(repository.state.indexChanges.length, 0); }); @@ -131,16 +149,19 @@ suite('git smoke test', function () { test('rename/delete conflict', async function () { await commands.executeCommand('workbench.view.scm'); + const appjs = file('app.js'); + const renamejs = file('rename.js'); + await repository.createBranch('test', true); // Delete file (test branch) - fs.unlinkSync(file('app.js')); + fs.unlinkSync(appjs); await repository.commit('commit on test', { all: true }); await repository.checkout('main'); // Rename file (main branch) - fs.renameSync(file('app.js'), file('rename.js')); + fs.renameSync(appjs, renamejs); await repository.commit('commit on main', { all: true }); try { @@ -148,6 +169,8 @@ suite('git smoke test', function () { } catch (e) { } assert.strictEqual(repository.state.mergeChanges.length, 1); + assert.strictEqual(repository.state.mergeChanges[0].status, Status.DELETED_BY_THEM); + 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 5788ecc53dd..90d8d28c396 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 1eeb1739901..d4ec49df47d 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 84ee599797d..00000000000 --- 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 e09d0e142b4..00000000000 --- 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 169abd1bc35..8b04fabe583 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 bf33411c3b3..7dd1cbafbdf 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -3,14 +3,17 @@ * 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'; -import * as byline from 'byline'; +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 75fb8217df7..1d330ea2516 100644 --- a/extensions/git/tsconfig.json +++ b/extensions/git/tsconfig.json @@ -10,7 +10,11 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.canonicalUriProvider.d.ts", "../../src/vscode-dts/vscode.proposed.diffCommand.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 +22,12 @@ "../../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.textDocumentEncoding.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/.npmrc b/extensions/github-authentication/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/github-authentication/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/github-authentication/extension.webpack.config.js b/extensions/github-authentication/extension.webpack.config.js index df3adb4ee7a..d356151d68c 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 c150fa7acc3..cbc9e16b75f 100644 --- a/extensions/github-authentication/package-lock.json +++ b/extensions/github-authentication/package-lock.json @@ -9,7 +9,7 @@ "version": "0.0.2", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "node-fetch": "2.6.7", "vscode-tas-client": "^0.1.84" }, @@ -23,118 +23,128 @@ } }, "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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "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.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@types/mocha": { "version": "9.1.1", @@ -162,13 +172,14 @@ } }, "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": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "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.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index 7fcbc7f151c..96fbcd75d40 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).*)" } } } @@ -70,7 +62,7 @@ }, "dependencies": { "node-fetch": "2.6.7", - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "vscode-tas-client": "^0.1.84" }, "devDependencies": { diff --git a/extensions/github-authentication/package.nls.json b/extensions/github-authentication/package.nls.json index 592a413b9a5..e326f00f5c9 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 30b9dd66265..8cd3a9ed793 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 a2497b2b0b2..8920ea5d357 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 d4ae337427c..d67fc99e08b 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 { @@ -332,21 +314,14 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid }); const sessions = await this._sessionsPromise; - - // First we use the account specified in the options, otherwise we use the first account we have to seed auth. - const loginWith = options?.account?.label ?? sessions[0]?.account.label; + const loginWith = options?.account?.label; this._logger.info(`Logging in with '${loginWith ? loginWith : 'any'}' account...`); - const scopeString = sortedScopes.join(' '); const token = await this._githubServer.login(scopeString, loginWith); 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)); @@ -424,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 58718078e69..ca344d436b1 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/.npmrc b/extensions/github/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/github/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/github/package-lock.json b/extensions/github/package-lock.json index 41de66d1a9b..cf7317a40a4 100644 --- a/extensions/github/package-lock.json +++ b/extensions/github/package-lock.json @@ -9,10 +9,10 @@ "version": "0.0.1", "license": "MIT", "dependencies": { - "@octokit/graphql": "5.0.5", + "@octokit/graphql": "8.2.0", "@octokit/graphql-schema": "14.4.0", - "@octokit/rest": "19.0.4", - "@vscode/extension-telemetry": "^0.9.0", + "@octokit/rest": "21.1.0", + "@vscode/extension-telemetry": "^0.9.8", "tunnel": "^0.0.6" }, "devDependencies": { @@ -23,210 +23,181 @@ } }, "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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "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.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "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": "5.1.2", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz", + "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==", + "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": ">= 18" } }, "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": "6.1.4", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.4.tgz", + "integrity": "sha512-lAS9k7d6I0MPN+gb9bKDt7X8SdxknYqAMh44S5L+lNqIN2NuV8nvv3g8rPp7MuRxcOpxpUIATWprO0C34a8Qmg==", + "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": "^5.0.0", + "@octokit/graphql": "^8.1.2", + "@octokit/request": "^9.2.1", + "@octokit/request-error": "^6.1.7", + "@octokit/types": "^13.6.2", + "before-after-hook": "^3.0.2", + "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": ">= 18" } }, "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": "10.1.3", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.3.tgz", + "integrity": "sha512-nBRBMpKPhQUxCsQQeW+rCJ/OPSMcj3g0nfHn01zGYZXuNDvvXudF/TYY6APj5THlurerpFN4a/dQAIAaM6BYhA==", + "license": "MIT", "dependencies": { - "@octokit/types": "^7.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" + "@octokit/types": "^13.6.2", + "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": ">= 18" } }, "node_modules/@octokit/graphql": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-5.0.5.tgz", - "integrity": "sha512-Qwfvh3xdqKtIznjX9lz2D458r7dJPP8l6r4GQkIdWQouZwHQK0mVT88uwiU2bdTU2OtT1uOlKpRciUWldpG0yQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.0.tgz", + "integrity": "sha512-gejfDywEml/45SqbWTWrhfwvLBrcGYhOn50sPOjIeVvH6i7D16/9xcFA8dAJNp2HMcd+g4vru41g4E2RBiZvfQ==", + "license": "MIT", "dependencies": { - "@octokit/request": "^6.0.0", - "@octokit/types": "^9.0.0", - "universal-user-agent": "^6.0.0" + "@octokit/request": "^9.1.4", + "@octokit/types": "^13.8.0", + "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "node_modules/@octokit/graphql-schema": { @@ -239,148 +210,103 @@ } }, "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": "23.0.1", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-23.0.1.tgz", + "integrity": "sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==", + "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": "11.4.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.4.2.tgz", + "integrity": "sha512-BXJ7XPCTDXFF+wxcg/zscfgw2O/iDPtNSkwwR1W1W5c4Mb3zav/M2XvxQ23nVmKj7jpweB4g8viMeCQdm7LMVA==", + "license": "MIT", "dependencies": { - "@octokit/types": "^7.2.0" + "@octokit/types": "^13.7.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" }, "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": "5.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-5.3.1.tgz", + "integrity": "sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==", + "license": "MIT", + "engines": { + "node": ">= 18" + }, "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": "13.3.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.3.1.tgz", + "integrity": "sha512-o8uOBdsyR+WR8MK9Cco8dCgvG13H1RlM1nWnK/W7TEACQBFux/vPREgKucxUfuDQ5yi1T3hGf4C5ZmZXAERgwQ==", + "license": "MIT", "dependencies": { - "@octokit/types": "^7.2.0", - "deprecation": "^2.3.1" + "@octokit/types": "^13.8.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" }, "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": "9.2.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.1.tgz", + "integrity": "sha512-TqHLIdw1KFvx8WvLc7Jv94r3C3+AzKY2FWq7c20zvrxmCIa6MCVkLCE/826NCXnml3LFJjLsidDh1BhMaGEDQw==", + "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": "^10.1.3", + "@octokit/request-error": "^6.1.6", + "@octokit/types": "^13.6.2", + "fast-content-type-parse": "^2.0.0", + "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "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": "6.1.7", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.7.tgz", + "integrity": "sha512-69NIppAwaauwZv6aOzb+VVLwt+0havz9GT5YplkeJv7fG7a40qpLt/yZKyiDxAhgz0EtgNdNcb96Z0u+Zyuy2g==", + "license": "MIT", "dependencies": { - "@octokit/types": "^7.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" + "@octokit/types": "^13.6.2" }, "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": ">= 18" } }, "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": "21.1.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.1.0.tgz", + "integrity": "sha512-93iLxcKDJboUpmnUyeJ6cRIi7z7cqTZT1K7kRK4LobGxwTwpsa+2tQQbRQNGy7IFDEAmrtkf4F4wBj3D5rVlJQ==", + "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": "^6.1.3", + "@octokit/plugin-paginate-rest": "^11.4.0", + "@octokit/plugin-request-log": "^5.3.1", + "@octokit/plugin-rest-endpoint-methods": "^13.3.0" }, "engines": { - "node": ">= 14" + "node": ">= 18" } }, "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": "13.8.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.8.0.tgz", + "integrity": "sha512-x7DjTIbEpEWXK99DMd01QfWy0hd5h4EN+Q7shkdKds3otGQP+oWE/y0A76i1OvH9fygo4ddvNf7ZvF0t78P98A==", + "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^17.1.0" + "@octokit/openapi-types": "^23.0.1" } }, "node_modules/@types/node": { @@ -393,27 +319,40 @@ } }, "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": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "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.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "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": "3.0.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", + "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", + "license": "Apache-2.0" + }, + "node_modules/fast-content-type-parse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", + "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "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 +376,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", @@ -497,28 +396,10 @@ "dev": true }, "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 ece19e32f54..86adc2ddc4e 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,11 +227,11 @@ "watch": "gulp watch-extension:github" }, "dependencies": { - "@octokit/graphql": "5.0.5", + "@octokit/graphql": "8.2.0", "@octokit/graphql-schema": "14.4.0", - "@octokit/rest": "19.0.4", + "@octokit/rest": "21.1.0", "tunnel": "^0.0.6", - "@vscode/extension-telemetry": "^0.9.0" + "@vscode/extension-telemetry": "^0.9.8" }, "devDependencies": { "@types/node": "20.x" diff --git a/extensions/github/package.nls.json b/extensions/github/package.nls.json index 5ead7903af2..40271bea980 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 a7d18c41b9f..52b4fbfae22 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 1f1504521f8..4e5587c09b5 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 1827768312e..de0349ba9d3 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 00000000000..b31126e2ada --- /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 911f0e5376b..fdcac0c5cfd 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 53cac4d5c70..d4ec49df47d 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 7ac67937a47..e600b767c7c 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 84ee599797d..00000000000 --- 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 6470557cac1..00000000000 --- 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 3d8bf4a40be..4c8a032405d 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 d7aed1836ee..8435c0d09e8 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 39db8ac979f..a6dbd5d1bf0 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": "b40fb01f2cf48bc24e6f4030373311b0402b158e" + "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.6" + "version": "0.7.9" } ], "version": 1 diff --git a/extensions/go/language-configuration.json b/extensions/go/language-configuration.json index a5e06a56bad..9238bf3529b 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 bc56a1b97ee..db17cad3f91 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/b40fb01f2cf48bc24e6f4030373311b0402b158e", + "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", @@ -2192,7 +2287,7 @@ ] } }, - "end": "(?:(?<=\\])((?:\\s+)(?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?:(?!(?:[\\[\\]\\*]+)?(?:\\bstruct\\b|\\binterface\\b|\\bfunc\\b))[\\w\\.\\-\\*\\[\\]]+(?:\\,\\s+[\\w\\.\\[\\]\\*]+)*))?)", + "end": "(?:(?<=\\])((?:\\s+)(?:\\=\\s*)?(?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?:(?!(?:[\\[\\]\\*]+)?(?:\\bstruct\\b|\\binterface\\b|\\bfunc\\b))[\\w\\.\\-\\*\\[\\]]+(?:\\,\\s*[\\w\\.\\[\\]\\*]+)*))?)", "endCaptures": { "1": { "patterns": [ @@ -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 a81a8864a51..39e5fd4092c 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/.npmrc b/extensions/grunt/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/grunt/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/gulp/.npmrc b/extensions/gulp/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/gulp/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/html-language-features/.npmrc b/extensions/html-language-features/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/html-language-features/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/html-language-features/cgmanifest.json b/extensions/html-language-features/cgmanifest.json index 7f57d61af72..8a0745255f7 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/client/src/languageParticipants.ts b/extensions/html-language-features/client/src/languageParticipants.ts index e3d5612b9b3..32b0bb5122d 100644 --- a/extensions/html-language-features/client/src/languageParticipants.ts +++ b/extensions/html-language-features/client/src/languageParticipants.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DocumentSelector } from 'vscode-languageclient'; import { Event, EventEmitter, extensions } from 'vscode'; /** @@ -22,7 +21,7 @@ interface LanguageParticipantContribution { export interface LanguageParticipants { readonly onDidChange: Event; - readonly documentSelector: DocumentSelector; + readonly documentSelector: string[]; hasLanguage(languageId: string): boolean; useAutoInsert(languageId: string): boolean; dispose(): void; diff --git a/extensions/html-language-features/client/tsconfig.json b/extensions/html-language-features/client/tsconfig.json index 349af163eea..615adaeea04 100644 --- a/extensions/html-language-features/client/tsconfig.json +++ b/extensions/html-language-features/client/tsconfig.json @@ -4,7 +4,8 @@ "outDir": "./out", "lib": [ "webworker" - ] + ], + "module": "Node16", }, "include": [ "src/**/*", diff --git a/extensions/html-language-features/package-lock.json b/extensions/html-language-features/package-lock.json index a7c6d22b395..20b14561533 100644 --- a/extensions/html-language-features/package-lock.json +++ b/extensions/html-language-features/package-lock.json @@ -9,8 +9,8 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", - "vscode-languageclient": "10.0.0-next.8", + "@vscode/extension-telemetry": "^0.9.8", + "vscode-languageclient": "^10.0.0-next.13", "vscode-uri": "^3.0.8" }, "devDependencies": { @@ -21,118 +21,128 @@ } }, "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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "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.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@types/node": { "version": "20.11.24", @@ -144,13 +154,14 @@ } }, "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": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "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.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" @@ -215,39 +226,40 @@ "dev": true }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.4", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.4.tgz", - "integrity": "sha512-zSVIr58lJSMYKIsZ5P7GtBbv1eEx25eNyOf0NmEzxmn1GhUNJAVAb5hkA1poKUwj1FRMwN6CeyWxZypmr8SsQQ==", + "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==", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageclient": { - "version": "10.0.0-next.8", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.8.tgz", - "integrity": "sha512-D9inIHgqKayO9Tv0MeLb3XIL76yTuWmKdHqcGZKzjtQrMGJgASJDYWTapu+yAjEpDp0gmVOaCYyIlLB86ncDoQ==", + "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==", + "license": "MIT", "dependencies": { "minimatch": "^9.0.3", "semver": "^7.6.0", - "vscode-languageserver-protocol": "3.17.6-next.6" + "vscode-languageserver-protocol": "3.17.6-next.11" }, "engines": { - "vscode": "^1.89.0" + "vscode": "^1.91.0" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.6", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.6.tgz", - "integrity": "sha512-naxM9kc/phpl0kAFNVPejMUWUtzFXdPYY/BtQTYtfbBbHf8sceHOrKkmf6yynZRu1A4oFtRZNqV3wyFRTWqUHw==", + "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==", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.4", - "vscode-languageserver-types": "3.17.6-next.4" + "vscode-jsonrpc": "9.0.0-next.6", + "vscode-languageserver-types": "3.17.6-next.5" } }, "node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.4", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.4.tgz", - "integrity": "sha512-SeJTpH/S14EbxOAVaOUoGVqPToqpRTld5QO5Ghig3AlbFJTFF9Wu7srHMfa85L0SX1RYAuuCSFKJVVCxDIk1/Q==" + "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/vscode-uri": { "version": "3.0.8", diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 5246391caf0..be411fe63a2 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -258,8 +258,8 @@ ] }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", - "vscode-languageclient": "10.0.0-next.8", + "@vscode/extension-telemetry": "^0.9.8", + "vscode-languageclient": "^10.0.0-next.13", "vscode-uri": "^3.0.8" }, "devDependencies": { diff --git a/extensions/html-language-features/server/.npmrc b/extensions/html-language-features/server/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/html-language-features/server/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/html-language-features/server/extension-browser.webpack.config.js b/extensions/html-language-features/server/extension-browser.webpack.config.js index 7e6256b4a45..07848486cbd 100644 --- a/extensions/html-language-features/server/extension-browser.webpack.config.js +++ b/extensions/html-language-features/server/extension-browser.webpack.config.js @@ -15,6 +15,12 @@ const serverConfig = withBrowserDefaults({ entry: { extension: './src/browser/htmlServerWorkerMain.ts', }, + resolve: { + extensionAlias: { + // this is needed to resolve dynamic imports that now require the .js extension + '.js': ['.js', '.ts'], + }, + }, output: { filename: 'htmlServerMain.js', path: path.join(__dirname, 'dist', 'browser'), diff --git a/extensions/html-language-features/server/package-lock.json b/extensions/html-language-features/server/package-lock.json index a6e67b1ef6b..5795436cf94 100644 --- a/extensions/html-language-features/server/package-lock.json +++ b/extensions/html-language-features/server/package-lock.json @@ -10,9 +10,9 @@ "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.6", + "vscode-css-languageservice": "^6.3.2", + "vscode-html-languageservice": "^5.3.2", + "vscode-languageserver": "^10.0.0-next.11", "vscode-languageserver-textdocument": "^1.0.12", "vscode-uri": "^3.0.8" }, @@ -51,9 +51,10 @@ "dev": true }, "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.2", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.2.tgz", + "integrity": "sha512-GEpPxrUTAeXWdZWHev1OJU9lz2Q2/PPBxQ2TIRmLGvQiH3WZbqaNoute0n0ewxlgtjzTW3AKZT+NHySk5Rf4Eg==", + "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", @@ -62,9 +63,10 @@ } }, "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.3.2", + "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.3.2.tgz", + "integrity": "sha512-3MgFQqVG+iQVNG7QI/slaoL7lJpne0nssX082kjUF1yn/YJa8BWCLeCJjM0YpTlp8A7JT1+J22mk4qSPx3NjSQ==", + "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", @@ -73,37 +75,38 @@ } }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.4", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.4.tgz", - "integrity": "sha512-zSVIr58lJSMYKIsZ5P7GtBbv1eEx25eNyOf0NmEzxmn1GhUNJAVAb5hkA1poKUwj1FRMwN6CeyWxZypmr8SsQQ==", + "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==", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageserver": { - "version": "10.0.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.6.tgz", - "integrity": "sha512-0Lh1nhQfSxo5Ob+ayYO1QTIsDix2/Lc72Urm1KZrCFxK5zIFYaEh3QFeM9oZih4Rzs0ZkQPXXnoHtpvs5GT+Zw==", + "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==", + "license": "MIT", "dependencies": { - "vscode-languageserver-protocol": "3.17.6-next.6" + "vscode-languageserver-protocol": "3.17.6-next.11" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.6", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.6.tgz", - "integrity": "sha512-naxM9kc/phpl0kAFNVPejMUWUtzFXdPYY/BtQTYtfbBbHf8sceHOrKkmf6yynZRu1A4oFtRZNqV3wyFRTWqUHw==", + "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==", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.4", - "vscode-languageserver-types": "3.17.6-next.4" + "vscode-jsonrpc": "9.0.0-next.6", + "vscode-languageserver-types": "3.17.6-next.5" } }, "node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.4", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.4.tgz", - "integrity": "sha512-SeJTpH/S14EbxOAVaOUoGVqPToqpRTld5QO5Ghig3AlbFJTFF9Wu7srHMfa85L0SX1RYAuuCSFKJVVCxDIk1/Q==" + "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/vscode-languageserver-textdocument": { "version": "1.0.12", diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 652340f49d7..7017ee85721 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -10,9 +10,9 @@ "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.6", + "vscode-css-languageservice": "^6.3.2", + "vscode-html-languageservice": "^5.3.2", + "vscode-languageserver": "^10.0.0-next.11", "vscode-languageserver-textdocument": "^1.0.12", "vscode-uri": "^3.0.8" }, diff --git a/extensions/html-language-features/server/src/browser/htmlServerWorkerMain.ts b/extensions/html-language-features/server/src/browser/htmlServerWorkerMain.ts index 600ae1ba8c5..f7c0de01bdd 100644 --- a/extensions/html-language-features/server/src/browser/htmlServerWorkerMain.ts +++ b/extensions/html-language-features/server/src/browser/htmlServerWorkerMain.ts @@ -22,7 +22,7 @@ const messageHandler = async (e: any) => { } else { l10nLog.push(`l10n: No bundle configured.`); } - await import('./htmlServerMain'); + await import('./htmlServerMain.js'); if (self.onmessage !== messageHandler) { pendingMessages.forEach(msg => self.onmessage?.(msg)); pendingMessages.length = 0; diff --git a/extensions/html-language-features/server/src/modes/embeddedSupport.ts b/extensions/html-language-features/server/src/modes/embeddedSupport.ts index 26ef68439da..a6874e043b4 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 a540745428c..d820810c920 100644 --- a/extensions/html-language-features/server/src/modes/javascriptMode.ts +++ b/extensions/html-language-features/server/src/modes/javascriptMode.ts @@ -22,7 +22,7 @@ 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').then(libs => { + const jsLanguageService = import(/* webpackChunkName: "javascriptLibs" */ './javascriptLibs.js').then(libs => { const host: ts.LanguageServiceHost = { getCompilationSettings: () => compilerOptions, getScriptFileNames: () => [currentTextDocument.uri, 'jquery'], diff --git a/extensions/html-language-features/server/src/modes/languageModes.ts b/extensions/html-language-features/server/src/modes/languageModes.ts index 4ab4a4a876e..803fa6c1c87 100644 --- a/extensions/html-language-features/server/src/modes/languageModes.ts +++ b/extensions/html-language-features/server/src/modes/languageModes.ts @@ -138,8 +138,8 @@ export function getLanguageModes(supportedLanguages: { [languageId: string]: boo return undefined; }, getModesInRange(document: TextDocument, range: Range): LanguageModeRange[] { - return documentRegions.get(document).getLanguageRanges(range).map(r => { - return { + return documentRegions.get(document).getLanguageRanges(range).map((r): LanguageModeRange => { + return { start: r.start, end: r.end, mode: r.languageId && modes[r.languageId], diff --git a/extensions/html-language-features/server/src/node/htmlServerNodeMain.ts b/extensions/html-language-features/server/src/node/htmlServerNodeMain.ts index c3262f1e0b0..664596ac0be 100644 --- a/extensions/html-language-features/server/src/node/htmlServerNodeMain.ts +++ b/extensions/html-language-features/server/src/node/htmlServerNodeMain.ts @@ -16,7 +16,7 @@ async function setupMain() { l10nLog.push(`l10n: Problems loading ${i10lLocation.toString()} : ${e}`); } } - await import('./htmlServerMain'); + await import('./htmlServerMain.js'); l10nLog.forEach(console.log); } setupMain(); 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 87698f39718..abba5b5858e 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/words.test.ts b/extensions/html-language-features/server/src/test/words.test.ts index 132589e6867..ea66e4d6ed8 100644 --- a/extensions/html-language-features/server/src/test/words.test.ts +++ b/extensions/html-language-features/server/src/test/words.test.ts @@ -2,7 +2,7 @@ * 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 assert from 'assert'; import * as words from '../utils/strings'; import * as fs from 'fs'; import * as path from 'path'; @@ -24,8 +24,8 @@ suite('HTML Language Configuration', () => { value = value.substr(0, offset) + value.substring(offset + 1); const actualRange = words.getWordAtText(value, offset, wordRegex); - assert(actualRange.start <= offset); - assert(actualRange.start + actualRange.length >= offset); + assert.ok(actualRange.start <= offset); + assert.ok(actualRange.start + actualRange.length >= offset); assert.strictEqual(value.substr(actualRange.start, actualRange.length), expected); } diff --git a/extensions/html-language-features/server/tsconfig.json b/extensions/html-language-features/server/tsconfig.json index ae02026a740..e21408bda97 100644 --- a/extensions/html-language-features/server/tsconfig.json +++ b/extensions/html-language-features/server/tsconfig.json @@ -5,7 +5,8 @@ "lib": [ "ES2020", "WebWorker" - ] + ], + "module": "Node16", }, "include": [ "src/**/*" diff --git a/extensions/ipynb/.npmrc b/extensions/ipynb/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/ipynb/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/ipynb/extension-browser.webpack.config.js b/extensions/ipynb/extension-browser.webpack.config.js index a9dbdb8b04c..32fc7de8ee4 100644 --- a/extensions/ipynb/extension-browser.webpack.config.js +++ b/extensions/ipynb/extension-browser.webpack.config.js @@ -8,15 +8,31 @@ 'use strict'; const withBrowserDefaults = require('../shared.webpack.config').browser; +const path = require('path'); -const config = withBrowserDefaults({ +const mainConfig = withBrowserDefaults({ context: __dirname, entry: { extension: './src/ipynbMain.browser.ts' }, output: { - filename: 'ipynbMain.browser.js' + filename: 'ipynbMain.browser.js', + path: path.join(__dirname, 'dist', 'browser') } }); -module.exports = config; + +const workerConfig = withBrowserDefaults({ + context: __dirname, + entry: { + notebookSerializerWorker: './src/notebookSerializerWorker.web.ts', + }, + output: { + filename: 'notebookSerializerWorker.js', + path: path.join(__dirname, 'dist', 'browser'), + libraryTarget: 'var', + library: 'serverExportVar' + }, +}); + +module.exports = [mainConfig, workerConfig]; diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index c2fe8c2a012..f7ee3c344e7 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,10 @@ "type": "boolean", "scope": "resource", "markdownDescription": "%ipynb.experimental.serialization%", - "default": false + "default": true, + "tags": [ + "experimental" + ] } } } @@ -69,6 +71,11 @@ "title": "%copyCellOutput.title%", "category": "Notebook" }, + { + "command": "notebook.cellOutput.addToChat", + "title": "%addCellOutputToChat.title%", + "category": "Notebook" + }, { "command": "notebook.cellOutput.openInTextEditor", "title": "%openCellOutput.title%", @@ -128,12 +135,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'" diff --git a/extensions/ipynb/package.nls.json b/extensions/ipynb/package.nls.json index 9fc163137f8..85ca7c5f2b3 100644 --- a/extensions/ipynb/package.nls.json +++ b/extensions/ipynb/package.nls.json @@ -2,12 +2,13 @@ "displayName": ".ipynb Support", "description": "Provides basic support for opening and reading Jupyter's .ipynb notebook files", "ipynb.pasteImagesAsAttachments.enabled": "Enable/disable pasting of images into Markdown cells in ipynb notebook files. Pasted images are inserted as attachments to the cell.", - "ipynb.experimental.serialization": "Experimental feature to serialize the Jupyter notebook in a worker thread. Not supported when the Extension host is running as a web worker.", + "ipynb.experimental.serialization": "Experimental feature to serialize the Jupyter notebook in a worker thread.", "newUntitledIpynb.title": "New Jupyter Notebook", "newUntitledIpynb.shortTitle": "Jupyter Notebook", "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 3fda0bc74f4..dbd3ea1a618 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/deserializers.ts b/extensions/ipynb/src/deserializers.ts index de467f66077..ad99a4fcea8 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/ipynbMain.ts b/extensions/ipynb/src/ipynbMain.ts index 5dc9098a99d..e4fe302d188 100644 --- a/extensions/ipynb/src/ipynbMain.ts +++ b/extensions/ipynb/src/ipynbMain.ts @@ -28,10 +28,17 @@ type NotebookMetadata = { [propName: string]: unknown; }; +type OptionsWithCellContentMetadata = vscode.NotebookDocumentContentOptions & { cellContentMetadata: { attachments: boolean } }; + + export function activate(context: vscode.ExtensionContext, serializer: vscode.NotebookSerializer) { keepNotebookModelStoreInSync(context); - context.subscriptions.push(vscode.workspace.registerNotebookSerializer('jupyter-notebook', serializer, { + const notebookSerializerOptions: OptionsWithCellContentMetadata = { transientOutputs: false, + transientDocumentMetadata: { + cells: true, + indentAmount: true + }, transientCellMetadata: { breakpointMargin: true, id: false, @@ -41,9 +48,10 @@ export function activate(context: vscode.ExtensionContext, serializer: vscode.No cellContentMetadata: { attachments: true } - } as vscode.NotebookDocumentContentOptions)); + }; + context.subscriptions.push(vscode.workspace.registerNotebookSerializer('jupyter-notebook', serializer, notebookSerializerOptions)); - context.subscriptions.push(vscode.workspace.registerNotebookSerializer('interactive', serializer, { + const interactiveSerializeOptions: OptionsWithCellContentMetadata = { transientOutputs: false, transientCellMetadata: { breakpointMargin: true, @@ -54,7 +62,8 @@ export function activate(context: vscode.ExtensionContext, serializer: vscode.No cellContentMetadata: { attachments: true } - } as vscode.NotebookDocumentContentOptions)); + }; + context.subscriptions.push(vscode.workspace.registerNotebookSerializer('interactive', serializer, interactiveSerializeOptions)); vscode.languages.registerCodeLensProvider({ pattern: '**/*.ipynb' }, { provideCodeLenses: (document) => { @@ -116,10 +125,10 @@ export function activate(context: vscode.ExtensionContext, serializer: vscode.No const edit = new vscode.WorkspaceEdit(); edit.set(resource, [vscode.NotebookEdit.updateNotebookMetadata({ ...document.metadata, - metadata: { + metadata: { ...(document.metadata.metadata ?? {}), ...metadata - }, + } satisfies NotebookMetadata, })]); return vscode.workspace.applyEdit(edit); }, diff --git a/extensions/ipynb/src/notebookImagePaste.ts b/extensions/ipynb/src/notebookImagePaste.ts index 5e188dd554a..70a24e9bf2d 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 451085718c6..1d83d980b2d 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 898b4ff9362..6eb1fab75d8 100644 --- a/extensions/ipynb/src/notebookSerializer.ts +++ b/extensions/ipynb/src/notebookSerializer.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import type * as nbformat from '@jupyterlab/nbformat'; -import * as detectIndent from 'detect-indent'; +import detectIndent from 'detect-indent'; import * as vscode from 'vscode'; import { getPreferredLanguage, jupyterNotebookModelToNotebookData } from './deserializers'; import * as fnv from '@enonic/fnv-plus'; diff --git a/extensions/ipynb/src/notebookSerializer.web.ts b/extensions/ipynb/src/notebookSerializer.web.ts index 6352cb97d20..54496108a8b 100644 --- a/extensions/ipynb/src/notebookSerializer.web.ts +++ b/extensions/ipynb/src/notebookSerializer.web.ts @@ -3,7 +3,83 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; +import { DeferredPromise, generateUuid } from './helper'; import { NotebookSerializerBase } from './notebookSerializer'; export class NotebookSerializer extends NotebookSerializerBase { + private experimentalSave = vscode.workspace.getConfiguration('ipynb').get('experimental.serialization', false); + private worker?: Worker; + private tasks = new Map>(); + + constructor(context: vscode.ExtensionContext) { + super(context); + context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('ipynb.experimental.serialization')) { + this.experimentalSave = vscode.workspace.getConfiguration('ipynb').get('experimental.serialization', false); + } + })); + } + + override dispose() { + try { + void this.worker?.terminate(); + } catch { + // + } + super.dispose(); + } + + public override async serializeNotebook(data: vscode.NotebookData, token: vscode.CancellationToken): Promise { + if (this.disposed) { + return new Uint8Array(0); + } + + if (this.experimentalSave) { + return this.serializeViaWorker(data); + } + + return super.serializeNotebook(data, token); + } + + private async startWorker() { + if (this.disposed) { + throw new Error('Serializer disposed'); + } + if (this.worker) { + return this.worker; + } + const entry = vscode.Uri.joinPath(this.context.extensionUri, 'dist', 'browser', 'notebookSerializerWorker.js'); + this.worker = new Worker(entry.toString()); + this.worker.addEventListener('exit', (exitCode) => { + if (!this.disposed) { + console.error(`IPynb Notebook Serializer Worker exited unexpectedly`, exitCode); + } + this.worker = undefined; + }); + this.worker.onmessage = (e) => { + const result = e.data as { id: string; data: Uint8Array }; + const task = this.tasks.get(result.id); + if (task) { + task.complete(result.data); + this.tasks.delete(result.id); + } + }; + this.worker.onerror = (err) => { + if (!this.disposed) { + console.error(`IPynb Notebook Serializer Worker errored unexpectedly`, err); + } + }; + return this.worker; + } + private async serializeViaWorker(data: vscode.NotebookData): Promise { + const worker = await this.startWorker(); + const id = generateUuid(); + + const deferred = new DeferredPromise(); + this.tasks.set(id, deferred); + worker.postMessage({ data, id }); + + return deferred.p; + } } diff --git a/extensions/ipynb/src/notebookSerializerWorker.web.ts b/extensions/ipynb/src/notebookSerializerWorker.web.ts new file mode 100644 index 00000000000..4ff8e12b8fa --- /dev/null +++ b/extensions/ipynb/src/notebookSerializerWorker.web.ts @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { serializeNotebookToString } from './serializers'; +import type { NotebookData } from 'vscode'; + +onmessage = (e) => { + const data = e.data as { id: string; data: NotebookData }; + const json = serializeNotebookToString(data.data); + const bytes = new TextEncoder().encode(json); + postMessage({ id: data.id, data: bytes }); +}; diff --git a/extensions/ipynb/src/serializers.ts b/extensions/ipynb/src/serializers.ts index e1896ee3be1..2d305df2fea 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; } } @@ -379,10 +382,10 @@ export function createMarkdownCellFromNotebookCell(cell: NotebookCellData): nbfo export function pruneCell(cell: nbformat.ICell): nbformat.ICell { // Source is usually a single string on input. Convert back to an array - const result = { + const result: nbformat.ICell = { ...cell, source: splitMultilineString(cell.source) - } as nbformat.ICell; + }; // Remove outputs and execution_count from non code cells if (result.cell_type !== 'code') { diff --git a/extensions/ipynb/src/test/clearOutputs.test.ts b/extensions/ipynb/src/test/clearOutputs.test.ts new file mode 100644 index 00000000000..0437e9838bb --- /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 acb6ec546bb..eab92167215 100644 --- a/extensions/ipynb/src/test/notebookModelStoreSync.test.ts +++ b/extensions/ipynb/src/test/notebookModelStoreSync.test.ts @@ -129,7 +129,7 @@ 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 () => { sinon.stub(notebook, 'metadata').get(() => ({ nbformat: 4, nbformat_minor: 5 })); @@ -160,7 +160,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 +196,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 +278,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 +372,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 +423,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 cb461539c5d..e132b6b2b1d 100644 --- a/extensions/ipynb/src/test/serializers.test.ts +++ b/extensions/ipynb/src/test/serializers.test.ts @@ -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]); }); diff --git a/extensions/ipynb/tsconfig.json b/extensions/ipynb/tsconfig.json index 2a6cc47eeeb..ee21f68d22a 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/.npmrc b/extensions/jake/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/jake/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/java/language-configuration.json b/extensions/java/language-configuration.json index 610adc686b4..6ba09bbd15c 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 f7c332337cb..c2444c453b2 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/languageParticipants.ts b/extensions/json-language-features/client/src/languageParticipants.ts index 7748d42589b..e0eb5262985 100644 --- a/extensions/json-language-features/client/src/languageParticipants.ts +++ b/extensions/json-language-features/client/src/languageParticipants.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DocumentSelector } from 'vscode-languageclient'; import { Event, EventEmitter, extensions } from 'vscode'; /** @@ -23,7 +22,7 @@ interface LanguageParticipantContribution { export interface LanguageParticipants { readonly onDidChange: Event; - readonly documentSelector: DocumentSelector; + readonly documentSelector: string[]; hasLanguage(languageId: string): boolean; useComments(languageId: string): boolean; dispose(): void; diff --git a/extensions/json-language-features/client/src/languageStatus.ts b/extensions/json-language-features/client/src/languageStatus.ts index 4aead6f99f2..6f3d7468b2e 100644 --- a/extensions/json-language-features/client/src/languageStatus.ts +++ b/extensions/json-language-features/client/src/languageStatus.ts @@ -6,10 +6,9 @@ import { window, languages, Uri, Disposable, commands, QuickPickItem, extensions, workspace, Extension, WorkspaceFolder, QuickPickItemKind, - ThemeIcon, TextDocument, LanguageStatusSeverity, l10n + ThemeIcon, TextDocument, LanguageStatusSeverity, l10n, DocumentSelector } from 'vscode'; import { JSONLanguageStatus, JSONSchemaSettings } from './jsonClient'; -import { DocumentSelector } from 'vscode-languageclient'; type ShowSchemasInput = { schemas: string[]; @@ -198,7 +197,7 @@ export function createLanguageStatusItem(documentSelector: DocumentSelector, sta statusItem.command = { command: '_json.showAssociatedSchemaList', title: l10n.t('Show Schemas'), - arguments: [{ schemas, uri: document.uri.toString() } as ShowSchemasInput] + arguments: [{ schemas, uri: document.uri.toString() } satisfies ShowSchemasInput] }; } catch (e) { statusItem.text = l10n.t('Unable to compute used schemas: {0}', e.message); diff --git a/extensions/json-language-features/client/tsconfig.json b/extensions/json-language-features/client/tsconfig.json index 89e6a6c12b7..cf91914c874 100644 --- a/extensions/json-language-features/client/tsconfig.json +++ b/extensions/json-language-features/client/tsconfig.json @@ -4,7 +4,8 @@ "outDir": "./out", "lib": [ "webworker" - ] + ], + "module": "Node16", }, "include": [ "src/**/*", diff --git a/extensions/json-language-features/package-lock.json b/extensions/json-language-features/package-lock.json index f3b999cc223..bde80cbfbe4 100644 --- a/extensions/json-language-features/package-lock.json +++ b/extensions/json-language-features/package-lock.json @@ -9,9 +9,9 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "request-light": "^0.8.0", - "vscode-languageclient": "10.0.0-next.8" + "vscode-languageclient": "^10.0.0-next.13" }, "devDependencies": { "@types/node": "20.x" @@ -21,118 +21,128 @@ } }, "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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "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.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@types/node": { "version": "20.11.24", @@ -144,13 +154,14 @@ } }, "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": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "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.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" @@ -220,39 +231,40 @@ "dev": true }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.4", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.4.tgz", - "integrity": "sha512-zSVIr58lJSMYKIsZ5P7GtBbv1eEx25eNyOf0NmEzxmn1GhUNJAVAb5hkA1poKUwj1FRMwN6CeyWxZypmr8SsQQ==", + "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==", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageclient": { - "version": "10.0.0-next.8", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.8.tgz", - "integrity": "sha512-D9inIHgqKayO9Tv0MeLb3XIL76yTuWmKdHqcGZKzjtQrMGJgASJDYWTapu+yAjEpDp0gmVOaCYyIlLB86ncDoQ==", + "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==", + "license": "MIT", "dependencies": { "minimatch": "^9.0.3", "semver": "^7.6.0", - "vscode-languageserver-protocol": "3.17.6-next.6" + "vscode-languageserver-protocol": "3.17.6-next.11" }, "engines": { - "vscode": "^1.89.0" + "vscode": "^1.91.0" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.6", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.6.tgz", - "integrity": "sha512-naxM9kc/phpl0kAFNVPejMUWUtzFXdPYY/BtQTYtfbBbHf8sceHOrKkmf6yynZRu1A4oFtRZNqV3wyFRTWqUHw==", + "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==", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.4", - "vscode-languageserver-types": "3.17.6-next.4" + "vscode-jsonrpc": "9.0.0-next.6", + "vscode-languageserver-types": "3.17.6-next.5" } }, "node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.4", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.4.tgz", - "integrity": "sha512-SeJTpH/S14EbxOAVaOUoGVqPToqpRTld5QO5Ghig3AlbFJTFF9Wu7srHMfa85L0SX1RYAuuCSFKJVVCxDIk1/Q==" + "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", diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index e19541b7631..cf4a7f162da 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,9 +167,9 @@ ] }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "request-light": "^0.8.0", - "vscode-languageclient": "10.0.0-next.8" + "vscode-languageclient": "^10.0.0-next.13" }, "devDependencies": { "@types/node": "20.x" diff --git a/extensions/json-language-features/server/.npmrc b/extensions/json-language-features/server/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/json-language-features/server/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/json-language-features/server/package-lock.json b/extensions/json-language-features/server/package-lock.json index 1a5e567c9b8..384ce045c9c 100644 --- a/extensions/json-language-features/server/package-lock.json +++ b/extensions/json-language-features/server/package-lock.json @@ -12,8 +12,8 @@ "@vscode/l10n": "^0.0.18", "jsonc-parser": "^3.3.1", "request-light": "^0.8.0", - "vscode-json-languageservice": "^5.4.1", - "vscode-languageserver": "10.0.0-next.6", + "vscode-json-languageservice": "^5.4.3", + "vscode-languageserver": "^10.0.0-next.11", "vscode-uri": "^3.0.8" }, "bin": { @@ -64,9 +64,10 @@ "dev": true }, "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.4.3", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.4.3.tgz", + "integrity": "sha512-NVSEQDloP9NYccuqKg4eI46kutZpwucBY4csBB6FCxbM7AZVoBt0oxTItPVA+ZwhnG1bg/fmiBRAwcGJyNQoPA==", + "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", "jsonc-parser": "^3.3.1", @@ -76,37 +77,38 @@ } }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.4", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.4.tgz", - "integrity": "sha512-zSVIr58lJSMYKIsZ5P7GtBbv1eEx25eNyOf0NmEzxmn1GhUNJAVAb5hkA1poKUwj1FRMwN6CeyWxZypmr8SsQQ==", + "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==", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageserver": { - "version": "10.0.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.6.tgz", - "integrity": "sha512-0Lh1nhQfSxo5Ob+ayYO1QTIsDix2/Lc72Urm1KZrCFxK5zIFYaEh3QFeM9oZih4Rzs0ZkQPXXnoHtpvs5GT+Zw==", + "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==", + "license": "MIT", "dependencies": { - "vscode-languageserver-protocol": "3.17.6-next.6" + "vscode-languageserver-protocol": "3.17.6-next.11" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.6", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.6.tgz", - "integrity": "sha512-naxM9kc/phpl0kAFNVPejMUWUtzFXdPYY/BtQTYtfbBbHf8sceHOrKkmf6yynZRu1A4oFtRZNqV3wyFRTWqUHw==", + "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==", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.4", - "vscode-languageserver-types": "3.17.6-next.4" + "vscode-jsonrpc": "9.0.0-next.6", + "vscode-languageserver-types": "3.17.6-next.5" } }, "node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.4", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.4.tgz", - "integrity": "sha512-SeJTpH/S14EbxOAVaOUoGVqPToqpRTld5QO5Ghig3AlbFJTFF9Wu7srHMfa85L0SX1RYAuuCSFKJVVCxDIk1/Q==" + "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/vscode-languageserver-textdocument": { "version": "1.0.12", diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index 48a6547f1d4..6dcd82930d2 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -15,8 +15,8 @@ "@vscode/l10n": "^0.0.18", "jsonc-parser": "^3.3.1", "request-light": "^0.8.0", - "vscode-json-languageservice": "^5.4.1", - "vscode-languageserver": "10.0.0-next.6", + "vscode-json-languageservice": "^5.4.3", + "vscode-languageserver": "^10.0.0-next.11", "vscode-uri": "^3.0.8" }, "devDependencies": { @@ -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/browser/jsonServerWorkerMain.ts b/extensions/json-language-features/server/src/browser/jsonServerWorkerMain.ts index bf1687d672e..dd991a3c156 100644 --- a/extensions/json-language-features/server/src/browser/jsonServerWorkerMain.ts +++ b/extensions/json-language-features/server/src/browser/jsonServerWorkerMain.ts @@ -22,7 +22,7 @@ const messageHandler = async (e: any) => { } else { l10nLog.push(`l10n: No bundle configured.`); } - await import('./jsonServerMain'); + await import('./jsonServerMain.js'); if (self.onmessage !== messageHandler) { pendingMessages.forEach(msg => self.onmessage?.(msg)); pendingMessages.length = 0; diff --git a/extensions/json-language-features/server/src/node/jsonServerMain.ts b/extensions/json-language-features/server/src/node/jsonServerMain.ts index ad1ae439e59..71da2377c77 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-language-features/server/src/node/jsonServerNodeMain.ts b/extensions/json-language-features/server/src/node/jsonServerNodeMain.ts index 52f048b6340..c30873e5b0e 100644 --- a/extensions/json-language-features/server/src/node/jsonServerNodeMain.ts +++ b/extensions/json-language-features/server/src/node/jsonServerNodeMain.ts @@ -16,7 +16,7 @@ async function setupMain() { l10nLog.push(`l10n: Problems loading ${i10lLocation.toString()} : ${e}`); } } - await import('./jsonServerMain'); + await import('./jsonServerMain.js'); l10nLog.forEach(console.log); } setupMain(); diff --git a/extensions/json-language-features/server/tsconfig.json b/extensions/json-language-features/server/tsconfig.json index 2e8a6e0e354..7526099651f 100644 --- a/extensions/json-language-features/server/tsconfig.json +++ b/extensions/json-language-features/server/tsconfig.json @@ -7,7 +7,8 @@ "lib": [ "ES2020", "WebWorker" - ] + ], + "module": "Node16", }, "include": [ "src/**/*" diff --git a/extensions/json/language-configuration.json b/extensions/json/language-configuration.json index f9ec3fec781..d47efe2587e 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 8844345d672..cd7cfb2fff9 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 31cdd4ce71a..12d38ed31b2 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 3c7203d5d2a..d937ba4f430 100644 --- a/extensions/latex/cgmanifest.json +++ b/extensions/latex/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "jlelong/vscode-latex-basics", "repositoryUrl": "https://github.com/jlelong/vscode-latex-basics", - "commitHash": "969429cb9230a63f9155987f069acd4234d10e1a" + "commitHash": "df6ef817c932d24da5cc72927344a547e463cc65" } }, "license": "MIT", diff --git a/extensions/latex/syntaxes/LaTeX.tmLanguage.json b/extensions/latex/syntaxes/LaTeX.tmLanguage.json index bc97a73bda8..06c4c59c60e 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/969429cb9230a63f9155987f069acd4234d10e1a", + "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": [ @@ -2002,7 +2045,7 @@ ] } }, - "contentName": "punctuation.definition.comment.latex", + "contentName": "comment.line.percentage.latex", "end": "(\\\\end\\{\\2\\})", "name": "meta.function.verbatim.latex" }, @@ -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/TeX.tmLanguage.json b/extensions/latex/syntaxes/TeX.tmLanguage.json index 0cb03e61466..b3a32817482 100644 --- a/extensions/latex/syntaxes/TeX.tmLanguage.json +++ b/extensions/latex/syntaxes/TeX.tmLanguage.json @@ -4,12 +4,12 @@ "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/5d7c2a4e451a932b776f6d9342087be6a1e8c0a1", + "version": "https://github.com/jlelong/vscode-latex-basics/commit/df6ef817c932d24da5cc72927344a547e463cc65", "name": "TeX", "scopeName": "text.tex", "patterns": [ { - "begin": "(?<=^\\s*)((\\\\)iffalse)", + "begin": "(?<=^\\s*)((\\\\)iffalse)(?!\\s*[{}]\\s*\\\\fi)", "beginCaptures": { "1": { "name": "keyword.control.tex" @@ -19,7 +19,7 @@ } }, "contentName": "comment.line.percentage.tex", - "end": "(?<=^\\s*)((\\\\)(?:else|fi))", + "end": "((\\\\)(?:else|fi))", "endCaptures": { "1": { "name": "keyword.control.tex" @@ -32,6 +32,9 @@ { "include": "#comment" }, + { + "include": "#braces" + }, { "include": "#conditionals" } diff --git a/extensions/latex/syntaxes/cpp-grammar-bailout.tmLanguage.json b/extensions/latex/syntaxes/cpp-grammar-bailout.tmLanguage.json index 1f62f492b76..4f70702d0bc 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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "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.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "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.0.5", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", + "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", "dev": true, "dependencies": { "@types/trusted-types": "*" @@ -205,10 +215,11 @@ "dev": true }, "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==", + "devOptional": true, + "license": "MIT" }, "node_modules/@types/vscode-notebook-renderer": { "version": "1.60.0", @@ -223,13 +234,14 @@ "dev": true }, "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": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "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.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" @@ -241,10 +253,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.1", + "resolved": "https://registry.npmjs.org/@vscode/markdown-it-katex/-/markdown-it-katex-1.1.1.tgz", + "integrity": "sha512-3KTlbsRBPJQLE2YmLL7K6nunTlU+W9T5+FjfNdWuIUKgxSS6HWLQHaO3L4MkJi7z7MpIPpY+g4N+cWNBPE/MSA==", "dev": true, + "license": "MIT", "dependencies": { "katex": "^0.16.4" } @@ -278,6 +291,7 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", "dev": true, + "license": "MIT", "engines": { "node": ">= 12" } @@ -352,9 +366,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.2.4", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", + "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } }, "node_modules/domutils": { "version": "3.1.0", @@ -397,14 +415,15 @@ } }, "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" }, @@ -618,15 +637,15 @@ "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.9", + "resolved": "https://registry.npmjs.org/vscode-markdown-languageserver/-/vscode-markdown-languageserver-0.5.0-alpha.9.tgz", + "integrity": "sha512-60jiPHgkstgkyODCN8qCJ4xAOrP/EoKyISmEAcJ7ILT5k2kAJF9JFEl3LvVZ+11HGGMJ2lm1L+lT2/JHvu5Pgg==", "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.8", "vscode-uri": "^3.0.7" }, "engines": { @@ -639,9 +658,9 @@ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" }, "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.8", + "resolved": "https://registry.npmjs.org/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.5.0-alpha.8.tgz", + "integrity": "sha512-b2NgVMZvzI/7hRL32Kcu9neAAPFQzkcf/Fqwlxbz9p1/Q7aIorGACOGGo00s72AJtwjkCJ29eVJwUlFMFbPKqA==", "dependencies": { "@vscode/l10n": "^0.0.10", "node-html-parser": "^6.1.5", diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index a4555a9e588..51b9f80eb3a 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,8 +763,8 @@ "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", + "@vscode/extension-telemetry": "^0.9.8", + "dompurify": "^3.2.4", "highlight.js": "^11.8.0", "markdown-it": "^12.3.2", "markdown-it-front-matter": "^0.2.4", @@ -773,17 +773,17 @@ "punycode": "^2.3.1", "vscode-languageclient": "^8.0.2", "vscode-languageserver-textdocument": "^1.0.11", - "vscode-markdown-languageserver": "^0.5.0-alpha.8", + "vscode-markdown-languageserver": "^0.5.0-alpha.9", "vscode-uri": "^3.0.3" }, "devDependencies": { - "@types/dompurify": "^3.0.2", + "@types/dompurify": "^3.0.5", "@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", + "@vscode/markdown-it-katex": "^1.1.1", "lodash.throttle": "^4.1.1", "vscode-languageserver-types": "^3.17.2", "vscode-markdown-languageservice": "^0.3.0-alpha.3" diff --git a/extensions/markdown-language-features/package.nls.json b/extensions/markdown-language-features/package.nls.json index da567b46665..fe98103daff 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/activeLineMarker.ts b/extensions/markdown-language-features/preview-src/activeLineMarker.ts index 2fbffb7371d..75c1ed7cbc9 100644 --- a/extensions/markdown-language-features/preview-src/activeLineMarker.ts +++ b/extensions/markdown-language-features/preview-src/activeLineMarker.ts @@ -12,20 +12,20 @@ export class ActiveLineMarker { this._update(previous && (previous.codeElement || previous.element)); } - _update(before: HTMLElement | undefined) { + private _update(before: HTMLElement | undefined) { this._unmarkActiveElement(this._current); this._markActiveElement(before); this._current = before; } - _unmarkActiveElement(element: HTMLElement | undefined) { + private _unmarkActiveElement(element: HTMLElement | undefined) { if (!element) { return; } element.classList.toggle('code-active-line', false); } - _markActiveElement(element: HTMLElement | undefined) { + private _markActiveElement(element: HTMLElement | undefined) { if (!element) { return; } diff --git a/extensions/markdown-language-features/preview-src/csp.ts b/extensions/markdown-language-features/preview-src/csp.ts index 49d8cb1efe6..ea960dd4ad3 100644 --- a/extensions/markdown-language-features/preview-src/csp.ts +++ b/extensions/markdown-language-features/preview-src/csp.ts @@ -11,45 +11,45 @@ import { getStrings } from './strings'; * Shows an alert when there is a content security policy violation. */ export class CspAlerter { - private didShow = false; - private didHaveCspWarning = false; + private _didShow = false; + private _didHaveCspWarning = false; - private messaging?: MessagePoster; + private _messaging?: MessagePoster; constructor( - private readonly settingsManager: SettingsManager, + private readonly _settingsManager: SettingsManager, ) { document.addEventListener('securitypolicyviolation', () => { - this.onCspWarning(); + this._onCspWarning(); }); window.addEventListener('message', (event) => { if (event && event.data && event.data.name === 'vscode-did-block-svg') { - this.onCspWarning(); + this._onCspWarning(); } }); } public setPoster(poster: MessagePoster) { - this.messaging = poster; - if (this.didHaveCspWarning) { - this.showCspWarning(); + this._messaging = poster; + if (this._didHaveCspWarning) { + this._showCspWarning(); } } - private onCspWarning() { - this.didHaveCspWarning = true; - this.showCspWarning(); + private _onCspWarning() { + this._didHaveCspWarning = true; + this._showCspWarning(); } - private showCspWarning() { + private _showCspWarning() { const strings = getStrings(); - const settings = this.settingsManager.settings; + const settings = this._settingsManager.settings; - if (this.didShow || settings.disableSecurityWarnings || !this.messaging) { + if (this._didShow || settings.disableSecurityWarnings || !this._messaging) { return; } - this.didShow = true; + this._didShow = true; const notification = document.createElement('a'); notification.innerText = strings.cspAlertMessageText; @@ -59,7 +59,7 @@ export class CspAlerter { notification.setAttribute('role', 'button'); notification.setAttribute('aria-label', strings.cspAlertMessageLabel); notification.onclick = () => { - this.messaging!.postMessage('showPreviewSecuritySelector', { source: settings.source }); + this._messaging!.postMessage('showPreviewSecuritySelector', { source: settings.source }); }; document.body.appendChild(notification); } diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index 33c84e0a384..08c6759f05b 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -7,10 +7,11 @@ 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 type { ToWebviewMessage } from '../types/previewMessaging'; +import { isOfScheme, Schemes } from '../src/util/schemes'; let scrollDisabledCount = 0; @@ -61,8 +62,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.appendChild(markDownHtml.body); + + // Restore const scrollProgress = state.scrollProgress; - addImageContexts(); if (typeof scrollProgress === 'number' && !settings.settings.fragment) { doAfterImagesLoaded(() => { @@ -132,7 +141,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 })); } } diff --git a/extensions/markdown-language-features/preview-src/loading.ts b/extensions/markdown-language-features/preview-src/loading.ts index c6439188766..bebd440374d 100644 --- a/extensions/markdown-language-features/preview-src/loading.ts +++ b/extensions/markdown-language-features/preview-src/loading.ts @@ -5,15 +5,15 @@ import { MessagePoster } from './messaging'; export class StyleLoadingMonitor { - private unloadedStyles: string[] = []; - private finishedLoading: boolean = false; + private _unloadedStyles: string[] = []; + private _finishedLoading: boolean = false; - private poster?: MessagePoster; + private _poster?: MessagePoster; constructor() { const onStyleLoadError = (event: any) => { const source = event.target.dataset.source; - this.unloadedStyles.push(source); + this._unloadedStyles.push(source); }; window.addEventListener('DOMContentLoaded', () => { @@ -25,18 +25,18 @@ export class StyleLoadingMonitor { }); window.addEventListener('load', () => { - if (!this.unloadedStyles.length) { + if (!this._unloadedStyles.length) { return; } - this.finishedLoading = true; - this.poster?.postMessage('previewStyleLoadError', { unloadedStyles: this.unloadedStyles }); + this._finishedLoading = true; + this._poster?.postMessage('previewStyleLoadError', { unloadedStyles: this._unloadedStyles }); }); } public setPoster(poster: MessagePoster): void { - this.poster = poster; - if (this.finishedLoading) { - poster.postMessage('previewStyleLoadError', { unloadedStyles: this.unloadedStyles }); + this._poster = poster; + if (this._finishedLoading) { + poster.postMessage('previewStyleLoadError', { unloadedStyles: this._unloadedStyles }); } } } diff --git a/extensions/markdown-language-features/preview-src/settings.ts b/extensions/markdown-language-features/preview-src/settings.ts index 1bbe3477f25..0fb5d0c2686 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 dabb8f1fc88..e2010edda8a 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 e1a4f2b41ff..382b2a8b6d5 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 9369141465e..e6ede436742 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 00000000000..64b1831df0d --- /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 dd41b5fa924..0c2461548ad 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 9fd3e4f388d..7791d6b19e4 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 661b0bfd05f..a947216fe32 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 4ab245c192b..273fa56a6bb 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) { @@ -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/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index f54c136ad1a..48e579da7fc 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 c8ad4c722fd..f8a7128eb05 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 6c114ae40e5..b5ea76f3608 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 5f6e746a82d..89363a77a86 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -129,7 +129,7 @@ export class MarkdownItEngine implements IMdParser { if (!this._md) { this._md = (async () => { const markdownIt = await import('markdown-it'); - let md: MarkdownIt = markdownIt(await getMarkdownOptions(() => md)); + let md: MarkdownIt = markdownIt.default(await getMarkdownOptions(() => md)); md.linkify.set({ fuzzyLink: false }); for (const plugin of this._contributionProvider.contributions.markdownItPlugins.values()) { @@ -143,7 +143,7 @@ export class MarkdownItEngine implements IMdParser { const frontMatterPlugin = await import('markdown-it-front-matter'); // Extract rules from front matter plugin and apply at a lower precedence let fontMatterRule: any; - frontMatterPlugin({ + frontMatterPlugin.default({ block: { ruler: { before: (_id: any, _id2: any, rule: any) => { fontMatterRule = rule; } @@ -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 331fb5566a0..8a39c9cdf4d 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 7ccbc625b47..2d4186df9ae 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 3b46c2a4322..3aa126da063 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 a786ab83b4f..035e7d74c6d 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 ea4a3f868af..45737c354da 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/)'); + }); }); @@ -181,7 +197,12 @@ suite('createEditAddingLinksForUriList', () => { }); test('Smart should be disabled in math blocks', async () => { - const katex = (await import('@vscode/markdown-it-katex')).default; + + let katex: any = (await import('@vscode/markdown-it-katex')).default; + if (typeof katex === 'object') { + katex = katex.default; + } + const engine = createNewMarkdownEngine(); (await engine.getEngine(undefined)).use(katex); assert.strictEqual( diff --git a/extensions/markdown-language-features/src/util/dom.ts b/extensions/markdown-language-features/src/util/dom.ts index 0f6c00da9da..8bbce79c303 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 f33b807b83e..32dcc021815 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/src/util/openDocumentLink.ts b/extensions/markdown-language-features/src/util/openDocumentLink.ts index 4fc423d3675..285dd91029b 100644 --- a/extensions/markdown-language-features/src/util/openDocumentLink.ts +++ b/extensions/markdown-language-features/src/util/openDocumentLink.ts @@ -49,10 +49,10 @@ export class MdLinkOpener { } } - return vscode.commands.executeCommand('vscode.open', uri, { + return vscode.commands.executeCommand('vscode.open', uri, { selection: resolved.position ? new vscode.Range(resolved.position.line, resolved.position.character, resolved.position.line, resolved.position.character) : undefined, viewColumn: viewColumn ?? getViewColumn(fromResource), - }); + } satisfies vscode.TextDocumentShowOptions); } } } diff --git a/extensions/markdown-language-features/tsconfig.json b/extensions/markdown-language-features/tsconfig.json index 75edc8fdacf..fcd79775de5 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 05d10af6597..686b21bf1a9 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/.npmrc b/extensions/markdown-math/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/markdown-math/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/markdown-math/package-lock.json b/extensions/markdown-math/package-lock.json index 53a0866e61f..73ae907e680 100644 --- a/extensions/markdown-math/package-lock.json +++ b/extensions/markdown-math/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/markdown-it-katex": "^1.1.0" + "@vscode/markdown-it-katex": "^1.1.1" }, "devDependencies": { "@types/markdown-it": "^0.0.0", @@ -32,9 +32,10 @@ "dev": true }, "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.1", + "resolved": "https://registry.npmjs.org/@vscode/markdown-it-katex/-/markdown-it-katex-1.1.1.tgz", + "integrity": "sha512-3KTlbsRBPJQLE2YmLL7K6nunTlU+W9T5+FjfNdWuIUKgxSS6HWLQHaO3L4MkJi7z7MpIPpY+g4N+cWNBPE/MSA==", + "license": "MIT", "dependencies": { "katex": "^0.16.4" } @@ -43,18 +44,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 9669efc2435..6e599ae2a0e 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,9 +110,6 @@ "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" @@ -118,5 +117,8 @@ "repository": { "type": "git", "url": "https://github.com/microsoft/vscode.git" + }, + "dependencies": { + "@vscode/markdown-it-katex": "^1.1.1" } } diff --git a/extensions/media-preview/.npmrc b/extensions/media-preview/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/media-preview/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/media-preview/package-lock.json b/extensions/media-preview/package-lock.json index 68391b8c4be..d26855f3ad2 100644 --- a/extensions/media-preview/package-lock.json +++ b/extensions/media-preview/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0", + "@vscode/extension-telemetry": "^0.9.8", "vscode-uri": "^3.0.6" }, "engines": { @@ -17,127 +17,138 @@ } }, "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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "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.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "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": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "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.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" diff --git a/extensions/media-preview/package.json b/extensions/media-preview/package.json index b42256a2260..7e2b70293fc 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,7 +126,7 @@ "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": "^0.9.8", "vscode-uri": "^3.0.6" }, "repository": { diff --git a/extensions/merge-conflict/.npmrc b/extensions/merge-conflict/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/merge-conflict/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/merge-conflict/package-lock.json b/extensions/merge-conflict/package-lock.json index a57272606cd..5ee68d290f0 100644 --- a/extensions/merge-conflict/package-lock.json +++ b/extensions/merge-conflict/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0" + "@vscode/extension-telemetry": "^0.9.8" }, "devDependencies": { "@types/node": "20.x" @@ -19,118 +19,128 @@ } }, "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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "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.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@types/node": { "version": "20.11.24", @@ -142,13 +152,14 @@ } }, "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": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "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.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" diff --git a/extensions/merge-conflict/package.json b/extensions/merge-conflict/package.json index cdda46fab32..de56c9c22cf 100644 --- a/extensions/merge-conflict/package.json +++ b/extensions/merge-conflict/package.json @@ -166,7 +166,7 @@ } }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.0" + "@vscode/extension-telemetry": "^0.9.8" }, "devDependencies": { "@types/node": "20.x" diff --git a/extensions/microsoft-authentication/.npmrc b/extensions/microsoft-authentication/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/microsoft-authentication/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/microsoft-authentication/.vscodeignore b/extensions/microsoft-authentication/.vscodeignore index 98b90d34d82..e7feddb5da8 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 45600607fc5..395c011b1db 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 8f05b14f02a..4aae40c5f17 100644 --- a/extensions/microsoft-authentication/package-lock.json +++ b/extensions/microsoft-authentication/package-lock.json @@ -10,8 +10,10 @@ "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": "^2.16.2", + "@azure/msal-node-extensions": "^1.5.0", + "@vscode/extension-telemetry": "^0.9.8", + "keytar": "file:./packageMocks/keytar", "vscode-tas-client": "^0.1.84" }, "devDependencies": { @@ -31,19 +33,21 @@ "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": "14.16.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.0.tgz", + "integrity": "sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA==", + "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": "2.16.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.16.2.tgz", + "integrity": "sha512-An7l1hEr0w1HMMh1LU+rtDtqL7/jw74ORlc9Wnh06v7TU/xpG39/Zdr1ZJu3QpjUfKJ+E0/OXMW8DRSWTlh7qQ==", + "license": "MIT", "dependencies": { - "@azure/msal-common": "14.14.2", + "@azure/msal-common": "14.16.0", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" }, @@ -51,119 +55,151 @@ "node": ">=16" } }, + "node_modules/@azure/msal-node-extensions": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@azure/msal-node-extensions/-/msal-node-extensions-1.5.0.tgz", + "integrity": "sha512-UfEyh2xmJHKH64zPS/SbN1bd9adV4ZWGp1j2OSwIuhVraqpUXyXZ1LpDpiUqg/peTgLLtx20qrHOzYT0kKzmxQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "14.16.0", + "@azure/msal-node-runtime": "^0.17.1", + "keytar": "^7.8.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@azure/msal-node-runtime": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/@azure/msal-node-runtime/-/msal-node-runtime-0.17.1.tgz", + "integrity": "sha512-qAfTg+iGJsg+XvD9nmknI63+XuoX32oT+SX4wJdFz7CS6ETVpSHoroHVaUmsTU1H7H0+q1/ZkP988gzPRMYRsg==", + "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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "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.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@types/node": { "version": "20.11.24", @@ -209,13 +245,14 @@ "dev": true }, "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": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "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.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "engines": { "vscode": "^1.75.0" @@ -315,6 +352,10 @@ "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", @@ -376,6 +417,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", @@ -435,6 +479,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 15acb5db286..4170a7787cf 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,38 @@ { "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" + ] + }, + "microsoft-authentication.clientIdVersion": { + "type": "string", + "default": "v1", + "enum": [ + "v2", + "v1" + ], + "enumDescriptions": [ + "%microsoft-authentication.clientIdVersion.enumDescriptions.v2%", + "%microsoft-authentication.clientIdVersion.enumDescriptions.v1%" + ], + "markdownDescription": "%microsoft-authentication.clientIdVersion.description%", + "tags": [ + "onExP", + "experimental" + ] } } } @@ -126,8 +156,10 @@ }, "dependencies": { "@azure/ms-rest-azure-env": "^2.0.0", - "@azure/msal-node": "^2.13.1", - "@vscode/extension-telemetry": "^0.9.0", + "@azure/msal-node": "^2.16.2", + "@azure/msal-node-extensions": "^1.5.0", + "@vscode/extension-telemetry": "^0.9.8", + "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 80cbb32d4ab..ece95ac75c3 100644 --- a/extensions/microsoft-authentication/package.nls.json +++ b/extensions/microsoft-authentication/package.nls.json @@ -3,7 +3,18 @@ "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-authentication.clientIdVersion.description": "The version of the Microsoft Account client ID to use for signing in with a Microsoft account. Only change this if you have been asked to. The default is `v1`.", + "microsoft-authentication.clientIdVersion.enumDescriptions.v1": "Use the v1 Microsoft Account client ID to sign in with a Microsoft account.", + "microsoft-authentication.clientIdVersion.enumDescriptions.v2": "Use the v2 Microsoft Account client ID 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/extensions/microsoft-authentication/packageMocks/dpapi/dpapi.js b/extensions/microsoft-authentication/packageMocks/dpapi/dpapi.js new file mode 100644 index 00000000000..636112a188f --- /dev/null +++ b/extensions/microsoft-authentication/packageMocks/dpapi/dpapi.js @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +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 00000000000..418d592ffdd --- /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 00000000000..0014152ac93 --- /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 00000000000..a8fdeefef98 --- /dev/null +++ b/extensions/microsoft-authentication/src/common/accountAccess.ts @@ -0,0 +1,106 @@ +/*--------------------------------------------------------------------------------------------- + * 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, SecretStorage } from 'vscode'; +import { AccountInfo } from '@azure/msal-node'; + +interface IAccountAccess { + onDidAccountAccessChange: Event; + isAllowedAccess(account: AccountInfo): boolean; + setAllowedAccess(account: AccountInfo, allowed: boolean): void; +} + +export class ScopedAccountAccess implements IAccountAccess { + private readonly _onDidAccountAccessChangeEmitter = new EventEmitter(); + readonly onDidAccountAccessChange = this._onDidAccountAccessChangeEmitter.event; + + private readonly _accountAccessSecretStorage: AccountAccessSecretStorage; + + private value = new Array(); + + constructor( + private readonly _secretStorage: SecretStorage, + private readonly _cloudName: string, + private readonly _clientId: string, + private readonly _authority: string + ) { + this._accountAccessSecretStorage = new AccountAccessSecretStorage(this._secretStorage, this._cloudName, this._clientId, this._authority); + this._accountAccessSecretStorage.onDidChange(() => this.update()); + } + + initialize() { + return 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(); + } + } +} + +export class AccountAccessSecretStorage { + private _disposable: Disposable; + + private readonly _onDidChangeEmitter = new EventEmitter; + readonly onDidChange: Event = this._onDidChangeEmitter.event; + + private readonly _key = `accounts-${this._cloudName}-${this._clientId}-${this._authority}`; + + constructor( + private readonly _secretStorage: SecretStorage, + private readonly _cloudName: string, + private readonly _clientId: string, + private readonly _authority: string + ) { + this._disposable = Disposable.from( + this._onDidChangeEmitter, + this._secretStorage.onDidChange(e => { + if (e.key === this._key) { + this._onDidChangeEmitter.fire(); + } + }) + ); + } + + 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 094861518fc..5f02cc5976d 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/env.ts b/extensions/microsoft-authentication/src/common/env.ts new file mode 100644 index 00000000000..5d19183e70c --- /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 86443c0281f..d572f655f92 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 3fbb0340037..e68663efe43 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/scopeData.ts b/extensions/microsoft-authentication/src/common/scopeData.ts index 4432abfed43..a43f2c431dd 100644 --- a/extensions/microsoft-authentication/src/common/scopeData.ts +++ b/extensions/microsoft-authentication/src/common/scopeData.ts @@ -3,14 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -const DEFAULT_CLIENT_ID = 'aebc6443-996d-45c2-90f0-388ff96faa56'; -const DEFAULT_TENANT = 'organizations'; +import { workspace } from 'vscode'; + +const DEFAULT_CLIENT_ID_V1 = 'aebc6443-996d-45c2-90f0-388ff96faa56'; +const DEFAULT_TENANT_V1 = 'organizations'; +const DEFAULT_CLIENT_ID_V2 = 'c27c220f-ce2f-4904-927d-333864217eeb'; +const DEFAULT_TENANT_V2 = 'common'; const OIDC_SCOPES = ['openid', 'email', 'profile', 'offline_access']; const GRAPH_TACK_ON_SCOPE = 'User.Read'; export class ScopeData { + private readonly _defaultClientId: string; + private readonly _defaultTenant: string; + /** * The full list of scopes including: * * the original scopes passed to the constructor @@ -40,6 +47,14 @@ export class ScopeData { readonly tenant: string; constructor(readonly originalScopes: readonly string[] = []) { + if (workspace.getConfiguration('microsoft-authentication').get<'v1' | 'v2'>('clientIdVersion') === 'v2') { + this._defaultClientId = DEFAULT_CLIENT_ID_V2; + this._defaultTenant = DEFAULT_TENANT_V2; + } else { + this._defaultClientId = DEFAULT_CLIENT_ID_V1; + this._defaultTenant = DEFAULT_TENANT_V1; + } + const modifiedScopes = [...originalScopes]; modifiedScopes.sort(); this.allScopes = modifiedScopes; @@ -55,7 +70,7 @@ export class ScopeData { return current.split('VSCODE_CLIENT_ID:')[1]; } return prev; - }, undefined) ?? DEFAULT_CLIENT_ID; + }, undefined) ?? this._defaultClientId; } private getTenantId(scopes: string[]) { @@ -64,7 +79,7 @@ export class ScopeData { return current.split('VSCODE_TENANT:')[1]; } return prev; - }, undefined) ?? DEFAULT_TENANT; + }, undefined) ?? this._defaultTenant; } private getScopesToSend(scopes: string[]) { diff --git a/extensions/microsoft-authentication/src/extension.ts b/extensions/microsoft-authentication/src/extension.ts index 3f9b5d3a4d1..bd32a82290a 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 f785adad85c..370ea0c76b1 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/node/authProvider.ts b/extensions/microsoft-authentication/src/node/authProvider.ts index 20c8bf305a2..40000e08620 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, 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 { 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'; @@ -43,16 +44,16 @@ export class MsalAuthProvider implements AuthenticationProvider { onDidChangeSessions = this._onDidChangeSessionsEmitter.event; constructor( - context: ExtensionContext, + private readonly _context: ExtensionContext, private readonly _telemetryReporter: MicrosoftAuthenticationTelemetryReporter, private readonly _logger: LogOutputChannel, private readonly _uriHandler: UriEventHandler, private readonly _env: Environment = Environment.AzureCloud ) { - this._disposables = context.subscriptions; + this._disposables = _context.subscriptions; this._publicClientManager = new CachedPublicClientApplicationManager( - context.globalState, - context.secrets, + _context.globalState, + _context.secrets, this._logger, this._env.name ); @@ -85,9 +86,41 @@ export class MsalAuthProvider implements AuthenticationProvider { ); } + /** + * 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, tenant, refreshTokens } of clientTenantMap.values()) { + await this.getOrCreatePublicClientApplication(clientId, tenant, refreshTokens); + } + } + async initialize(): Promise { await this._eventBufferer.bufferEventsAsync(() => this._publicClientManager.initialize()); + if (!this._context.globalState.get('msalMigration', false)) { + await this._migrateSessions(); + } + // Send telemetry for existing accounts for (const cachedPca of this._publicClientManager.getAll()) { for (const account of cachedPca.accounts) { @@ -147,84 +180,75 @@ export class MsalAuthProvider implements AuthenticationProvider { } - async createSession(scopes: readonly string[]): Promise { + async createSession(scopes: readonly string[], options: AuthenticationProviderSessionOptions): Promise { const scopeData = new ScopeData(scopes); // 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; - // Currently, the http://localhost redirect URI is only in the AzureCloud environment... even though I did make the change in the SovereignCloud environments... - // TODO: Remove this check when the change is in all environments. - let useLoopBack = this._env !== Environment.AzureCloud && scopeData.clientId === 'aebc6443-996d-45c2-90f0-388ff96faa56'; - if (!useLoopBack) { - 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 - }); - } 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; - } - } - // 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) { + // 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; + } + 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(); + } + }; + + const flows = getMsalFlows({ + extensionHost: typeof navigator === 'undefined' + ? this._context.extension.extensionKind === ExtensionKind.UI ? ExtensionHost.Local : ExtensionHost.Remote + : ExtensionHost.WebWorker, + }); + + let lastError: Error | undefined; + for (const flow of flows) { + if (flow !== flows[0]) { + try { + await promptToContinue(flow.label); + } finally { this._telemetryReporter.sendLoginFailedEvent(); - throw e; } - - // The user wants to try the loopback client or we got an error likely due to spinning up the server - useLoopBack = true; } - } - - if (useLoopBack) { - const loopbackClient = new UriHandlerLoopbackClient(this._uriHandler, redirectUri, this._logger); try { - result = await cachedPca.acquireTokenInteractive({ - openBrowser: (url: string) => loopbackClient.openBrowser(url), + const result = await flow.trigger({ + cachedPca, scopes: scopeData.scopesToSend, - loopbackClient + loginHint: options.account?.label, + 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 { @@ -257,9 +281,9 @@ export class MsalAuthProvider implements AuthenticationProvider { //#endregion - private async getOrCreatePublicClientApplication(clientId: string, tenant: string): Promise { + private async getOrCreatePublicClientApplication(clientId: string, tenant: string, refreshTokensToMigrate?: string[]): Promise { const authority = new URL(tenant, this._env.activeDirectoryEndpointUrl).toString(); - return await this._publicClientManager.getOrCreate(clientId, authority); + return await this._publicClientManager.getOrCreate(clientId, authority, refreshTokensToMigrate); } private async getAllSessionsForPca( diff --git a/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts b/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts index c679610466a..8d081f1a825 100644 --- a/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts +++ b/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts @@ -3,48 +3,32 @@ * 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 { PublicClientApplication, AccountInfo, Configuration, SilentFlowRequest, AuthenticationResult, InteractiveRequest, LogLevel, RefreshTokenRequest } from '@azure/msal-node'; +import { NativeBrokerPlugin } from '@azure/msal-node-extensions'; import { Disposable, Memento, SecretStorage, LogOutputChannel, window, ProgressLocation, l10n, EventEmitter } from 'vscode'; -import { Delayer, raceCancellationAndTimeoutError } from '../common/async'; +import { raceCancellationAndTimeoutError } from '../common/async'; import { SecretStorageCachePlugin } from '../common/cachePlugin'; import { MsalLoggerOptions } from '../common/loggerOptions'; import { ICachedPublicClientApplication } from '../common/publicClientCache'; +import { ScopedAccountAccess } 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 })}` ); - 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 _accountAccess = new ScopedAccountAccess(this._secretStorage, this._cloudName, this._clientId, this._authority); + private readonly _isBrokerAvailable: boolean; //#region Events @@ -59,12 +43,28 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica constructor( private readonly _clientId: string, private readonly _authority: string, + private readonly _cloudName: string, private readonly _globalMemento: Memento, private readonly _secretStorage: SecretStorage, private readonly _logger: LogOutputChannel ) { - this._pca = new PublicClientApplication(this._config); - this._lastCreated = new Date(); + // TODO:@TylerLeonhardt clean up old use of memento. Remove this in an iteration + this._globalMemento.update(`lastRemoval:${this._clientId}:${this._authority}`, undefined); + const loggerOptions = new MsalLoggerOptions(_logger); + const nativeBrokerPlugin = new NativeBrokerPlugin(); + this._isBrokerAvailable = nativeBrokerPlugin.isBrokerAvailable ?? false; + this._pca = new PublicClientApplication({ + auth: { clientId: _clientId, authority: _authority }, + system: { + loggerOptions: { + correlationId: `${_clientId}] [${_authority}`, + 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, @@ -76,8 +76,11 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica get clientId(): string { return this._clientId; } get authority(): string { return this._authority; } - initialize(): Promise { - return this._update(); + async initialize(): Promise { + if (this._isBrokerAvailable) { + await this._accountAccess.initialize(); + } + await this._sequencer.queue(() => this._update()); } dispose(): void { @@ -86,11 +89,59 @@ 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)); + let 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) { + // 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}] [${this._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}] [${this._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}] [${this._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}] [${this._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}] [${this._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}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] id token is still expired.`); + } + } + } + } + } + } + + if (result.account && !result.fromCache && this._verifyIfUsingBroker(result)) { this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] firing event due to change`); - this._setupRefresh(result); this._onDidAccountsChangeEmitter.fire({ added: [], changed: [result.account], deleted: [] }); } return result; @@ -98,43 +149,93 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica 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( + 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) { + this._logger.debug(`[acquireTokenByRefreshToken] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}]`); + const result = await this._sequencer.queue(() => this._pca.acquireTokenByRefreshToken(request)); + 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; + } + const 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(); + // 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}`); @@ -153,25 +254,6 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica } 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 - ); - } } export class Sequencer { @@ -182,17 +264,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 00000000000..c6f40a943f6 --- /dev/null +++ b/extensions/microsoft-authentication/src/node/flows.ts @@ -0,0 +1,105 @@ +/*--------------------------------------------------------------------------------------------- + * 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; + 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, 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, + 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, 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, + 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 fc6ce38e975..f4f19ff8df5 100644 --- a/extensions/microsoft-authentication/src/node/publicClientCache.ts +++ b/extensions/microsoft-authentication/src/node/publicClientCache.ts @@ -28,9 +28,9 @@ export class CachedPublicClientApplicationManager implements ICachedPublicClient private readonly _globalMemento: Memento, private readonly _secretStorage: SecretStorage, private readonly _logger: LogOutputChannel, - cloudName: string + private readonly _cloudName: string ) { - this._pcasSecretStorage = new PublicClientApplicationsSecretStorage(_secretStorage, cloudName); + this._pcasSecretStorage = new PublicClientApplicationsSecretStorage(_secretStorage, _cloudName); this._disposable = Disposable.from( this._pcasSecretStorage, this._registerSecretStorageHandler(), @@ -94,24 +94,42 @@ export class CachedPublicClientApplicationManager implements ICachedPublicClient Disposable.from(...this._pcaDisposables.values()).dispose(); } - async getOrCreate(clientId: string, authority: string): Promise { + async getOrCreate(clientId: string, authority: string, refreshTokensToMigrate?: string[]): Promise { // Use the clientId and authority as the key const pcasKey = JSON.stringify({ clientId, authority }); let pca = this._pcas.get(pcasKey); if (pca) { this._logger.debug(`[getOrCreate] [${clientId}] [${authority}] PublicClientApplicationManager cache hit`); - return pca; + } else { + 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.`); } - 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}] [${authority}] 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}] [${authority}] Refresh token migrated to PCA.`); + } + } catch (e) { + this._logger.error(`[getOrCreate] [${clientId}] [${authority}] Error migrating refresh token:`, e); + } + } + // reinitialize the PCA so the account is properly cached + await pca.initialize(); + } return pca; } private async _doCreatePublicClientApplication(clientId: string, authority: string, pcasKey: string) { - const pca = new CachedPublicClientApplication(clientId, authority, this._globalMemento, this._secretStorage, this._logger); + const pca = new CachedPublicClientApplication(clientId, authority, this._cloudName, this._globalMemento, this._secretStorage, this._logger); this._pcas.set(pcasKey, pca); const disposable = Disposable.from( pca, @@ -160,11 +178,7 @@ 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}`); } } diff --git a/extensions/microsoft-authentication/tsconfig.json b/extensions/microsoft-authentication/tsconfig.json index 4b9d06d1847..b40c2eb8716 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/.npmrc b/extensions/notebook-renderers/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/notebook-renderers/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/notebook-renderers/src/index.ts b/extensions/notebook-renderers/src/index.ts index 8f5fa908cb9..1c81a249d82 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 dfc7e2b15f8..9dc8f6c845e 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/.npmrc b/extensions/npm/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/npm/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/npm/.vscode/launch.json b/extensions/npm/.vscode/launch.json index 017c8762415..b5cc96144bc 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 215ca927ff4..d5538706019 100644 --- a/extensions/npm/README.md +++ b/extensions/npm/README.md @@ -34,7 +34,8 @@ The extension fetches data from and 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 c8e506904f8..84dfeca86aa 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 38768b47710..027d7f60e54 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'; @@ -91,9 +92,11 @@ class NpmScript extends TreeItem { command: 'vscode.open', arguments: [ this.taskLocation?.uri, - this.taskLocation ? { - selection: new Range(this.taskLocation.range.start, this.taskLocation.range.start) - } : undefined + this.taskLocation ? + { + selection: new Range(this.taskLocation.range.start, this.taskLocation.range.start) + } satisfies TextDocumentShowOptions + : undefined ] }, 'run': { @@ -148,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); } @@ -179,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 0c5b383c6a6..452319671ea 100644 --- a/extensions/npm/src/preferred-pm.ts +++ b/extensions/npm/src/preferred-pm.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import findWorkspaceRoot = require('../node_modules/find-yarn-workspace-root'); -import * as findUp from 'find-up'; +import findUp from 'find-up'; import * as path from 'path'; -import * as whichPM from 'which-pm'; +import whichPM from 'which-pm'; import { Uri, workspace } from 'vscode'; interface PreferredProperties { @@ -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 b3a87e05198..33f2346e815 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 2bb314c9fa1..19a45488c07 100644 --- a/extensions/npm/src/tasks.ts +++ b/extensions/npm/src/tasks.ts @@ -10,7 +10,7 @@ import { } from 'vscode'; import * as path from 'path'; import * as fs from 'fs'; -import * as minimatch from 'minimatch'; +import minimatch from 'minimatch'; import { Utils } from 'vscode-uri'; import { findPreferredPM } from './preferred-pm'; import { readScripts } from './readScripts'; @@ -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 a81a8864a51..39e5fd4092c 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 32a0865af3d..e3e87e1f52d 100644 --- a/extensions/package-lock.json +++ b/extensions/package-lock.json @@ -10,22 +10,23 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "typescript": "5.6.2" + "typescript": "^5.8.2" }, "devDependencies": { - "@parcel/watcher": "2.1.0", - "esbuild": "0.23.0", + "@parcel/watcher": "2.5.1", + "esbuild": "0.25.0", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -290,14 +307,32 @@ "node": ">=18" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -339,13 +376,14 @@ } }, "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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" @@ -355,13 +393,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -371,13 +410,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -387,13 +427,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.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -403,17 +444,306 @@ } }, "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.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { + "detect-libc": "^1.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" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "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.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "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.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "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.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "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.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "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.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "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.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "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.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "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.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "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.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "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.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { "node": ">= 10.0.0" }, @@ -459,12 +789,26 @@ "node": ">=10.13" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "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.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -472,30 +816,31 @@ "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.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" } }, "node_modules/fast-plist": { @@ -561,20 +906,13 @@ } }, "node_modules/node-addon-api": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", - "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", - "dev": true - }, - "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==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz", + "integrity": "sha512-mNcltoe1R8o7STTegSOHdnJNN7s5EUvhoS7ShnTHDyOSd+8H+UdWODq6qSv67PjC8Zc5JRT8+oLAMCr0SIXw7g==", "dev": true, - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" + "license": "MIT", + "engines": { + "node": "^16 || ^18 || >= 20" } }, "node_modules/picomatch": { @@ -602,9 +940,10 @@ } }, "node_modules/typescript": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", - "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/extensions/package.json b/extensions/package.json index 5cd6099229c..d24575b17f5 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -4,17 +4,17 @@ "license": "MIT", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "5.6.2" + "typescript": "^5.8.2" }, "scripts": { "postinstall": "node ./postinstall.mjs" }, "devDependencies": { - "@parcel/watcher": "2.1.0", - "esbuild": "0.23.0", + "@parcel/watcher": "2.5.1", + "esbuild": "0.25.0", "vscode-grammar-updater": "^1.1.0" }, - "resolutions": { + "overrides": { "node-gyp-build": "4.8.1" } } diff --git a/extensions/php-language-features/.npmrc b/extensions/php-language-features/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/php-language-features/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/php-language-features/src/features/phpGlobalFunctions.ts b/extensions/php-language-features/src/features/phpGlobalFunctions.ts index caf9eb11b71..ab1c5487ae8 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-features/src/features/validationProvider.ts b/extensions/php-language-features/src/features/validationProvider.ts index a4adc43617d..b6e38ee27cc 100644 --- a/extensions/php-language-features/src/features/validationProvider.ts +++ b/extensions/php-language-features/src/features/validationProvider.ts @@ -5,7 +5,7 @@ import * as cp from 'child_process'; import { StringDecoder } from 'string_decoder'; -import * as which from 'which'; +import which from 'which'; import * as path from 'path'; import * as vscode from 'vscode'; import { ThrottledDelayer } from './utils/async'; diff --git a/extensions/php/language-configuration.json b/extensions/php/language-configuration.json index f44d7a25cb6..d696ffa2950 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/postinstall.mjs b/extensions/postinstall.mjs index 04e54dd6e2c..f073aa720c9 100644 --- a/extensions/postinstall.mjs +++ b/extensions/postinstall.mjs @@ -26,7 +26,10 @@ function processRoot() { function processLib() { const toDelete = new Set([ 'tsc.js', + '_tsc.js', + 'typescriptServices.js', + '_typescriptServices.js', ]); const libRoot = path.join(root, 'lib'); diff --git a/extensions/python/package.json b/extensions/python/package.json index 543268de715..9ffffb90e06 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/r/cgmanifest.json b/extensions/r/cgmanifest.json index b1ccd271d81..7f012c6076e 100644 --- a/extensions/r/cgmanifest.json +++ b/extensions/r/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "REditorSupport/vscode-R", "repositoryUrl": "https://github.com/REditorSupport/vscode-R", - "commitHash": "ad966f3b2de8a83594f90aa5b51f5b569cf02c08" + "commitHash": "c937cdd39982995d8ee1f1125919f7c2d150b35d" } }, "license": "MIT", - "version": "2.8.1" + "version": "2.8.4" } ], "version": 1 diff --git a/extensions/r/syntaxes/r.tmLanguage.json b/extensions/r/syntaxes/r.tmLanguage.json index d50ab5b6d78..dcdc9622bf7 100644 --- a/extensions/r/syntaxes/r.tmLanguage.json +++ b/extensions/r/syntaxes/r.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/REditorSupport/vscode-R/commit/ad966f3b2de8a83594f90aa5b51f5b569cf02c08", + "version": "https://github.com/REditorSupport/vscode-R/commit/c937cdd39982995d8ee1f1125919f7c2d150b35d", "name": "R", "scopeName": "source.r", "patterns": [ @@ -158,14 +158,6 @@ { "match": "\\b([[:alnum:]_]+)(?=::)", "name": "entity.namespace.r" - }, - { - "match": "\\b([[:alnum:]._]+)\\b", - "name": "variable.other.r" - }, - { - "match": "(`[^`]+`)", - "name": "variable.other.r" } ] }, diff --git a/extensions/razor/cgmanifest.json b/extensions/razor/cgmanifest.json index b8b0e5dae4f..188b960c4ec 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 71055e66e10..27f953ea462 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/.npmrc b/extensions/references-view/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/references-view/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/references-view/src/references/model.ts b/extensions/references-view/src/references/model.ts index 33b5c497f1e..e6c4e23ab93 100644 --- a/extensions/references-view/src/references/model.ts +++ b/extensions/references-view/src/references/model.ts @@ -20,7 +20,7 @@ export class ReferencesTreeInput implements SymbolTreeInput | undefined> { let model: ReferencesModel; if (this._result) { @@ -35,8 +35,7 @@ export class ReferencesTreeInput implements SymbolTreeInput>{ + return { provider, get message() { return model.message; }, navigation: model, @@ -302,7 +301,7 @@ class ReferencesTreeDataProvider implements vscode.TreeDataProvider{ selection: range.with({ end: range.start }) } + { selection: range.with({ end: range.start }) } satisfies vscode.TextDocumentShowOptions ] }; return result; diff --git a/extensions/references-view/src/tree.ts b/extensions/references-view/src/tree.ts index 9432fd2a80f..4a6bf189675 100644 --- a/extensions/references-view/src/tree.ts +++ b/extensions/references-view/src/tree.ts @@ -276,11 +276,11 @@ class TreeInputHistory implements vscode.TreeDataProvider { item: HistoryItem; } const entries = await this.getChildren(); - const picks = entries.map(item => { + const picks = entries.map((item): HistoryPick => ({ label: item.word, description: item.description, item - }); + })); const pick = await vscode.window.showQuickPick(picks, { placeHolder: vscode.l10n.t('Select previous reference search') }); if (pick) { this._reRunHistoryItem(pick.item); diff --git a/extensions/references-view/src/types/model.ts b/extensions/references-view/src/types/model.ts index 89dd001df55..623c41c8d5f 100644 --- a/extensions/references-view/src/types/model.ts +++ b/extensions/references-view/src/types/model.ts @@ -177,7 +177,7 @@ class TypeItemDataProvider implements vscode.TreeDataProvider { title: vscode.l10n.t('Open Type'), arguments: [ element.item.uri, - { selection: element.item.selectionRange.with({ end: element.item.selectionRange.start }) } + { selection: element.item.selectionRange.with({ end: element.item.selectionRange.start }) } satisfies vscode.TextDocumentShowOptions ] }; item.collapsibleState = vscode.TreeItemCollapsibleState.Collapsed; diff --git a/extensions/ruby/cgmanifest.json b/extensions/ruby/cgmanifest.json index 2cd9595e4e5..f54ddba7ce7 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 70dd99f26b5..355e0cd58a9 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 84d619cc51a..785b0e4a928 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 ecb0007f6ea..490f4409c65 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 12247769ce2..a67a4f54609 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 6e5b9fd95ac..ad9d70c2490 100644 --- a/extensions/shared.webpack.config.js +++ b/extensions/shared.webpack.config.js @@ -32,7 +32,11 @@ function withNodeDefaults(/**@type WebpackConfig & { context: string }*/extConfi resolve: { conditionNames: ['import', 'require', 'node-addons', 'node'], mainFields: ['module', 'main'], - extensions: ['.ts', '.js'] // support ts-files and js-files + extensions: ['.ts', '.js'], // support ts-files and js-files + extensionAlias: { + // this is needed to resolve dynamic imports that now require the .js extension + '.js': ['.js', '.ts'], + } }, module: { rules: [{ @@ -52,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 @@ -110,7 +115,11 @@ function withBrowserDefaults(/**@type WebpackConfig & { context: string }*/extCo 'path': require.resolve('path-browserify'), 'os': require.resolve('os-browserify'), 'util': require.resolve('util') - } + }, + extensionAlias: { + // this is needed to resolve dynamic imports that now require the .js extension + '.js': ['.js', '.ts'], + }, }, module: { rules: [{ @@ -123,7 +132,7 @@ function withBrowserDefaults(/**@type WebpackConfig & { context: string }*/extCo loader: 'ts-loader', options: { ...tsLoaderOptions, - ...(additionalOptions ? {} : { configFile: additionalOptions.configFile }), + // ...(additionalOptions ? {} : { configFile: additionalOptions.configFile }), } }, { @@ -196,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 93333abd313..ab9be7b29ad 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/.npmrc b/extensions/simple-browser/.npmrc new file mode 100644 index 00000000000..a9c57709666 --- /dev/null +++ b/extensions/simple-browser/.npmrc @@ -0,0 +1,2 @@ +legacy-peer-deps="true" +timeout=180000 diff --git a/extensions/simple-browser/esbuild-preview.js b/extensions/simple-browser/esbuild-preview.js index cf6e99ca6cb..9c94a67d56f 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 b5d97e00873..c6d9b23636a 100644 --- a/extensions/simple-browser/package-lock.json +++ b/extensions/simple-browser/package-lock.json @@ -9,129 +9,139 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.0" + "@vscode/extension-telemetry": "^0.9.8" }, "devDependencies": { "@types/vscode-webview": "^1.57.0", - "vscode-codicons": "^0.0.14" + "@vscode/codicons": "^0.0.36" }, "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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", + "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", + "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.0.3", + "@microsoft/1ds-core-js": "4.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", + "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.0.4", - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", + "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.0.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", + "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.4", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", + "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "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.4", + "@microsoft/applicationinsights-common": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.4", "@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.2 < 2.x", + "@nevware21/ts-utils": ">= 0.11.3 < 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.11.6", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", + "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "license": "MIT" }, "node_modules/@types/vscode-webview": { "version": "1.57.0", @@ -139,25 +149,26 @@ "integrity": "sha512-x3Cb/SMa1IwRHfSvKaZDZOTh4cNoG505c3NjTqGlMC082m++x/ETUmtYniDsw6SSmYzZXO8KBNhYxR0+VqymqA==", "dev": true }, + "node_modules/@vscode/codicons": { + "version": "0.0.36", + "resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.36.tgz", + "integrity": "sha512-wsNOvNMMJ2BY8rC2N2MNBG7yOowV3ov8KlvUE/AiVUlHKTfWsw3OgAOQduX7h0Un6GssKD3aoTVH+TF3DSQwKQ==", + "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": "0.9.8", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", + "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "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.4", + "@microsoft/1ds-post-js": "^4.3.4", + "@microsoft/applicationinsights-web-basic": "^3.3.4" }, "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 982333b33ef..9aba9ad2503 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": "^0.9.8" }, "devDependencies": { "@types/vscode-webview": "^1.57.0", - "vscode-codicons": "^0.0.14" + "@vscode/codicons": "^0.0.36" }, "repository": { "type": "git", diff --git a/extensions/sql/cgmanifest.json b/extensions/sql/cgmanifest.json index fec241862ef..4a1455ebd31 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 9db45221026..4341bc81528 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 cb1ca02310f..d40d7c7e6e5 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 54095ef5212..e1ceb1f6bc6 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 b18b340f2c6..7d6694cbead 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 00000000000..76b510c710f --- /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 00000000000..017c8762415 --- /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 00000000000..b7c8a281635 --- /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 00000000000..f05a79416be --- /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 00000000000..cd1c1f4afa5 --- /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 00000000000..761d5b01a42 --- /dev/null +++ b/extensions/terminal-suggest/ThirdPartyNotices.txt @@ -0,0 +1,32 @@ +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 00000000000..f39b1fae371 --- /dev/null +++ b/extensions/terminal-suggest/cgmanifest.json @@ -0,0 +1,88 @@ +{ + "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" + } + ], + "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 00000000000..89f3ea28d87 --- /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 00000000000..72075545c05 --- /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 00000000000..8e816c5fe18 --- /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 00000000000..ba6858ea1a5 --- /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 00000000000..624f0166371 --- /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 00000000000..d2c5977e318 --- /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 00000000000..4783411a82b --- /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 00000000000..30b8788a90f --- /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 00000000000..9cbf4ab2ffb --- /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 00000000000..239e60b0c25 --- /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 00000000000..dcc02bff6a2 --- /dev/null +++ b/extensions/terminal-suggest/package.json @@ -0,0 +1,34 @@ +{ + "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" + }, + "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 00000000000..2fff64874d7 --- /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 00000000000..11ec13e560e --- /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 00000000000..11ec13e560e --- /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/pullZshBuiltins.ts b/extensions/terminal-suggest/scripts/pullZshBuiltins.ts new file mode 100644 index 00000000000..cedfc85cd13 --- /dev/null +++ b/extensions/terminal-suggest/scripts/pullZshBuiltins.ts @@ -0,0 +1,268 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { exec } from 'child_process'; +import { promisify } from 'util'; +import * as fs from 'fs/promises'; +import * as path from 'path'; +import { platform } from 'os'; + +if (platform() === 'win32') { + console.error('\x1b[31mThis command is not supported on Windows\x1b[0m'); + process.exit(1); +} + +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'], +]); + +const execAsync = promisify(exec); + +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); + } +}; + +const copyright = ` +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/`; + +main(); diff --git a/extensions/terminal-suggest/scripts/update-specs.js b/extensions/terminal-suggest/scripts/update-specs.js new file mode 100644 index 00000000000..5f6e0ec5717 --- /dev/null +++ b/extensions/terminal-suggest/scripts/update-specs.js @@ -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. + *--------------------------------------------------------------------------------------------*/ + +// @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\';' + ] +] + +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]); + } + 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 00000000000..0f129190379 --- /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 00000000000..4efd5bbf20d --- /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 00000000000..ee38ca1a526 --- /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 00000000000..89d01dc536f --- /dev/null +++ b/extensions/terminal-suggest/src/completions/code-insiders.ts @@ -0,0 +1,13 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import code from './code'; + +const codeInsidersCompletionSpec: Fig.Spec = { + ...code, + name: 'code-insiders', + description: 'Visual Studio 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 00000000000..c514a32e2e1 --- /dev/null +++ b/extensions/terminal-suggest/src/completions/code.ts @@ -0,0 +1,318 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +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]', + // TODO: Support :line[:character] completion? + 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', + }, +]; + +const extensionManagementOptions: 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: { + // TODO: Create extension ID generator + name: 'extension-id[@version] | path-to-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: { + // TODO: Create extension ID generator + name: 'extension-id', + }, + }, + { + name: '--enable-proposed-api', + description: + 'Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually', + }, +]; + +const troubleshootingOptions: 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: { + // TODO: Create extension ID generator + name: 'extension-id', + }, + }, + { + 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', + }, +]; + +const codeCompletionSpec: Fig.Spec = { + name: 'code', + description: 'Visual Studio Code', + args: { + template: ['filepaths', 'folders'], + isVariadic: true, + }, + options: [ + ...commonOptions, + ...extensionManagementOptions, + ...troubleshootingOptions, + ], +}; + +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 00000000000..44dcb981445 --- /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/code/browser/workbench/workbench-dev.html b/src/vs/code/browser/workbench/workbench-dev.html index be6d30bfb98..140030d8abe 100644 --- a/src/vs/code/browser/workbench/workbench-dev.html +++ b/src/vs/code/browser/workbench/workbench-dev.html @@ -28,6 +28,7 @@ + @@ -35,31 +36,32 @@ - - + diff --git a/src/vs/code/browser/workbench/workbench.esm.html b/src/vs/code/browser/workbench/workbench.esm.html deleted file mode 100644 index 77881982735..00000000000 --- a/src/vs/code/browser/workbench/workbench.esm.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index a4e95165cfb..77881982735 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -25,7 +25,7 @@ - + @@ -33,38 +33,16 @@ - - - + - - - + + diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index b98867e7ac3..faa711a30c5 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -43,6 +43,14 @@ const enum AESConstants { IV_LENGTH = 12, } +class NetworkError extends Error { + constructor(inner: Error) { + super(inner.message); + this.name = inner.name; + this.stack = inner.stack; + } +} + class ServerKeyedAESCrypto implements ISecretStorageCrypto { private _serverKey: Uint8Array | undefined; @@ -138,7 +146,7 @@ class ServerKeyedAESCrypto implements ISecretStorageCrypto { } let attempt = 0; - let lastError: unknown | undefined; + let lastError: Error | undefined; while (attempt <= 3) { try { @@ -146,14 +154,14 @@ class ServerKeyedAESCrypto implements ISecretStorageCrypto { if (!res.ok) { throw new Error(res.statusText); } - const serverKey = new Uint8Array(await await res.arrayBuffer()); + const serverKey = new Uint8Array(await res.arrayBuffer()); if (serverKey.byteLength !== AESConstants.KEY_LENGTH / 8) { throw Error(`The key retrieved by the server is not ${AESConstants.KEY_LENGTH} bit long.`); } this._serverKey = serverKey; return this._serverKey; } catch (e) { - lastError = e; + lastError = e instanceof Error ? e : new Error(String(e)); attempt++; // exponential backoff @@ -161,7 +169,10 @@ class ServerKeyedAESCrypto implements ISecretStorageCrypto { } } - throw lastError; + if (lastError) { + throw new NetworkError(lastError); + } + throw new Error('Unknown error'); } } @@ -187,7 +198,9 @@ export class LocalStorageSecretStorageProvider implements ISecretStorageProvider } catch (err) { // TODO: send telemetry console.error('Failed to decrypt secrets from localStorage', err); - localStorage.removeItem(this._storageKey); + if (!(err instanceof NetworkError)) { + localStorage.removeItem(this._storageKey); + } } } diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index faadd1cdcf2..ce66f6009f8 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -51,9 +51,8 @@ import { DiskFileSystemProvider } from '../../platform/files/node/diskFileSystem import { SyncDescriptor } from '../../platform/instantiation/common/descriptors.js'; import { IInstantiationService, ServicesAccessor } from '../../platform/instantiation/common/instantiation.js'; import { ServiceCollection } from '../../platform/instantiation/common/serviceCollection.js'; -import { IProcessMainService, IIssueMainService } from '../../platform/issue/common/issue.js'; -import { IssueMainService } from '../../platform/issue/electron-main/issueMainService.js'; -import { ProcessMainService } from '../../platform/issue/electron-main/processMainService.js'; +import { IProcessMainService } from '../../platform/process/common/process.js'; +import { ProcessMainService } from '../../platform/process/electron-main/processMainService.js'; import { IKeyboardLayoutMainService, KeyboardLayoutMainService } from '../../platform/keyboardLayout/electron-main/keyboardLayoutMainService.js'; import { ILaunchMainService, LaunchMainService } from '../../platform/launch/electron-main/launchMainService.js'; import { ILifecycleMainService, LifecycleMainPhase, ShutdownReason } from '../../platform/lifecycle/electron-main/lifecycleMainService.js'; @@ -103,7 +102,7 @@ import { ExtensionsScannerService } from '../../platform/extensionManagement/nod import { UserDataProfilesHandler } from '../../platform/userDataProfile/electron-main/userDataProfilesHandler.js'; import { ProfileStorageChangesListenerChannel } from '../../platform/userDataProfile/electron-main/userDataProfileStorageIpc.js'; import { Promises, RunOnceScheduler, runWhenGlobalIdle } from '../../base/common/async.js'; -import { resolveMachineId, resolveSqmId, resolvedevDeviceId } from '../../platform/telemetry/electron-main/telemetryUtils.js'; +import { resolveMachineId, resolveSqmId, resolvedevDeviceId, validatedevDeviceId } from '../../platform/telemetry/electron-main/telemetryUtils.js'; import { ExtensionsProfileScannerService } from '../../platform/extensionManagement/node/extensionsProfileScannerService.js'; import { LoggerChannel } from '../../platform/log/electron-main/logIpc.js'; import { ILoggerMainService } from '../../platform/log/electron-main/loggerService.js'; @@ -119,13 +118,15 @@ import { IAuxiliaryWindowsMainService } from '../../platform/auxiliaryWindow/ele import { AuxiliaryWindowsMainService } from '../../platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService.js'; import { normalizeNFC } from '../../base/common/normalization.js'; import { ICSSDevelopmentService, CSSDevelopmentService } from '../../platform/cssDev/node/cssDevService.js'; -import { ExtensionSignatureVerificationService, IExtensionSignatureVerificationService } from '../../platform/extensionManagement/node/extensionSignatureVerificationService.js'; -import { LLMMessageChannel } from '../../workbench/contrib/void/electron-main/llmMessageChannel.js'; +// in theory this is not allowed +// ignore the eslint errors below import { IMetricsService } from '../../workbench/contrib/void/common/metricsService.js'; +import { IVoidUpdateService } from '../../workbench/contrib/void/common/voidUpdateService.js'; import { MetricsMainService } from '../../workbench/contrib/void/electron-main/metricsMainService.js'; import { VoidMainUpdateService } from '../../workbench/contrib/void/electron-main/voidUpdateMainService.js'; -import { IVoidUpdateService } from '../../workbench/contrib/void/common/voidUpdateService.js'; +import { LLMMessageChannel } from '../../workbench/contrib/void/electron-main/llmMessageChannel.js'; + /** * The main VS Code application. There will only ever be one instance, * even if the user starts many instances (e.g. from the command line). @@ -168,18 +169,32 @@ export class CodeApplication extends Disposable { // !!! DO NOT CHANGE without consulting the documentation !!! // + const isUrlFromWindow = (requestingUrl?: string | undefined) => requestingUrl?.startsWith(`${Schemas.vscodeFileResource}://${VSCODE_AUTHORITY}`); const isUrlFromWebview = (requestingUrl: string | undefined) => requestingUrl?.startsWith(`${Schemas.vscodeWebview}://`); const allowedPermissionsInWebview = new Set([ 'clipboard-read', 'clipboard-sanitized-write', + // TODO(deepak1556): Should be removed once migration is complete + // https://github.com/microsoft/vscode/issues/239228 + 'deprecated-sync-clipboard-read', + ]); + + const allowedPermissionsInCore = new Set([ + 'media', + 'local-fonts', + // TODO(deepak1556): Should be removed once migration is complete + // https://github.com/microsoft/vscode/issues/239228 + 'deprecated-sync-clipboard-read', ]); session.defaultSession.setPermissionRequestHandler((_webContents, permission, callback, details) => { if (isUrlFromWebview(details.requestingUrl)) { return callback(allowedPermissionsInWebview.has(permission)); } - + if (isUrlFromWindow(details.requestingUrl)) { + return callback(allowedPermissionsInCore.has(permission)); + } return callback(false); }); @@ -187,7 +202,9 @@ export class CodeApplication extends Disposable { if (isUrlFromWebview(details.requestingUrl)) { return allowedPermissionsInWebview.has(permission); } - + if (isUrlFromWindow(details.requestingUrl)) { + return allowedPermissionsInCore.has(permission); + } return false; }); @@ -199,7 +216,7 @@ export class CodeApplication extends Disposable { const supportedSvgSchemes = new Set([Schemas.file, Schemas.vscodeFileResource, Schemas.vscodeRemoteResource, Schemas.vscodeManagedRemoteResource, 'devtools']); // But allow them if they are made from inside an webview - const isSafeFrame = (requestFrame: WebFrameMain | undefined): boolean => { + const isSafeFrame = (requestFrame: WebFrameMain | null | undefined): boolean => { for (let frame: WebFrameMain | null | undefined = requestFrame; frame; frame = frame.parent) { if (frame.url.startsWith(`${Schemas.vscodeWebview}://`)) { return true; @@ -612,7 +629,7 @@ export class CodeApplication extends Disposable { // Setup Protocol URL Handlers const initialProtocolUrls = await appInstantiationService.invokeFunction(accessor => this.setupProtocolUrlHandlers(accessor, mainProcessElectronServer)); - // Setup vscode-remote-resource protocol handler. + // Setup vscode-remote-resource protocol handler this.setupManagedRemoteResourceUrlHandler(mainProcessElectronServer); // Signal phase: ready - before opening first window @@ -629,7 +646,14 @@ export class CodeApplication extends Disposable { // Set lifecycle phase to `Eventually` after a short delay and when idle (min 2.5sec, max 5sec) const eventuallyPhaseScheduler = this._register(new RunOnceScheduler(() => { - this._register(runWhenGlobalIdle(() => this.lifecycleMainService.phase = LifecycleMainPhase.Eventually, 2500)); + this._register(runWhenGlobalIdle(() => { + + // Signal phase: eventually + this.lifecycleMainService.phase = LifecycleMainPhase.Eventually; + + // Eventually Post Open Window Tasks + this.eventuallyAfterWindowOpen(); + }, 2500)); }, 2500)); eventuallyPhaseScheduler.schedule(); } @@ -1028,9 +1052,6 @@ export class CodeApplication extends Disposable { services.set(IDiagnosticsMainService, new SyncDescriptor(DiagnosticsMainService, undefined, false /* proxied to other processes */)); services.set(IDiagnosticsService, ProxyChannel.toService(getDelayedChannel(sharedProcessReady.then(client => client.getChannel('diagnostics'))))); - // Issues - services.set(IIssueMainService, new SyncDescriptor(IssueMainService, [this.userEnv])); - // Process services.set(IProcessMainService, new SyncDescriptor(ProcessMainService, [this.userEnv])); @@ -1123,11 +1144,6 @@ export class CodeApplication extends Disposable { // Dev Only: CSS service (for ESM) services.set(ICSSDevelopmentService, new SyncDescriptor(CSSDevelopmentService, undefined, true)); - if (this.productService.quality !== 'stable') { - // extensions signature verification service - services.set(IExtensionSignatureVerificationService, new SyncDescriptor(ExtensionSignatureVerificationService, undefined, true)); - } - // Init services that require it await Promises.settled([ backupMainService.initialize(), @@ -1169,21 +1185,10 @@ export class CodeApplication extends Disposable { mainProcessElectronServer.registerChannel('userDataProfiles', userDataProfilesService); sharedProcessClient.then(client => client.registerChannel('userDataProfiles', userDataProfilesService)); - if (this.productService.quality !== 'stable') { - // Extension signature verification service - const extensionSignatureVerificationService = accessor.get(IExtensionSignatureVerificationService); - sharedProcessClient.then(client => client.registerChannel('signatureVerificationService', - ProxyChannel.fromService(extensionSignatureVerificationService, disposables))); - } - // Update const updateChannel = new UpdateChannel(accessor.get(IUpdateService)); mainProcessElectronServer.registerChannel('update', updateChannel); - // Issues - const issueChannel = ProxyChannel.fromService(accessor.get(IIssueMainService), disposables); - mainProcessElectronServer.registerChannel('issue', issueChannel); - // Process const processChannel = ProxyChannel.fromService(accessor.get(IProcessMainService), disposables); mainProcessElectronServer.registerChannel('process', processChannel); @@ -1452,7 +1457,7 @@ export class CodeApplication extends Disposable { try { const argvContent = await this.fileService.readFile(this.environmentMainService.argvResource); const argvString = argvContent.value.toString(); - const argvJSON = parse(argvString); + const argvJSON = parse<{ 'enable-crash-reporter'?: boolean }>(argvString); const telemetryLevel = getTelemetryLevel(this.configurationService); const enableCrashReporter = telemetryLevel >= TelemetryLevel.CRASH; @@ -1488,4 +1493,11 @@ export class CodeApplication extends Disposable { this.windowsMainService?.sendToFocused('vscode:showArgvParseWarning'); } } + + private eventuallyAfterWindowOpen(): void { + + // Validate Device ID is up to date (delay this as it has shown significant perf impact) + // Refs: https://github.com/microsoft/vscode/issues/234064 + validatedevDeviceId(this.stateService, this.logService); + } } diff --git a/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css b/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css index 3baf48ce1af..4d915c2023b 100644 --- a/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css +++ b/src/vs/code/electron-sandbox/processExplorer/media/processExplorer.css @@ -13,7 +13,7 @@ html { .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, "Nanum Gothic", "Apple SD Gothic Neo", "AppleGothic", sans-serif; } +.mac:lang(ko) { font-family: -apple-system, BlinkMacSystemFont, "Apple SD Gothic Neo", "Nanum Gothic", "AppleGothic", 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; } diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorer-dev.esm.html b/src/vs/code/electron-sandbox/processExplorer/processExplorer-dev.esm.html deleted file mode 100644 index 19d194fc1c5..00000000000 --- a/src/vs/code/electron-sandbox/processExplorer/processExplorer-dev.esm.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - -
- - - - - - diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorer-dev.html b/src/vs/code/electron-sandbox/processExplorer/processExplorer-dev.html index 5bdf62c8230..19d194fc1c5 100644 --- a/src/vs/code/electron-sandbox/processExplorer/processExplorer-dev.html +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorer-dev.html @@ -15,6 +15,8 @@ ; script-src 'self' + blob: + 'nonce-0c6a828f1297' ; style-src 'self' diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorer.esm.html b/src/vs/code/electron-sandbox/processExplorer/processExplorer.esm.html deleted file mode 100644 index d2747202950..00000000000 --- a/src/vs/code/electron-sandbox/processExplorer/processExplorer.esm.html +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - -
- - - - - diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorer.html b/src/vs/code/electron-sandbox/processExplorer/processExplorer.html index 845d024e626..d2747202950 100644 --- a/src/vs/code/electron-sandbox/processExplorer/processExplorer.html +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorer.html @@ -15,6 +15,8 @@ ; script-src 'self' + blob: + 'nonce-0c6a828f1297' ; style-src 'self' @@ -28,6 +30,7 @@ 'self' ; "> + @@ -35,5 +38,5 @@ - + diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorer.js b/src/vs/code/electron-sandbox/processExplorer/processExplorer.js deleted file mode 100644 index 98e67e7c25c..00000000000 --- a/src/vs/code/electron-sandbox/processExplorer/processExplorer.js +++ /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. - *--------------------------------------------------------------------------------------------*/ - -//@ts-check -'use strict'; - -(function () { - - /** - * @import { ISandboxConfiguration } from '../../../base/parts/sandbox/common/sandboxTypes' - */ - - const bootstrapWindow = bootstrapWindowLib(); - - // Load process explorer into window - bootstrapWindow.load(['vs/code/electron-sandbox/processExplorer/processExplorerMain'], function (processExplorer, configuration) { - return processExplorer.startup(configuration); - }, { - configureDeveloperSettings: function () { - return { - forceEnableDeveloperKeybindings: true - }; - }, - }); - - /** - * @returns {{ - * load: ( - * modules: string[], - * resultCallback: (result: any, configuration: ISandboxConfiguration) => unknown, - * options?: { - * configureDeveloperSettings?: (config: ISandboxConfiguration) => { - * forceEnableDeveloperKeybindings?: boolean, - * disallowReloadKeybinding?: boolean, - * removeDeveloperKeybindingsAfterLoad?: boolean - * } - * } - * ) => Promise - * }} - */ - function bootstrapWindowLib() { - // @ts-ignore (defined in bootstrap-window.js) - return window.MonacoBootstrapWindow; - } -}()); diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorer.ts b/src/vs/code/electron-sandbox/processExplorer/processExplorer.ts new file mode 100644 index 00000000000..17be24c02f0 --- /dev/null +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorer.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * 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('../../../platform/window/electron-sandbox/window.js').IBootstrapWindow; + type IProcessExplorerMain = import('./processExplorerMain.js').IProcessExplorerMain; + type ProcessExplorerWindowConfiguration = import('../../../platform/process/common/process.js').ProcessExplorerWindowConfiguration; + + const bootstrapWindow: IBootstrapWindow = (window as any).MonacoBootstrapWindow; // defined by bootstrap-window.ts + + const { result, configuration } = await bootstrapWindow.load('vs/code/electron-sandbox/processExplorer/processExplorerMain', { + configureDeveloperSettings: function () { + return { + forceEnableDeveloperKeybindings: true + }; + }, + }); + + result.startup(configuration); +}()); diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts index 6ee7d3e2963..616165dc977 100644 --- a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts @@ -6,7 +6,8 @@ import './media/processExplorer.css'; import '../../../base/browser/ui/codicons/codiconStyles.js'; // make sure codicon css is loaded import { localize } from '../../../nls.js'; -import { $, append, createStyleSheet } from '../../../base/browser/dom.js'; +import { $, append } from '../../../base/browser/dom.js'; +import { createStyleSheet } from '../../../base/browser/domStylesheets.js'; import { IListVirtualDelegate } from '../../../base/browser/ui/list/list.js'; import { DataTree } from '../../../base/browser/ui/tree/dataTree.js'; import { IDataSource, ITreeNode, ITreeRenderer } from '../../../base/browser/ui/tree/tree.js'; @@ -18,7 +19,7 @@ import { ipcRenderer } from '../../../base/parts/sandbox/electron-sandbox/global import { IRemoteDiagnosticError, isRemoteDiagnosticError } from '../../../platform/diagnostics/common/diagnostics.js'; import { ByteSize } from '../../../platform/files/common/files.js'; import { ElectronIPCMainProcessService } from '../../../platform/ipc/electron-sandbox/mainProcessService.js'; -import { ProcessExplorerData, ProcessExplorerStyles, ProcessExplorerWindowConfiguration } from '../../../platform/issue/common/issue.js'; +import { ProcessExplorerData, ProcessExplorerStyles, ProcessExplorerWindowConfiguration } from '../../../platform/process/common/process.js'; import { INativeHostService } from '../../../platform/native/common/native.js'; import { NativeHostService } from '../../../platform/native/common/nativeHostService.js'; import { getIconsStyleSheet } from '../../../platform/theme/browser/iconsStyleSheet.js'; @@ -591,6 +592,10 @@ function createCodiconStyleSheet() { delayer.schedule(); } +export interface IProcessExplorerMain { + startup(configuration: ProcessExplorerWindowConfiguration): void; +} + export function startup(configuration: ProcessExplorerWindowConfiguration): void { const platformClass = configuration.data.platform === 'win32' ? 'windows' : configuration.data.platform === 'linux' ? 'linux' : 'mac'; mainWindow.document.body.classList.add(platformClass); // used by our fonts diff --git a/src/vs/code/electron-sandbox/workbench/workbench-dev.esm.html b/src/vs/code/electron-sandbox/workbench/workbench-dev.esm.html deleted file mode 100644 index ea5cbee848a..00000000000 --- a/src/vs/code/electron-sandbox/workbench/workbench-dev.esm.html +++ /dev/null @@ -1,75 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/src/vs/code/electron-sandbox/workbench/workbench-dev.html b/src/vs/code/electron-sandbox/workbench/workbench-dev.html index b1be2b7527c..ef6ebd56d9d 100644 --- a/src/vs/code/electron-sandbox/workbench/workbench-dev.html +++ b/src/vs/code/electron-sandbox/workbench/workbench-dev.html @@ -28,6 +28,7 @@ 'self' 'unsafe-eval' blob: + 'nonce-0c6a828f1297' ; style-src 'self' @@ -48,6 +49,7 @@ 'script' ; trusted-types + vscode-bootstrapImportMap amdLoader cellRendererEditorText defaultWorkerFactory @@ -60,6 +62,7 @@ notebookRenderer stickyScrollViewLayer tokenizeToString + notebookChatEditController ; "/> @@ -68,7 +71,6 @@ - diff --git a/src/vs/code/electron-sandbox/workbench/workbench.esm.html b/src/vs/code/electron-sandbox/workbench/workbench.esm.html deleted file mode 100644 index 1e89448e605..00000000000 --- a/src/vs/code/electron-sandbox/workbench/workbench.esm.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/vs/code/electron-sandbox/workbench/workbench.html b/src/vs/code/electron-sandbox/workbench/workbench.html index eb525bd59f6..0a0fb8242e0 100644 --- a/src/vs/code/electron-sandbox/workbench/workbench.html +++ b/src/vs/code/electron-sandbox/workbench/workbench.html @@ -60,13 +60,17 @@ notebookRenderer stickyScrollViewLayer tokenizeToString + notebookChatEditController ; "/> + + + - + diff --git a/src/vs/code/electron-sandbox/workbench/workbench.js b/src/vs/code/electron-sandbox/workbench/workbench.js deleted file mode 100644 index 0ddbcd49ba0..00000000000 --- a/src/vs/code/electron-sandbox/workbench/workbench.js +++ /dev/null @@ -1,315 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * 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'; - -(function () { - - /** - * @import {INativeWindowConfiguration} from '../../../platform/window/common/window' - * @import {NativeParsedArgs} from '../../../platform/environment/common/argv' - * @import {ISandboxConfiguration} from '../../../base/parts/sandbox/common/sandboxTypes' - */ - - const bootstrapWindow = bootstrapWindowLib(); - - // Add a perf entry right from the top - performance.mark('code/didStartRenderer'); - - // Load workbench main JS and CSS all in parallel. This is an - // optimization to prevent a waterfall of loading to happen, because - // we know for a fact that workbench.desktop.main will depend on - // the related CSS counterpart. - bootstrapWindow.load([ - 'vs/workbench/workbench.desktop.main', - 'vs/css!vs/workbench/workbench.desktop.main' - ], - function (desktopMain, configuration) { - - // Mark start of workbench - performance.mark('code/didLoadWorkbenchMain'); - - return desktopMain.main(configuration); - }, - { - configureDeveloperSettings: function (windowConfig) { - return { - // disable automated devtools opening on error when running extension tests - // as this can lead to nondeterministic test execution (devtools steals focus) - forceDisableShowDevtoolsOnError: typeof windowConfig.extensionTestsPath === 'string' || windowConfig['enable-smoke-test-driver'] === true, - // enable devtools keybindings in extension development window - forceEnableDeveloperKeybindings: Array.isArray(windowConfig.extensionDevelopmentPath) && windowConfig.extensionDevelopmentPath.length > 0, - removeDeveloperKeybindingsAfterLoad: true - }; - }, - canModifyDOM: function (windowConfig) { - showSplash(windowConfig); - }, - beforeLoaderConfig: function (loaderConfig) { - // @ts-ignore - loaderConfig.recordStats = true; - }, - beforeRequire: function (windowConfig) { - performance.mark('code/willLoadWorkbenchMain'); - - // Code windows have a `vscodeWindowId` property to identify them - Object.defineProperty(window, 'vscodeWindowId', { - get: () => windowConfig.windowId - }); - - // It looks like browsers only lazily enable - // the element when needed. Since we - // leverage canvas elements in our code in many - // locations, we try to help the browser to - // initialize canvas when it is idle, right - // before we wait for the scripts to be loaded. - window.requestIdleCallback(() => { - const canvas = document.createElement('canvas'); - const context = canvas.getContext('2d'); - context?.clearRect(0, 0, canvas.width, canvas.height); - canvas.remove(); - }, { timeout: 50 }); - } - } - ); - - //#region Helpers - - /** - * @returns {{ - * load: ( - * modules: string[], - * resultCallback: (result: any, configuration: INativeWindowConfiguration & NativeParsedArgs) => unknown, - * options?: { - * configureDeveloperSettings?: (config: INativeWindowConfiguration & NativeParsedArgs) => { - * forceDisableShowDevtoolsOnError?: boolean, - * forceEnableDeveloperKeybindings?: boolean, - * disallowReloadKeybinding?: boolean, - * removeDeveloperKeybindingsAfterLoad?: boolean - * }, - * canModifyDOM?: (config: INativeWindowConfiguration & NativeParsedArgs) => void, - * beforeLoaderConfig?: (loaderConfig: object) => void, - * beforeRequire?: (config: ISandboxConfiguration) => void - * } - * ) => Promise - * }} - */ - function bootstrapWindowLib() { - // @ts-ignore (defined in bootstrap-window.js) - return window.MonacoBootstrapWindow; - } - - /** - * @param {INativeWindowConfiguration & NativeParsedArgs} configuration - */ - function showSplash(configuration) { - performance.mark('code/willShowPartsSplash'); - - let data = configuration.partsSplash; - - if (data) { - // high contrast mode has been turned by the OS -> ignore stored colors and layouts - if (configuration.autoDetectHighContrast && configuration.colorScheme.highContrast) { - if ((configuration.colorScheme.dark && data.baseTheme !== 'hc-black') || (!configuration.colorScheme.dark && data.baseTheme !== 'hc-light')) { - data = undefined; - } - } else if (configuration.autoDetectColorScheme) { - // OS color scheme is tracked and has changed - if ((configuration.colorScheme.dark && data.baseTheme !== 'vs-dark') || (!configuration.colorScheme.dark && data.baseTheme !== 'vs')) { - data = undefined; - } - } - } - - // developing an extension -> ignore stored layouts - if (data && configuration.extensionDevelopmentPath) { - data.layoutInfo = undefined; - } - - // minimal color configuration (works with or without persisted data) - let baseTheme; - let shellBackground; - let shellForeground; - if (data) { - baseTheme = data.baseTheme; - shellBackground = data.colorInfo.editorBackground; - shellForeground = data.colorInfo.foreground; - } else if (configuration.autoDetectHighContrast && configuration.colorScheme.highContrast) { - if (configuration.colorScheme.dark) { - baseTheme = 'hc-black'; - shellBackground = '#000000'; - shellForeground = '#FFFFFF'; - } else { - baseTheme = 'hc-light'; - shellBackground = '#FFFFFF'; - shellForeground = '#000000'; - } - } else if (configuration.autoDetectColorScheme) { - if (configuration.colorScheme.dark) { - baseTheme = 'vs-dark'; - shellBackground = '#1E1E1E'; - shellForeground = '#CCCCCC'; - } else { - baseTheme = 'vs'; - shellBackground = '#FFFFFF'; - shellForeground = '#000000'; - } - } - - const style = document.createElement('style'); - style.className = 'initialShellColors'; - document.head.appendChild(style); - style.textContent = `body { - background-color: ${shellBackground}; - color: ${shellForeground}; - margin: 0; - padding: 0; - }`; - - // set zoom level as soon as possible - // @ts-ignore - if (typeof data?.zoomLevel === 'number' && typeof globalThis.vscode?.webFrame?.setZoomLevel === 'function') { - // @ts-ignore - globalThis.vscode.webFrame.setZoomLevel(data.zoomLevel); - } - - // restore parts if possible (we might not always store layout info) - if (data?.layoutInfo) { - const { layoutInfo, colorInfo } = data; - - const splash = document.createElement('div'); - splash.id = 'monaco-parts-splash'; - splash.className = baseTheme ?? 'vs-dark'; - - if (layoutInfo.windowBorder && colorInfo.windowBorder) { - splash.setAttribute('style', ` - position: relative; - height: calc(100vh - 2px); - width: calc(100vw - 2px); - border: 1px solid var(--window-border-color); - `); - splash.style.setProperty('--window-border-color', colorInfo.windowBorder); - - if (layoutInfo.windowBorderRadius) { - splash.style.borderRadius = layoutInfo.windowBorderRadius; - } - } - - // ensure there is enough space - layoutInfo.sideBarWidth = Math.min(layoutInfo.sideBarWidth, window.innerWidth - (layoutInfo.activityBarWidth + layoutInfo.editorPartMinWidth)); - - // part: title - const titleDiv = document.createElement('div'); - titleDiv.setAttribute('style', ` - position: absolute; - width: 100%; - height: ${layoutInfo.titleBarHeight}px; - left: 0; - top: 0; - background-color: ${colorInfo.titleBarBackground}; - -webkit-app-region: drag; - `); - splash.appendChild(titleDiv); - - if (colorInfo.titleBarBorder && layoutInfo.titleBarHeight > 0) { - const titleBorder = document.createElement('div'); - titleBorder.setAttribute('style', ` - position: absolute; - width: 100%; - height: 1px; - left: 0; - bottom: 0; - border-bottom: 1px solid ${colorInfo.titleBarBorder}; - `); - titleDiv.appendChild(titleBorder); - } - - // part: activity bar - const activityDiv = document.createElement('div'); - activityDiv.setAttribute('style', ` - position: absolute; - width: ${layoutInfo.activityBarWidth}px; - height: calc(100% - ${layoutInfo.titleBarHeight + layoutInfo.statusBarHeight}px); - top: ${layoutInfo.titleBarHeight}px; - ${layoutInfo.sideBarSide}: 0; - background-color: ${colorInfo.activityBarBackground}; - `); - splash.appendChild(activityDiv); - - if (colorInfo.activityBarBorder && layoutInfo.activityBarWidth > 0) { - const activityBorderDiv = document.createElement('div'); - activityBorderDiv.setAttribute('style', ` - position: absolute; - width: 1px; - height: 100%; - top: 0; - ${layoutInfo.sideBarSide === 'left' ? 'right' : 'left'}: 0; - ${layoutInfo.sideBarSide === 'left' ? 'border-right' : 'border-left'}: 1px solid ${colorInfo.activityBarBorder}; - `); - activityDiv.appendChild(activityBorderDiv); - } - - // part: side bar (only when opening workspace/folder) - // folder or workspace -> status bar color, sidebar - if (configuration.workspace) { - const sideDiv = document.createElement('div'); - sideDiv.setAttribute('style', ` - position: absolute; - width: ${layoutInfo.sideBarWidth}px; - height: calc(100% - ${layoutInfo.titleBarHeight + layoutInfo.statusBarHeight}px); - top: ${layoutInfo.titleBarHeight}px; - ${layoutInfo.sideBarSide}: ${layoutInfo.activityBarWidth}px; - background-color: ${colorInfo.sideBarBackground}; - `); - splash.appendChild(sideDiv); - - if (colorInfo.sideBarBorder && layoutInfo.sideBarWidth > 0) { - const sideBorderDiv = document.createElement('div'); - sideBorderDiv.setAttribute('style', ` - position: absolute; - width: 1px; - height: 100%; - top: 0; - right: 0; - ${layoutInfo.sideBarSide === 'left' ? 'right' : 'left'}: 0; - ${layoutInfo.sideBarSide === 'left' ? 'border-right' : 'border-left'}: 1px solid ${colorInfo.sideBarBorder}; - `); - sideDiv.appendChild(sideBorderDiv); - } - } - - // part: statusbar - const statusDiv = document.createElement('div'); - statusDiv.setAttribute('style', ` - position: absolute; - width: 100%; - height: ${layoutInfo.statusBarHeight}px; - bottom: 0; - left: 0; - background-color: ${configuration.workspace ? colorInfo.statusBarBackground : colorInfo.statusBarNoFolderBackground}; - `); - splash.appendChild(statusDiv); - - if (colorInfo.statusBarBorder && layoutInfo.statusBarHeight > 0) { - const statusBorderDiv = document.createElement('div'); - statusBorderDiv.setAttribute('style', ` - position: absolute; - width: 100%; - height: 1px; - top: 0; - border-top: 1px solid ${colorInfo.statusBarBorder}; - `); - statusDiv.appendChild(statusBorderDiv); - } - - document.body.appendChild(splash); - } - - performance.mark('code/didShowPartsSplash'); - } - - //#endregion -}()); diff --git a/src/vs/code/electron-sandbox/workbench/workbench.ts b/src/vs/code/electron-sandbox/workbench/workbench.ts new file mode 100644 index 00000000000..47c3d28a6fc --- /dev/null +++ b/src/vs/code/electron-sandbox/workbench/workbench.ts @@ -0,0 +1,308 @@ +/*--------------------------------------------------------------------------------------------- + * 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 () { + + // Add a perf entry right from the top + performance.mark('code/didStartRenderer'); + + type INativeWindowConfiguration = import('../../../platform/window/common/window.ts').INativeWindowConfiguration; + type IBootstrapWindow = import('../../../platform/window/electron-sandbox/window.js').IBootstrapWindow; + type IMainWindowSandboxGlobals = import('../../../base/parts/sandbox/electron-sandbox/globals.js').IMainWindowSandboxGlobals; + type IDesktopMain = import('../../../workbench/electron-sandbox/desktop.main.js').IDesktopMain; + + const bootstrapWindow: IBootstrapWindow = (window as any).MonacoBootstrapWindow; // defined by bootstrap-window.ts + const preloadGlobals: IMainWindowSandboxGlobals = (window as any).vscode; // defined by preload.ts + + //#region Splash Screen Helpers + + function showSplash(configuration: INativeWindowConfiguration) { + performance.mark('code/willShowPartsSplash'); + + let data = configuration.partsSplash; + if (data) { + if (configuration.autoDetectHighContrast && configuration.colorScheme.highContrast) { + if ((configuration.colorScheme.dark && data.baseTheme !== 'hc-black') || (!configuration.colorScheme.dark && data.baseTheme !== 'hc-light')) { + data = undefined; // high contrast mode has been turned by the OS -> ignore stored colors and layouts + } + } else if (configuration.autoDetectColorScheme) { + if ((configuration.colorScheme.dark && data.baseTheme !== 'vs-dark') || (!configuration.colorScheme.dark && data.baseTheme !== 'vs')) { + data = undefined; // OS color scheme is tracked and has changed + } + } + } + + // developing an extension -> ignore stored layouts + if (data && configuration.extensionDevelopmentPath) { + data.layoutInfo = undefined; + } + + // minimal color configuration (works with or without persisted data) + let baseTheme; + let shellBackground; + let shellForeground; + if (data) { + baseTheme = data.baseTheme; + shellBackground = data.colorInfo.editorBackground; + shellForeground = data.colorInfo.foreground; + } else if (configuration.autoDetectHighContrast && configuration.colorScheme.highContrast) { + if (configuration.colorScheme.dark) { + baseTheme = 'hc-black'; + shellBackground = '#000000'; + shellForeground = '#FFFFFF'; + } else { + baseTheme = 'hc-light'; + shellBackground = '#FFFFFF'; + shellForeground = '#000000'; + } + } else if (configuration.autoDetectColorScheme) { + if (configuration.colorScheme.dark) { + baseTheme = 'vs-dark'; + shellBackground = '#1E1E1E'; + shellForeground = '#CCCCCC'; + } else { + baseTheme = 'vs'; + shellBackground = '#FFFFFF'; + shellForeground = '#000000'; + } + } + + const style = document.createElement('style'); + style.className = 'initialShellColors'; + window.document.head.appendChild(style); + style.textContent = `body { background-color: ${shellBackground}; color: ${shellForeground}; margin: 0; padding: 0; }`; + + // set zoom level as soon as possible + if (typeof data?.zoomLevel === 'number' && typeof preloadGlobals?.webFrame?.setZoomLevel === 'function') { + preloadGlobals.webFrame.setZoomLevel(data.zoomLevel); + } + + // restore parts if possible (we might not always store layout info) + if (data?.layoutInfo) { + const { layoutInfo, colorInfo } = data; + + const splash = document.createElement('div'); + splash.id = 'monaco-parts-splash'; + splash.className = baseTheme ?? 'vs-dark'; + + if (layoutInfo.windowBorder && colorInfo.windowBorder) { + splash.style.position = 'relative'; + splash.style.height = 'calc(100vh - 2px)'; + splash.style.width = 'calc(100vw - 2px)'; + splash.style.border = `1px solid var(--window-border-color)`; + splash.style.setProperty('--window-border-color', colorInfo.windowBorder); + + if (layoutInfo.windowBorderRadius) { + splash.style.borderRadius = layoutInfo.windowBorderRadius; + } + } + + // ensure there is enough space + layoutInfo.auxiliarySideBarWidth = Math.min(layoutInfo.auxiliarySideBarWidth, window.innerWidth - (layoutInfo.activityBarWidth + layoutInfo.editorPartMinWidth + layoutInfo.sideBarWidth)); + layoutInfo.sideBarWidth = Math.min(layoutInfo.sideBarWidth, window.innerWidth - (layoutInfo.activityBarWidth + layoutInfo.editorPartMinWidth + layoutInfo.auxiliarySideBarWidth)); + + // part: title + if (layoutInfo.titleBarHeight > 0) { + const titleDiv = document.createElement('div'); + titleDiv.style.position = 'absolute'; + titleDiv.style.width = '100%'; + titleDiv.style.height = `${layoutInfo.titleBarHeight}px`; + titleDiv.style.left = '0'; + titleDiv.style.top = '0'; + titleDiv.style.backgroundColor = `${colorInfo.titleBarBackground}`; + (titleDiv.style as any)['-webkit-app-region'] = 'drag'; + splash.appendChild(titleDiv); + + if (colorInfo.titleBarBorder) { + const titleBorder = document.createElement('div'); + titleBorder.style.position = 'absolute'; + titleBorder.style.width = '100%'; + titleBorder.style.height = '1px'; + titleBorder.style.left = '0'; + titleBorder.style.bottom = '0'; + titleBorder.style.borderBottom = `1px solid ${colorInfo.titleBarBorder}`; + titleDiv.appendChild(titleBorder); + } + } + + // part: activity bar + if (layoutInfo.activityBarWidth > 0) { + const activityDiv = document.createElement('div'); + activityDiv.style.position = 'absolute'; + activityDiv.style.width = `${layoutInfo.activityBarWidth}px`; + activityDiv.style.height = `calc(100% - ${layoutInfo.titleBarHeight + layoutInfo.statusBarHeight}px)`; + activityDiv.style.top = `${layoutInfo.titleBarHeight}px`; + if (layoutInfo.sideBarSide === 'left') { + activityDiv.style.left = '0'; + } else { + activityDiv.style.right = '0'; + } + activityDiv.style.backgroundColor = `${colorInfo.activityBarBackground}`; + splash.appendChild(activityDiv); + + if (colorInfo.activityBarBorder) { + const activityBorderDiv = document.createElement('div'); + activityBorderDiv.style.position = 'absolute'; + activityBorderDiv.style.width = '1px'; + activityBorderDiv.style.height = '100%'; + activityBorderDiv.style.top = '0'; + if (layoutInfo.sideBarSide === 'left') { + activityBorderDiv.style.right = '0'; + activityBorderDiv.style.borderRight = `1px solid ${colorInfo.activityBarBorder}`; + } else { + activityBorderDiv.style.left = '0'; + activityBorderDiv.style.borderLeft = `1px solid ${colorInfo.activityBarBorder}`; + } + activityDiv.appendChild(activityBorderDiv); + } + } + + // part: side bar (only when opening workspace/folder) + if (configuration.workspace && layoutInfo.sideBarWidth > 0) { + const sideDiv = document.createElement('div'); + sideDiv.style.position = 'absolute'; + sideDiv.style.width = `${layoutInfo.sideBarWidth}px`; + sideDiv.style.height = `calc(100% - ${layoutInfo.titleBarHeight + layoutInfo.statusBarHeight}px)`; + sideDiv.style.top = `${layoutInfo.titleBarHeight}px`; + if (layoutInfo.sideBarSide === 'left') { + sideDiv.style.left = `${layoutInfo.activityBarWidth}px`; + } else { + sideDiv.style.right = `${layoutInfo.activityBarWidth}px`; + } + sideDiv.style.backgroundColor = `${colorInfo.sideBarBackground}`; + splash.appendChild(sideDiv); + + if (colorInfo.sideBarBorder) { + const sideBorderDiv = document.createElement('div'); + sideBorderDiv.style.position = 'absolute'; + sideBorderDiv.style.width = '1px'; + sideBorderDiv.style.height = '100%'; + sideBorderDiv.style.top = '0'; + sideBorderDiv.style.right = '0'; + if (layoutInfo.sideBarSide === 'left') { + sideBorderDiv.style.borderRight = `1px solid ${colorInfo.sideBarBorder}`; + } else { + sideBorderDiv.style.left = '0'; + sideBorderDiv.style.borderLeft = `1px solid ${colorInfo.sideBarBorder}`; + } + sideDiv.appendChild(sideBorderDiv); + } + } + + // part: auxiliary sidebar + if (layoutInfo.auxiliarySideBarWidth > 0) { + const auxSideDiv = document.createElement('div'); + auxSideDiv.style.position = 'absolute'; + auxSideDiv.style.width = `${layoutInfo.auxiliarySideBarWidth}px`; + auxSideDiv.style.height = `calc(100% - ${layoutInfo.titleBarHeight + layoutInfo.statusBarHeight}px)`; + auxSideDiv.style.top = `${layoutInfo.titleBarHeight}px`; + if (layoutInfo.sideBarSide === 'left') { + auxSideDiv.style.right = '0'; + } else { + auxSideDiv.style.left = '0'; + } + auxSideDiv.style.backgroundColor = `${colorInfo.sideBarBackground}`; + splash.appendChild(auxSideDiv); + + if (colorInfo.sideBarBorder) { + const auxSideBorderDiv = document.createElement('div'); + auxSideBorderDiv.style.position = 'absolute'; + auxSideBorderDiv.style.width = '1px'; + auxSideBorderDiv.style.height = '100%'; + auxSideBorderDiv.style.top = '0'; + if (layoutInfo.sideBarSide === 'left') { + auxSideBorderDiv.style.left = '0'; + auxSideBorderDiv.style.borderLeft = `1px solid ${colorInfo.sideBarBorder}`; + } else { + auxSideBorderDiv.style.right = '0'; + auxSideBorderDiv.style.borderRight = `1px solid ${colorInfo.sideBarBorder}`; + } + auxSideDiv.appendChild(auxSideBorderDiv); + } + } + + // part: statusbar + if (layoutInfo.statusBarHeight > 0) { + const statusDiv = document.createElement('div'); + statusDiv.style.position = 'absolute'; + statusDiv.style.width = '100%'; + statusDiv.style.height = `${layoutInfo.statusBarHeight}px`; + statusDiv.style.bottom = '0'; + statusDiv.style.left = '0'; + if (configuration.workspace && colorInfo.statusBarBackground) { + statusDiv.style.backgroundColor = colorInfo.statusBarBackground; + } else if (!configuration.workspace && colorInfo.statusBarNoFolderBackground) { + statusDiv.style.backgroundColor = colorInfo.statusBarNoFolderBackground; + } + splash.appendChild(statusDiv); + + if (colorInfo.statusBarBorder) { + const statusBorderDiv = document.createElement('div'); + statusBorderDiv.style.position = 'absolute'; + statusBorderDiv.style.width = '100%'; + statusBorderDiv.style.height = '1px'; + statusBorderDiv.style.top = '0'; + statusBorderDiv.style.borderTop = `1px solid ${colorInfo.statusBarBorder}`; + statusDiv.appendChild(statusBorderDiv); + } + } + + window.document.body.appendChild(splash); + } + + performance.mark('code/didShowPartsSplash'); + } + + //#endregion + + const { result, configuration } = await bootstrapWindow.load('vs/workbench/workbench.desktop.main', + { + configureDeveloperSettings: function (windowConfig) { + return { + // disable automated devtools opening on error when running extension tests + // as this can lead to nondeterministic test execution (devtools steals focus) + forceDisableShowDevtoolsOnError: typeof windowConfig.extensionTestsPath === 'string' || windowConfig['enable-smoke-test-driver'] === true, + // enable devtools keybindings in extension development window + forceEnableDeveloperKeybindings: Array.isArray(windowConfig.extensionDevelopmentPath) && windowConfig.extensionDevelopmentPath.length > 0, + removeDeveloperKeybindingsAfterLoad: true + }; + }, + beforeImport: function (windowConfig) { + + // Show our splash as early as possible + showSplash(windowConfig); + + // Code windows have a `vscodeWindowId` property to identify them + Object.defineProperty(window, 'vscodeWindowId', { + get: () => windowConfig.windowId + }); + + // It looks like browsers only lazily enable + // the element when needed. Since we + // leverage canvas elements in our code in many + // locations, we try to help the browser to + // initialize canvas when it is idle, right + // before we wait for the scripts to be loaded. + window.requestIdleCallback(() => { + const canvas = document.createElement('canvas'); + const context = canvas.getContext('2d'); + context?.clearRect(0, 0, canvas.width, canvas.height); + canvas.remove(); + }, { timeout: 50 }); + + // Track import() perf + performance.mark('code/willLoadWorkbenchMain'); + } + } + ); + + // Mark start of workbench + performance.mark('code/didLoadWorkbenchMain'); + + // Load workbench + result.main(configuration); +}()); diff --git a/src/vs/code/electron-utility/sharedProcess/contrib/defaultExtensionsInitializer.ts b/src/vs/code/electron-utility/sharedProcess/contrib/defaultExtensionsInitializer.ts new file mode 100644 index 00000000000..739c184a107 --- /dev/null +++ b/src/vs/code/electron-utility/sharedProcess/contrib/defaultExtensionsInitializer.ts @@ -0,0 +1,78 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { dirname, join } from 'path'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { isWindows } from '../../../../base/common/platform.js'; +import { URI } from '../../../../base/common/uri.js'; +import { INativeEnvironmentService } from '../../../../platform/environment/common/environment.js'; +import { INativeServerExtensionManagementService } from '../../../../platform/extensionManagement/node/extensionManagementService.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +import { FileOperationResult, IFileService, IFileStat, toFileOperationResult } from '../../../../platform/files/common/files.js'; +import { getErrorMessage } from '../../../../base/common/errors.js'; + +const defaultExtensionsInitStatusKey = 'initializing-default-extensions'; + +export class DefaultExtensionsInitializer extends Disposable { + constructor( + @INativeEnvironmentService private readonly environmentService: INativeEnvironmentService, + @INativeServerExtensionManagementService private readonly extensionManagementService: INativeServerExtensionManagementService, + @IStorageService storageService: IStorageService, + @IFileService private readonly fileService: IFileService, + @ILogService private readonly logService: ILogService, + ) { + super(); + + if (isWindows && storageService.getBoolean(defaultExtensionsInitStatusKey, StorageScope.APPLICATION, true)) { + storageService.store(defaultExtensionsInitStatusKey, true, StorageScope.APPLICATION, StorageTarget.MACHINE); + this.initializeDefaultExtensions().then(() => storageService.store(defaultExtensionsInitStatusKey, false, StorageScope.APPLICATION, StorageTarget.MACHINE)); + } + } + + private async initializeDefaultExtensions(): Promise { + const extensionsLocation = this.getDefaultExtensionVSIXsLocation(); + let stat: IFileStat; + try { + stat = await this.fileService.resolve(extensionsLocation); + if (!stat.children) { + this.logService.debug('There are no default extensions to initialize', extensionsLocation.toString()); + return; + } + } catch (error) { + if (toFileOperationResult(error) === FileOperationResult.FILE_NOT_FOUND) { + this.logService.debug('There are no default extensions to initialize', extensionsLocation.toString()); + return; + } + this.logService.error('Error initializing extensions', error); + return; + } + + const vsixs = stat.children.filter(child => child.name.toLowerCase().endsWith('.vsix')); + if (vsixs.length === 0) { + this.logService.debug('There are no default extensions to initialize', extensionsLocation.toString()); + return; + } + + this.logService.info('Initializing default extensions', extensionsLocation.toString()); + await Promise.all(vsixs.map(async vsix => { + this.logService.info('Installing default extension', vsix.resource.toString()); + try { + await this.extensionManagementService.install(vsix.resource, { donotIncludePackAndDependencies: true, keepExisting: false }); + this.logService.info('Default extension installed', vsix.resource.toString()); + } catch (error) { + this.logService.error('Error installing default extension', vsix.resource.toString(), getErrorMessage(error)); + } + })); + this.logService.info('Default extensions initialized', extensionsLocation.toString()); + } + + private getDefaultExtensionVSIXsLocation(): URI { + // appRoot = C:\Users\\AppData\Local\Programs\Microsoft VS Code Insiders\resources\app + // extensionsPath = C:\Users\\AppData\Local\Programs\Microsoft VS Code Insiders\bootstrap\extensions + return URI.file(join(dirname(dirname(this.environmentService.appRoot)), 'bootstrap', 'extensions')); + } + +} diff --git a/src/vs/code/electron-utility/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-utility/sharedProcess/sharedProcessMain.ts index 80e8fdab810..16e9793483b 100644 --- a/src/vs/code/electron-utility/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-utility/sharedProcess/sharedProcessMain.ts @@ -29,7 +29,7 @@ import { DownloadService } from '../../../platform/download/common/downloadServi import { INativeEnvironmentService } from '../../../platform/environment/common/environment.js'; import { GlobalExtensionEnablementService } from '../../../platform/extensionManagement/common/extensionEnablementService.js'; import { ExtensionGalleryService } from '../../../platform/extensionManagement/common/extensionGalleryService.js'; -import { IExtensionGalleryService, IExtensionManagementService, IExtensionTipsService, IGlobalExtensionEnablementService } from '../../../platform/extensionManagement/common/extensionManagement.js'; +import { IAllowedExtensionsService, IExtensionGalleryService, IExtensionManagementService, IExtensionTipsService, IGlobalExtensionEnablementService } from '../../../platform/extensionManagement/common/extensionManagement.js'; import { ExtensionSignatureVerificationService, IExtensionSignatureVerificationService } from '../../../platform/extensionManagement/node/extensionSignatureVerificationService.js'; import { ExtensionManagementChannel, ExtensionTipsChannel } from '../../../platform/extensionManagement/common/extensionManagementIpc.js'; import { ExtensionManagementService, INativeServerExtensionManagementService } from '../../../platform/extensionManagement/node/extensionManagementService.js'; @@ -43,7 +43,7 @@ import { InstantiationService } from '../../../platform/instantiation/common/ins import { ServiceCollection } from '../../../platform/instantiation/common/serviceCollection.js'; import { ILanguagePackService } from '../../../platform/languagePacks/common/languagePacks.js'; import { NativeLanguagePackService } from '../../../platform/languagePacks/node/languagePacks.js'; -import { ConsoleLogger, ILoggerService, ILogService } from '../../../platform/log/common/log.js'; +import { ConsoleLogger, ILoggerService, ILogService, LoggerGroup } from '../../../platform/log/common/log.js'; import { LoggerChannelClient } from '../../../platform/log/common/logIpc.js'; import product from '../../../platform/product/common/product.js'; import { IProductService } from '../../../platform/product/common/productService.js'; @@ -118,6 +118,8 @@ import { getOSReleaseInfo } from '../../../base/node/osReleaseInfo.js'; import { getDesktopEnvironment } from '../../../base/common/desktopEnvironmentInfo.js'; import { getCodeDisplayProtocol, getDisplayProtocol } from '../../../base/node/osDisplayProtocolInfo.js'; import { RequestService } from '../../../platform/request/electron-utility/requestService.js'; +import { DefaultExtensionsInitializer } from './contrib/defaultExtensionsInitializer.js'; +import { AllowedExtensionsService } from '../../../platform/extensionManagement/common/allowedExtensionsService.js'; class SharedProcessMain extends Disposable implements IClientConnectionFilter { @@ -160,7 +162,6 @@ class SharedProcessMain extends Disposable implements IClientConnectionFilter { instantiationService.invokeFunction(accessor => { const logService = accessor.get(ILogService); const telemetryService = accessor.get(ITelemetryService); - const userDataProfilesService = accessor.get(IUserDataProfilesService); // Log info logService.trace('sharedProcess configuration', JSON.stringify(this.configuration)); @@ -171,10 +172,6 @@ class SharedProcessMain extends Disposable implements IClientConnectionFilter { // Error handler this.registerErrorHandler(logService); - // Report Profiles Info - this.reportProfilesInfo(telemetryService, userDataProfilesService); - this._register(userDataProfilesService.onDidChangeProfiles(() => this.reportProfilesInfo(telemetryService, userDataProfilesService))); - // Report Client OS/DE Info this.reportClientOSInfo(telemetryService, logService); }); @@ -187,7 +184,8 @@ class SharedProcessMain extends Disposable implements IClientConnectionFilter { instantiationService.createInstance(LogsDataCleaner), instantiationService.createInstance(LocalizationsUpdater), instantiationService.createInstance(ExtensionsContributions), - instantiationService.createInstance(UserDataProfilesCleaner) + instantiationService.createInstance(UserDataProfilesCleaner), + instantiationService.createInstance(DefaultExtensionsInitializer) )); } @@ -216,7 +214,8 @@ class SharedProcessMain extends Disposable implements IClientConnectionFilter { services.set(ILoggerService, loggerService); // Log - const logger = this._register(loggerService.createLogger('sharedprocess', { name: localize('sharedLog', "Shared") })); + const sharedLogGroup: LoggerGroup = { id: 'shared', name: localize('sharedLog', "Shared") }; + const logger = this._register(loggerService.createLogger('sharedprocess', { name: localize('sharedLog', "Shared"), group: sharedLogGroup })); const consoleLogger = this._register(new ConsoleLogger(logger.getLevel())); const logService = this._register(new LogService(logger, [consoleLogger])); services.set(ILogService, logService); @@ -270,7 +269,8 @@ class SharedProcessMain extends Disposable implements IClientConnectionFilter { ]); // Request - const requestService = new RequestService(configurationService, environmentService, logService); + const networkLogger = this._register(loggerService.createLogger(`network-shared`, { name: localize('networkk', "Network"), group: sharedLogGroup })); + const requestService = new RequestService(configurationService, environmentService, this._register(new LogService(networkLogger))); services.set(IRequestService, requestService); // Checksum @@ -296,7 +296,7 @@ class SharedProcessMain extends Disposable implements IClientConnectionFilter { const appenders: ITelemetryAppender[] = []; const internalTelemetry = isInternalTelemetry(productService, configurationService); if (supportsTelemetry(productService, environmentService)) { - const logAppender = new TelemetryLogAppender(logService, loggerService, environmentService, productService); + const logAppender = new TelemetryLogAppender('', false, loggerService, environmentService, productService); appenders.push(logAppender); if (!isLoggingOnly(productService, environmentService) && productService.aiConfig?.ariaKey) { const collectorAppender = new OneDataSystemAppender(requestService, internalTelemetry, 'monacoworkbench', null, productService.aiConfig.ariaKey); @@ -320,20 +320,14 @@ class SharedProcessMain extends Disposable implements IClientConnectionFilter { services.set(ITelemetryService, telemetryService); // Custom Endpoint Telemetry - const customEndpointTelemetryService = new CustomEndpointTelemetryService(configurationService, telemetryService, logService, loggerService, environmentService, productService); + const customEndpointTelemetryService = new CustomEndpointTelemetryService(configurationService, telemetryService, loggerService, environmentService, productService); services.set(ICustomEndpointTelemetryService, customEndpointTelemetryService); // Extension Management services.set(IExtensionsProfileScannerService, new SyncDescriptor(ExtensionsProfileScannerService, undefined, true)); services.set(IExtensionsScannerService, new SyncDescriptor(ExtensionsScannerService, undefined, true)); - - if (productService.quality === 'stable') { - services.set(IExtensionSignatureVerificationService, new SyncDescriptor(ExtensionSignatureVerificationService, undefined, true)); - } else { - // Do extension signature verification in the main process in insiders - services.set(IExtensionSignatureVerificationService, ProxyChannel.toService(mainProcessService.getChannel('signatureVerificationService'))); - } - + services.set(IExtensionSignatureVerificationService, new SyncDescriptor(ExtensionSignatureVerificationService, undefined, true)); + services.set(IAllowedExtensionsService, new SyncDescriptor(AllowedExtensionsService, undefined, true)); services.set(INativeServerExtensionManagementService, new SyncDescriptor(ExtensionManagementService, undefined, true)); // Extension Gallery @@ -456,20 +450,6 @@ class SharedProcessMain extends Disposable implements IClientConnectionFilter { }); } - private reportProfilesInfo(telemetryService: ITelemetryService, userDataProfilesService: IUserDataProfilesService): void { - type ProfilesInfoClassification = { - owner: 'sandy081'; - comment: 'Report profiles information'; - count: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Number of profiles' }; - }; - type ProfilesInfoEvent = { - count: number; - }; - telemetryService.publicLog2('profilesInfo', { - count: userDataProfilesService.profiles.length - }); - } - private async reportClientOSInfo(telemetryService: ITelemetryService, logService: ILogService): Promise { if (isLinux) { const [releaseInfo, displayProtocol] = await Promise.all([ diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index b25377a804d..e91bea07d6a 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -58,7 +58,7 @@ export async function main(argv: string[]): Promise { const env: IProcessEnvironment = { ...process.env }; - // bootstrap-amd.js determines the electron environment based + // bootstrap-esm.js determines the electron environment based // on the following variable. For the server we need to unset // it to prevent importing any electron specific modules. // Refs https://github.com/microsoft/vscode/issues/221883 @@ -109,7 +109,7 @@ export async function main(argv: string[]): Promise { // Usage: `[[ "$TERM_PROGRAM" == "vscode" ]] && . "$(code --locate-shell-integration-path zsh)"` case 'zsh': file = 'shellIntegration-rc.zsh'; break; // Usage: `string match -q "$TERM_PROGRAM" "vscode"; and . (code --locate-shell-integration-path fish)` - case 'fish': file = 'fish_xdg_data/fish/vendor_conf.d/shellIntegration.fish'; break; + case 'fish': file = 'shellIntegration.fish'; break; default: throw new Error('Error using --locate-shell-integration-path: Invalid shell type'); } console.log(join(getAppRoot(), 'out', 'vs', 'workbench', 'contrib', 'terminal', 'common', 'scripts', file)); @@ -117,7 +117,20 @@ export async function main(argv: string[]): Promise { // Extensions Management else if (shouldSpawnCliProcess(args)) { - const cli = await import(['./cliProcessMain.js'].join('/') /* TODO@esm workaround to prevent esbuild from inlining this */); + + // We do not bundle `cliProcessMain.js` into this file because + // it is rather large and only needed for very few CLI operations. + // This has the downside that we need to know if we run OSS or + // built, because our location on disk is different if built. + + let cliProcessMain: string; + if (process.env['VSCODE_DEV']) { + cliProcessMain = './cliProcessMain.js'; + } else { + cliProcessMain = './vs/code/node/cliProcessMain.js'; + } + + const cli = await import(cliProcessMain); await cli.main(args); return; @@ -125,14 +138,26 @@ export async function main(argv: string[]): Promise { // Write File else if (args['file-write']) { - const source = args._[0]; - const target = args._[1]; + const argsFile = args._[0]; + if (!argsFile || !isAbsolute(argsFile) || !existsSync(argsFile) || !statSync(argsFile).isFile()) { + throw new Error('Using --file-write with invalid arguments.'); + } + + let source: string | undefined; + let target: string | undefined; + try { + const argsContents: { source: string; target: string } = JSON.parse(readFileSync(argsFile, 'utf8')); + source = argsContents.source; + target = argsContents.target; + } catch (error) { + throw new Error('Using --file-write with invalid arguments.'); + } // Windows: set the paths as allowed UNC paths given // they are explicitly provided by the user as arguments if (isWindows) { for (const path of [source, target]) { - if (isUNC(path)) { + if (typeof path === 'string' && isUNC(path)) { addUNCHostToAllowlist(URI.file(path).authority); } } @@ -397,10 +422,7 @@ export async function main(argv: string[]): Promise { return false; } if (target.type === 'page') { - return target.url.indexOf('workbench/workbench.html') > 0 || - target.url.indexOf('workbench/workbench-dev.html') > 0 || - target.url.indexOf('workbench/workbench.esm.html') > 0 || - target.url.indexOf('workbench/workbench-dev.esm.html') > 0; + return target.url.indexOf('workbench/workbench.html') > 0 || target.url.indexOf('workbench/workbench-dev.html') > 0; } else { return true; } diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index d3b20ecea47..aff3b2da579 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -22,7 +22,7 @@ import { NativeParsedArgs } from '../../platform/environment/common/argv.js'; import { INativeEnvironmentService } from '../../platform/environment/common/environment.js'; import { NativeEnvironmentService } from '../../platform/environment/node/environmentService.js'; import { ExtensionGalleryServiceWithNoStorageService } from '../../platform/extensionManagement/common/extensionGalleryService.js'; -import { IExtensionGalleryService, InstallOptions } from '../../platform/extensionManagement/common/extensionManagement.js'; +import { IAllowedExtensionsService, IExtensionGalleryService, InstallOptions } from '../../platform/extensionManagement/common/extensionManagement.js'; import { ExtensionSignatureVerificationService, IExtensionSignatureVerificationService } from '../../platform/extensionManagement/node/extensionSignatureVerificationService.js'; import { ExtensionManagementCLI } from '../../platform/extensionManagement/common/extensionManagementCLI.js'; import { IExtensionsProfileScannerService } from '../../platform/extensionManagement/common/extensionsProfileScannerService.js'; @@ -64,6 +64,7 @@ import { LoggerService } from '../../platform/log/node/loggerService.js'; import { localize } from '../../nls.js'; import { FileUserDataProvider } from '../../platform/userData/common/fileUserDataProvider.js'; import { addUNCHostToAllowlist, getUNCHost } from '../../base/node/unc.js'; +import { AllowedExtensionsService } from '../../platform/extensionManagement/common/allowedExtensionsService.js'; class CliMain extends Disposable { @@ -195,7 +196,7 @@ class CliMain extends Disposable { services.set(IUriIdentityService, new UriIdentityService(fileService)); // Request - const requestService = new RequestService(configurationService, environmentService, logService); + const requestService = new RequestService('local', configurationService, environmentService, logService); services.set(IRequestService, requestService); // Download Service @@ -205,6 +206,7 @@ class CliMain extends Disposable { services.set(IExtensionsProfileScannerService, new SyncDescriptor(ExtensionsProfileScannerService, undefined, true)); services.set(IExtensionsScannerService, new SyncDescriptor(ExtensionsScannerService, undefined, true)); services.set(IExtensionSignatureVerificationService, new SyncDescriptor(ExtensionSignatureVerificationService, undefined, true)); + services.set(IAllowedExtensionsService, new SyncDescriptor(AllowedExtensionsService, undefined, true)); services.set(INativeServerExtensionManagementService, new SyncDescriptor(ExtensionManagementService, undefined, true)); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryServiceWithNoStorageService, undefined, true)); @@ -284,7 +286,7 @@ class CliMain extends Disposable { // Install Extension else if (this.argv['install-extension'] || this.argv['install-builtin-extension']) { - const installOptions: InstallOptions = { isMachineScoped: !!this.argv['do-not-sync'], installPreReleaseVersion: !!this.argv['pre-release'], profileLocation }; + const installOptions: InstallOptions = { isMachineScoped: !!this.argv['do-not-sync'], installPreReleaseVersion: !!this.argv['pre-release'], donotIncludePackAndDependencies: !!this.argv['do-not-include-pack-dependencies'], profileLocation }; return instantiationService.createInstance(ExtensionManagementCLI, new ConsoleLogger(LogLevel.Info, false)).installExtensions(this.asExtensionIdOrVSIX(this.argv['install-extension'] || []), this.asExtensionIdOrVSIX(this.argv['install-builtin-extension'] || []), installOptions, !!this.argv['force']); } diff --git a/src/vs/css.build.ts b/src/vs/css.build.ts deleted file mode 100644 index d4b3fcc96b2..00000000000 --- a/src/vs/css.build.ts +++ /dev/null @@ -1,304 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -interface ICSSPluginConfig { - inlineResources?: boolean | 'base64'; - inlineResourcesLimit?: number; -} - -interface ICSSEntryPointData { - moduleName: string; - contents: string; - fsPath: string; -} - -// This file gets compiled also with the standalone editor, -// so we cannot depend on types from node.d.ts -interface INodeFS { - readFileSync(path: string, encoding: 'utf8'): string; - readFileSync(path: string): INodeBuffer; -} -interface INodeBuffer { - length: number; - toString(encoding?: 'base64'): string; -} -interface INodePath { - dirname(p: string): string; - join(...paths: string[]): string; -} - -const nodeReq = (module: string): T | undefined => { - if (typeof (require).__$__nodeRequire === 'function') { - return (require).__$__nodeRequire(module); - } - return undefined; -}; - -const fs = nodeReq('fs'); -const path = nodeReq('path'); - -let inlineResources: boolean | 'base64' = false; -let inlineResourcesLimit: number = 5000; - -const contentsMap: { [moduleName: string]: string } = {}; -const pathMap: { [moduleName: string]: string } = {}; -const entryPoints: { [entryPoint: string]: ICSSEntryPointData[] } = {}; -const inlinedResources: string[] = []; - -/** - * Invoked by the loader at build-time - */ -export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { - if (!fs) { - throw new Error(`Cannot load files without 'fs'!`); - } - config = config || {}; - const myConfig = (config['vs/css'] || {}); - inlineResources = (typeof myConfig.inlineResources === 'undefined' ? false : myConfig.inlineResources); - inlineResourcesLimit = (myConfig.inlineResourcesLimit || 5000); - const cssUrl = req.toUrl(name + '.css'); - let contents = fs.readFileSync(cssUrl, 'utf8'); - if (contents.charCodeAt(0) === 65279 /* BOM */) { - // Remove BOM - contents = contents.substring(1); - } - if (config.isBuild) { - contentsMap[name] = contents; - pathMap[name] = cssUrl; - } - load({}); -} - -/** - * Invoked by the loader at build-time - */ -export function write(pluginName: string, moduleName: string, write: AMDLoader.IPluginWriteCallback): void { - const entryPoint = write.getEntryPoint(); - - entryPoints[entryPoint] = entryPoints[entryPoint] || []; - entryPoints[entryPoint].push({ - moduleName: moduleName, - contents: contentsMap[moduleName], - fsPath: pathMap[moduleName], - }); - - write.asModule(pluginName + '!' + moduleName, - 'define([\'vs/css!' + entryPoint + '\'], {});' - ); -} - -/** - * Invoked by the loader at build-time - */ -export function writeFile(pluginName: string, moduleName: string, req: AMDLoader.IRelativeRequire, write: AMDLoader.IPluginWriteFileCallback, config: AMDLoader.IConfigurationOptions): void { - if (entryPoints && entryPoints.hasOwnProperty(moduleName)) { - const fileName = req.toUrl(moduleName + '.css'); - const contents = [ - '/*---------------------------------------------------------', - ' * Copyright (c) Microsoft Corporation. All rights reserved.', - ' *--------------------------------------------------------*/' - ], - entries = entryPoints[moduleName]; - for (let i = 0; i < entries.length; i++) { - if (inlineResources) { - contents.push(rewriteOrInlineUrls(entries[i].fsPath, entries[i].moduleName, moduleName, entries[i].contents, inlineResources === 'base64', inlineResourcesLimit)); - } else { - contents.push(rewriteUrls(entries[i].moduleName, moduleName, entries[i].contents)); - } - } - write(fileName, contents.join('\r\n')); - } -} - -export function getInlinedResources(): string[] { - return inlinedResources || []; -} - -function rewriteOrInlineUrls(originalFileFSPath: string, originalFile: string, newFile: string, contents: string, forceBase64: boolean, inlineByteLimit: number): string { - if (!fs || !path) { - throw new Error(`Cannot rewrite or inline urls without 'fs' or 'path'!`); - } - return CSSPluginUtilities.replaceURL(contents, (url) => { - if (/\.(svg|png)$/.test(url)) { - const fsPath = path.join(path.dirname(originalFileFSPath), url); - const fileContents = fs.readFileSync(fsPath); - - if (fileContents.length < inlineByteLimit) { - const normalizedFSPath = fsPath.replace(/\\/g, '/'); - inlinedResources.push(normalizedFSPath); - - const MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; - let DATA = ';base64,' + fileContents.toString('base64'); - - if (!forceBase64 && /\.svg$/.test(url)) { - // .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris - const newText = fileContents.toString() - .replace(/"/g, '\'') - .replace(/%/g, '%25') - .replace(//g, '%3E') - .replace(/&/g, '%26') - .replace(/#/g, '%23') - .replace(/\s+/g, ' '); - const encodedData = ',' + newText; - if (encodedData.length < DATA.length) { - DATA = encodedData; - } - } - return '"data:' + MIME + DATA + '"'; - } - } - - const absoluteUrl = CSSPluginUtilities.joinPaths(CSSPluginUtilities.pathOf(originalFile), url); - return CSSPluginUtilities.relativePath(newFile, absoluteUrl); - }); -} - -export function rewriteUrls(originalFile: string, newFile: string, contents: string): string { - return CSSPluginUtilities.replaceURL(contents, (url) => { - const absoluteUrl = CSSPluginUtilities.joinPaths(CSSPluginUtilities.pathOf(originalFile), url); - return CSSPluginUtilities.relativePath(newFile, absoluteUrl); - }); -} - -export class CSSPluginUtilities { - - public static startsWith(haystack: string, needle: string): boolean { - return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; - } - - /** - * Find the path of a file. - */ - public static pathOf(filename: string): string { - const lastSlash = filename.lastIndexOf('/'); - if (lastSlash !== -1) { - return filename.substr(0, lastSlash + 1); - } else { - return ''; - } - } - - /** - * A conceptual a + b for paths. - * Takes into account if `a` contains a protocol. - * Also normalizes the result: e.g.: a/b/ + ../c => a/c - */ - public static joinPaths(a: string, b: string): string { - - function findSlashIndexAfterPrefix(haystack: string, prefix: string): number { - if (CSSPluginUtilities.startsWith(haystack, prefix)) { - return Math.max(prefix.length, haystack.indexOf('/', prefix.length)); - } - return 0; - } - - let aPathStartIndex = 0; - aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, '//'); - aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'http://'); - aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'https://'); - - function pushPiece(pieces: string[], piece: string): void { - if (piece === './') { - // Ignore - return; - } - if (piece === '../') { - const prevPiece = (pieces.length > 0 ? pieces[pieces.length - 1] : null); - if (prevPiece && prevPiece === '/') { - // Ignore - return; - } - if (prevPiece && prevPiece !== '../') { - // Pop - pieces.pop(); - return; - } - } - // Push - pieces.push(piece); - } - - function push(pieces: string[], path: string): void { - while (path.length > 0) { - const slashIndex = path.indexOf('/'); - const piece = (slashIndex >= 0 ? path.substring(0, slashIndex + 1) : path); - path = (slashIndex >= 0 ? path.substring(slashIndex + 1) : ''); - pushPiece(pieces, piece); - } - } - - let pieces: string[] = []; - push(pieces, a.substr(aPathStartIndex)); - if (b.length > 0 && b.charAt(0) === '/') { - pieces = []; - } - push(pieces, b); - - return a.substring(0, aPathStartIndex) + pieces.join(''); - } - - public static commonPrefix(str1: string, str2: string): string { - const len = Math.min(str1.length, str2.length); - for (let i = 0; i < len; i++) { - if (str1.charCodeAt(i) !== str2.charCodeAt(i)) { - return str1.substring(0, i); - } - } - return str1.substring(0, len); - } - - public static commonFolderPrefix(fromPath: string, toPath: string): string { - const prefix = CSSPluginUtilities.commonPrefix(fromPath, toPath); - const slashIndex = prefix.lastIndexOf('/'); - if (slashIndex === -1) { - return ''; - } - return prefix.substring(0, slashIndex + 1); - } - - public static relativePath(fromPath: string, toPath: string): string { - if (CSSPluginUtilities.startsWith(toPath, '/') || CSSPluginUtilities.startsWith(toPath, 'http://') || CSSPluginUtilities.startsWith(toPath, 'https://')) { - return toPath; - } - - // Ignore common folder prefix - const prefix = CSSPluginUtilities.commonFolderPrefix(fromPath, toPath); - fromPath = fromPath.substr(prefix.length); - toPath = toPath.substr(prefix.length); - - const upCount = fromPath.split('/').length; - let result = ''; - for (let i = 1; i < upCount; i++) { - result += '../'; - } - return result + toPath; - } - - public static replaceURL(contents: string, replacer: (url: string) => string): string { - // Use ")" as the terminator as quotes are oftentimes not used at all - return contents.replace(/url\(\s*([^\)]+)\s*\)?/g, (_: string, ...matches: string[]) => { - let url = matches[0]; - // Eliminate starting quotes (the initial whitespace is not captured) - if (url.charAt(0) === '"' || url.charAt(0) === '\'') { - url = url.substring(1); - } - // The ending whitespace is captured - while (url.length > 0 && (url.charAt(url.length - 1) === ' ' || url.charAt(url.length - 1) === '\t')) { - url = url.substring(0, url.length - 1); - } - // Eliminate ending quotes - if (url.charAt(url.length - 1) === '"' || url.charAt(url.length - 1) === '\'') { - url = url.substring(0, url.length - 1); - } - - if (!CSSPluginUtilities.startsWith(url, 'data:') && !CSSPluginUtilities.startsWith(url, 'http://') && !CSSPluginUtilities.startsWith(url, 'https://')) { - url = replacer(url); - } - - return 'url(' + url + ')'; - }); - } -} diff --git a/src/vs/css.ts b/src/vs/css.ts deleted file mode 100644 index e142f8dc8b5..00000000000 --- a/src/vs/css.ts +++ /dev/null @@ -1,85 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -interface ICSSPluginConfig { - disabled?: boolean; -} - -/** - * Invoked by the loader at run-time - * - * @skipMangle - */ -export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { - config = config || {}; - const cssConfig = (config['vs/css'] || {}); - - if (cssConfig.disabled) { - // the plugin is asked to not create any style sheets - load({}); - return; - } - - const cssUrl = req.toUrl(name + '.css'); - loadCSS(name, cssUrl, () => { - load({}); - }, (err: any) => { - if (typeof load.error === 'function') { - load.error('Could not find ' + cssUrl + '.'); - } - }); -} - -function loadCSS(name: string, cssUrl: string, callback: () => void, errorback: (err: any) => void): void { - if (linkTagExists(name, cssUrl)) { - callback(); - return; - } - createLinkTag(name, cssUrl, callback, errorback); -} - -function linkTagExists(name: string, cssUrl: string): boolean { - // eslint-disable-next-line no-restricted-globals - const links = window.document.getElementsByTagName('link'); - for (let i = 0, len = links.length; i < len; i++) { - const nameAttr = links[i].getAttribute('data-name'); - const hrefAttr = links[i].getAttribute('href'); - if (nameAttr === name || hrefAttr === cssUrl) { - return true; - } - } - return false; -} - -function createLinkTag(name: string, cssUrl: string, callback: () => void, errorback: (err: any) => void): void { - const linkNode = document.createElement('link'); - linkNode.setAttribute('rel', 'stylesheet'); - linkNode.setAttribute('type', 'text/css'); - linkNode.setAttribute('data-name', name); - - attachListeners(name, linkNode, callback, errorback); - linkNode.setAttribute('href', cssUrl); - - // eslint-disable-next-line no-restricted-globals - const head = window.document.head || window.document.getElementsByTagName('head')[0]; - head.appendChild(linkNode); -} - -function attachListeners(name: string, linkNode: HTMLLinkElement, callback: () => void, errorback: (err: any) => void): void { - const unbind = () => { - linkNode.removeEventListener('load', loadEventListener); - linkNode.removeEventListener('error', errorEventListener); - }; - const loadEventListener = (e: any) => { - unbind(); - callback(); - }; - const errorEventListener = (e: any) => { - unbind(); - errorback(e); - }; - linkNode.addEventListener('load', loadEventListener); - linkNode.addEventListener('error', errorEventListener); -} diff --git a/src/vs/editor/browser/config/editorConfiguration.ts b/src/vs/editor/browser/config/editorConfiguration.ts index d65d85cfe25..701c940c736 100644 --- a/src/vs/editor/browser/config/editorConfiguration.ts +++ b/src/vs/editor/browser/config/editorConfiguration.ts @@ -22,6 +22,7 @@ import { AccessibilitySupport, IAccessibilityService } from '../../../platform/a import { getWindow, getWindowById } from '../../../base/browser/dom.js'; import { PixelRatio } from '../../../base/browser/pixelRatio.js'; import { MenuId } from '../../../platform/actions/common/actions.js'; +import { InputMode } from '../../common/inputMode.js'; export interface IEditorConstructionOptions extends IEditorOptions { /** @@ -95,6 +96,7 @@ export class EditorConfiguration extends Disposable implements IEditorConfigurat this._register(FontMeasurements.onDidChange(() => this._recomputeOptions())); this._register(PixelRatio.getInstance(getWindow(container)).onDidChange(() => this._recomputeOptions())); this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => this._recomputeOptions())); + this._register(InputMode.onDidChangeInputMode(() => this._recomputeOptions())); } private _recomputeOptions(): void { @@ -126,6 +128,7 @@ export class EditorConfiguration extends Disposable implements IEditorConfigurat emptySelectionClipboard: partialEnv.emptySelectionClipboard, pixelRatio: partialEnv.pixelRatio, tabFocusMode: TabFocus.getTabFocusMode(), + inputMode: InputMode.getInputMode(), accessibilitySupport: partialEnv.accessibilitySupport, glyphMarginDecorationLaneCount: this._glyphMarginDecorationLaneCount }; diff --git a/src/vs/editor/browser/config/migrateOptions.ts b/src/vs/editor/browser/config/migrateOptions.ts index 622994ed7f5..4adf4e3d848 100644 --- a/src/vs/editor/browser/config/migrateOptions.ts +++ b/src/vs/editor/browser/config/migrateOptions.ts @@ -93,6 +93,7 @@ registerSimpleEditorSettingMigration('renderFinalNewline', [[true, 'on'], [false registerSimpleEditorSettingMigration('cursorSmoothCaretAnimation', [[true, 'on'], [false, 'off']]); registerSimpleEditorSettingMigration('occurrencesHighlight', [[true, 'singleFile'], [false, 'off']]); registerSimpleEditorSettingMigration('wordBasedSuggestions', [[true, 'matchingDocuments'], [false, 'off']]); +registerSimpleEditorSettingMigration('defaultColorDecorators', [[true, 'auto'], [false, 'never']]); registerEditorSettingMigration('autoClosingBrackets', (value, read, write) => { if (value === false) { diff --git a/src/vs/editor/browser/controller/editContext/clipboardUtils.ts b/src/vs/editor/browser/controller/editContext/clipboardUtils.ts index d7091d613d6..6095ad06da7 100644 --- a/src/vs/editor/browser/controller/editContext/clipboardUtils.ts +++ b/src/vs/editor/browser/controller/editContext/clipboardUtils.ts @@ -5,6 +5,7 @@ import { IViewModel } from '../../../common/viewModel.js'; import { Range } from '../../../common/core/range.js'; import { isWindows } from '../../../../base/common/platform.js'; +import { Mimes } from '../../../../base/common/mime.js'; export function getDataToCopy(viewModel: IViewModel, modelSelections: Range[], emptySelectionClipboard: boolean, copyWithSyntaxHighlighting: boolean): ClipboardDataToCopy { const rawTextToCopy = viewModel.getPlainTextToCopy(modelSelections, emptySelectionClipboard, isWindows); @@ -84,3 +85,36 @@ interface InMemoryClipboardMetadata { lastCopiedValue: string; data: ClipboardStoredMetadata; } + +export const ClipboardEventUtils = { + + getTextData(clipboardData: DataTransfer): [string, ClipboardStoredMetadata | null] { + const text = clipboardData.getData(Mimes.text); + let metadata: ClipboardStoredMetadata | null = null; + const rawmetadata = clipboardData.getData('vscode-editor-data'); + if (typeof rawmetadata === 'string') { + try { + metadata = JSON.parse(rawmetadata); + if (metadata.version !== 1) { + metadata = null; + } + } catch (err) { + // no problem! + } + } + if (text.length === 0 && metadata === null && clipboardData.files.length > 0) { + // no textual data pasted, generate text from file names + const files: File[] = Array.prototype.slice.call(clipboardData.files, 0); + return [files.map(file => file.name).join('\n'), null]; + } + return [text, metadata]; + }, + + setTextData(clipboardData: DataTransfer, text: string, html: string | null | undefined, metadata: ClipboardStoredMetadata): void { + clipboardData.setData(Mimes.text, text); + if (typeof html === 'string') { + clipboardData.setData('text/html', html); + } + clipboardData.setData('vscode-editor-data', JSON.stringify(metadata)); + } +}; diff --git a/src/vs/editor/browser/controller/editContext/editContextUtils.ts b/src/vs/editor/browser/controller/editContext/editContext.ts similarity index 92% rename from src/vs/editor/browser/controller/editContext/editContextUtils.ts rename to src/vs/editor/browser/controller/editContext/editContext.ts index 19f284b10c0..edcf2be3361 100644 --- a/src/vs/editor/browser/controller/editContext/editContextUtils.ts +++ b/src/vs/editor/browser/controller/editContext/editContext.ts @@ -10,7 +10,6 @@ import { ViewPart } from '../../view/viewPart.js'; export abstract class AbstractEditContext extends ViewPart { abstract domNode: FastDomNode; - abstract appendTo(overflowGuardContainer: FastDomNode): void; abstract focus(): void; abstract isFocused(): boolean; abstract refreshFocusState(): void; diff --git a/src/vs/editor/browser/controller/editContext/native/debugEditContext.ts b/src/vs/editor/browser/controller/editContext/native/debugEditContext.ts index 301c21dc5bb..5fa3d47cc1b 100644 --- a/src/vs/editor/browser/controller/editContext/native/debugEditContext.ts +++ b/src/vs/editor/browser/controller/editContext/native/debugEditContext.ts @@ -3,49 +3,69 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { EditContext } from './editContextFactory.js'; + const COLOR_FOR_CONTROL_BOUNDS = 'blue'; const COLOR_FOR_SELECTION_BOUNDS = 'red'; const COLOR_FOR_CHARACTER_BOUNDS = 'green'; -export class DebugEditContext extends EditContext { +export class DebugEditContext { private _isDebugging = true; private _controlBounds: DOMRect | null = null; private _selectionBounds: DOMRect | null = null; private _characterBounds: { rangeStart: number; characterBounds: DOMRect[] } | null = null; - constructor(options?: EditContextInit | undefined) { - super(options); + private _editContext: EditContext; + + constructor(window: Window, options?: EditContextInit | undefined) { + this._editContext = EditContext.create(window, options); + } + + get text(): DOMString { + return this._editContext.text; + } + + get selectionStart(): number { + return this._editContext.selectionStart; + } + + get selectionEnd(): number { + return this._editContext.selectionEnd; + } + + get characterBoundsRangeStart(): number { + return this._editContext.characterBoundsRangeStart; } - override updateText(rangeStart: number, rangeEnd: number, text: string): void { - super.updateText(rangeStart, rangeEnd, text); + updateText(rangeStart: number, rangeEnd: number, text: string): void { + this._editContext.updateText(rangeStart, rangeEnd, text); this.renderDebug(); } - override updateSelection(start: number, end: number): void { - super.updateSelection(start, end); + updateSelection(start: number, end: number): void { + this._editContext.updateSelection(start, end); this.renderDebug(); } - override updateControlBounds(controlBounds: DOMRect): void { - super.updateControlBounds(controlBounds); + updateControlBounds(controlBounds: DOMRect): void { + this._editContext.updateControlBounds(controlBounds); this._controlBounds = controlBounds; this.renderDebug(); } - override updateSelectionBounds(selectionBounds: DOMRect): void { - super.updateSelectionBounds(selectionBounds); + updateSelectionBounds(selectionBounds: DOMRect): void { + this._editContext.updateSelectionBounds(selectionBounds); this._selectionBounds = selectionBounds; this.renderDebug(); } - override updateCharacterBounds(rangeStart: number, characterBounds: DOMRect[]): void { - super.updateCharacterBounds(rangeStart, characterBounds); + updateCharacterBounds(rangeStart: number, characterBounds: DOMRect[]): void { + this._editContext.updateCharacterBounds(rangeStart, characterBounds); this._characterBounds = { rangeStart, characterBounds }; this.renderDebug(); } - override attachedElements(): HTMLElement[] { - return super.attachedElements(); + attachedElements(): HTMLElement[] { + return this._editContext.attachedElements(); } - override characterBounds(): DOMRect[] { - return super.characterBounds(); + characterBounds(): DOMRect[] { + return this._editContext.characterBounds(); } private readonly _ontextupdateWrapper = new EventListenerWrapper('textupdate', this); @@ -54,22 +74,22 @@ export class DebugEditContext extends EditContext { private readonly _oncompositionstartWrapper = new EventListenerWrapper('compositionstart', this); private readonly _oncompositionendWrapper = new EventListenerWrapper('compositionend', this); - override get ontextupdate(): EventHandler | null { return this._ontextupdateWrapper.eventHandler; } - override set ontextupdate(value: EventHandler | null) { this._ontextupdateWrapper.eventHandler = value; } - override get ontextformatupdate(): EventHandler | null { return this._ontextformatupdateWrapper.eventHandler; } - override set ontextformatupdate(value: EventHandler | null) { this._ontextformatupdateWrapper.eventHandler = value; } - override get oncharacterboundsupdate(): EventHandler | null { return this._oncharacterboundsupdateWrapper.eventHandler; } - override set oncharacterboundsupdate(value: EventHandler | null) { this._oncharacterboundsupdateWrapper.eventHandler = value; } - override get oncompositionstart(): EventHandler | null { return this._oncompositionstartWrapper.eventHandler; } - override set oncompositionstart(value: EventHandler | null) { this._oncompositionstartWrapper.eventHandler = value; } - override get oncompositionend(): EventHandler | null { return this._oncompositionendWrapper.eventHandler; } - override set oncompositionend(value: EventHandler | null) { this._oncompositionendWrapper.eventHandler = value; } + get ontextupdate(): EventHandler | null { return this._ontextupdateWrapper.eventHandler; } + set ontextupdate(value: EventHandler | null) { this._ontextupdateWrapper.eventHandler = value; } + get ontextformatupdate(): EventHandler | null { return this._ontextformatupdateWrapper.eventHandler; } + set ontextformatupdate(value: EventHandler | null) { this._ontextformatupdateWrapper.eventHandler = value; } + get oncharacterboundsupdate(): EventHandler | null { return this._oncharacterboundsupdateWrapper.eventHandler; } + set oncharacterboundsupdate(value: EventHandler | null) { this._oncharacterboundsupdateWrapper.eventHandler = value; } + get oncompositionstart(): EventHandler | null { return this._oncompositionstartWrapper.eventHandler; } + set oncompositionstart(value: EventHandler | null) { this._oncompositionstartWrapper.eventHandler = value; } + get oncompositionend(): EventHandler | null { return this._oncompositionendWrapper.eventHandler; } + set oncompositionend(value: EventHandler | null) { this._oncompositionendWrapper.eventHandler = value; } private readonly _listenerMap = new Map(); - override addEventListener(type: K, listener: (this: GlobalEventHandlers, ev: EditContextEventHandlersEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; - override addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void { + addEventListener(type: K, listener: (this: GlobalEventHandlers, ev: EditContextEventHandlersEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void; + addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void { if (!listener) { return; } const debugListener = (event: Event) => { @@ -84,22 +104,22 @@ export class DebugEditContext extends EditContext { } }; this._listenerMap.set(listener, debugListener); - super.addEventListener(type, debugListener, options); + this._editContext.addEventListener(type, debugListener, options); this.renderDebug(); } - override removeEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | EventListenerOptions | undefined): void { + removeEventListener(type: string, listener: EventListenerOrEventListenerObject | null, options?: boolean | EventListenerOptions | undefined): void { if (!listener) { return; } const debugListener = this._listenerMap.get(listener); if (debugListener) { - super.removeEventListener(type, debugListener, options); + this._editContext.removeEventListener(type, debugListener, options); this._listenerMap.delete(listener); } this.renderDebug(); } - override dispatchEvent(event: Event): boolean { - return super.dispatchEvent(event); + dispatchEvent(event: Event): boolean { + return this._editContext.dispatchEvent(event); } public startDebugging() { @@ -131,7 +151,7 @@ export class DebugEditContext extends EditContext { this._disposables.push(createRect(rect, COLOR_FOR_CHARACTER_BOUNDS)); } } - this._disposables.push(createDiv(this.text, this.selectionStart, this.selectionEnd)); + this._disposables.push(createDiv(this._editContext.text, this._editContext.selectionStart, this._editContext.selectionEnd)); } } diff --git a/src/vs/editor/browser/controller/editContext/native/editContextFactory.ts b/src/vs/editor/browser/controller/editContext/native/editContextFactory.ts new file mode 100644 index 00000000000..84b9cc52df3 --- /dev/null +++ b/src/vs/editor/browser/controller/editContext/native/editContextFactory.ts @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +export namespace EditContext { + + /** + * Create an edit context. + */ + export function create(window: Window, options?: EditContextInit): EditContext { + return new (window as any).EditContext(options); + } +} diff --git a/src/vs/editor/browser/controller/editContext/native/nativeEditContext.css b/src/vs/editor/browser/controller/editContext/native/nativeEditContext.css index e093d99a73f..792c15feec2 100644 --- a/src/vs/editor/browser/controller/editContext/native/nativeEditContext.css +++ b/src/vs/editor/browser/controller/editContext/native/nativeEditContext.css @@ -13,44 +13,38 @@ text-wrap: nowrap; } -.monaco-editor .edit-context-format-decoration { - text-decoration-line: underline; - /* TODO @aiday chose a theme color */ - text-decoration-color: blue; - color: var(--vscode-editor-foreground, inherit); - opacity: 0.8; - - &.underline-style-none { - text-decoration-line: none; - text-decoration-style: none; - } - - &.underline-style-solid { - text-decoration-style: solid; - } - - &.underline-style-dotted { - text-decoration-style: dotted; - } +.monaco-editor .native-edit-context-textarea { + min-width: 0; + min-height: 0; + margin: 0; + padding: 0; + position: absolute; + outline: none !important; + resize: none; + border: none; + overflow: hidden; + color: transparent; + background-color: transparent; + z-index: -10; +} - &.underline-style-dashed { - text-decoration-style: dashed; - } +.monaco-editor .edit-context-composition-none { + background-color: transparent; + border-bottom: none; +} - &.underline-style-wavy { - text-decoration-style: wavy; - } +.monaco-editor :not(.hc-black, .hc-light) .edit-context-composition-secondary { + border-bottom: 1px solid var(--vscode-editor-compositionBorder); +} - &.underline-thickness-none { - text-decoration-line: none; - text-decoration-thickness: none; - } +.monaco-editor :not(.hc-black, .hc-light) .edit-context-composition-primary { + border-bottom: 2px solid var(--vscode-editor-compositionBorder); +} - &.underline-thickness-thin { - text-decoration-thickness: 1px; - } +.monaco-editor :is(.hc-black, .hc-light) .edit-context-composition-secondary { + border: 1px solid var(--vscode-editor-compositionBorder); +} - &.underline-thickness-thick { - text-decoration-thickness: 2px; - } +.monaco-editor :is(.hc-black, .hc-light) .edit-context-composition-primary { + border: 2px solid var(--vscode-editor-compositionBorder); } diff --git a/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts b/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts index 541a4bc7e0b..75acb7c2c8d 100644 --- a/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts +++ b/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts @@ -5,20 +5,19 @@ import './nativeEditContext.css'; import { isFirefox } from '../../../../../base/browser/browser.js'; -import { addDisposableListener } from '../../../../../base/browser/dom.js'; +import { addDisposableListener, getActiveWindow, getWindow, getWindowId } from '../../../../../base/browser/dom.js'; import { FastDomNode } from '../../../../../base/browser/fastDomNode.js'; import { StandardKeyboardEvent } from '../../../../../base/browser/keyboardEvent.js'; import { KeyCode } from '../../../../../base/common/keyCodes.js'; -import { IClipboardService } from '../../../../../platform/clipboard/common/clipboardService.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; -import { EndOfLinePreference, IModelDeltaDecoration } from '../../../../common/model.js'; -import { ViewConfigurationChangedEvent, ViewCursorStateChangedEvent } from '../../../../common/viewEvents.js'; +import { EndOfLinePreference, EndOfLineSequence, IModelDeltaDecoration } from '../../../../common/model.js'; +import { ViewConfigurationChangedEvent, ViewCursorStateChangedEvent, ViewDecorationsChangedEvent, ViewFlushedEvent, ViewLinesChangedEvent, ViewLinesDeletedEvent, ViewLinesInsertedEvent, ViewScrollChangedEvent, ViewZonesChangedEvent } from '../../../../common/viewEvents.js'; import { ViewContext } from '../../../../common/viewModel/viewContext.js'; import { RestrictedRenderingContext, RenderingContext } from '../../../view/renderingContext.js'; import { ViewController } from '../../../view/viewController.js'; -import { ClipboardStoredMetadata, getDataToCopy, InMemoryClipboardMetadataManager } from '../clipboardUtils.js'; -import { AbstractEditContext } from '../editContextUtils.js'; +import { ClipboardEventUtils, ClipboardStoredMetadata, getDataToCopy, InMemoryClipboardMetadataManager } from '../clipboardUtils.js'; +import { AbstractEditContext } from '../editContext.js'; import { editContextAddDisposableListener, FocusTracker, ITypeData } from './nativeEditContextUtils.js'; import { ScreenReaderSupport } from './screenReaderSupport.js'; import { Range } from '../../../../common/core/range.js'; @@ -26,45 +25,88 @@ import { Selection } from '../../../../common/core/selection.js'; import { Position } from '../../../../common/core/position.js'; import { IVisibleRangeProvider } from '../textArea/textAreaEditContext.js'; import { PositionOffsetTransformer } from '../../../../common/core/positionToOffset.js'; +import { IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; +import { EditContext } from './editContextFactory.js'; +import { IAccessibilityService } from '../../../../../platform/accessibility/common/accessibility.js'; +import { NativeEditContextRegistry } from './nativeEditContextRegistry.js'; +import { IEditorAriaOptions } from '../../../editorBrowser.js'; + +// Corresponds to classes in nativeEditContext.css +enum CompositionClassName { + NONE = 'edit-context-composition-none', + SECONDARY = 'edit-context-composition-secondary', + PRIMARY = 'edit-context-composition-primary', +} export class NativeEditContext extends AbstractEditContext { + // Text area used to handle paste events + public readonly textArea: FastDomNode; public readonly domNode: FastDomNode; private readonly _editContext: EditContext; private readonly _screenReaderSupport: ScreenReaderSupport; + private _editContextPrimarySelection: Selection = new Selection(1, 1, 1, 1); // Overflow guard container private _parent: HTMLElement | undefined; private _decorations: string[] = []; private _primarySelection: Selection = new Selection(1, 1, 1, 1); - private _textStartPositionWithinEditor: Position = new Position(1, 1); + + private _targetWindowId: number = -1; + private _scrollTop: number = 0; + private _scrollLeft: number = 0; private readonly _focusTracker: FocusTracker; + private readonly _selectionChangeListener: MutableDisposable; + constructor( + ownerID: string, context: ViewContext, + overflowGuardContainer: FastDomNode, viewController: ViewController, private readonly _visibleRangeProvider: IVisibleRangeProvider, @IInstantiationService instantiationService: IInstantiationService, - @IClipboardService clipboardService: IClipboardService, + @IAccessibilityService private readonly _accessibilityService: IAccessibilityService ) { super(context); this.domNode = new FastDomNode(document.createElement('div')); this.domNode.setClassName(`native-edit-context`); + this.textArea = new FastDomNode(document.createElement('textarea')); + this.textArea.setClassName('native-edit-context-textarea'); + this.textArea.setAttribute('tabindex', '-1'); + this._updateDomAttributes(); - this._focusTracker = this._register(new FocusTracker(this.domNode.domNode, (newFocusValue: boolean) => this._context.viewModel.setHasFocus(newFocusValue))); + overflowGuardContainer.appendChild(this.domNode); + overflowGuardContainer.appendChild(this.textArea); + this._parent = overflowGuardContainer.domNode; + + this._selectionChangeListener = this._register(new MutableDisposable()); + this._focusTracker = this._register(new FocusTracker(this.domNode.domNode, (newFocusValue: boolean) => { + if (newFocusValue) { + this._selectionChangeListener.value = this._setSelectionChangeListener(viewController); + this._screenReaderSupport.setIgnoreSelectionChangeTime('onFocus'); + } else { + this._selectionChangeListener.value = undefined; + } + this._context.viewModel.setHasFocus(newFocusValue); + })); - this._editContext = new EditContext(); - this.domNode.domNode.editContext = this._editContext; + const window = getWindow(this.domNode.domNode); + this._editContext = EditContext.create(window); + this.setEditContextOnDomNode(); this._screenReaderSupport = instantiationService.createInstance(ScreenReaderSupport, this.domNode, context); - this._register(addDisposableListener(this.domNode.domNode, 'copy', () => this._ensureClipboardGetsEditorSelection(clipboardService))); - this._register(addDisposableListener(this.domNode.domNode, 'cut', () => { - this._ensureClipboardGetsEditorSelection(clipboardService); + this._register(addDisposableListener(this.domNode.domNode, 'copy', (e) => this._ensureClipboardGetsEditorSelection(e))); + this._register(addDisposableListener(this.domNode.domNode, 'cut', (e) => { + // Pretend here we touched the text area, as the `cut` event will most likely + // result in a `selectionchange` event which we want to ignore + this._screenReaderSupport.setIgnoreSelectionChangeTime('onCut'); + this._ensureClipboardGetsEditorSelection(e); viewController.cut(); })); @@ -80,7 +122,7 @@ export class NativeEditContext extends AbstractEditContext { viewController.emitKeyDown(standardKeyboardEvent); })); this._register(addDisposableListener(this.domNode.domNode, 'beforeinput', async (e) => { - if (e.inputType === 'insertParagraph') { + if (e.inputType === 'insertParagraph' || e.inputType === 'insertLineBreak') { this._onType(viewController, { text: '\n', replacePrevCharCnt: 0, replaceNextCharCnt: 0, positionDelta: 0 }); } })); @@ -105,6 +147,32 @@ export class NativeEditContext extends AbstractEditContext { // Emits ViewCompositionEndEvent which can be depended on by ViewEventHandlers this._context.viewModel.onCompositionEnd(); })); + this._register(addDisposableListener(this.textArea.domNode, 'paste', (e) => { + // Pretend here we touched the text area, as the `paste` event will most likely + // result in a `selectionchange` event which we want to ignore + this._screenReaderSupport.setIgnoreSelectionChangeTime('onPaste'); + e.preventDefault(); + if (!e.clipboardData) { + return; + } + let [text, metadata] = ClipboardEventUtils.getTextData(e.clipboardData); + if (!text) { + return; + } + metadata = metadata || InMemoryClipboardMetadataManager.INSTANCE.get(text); + let pasteOnNewLine = false; + let multicursorText: string[] | null = null; + let mode: string | null = null; + if (metadata) { + const options = this._context.configuration.options; + const emptySelectionClipboard = options.get(EditorOption.emptySelectionClipboard); + pasteOnNewLine = emptySelectionClipboard && !!metadata.isFromEmptySelection; + multicursorText = typeof metadata.multicursorText !== 'undefined' ? metadata.multicursorText : null; + mode = metadata.mode; + } + viewController.paste(text, pasteOnNewLine, multicursorText, mode); + })); + this._register(NativeEditContextRegistry.register(ownerID, this)); } // --- Public methods --- @@ -113,16 +181,12 @@ export class NativeEditContext extends AbstractEditContext { // Force blue the dom node so can write in pane with no native edit context after disposal this.domNode.domNode.blur(); this.domNode.domNode.remove(); + this.textArea.domNode.remove(); super.dispose(); } - public appendTo(overflowGuardContainer: FastDomNode): void { - overflowGuardContainer.appendChild(this.domNode); - this._parent = overflowGuardContainer.domNode; - } - - public setAriaOptions(): void { - this._screenReaderSupport.setAriaOptions(); + public setAriaOptions(options: IEditorAriaOptions): void { + this._screenReaderSupport.setAriaOptions(options); } /* Last rendered data needed for correct hit-testing and determining the mouse position. @@ -153,15 +217,70 @@ export class NativeEditContext extends AbstractEditContext { return true; } + public override onDecorationsChanged(e: ViewDecorationsChangedEvent): boolean { + // true for inline decorations that can end up relayouting text + return true; + } + + public override onFlushed(e: ViewFlushedEvent): boolean { + return true; + } + + public override onLinesChanged(e: ViewLinesChangedEvent): boolean { + return true; + } + + public override onLinesDeleted(e: ViewLinesDeletedEvent): boolean { + return true; + } + + public override onLinesInserted(e: ViewLinesInsertedEvent): boolean { + return true; + } + + public override onScrollChanged(e: ViewScrollChangedEvent): boolean { + this._scrollLeft = e.scrollLeft; + this._scrollTop = e.scrollTop; + return true; + } + + public override onZonesChanged(e: ViewZonesChangedEvent): boolean { + return true; + } + + public onWillPaste(): void { + this._screenReaderSupport.setIgnoreSelectionChangeTime('onWillPaste'); + } + public writeScreenReaderContent(): void { this._screenReaderSupport.writeScreenReaderContent(); } - public isFocused(): boolean { return this._focusTracker.isFocused; } + public isFocused(): boolean { + return this._focusTracker.isFocused || (getActiveWindow().document.activeElement === this.textArea.domNode); + } + + public focus(): void { + this._focusTracker.focus(); + + // If the editor is off DOM, focus cannot be really set, so let's double check that we have managed to set the focus + this.refreshFocusState(); + } - public focus(): void { this._focusTracker.focus(); } + public refreshFocusState(): void { + this._focusTracker.refreshFocusState(); + } - public refreshFocusState(): void { } + // TODO: added as a workaround fix for https://github.com/microsoft/vscode/issues/229825 + // When this issue will be fixed the following should be removed. + public setEditContextOnDomNode(): void { + const targetWindow = getWindow(this.domNode.domNode); + const targetWindowId = getWindowId(targetWindow); + if (this._targetWindowId !== targetWindowId) { + this.domNode.domNode.editContext = this._editContext; + this._targetWindowId = targetWindowId; + } + } // --- Private methods --- @@ -172,17 +291,24 @@ export class NativeEditContext extends AbstractEditContext { private _updateEditContext(): void { const editContextState = this._getNewEditContextState(); + if (!editContextState) { + return; + } this._editContext.updateText(0, Number.MAX_SAFE_INTEGER, editContextState.text); this._editContext.updateSelection(editContextState.selectionStartOffset, editContextState.selectionEndOffset); - this._textStartPositionWithinEditor = editContextState.textStartPositionWithinEditor; + this._editContextPrimarySelection = editContextState.editContextPrimarySelection; } private _emitTypeEvent(viewController: ViewController, e: TextUpdateEvent): void { if (!this._editContext) { return; } + if (!this._editContextPrimarySelection.equalsSelection(this._primarySelection)) { + return; + } const model = this._context.viewModel.model; - const offsetOfStartOfText = model.getOffsetAt(this._textStartPositionWithinEditor); + const startPositionOfEditContext = this._editContextStartPosition(); + const offsetOfStartOfText = model.getOffsetAt(startPositionOfEditContext); const offsetOfSelectionEnd = model.getOffsetAt(this._primarySelection.getEndPosition()); const offsetOfSelectionStart = model.getOffsetAt(this._primarySelection.getStartPosition()); const selectionEndOffset = offsetOfSelectionEnd - offsetOfStartOfText; @@ -204,11 +330,15 @@ export class NativeEditContext extends AbstractEditContext { if (selectionEndOffset > e.updateRangeEnd) { text += this._editContext.text.substring(e.updateRangeEnd, selectionEndOffset); } + let positionDelta = 0; + if (e.selectionStart === e.selectionEnd && selectionStartOffset === selectionEndOffset) { + positionDelta = e.selectionStart - (e.updateRangeStart + e.text.length); + } const typeInput: ITypeData = { text, replacePrevCharCnt, replaceNextCharCnt, - positionDelta: 0, + positionDelta }; this._onType(viewController, typeInput); @@ -226,47 +356,59 @@ export class NativeEditContext extends AbstractEditContext { } } - private _getNewEditContextState(): { text: string; selectionStartOffset: number; selectionEndOffset: number; textStartPositionWithinEditor: Position } { + private _getNewEditContextState(): { text: string; selectionStartOffset: number; selectionEndOffset: number; editContextPrimarySelection: Selection } | undefined { + const editContextPrimarySelection = this._primarySelection; const model = this._context.viewModel.model; - const primarySelectionStartLine = this._primarySelection.startLineNumber; - const primarySelectionEndLine = this._primarySelection.endLineNumber; + if (!model.isValidRange(editContextPrimarySelection)) { + return; + } + const primarySelectionStartLine = editContextPrimarySelection.startLineNumber; + const primarySelectionEndLine = editContextPrimarySelection.endLineNumber; const endColumnOfEndLineNumber = model.getLineMaxColumn(primarySelectionEndLine); const rangeOfText = new Range(primarySelectionStartLine, 1, primarySelectionEndLine, endColumnOfEndLineNumber); const text = model.getValueInRange(rangeOfText, EndOfLinePreference.TextDefined); - const selectionStartOffset = this._primarySelection.startColumn - 1; - const selectionEndOffset = text.length + this._primarySelection.endColumn - endColumnOfEndLineNumber; - const textStartPositionWithinEditor = rangeOfText.getStartPosition(); + const selectionStartOffset = editContextPrimarySelection.startColumn - 1; + const selectionEndOffset = text.length + editContextPrimarySelection.endColumn - endColumnOfEndLineNumber; return { text, selectionStartOffset, selectionEndOffset, - textStartPositionWithinEditor + editContextPrimarySelection }; } + private _editContextStartPosition(): Position { + return new Position(this._editContextPrimarySelection.startLineNumber, 1); + } + private _handleTextFormatUpdate(e: TextFormatUpdateEvent): void { if (!this._editContext) { return; } const formats = e.getTextFormats(); - const textStartPositionWithinEditor = this._textStartPositionWithinEditor; + const editContextStartPosition = this._editContextStartPosition(); const decorations: IModelDeltaDecoration[] = []; formats.forEach(f => { const textModel = this._context.viewModel.model; - const offsetOfEditContextText = textModel.getOffsetAt(textStartPositionWithinEditor); + const offsetOfEditContextText = textModel.getOffsetAt(editContextStartPosition); const startPositionOfDecoration = textModel.getPositionAt(offsetOfEditContextText + f.rangeStart); const endPositionOfDecoration = textModel.getPositionAt(offsetOfEditContextText + f.rangeEnd); const decorationRange = Range.fromPositions(startPositionOfDecoration, endPositionOfDecoration); - const classNames = [ - 'edit-context-format-decoration', - `underline-style-${f.underlineStyle.toLowerCase()}`, - `underline-thickness-${f.underlineThickness.toLowerCase()}`, - ]; + const thickness = f.underlineThickness.toLowerCase(); + let decorationClassName: string = CompositionClassName.NONE; + switch (thickness) { + case 'thin': + decorationClassName = CompositionClassName.SECONDARY; + break; + case 'thick': + decorationClassName = CompositionClassName.PRIMARY; + break; + } decorations.push({ range: decorationRange, options: { description: 'textFormatDecoration', - inlineClassName: classNames.join(' '), + inlineClassName: decorationClassName, } }); }); @@ -284,11 +426,10 @@ export class NativeEditContext extends AbstractEditContext { const modelStartPosition = this._primarySelection.getStartPosition(); const viewStartPosition = this._context.viewModel.coordinatesConverter.convertModelPositionToViewPosition(modelStartPosition); const verticalOffsetStart = this._context.viewLayout.getVerticalOffsetForLineNumber(viewStartPosition.lineNumber); - const editorScrollTop = this._context.viewLayout.getCurrentScrollTop(); - const top = parentBounds.top + verticalOffsetStart - editorScrollTop; + const top = parentBounds.top + verticalOffsetStart - this._scrollTop; const height = (this._primarySelection.endLineNumber - this._primarySelection.startLineNumber + 1) * lineHeight; - let left = parentBounds.left + contentLeft; + let left = parentBounds.left + contentLeft - this._scrollLeft; let width: number; if (this._primarySelection.isEmpty()) { @@ -320,15 +461,14 @@ export class NativeEditContext extends AbstractEditContext { const offsetTransformer = new PositionOffsetTransformer(this._editContext.text); for (let offset = e.rangeStart; offset < e.rangeEnd; offset++) { const editContextStartPosition = offsetTransformer.getPosition(offset); - const textStartLineOffsetWithinEditor = this._textStartPositionWithinEditor.lineNumber - 1; + const textStartLineOffsetWithinEditor = this._editContextPrimarySelection.startLineNumber - 1; const characterStartPosition = new Position(textStartLineOffsetWithinEditor + editContextStartPosition.lineNumber, editContextStartPosition.column); const characterEndPosition = characterStartPosition.delta(0, 1); const characterModelRange = Range.fromPositions(characterStartPosition, characterEndPosition); const characterViewRange = this._context.viewModel.coordinatesConverter.convertModelRangeToViewRange(characterModelRange); const characterLinesVisibleRanges = this._visibleRangeProvider.linesVisibleRangesForRange(characterViewRange, true) ?? []; const characterVerticalOffset = this._context.viewLayout.getVerticalOffsetForLineNumber(characterViewRange.startLineNumber); - const editorScrollTop = this._context.viewLayout.getCurrentScrollTop(); - const top = parentBounds.top + characterVerticalOffset - editorScrollTop; + const top = parentBounds.top + characterVerticalOffset - this._scrollTop; let left = 0; let width = typicalHalfWidthCharacterWidth; @@ -339,12 +479,12 @@ export class NativeEditContext extends AbstractEditContext { break; } } - characterBounds.push(new DOMRect(parentBounds.left + contentLeft + left, top, width, lineHeight)); + characterBounds.push(new DOMRect(parentBounds.left + contentLeft + left - this._scrollLeft, top, width, lineHeight)); } this._editContext.updateCharacterBounds(e.rangeStart, characterBounds); } - private _ensureClipboardGetsEditorSelection(clipboardService: IClipboardService): void { + private _ensureClipboardGetsEditorSelection(e: ClipboardEvent): void { const options = this._context.configuration.options; const emptySelectionClipboard = options.get(EditorOption.emptySelectionClipboard); const copyWithSyntaxHighlighting = options.get(EditorOption.copyWithSyntaxHighlighting); @@ -362,6 +502,74 @@ export class NativeEditContext extends AbstractEditContext { (isFirefox ? dataToCopy.text.replace(/\r\n/g, '\n') : dataToCopy.text), storedMetadata ); - clipboardService.writeText(dataToCopy.text); + e.preventDefault(); + if (e.clipboardData) { + ClipboardEventUtils.setTextData(e.clipboardData, dataToCopy.text, dataToCopy.html, storedMetadata); + } + } + + private _setSelectionChangeListener(viewController: ViewController): IDisposable { + // See https://github.com/microsoft/vscode/issues/27216 and https://github.com/microsoft/vscode/issues/98256 + // When using a Braille display or NVDA for example, it is possible for users to reposition the + // system caret. This is reflected in Chrome as a `selectionchange` event and needs to be reflected within the editor. + + // `selectionchange` events often come multiple times for a single logical change + // so throttle multiple `selectionchange` events that burst in a short period of time. + let previousSelectionChangeEventTime = 0; + return addDisposableListener(this.domNode.domNode.ownerDocument, 'selectionchange', () => { + const isScreenReaderOptimized = this._accessibilityService.isScreenReaderOptimized(); + if (!this.isFocused() || !isScreenReaderOptimized) { + return; + } + const screenReaderContentState = this._screenReaderSupport.screenReaderContentState; + if (!screenReaderContentState) { + return; + } + const now = Date.now(); + const delta1 = now - previousSelectionChangeEventTime; + previousSelectionChangeEventTime = now; + if (delta1 < 5) { + // received another `selectionchange` event within 5ms of the previous `selectionchange` event + // => ignore it + return; + } + const delta2 = now - this._screenReaderSupport.getIgnoreSelectionChangeTime(); + this._screenReaderSupport.resetSelectionChangeTime(); + if (delta2 < 100) { + // received a `selectionchange` event within 100ms since we touched the textarea + // => ignore it, since we caused it + return; + } + const activeDocument = getActiveWindow().document; + const activeDocumentSelection = activeDocument.getSelection(); + if (!activeDocumentSelection) { + return; + } + const rangeCount = activeDocumentSelection.rangeCount; + if (rangeCount === 0) { + return; + } + const range = activeDocumentSelection.getRangeAt(0); + const viewModel = this._context.viewModel; + const model = viewModel.model; + const coordinatesConverter = viewModel.coordinatesConverter; + const modelScreenReaderContentStartPositionWithinEditor = coordinatesConverter.convertViewPositionToModelPosition(screenReaderContentState.startPositionWithinEditor); + const offsetOfStartOfScreenReaderContent = model.getOffsetAt(modelScreenReaderContentStartPositionWithinEditor); + let offsetOfSelectionStart = range.startOffset + offsetOfStartOfScreenReaderContent; + let offsetOfSelectionEnd = range.endOffset + offsetOfStartOfScreenReaderContent; + const modelUsesCRLF = model.getEndOfLineSequence() === EndOfLineSequence.CRLF; + if (modelUsesCRLF) { + const screenReaderContentText = screenReaderContentState.value; + const offsetTransformer = new PositionOffsetTransformer(screenReaderContentText); + const positionOfStartWithinText = offsetTransformer.getPosition(range.startOffset); + const positionOfEndWithinText = offsetTransformer.getPosition(range.endOffset); + offsetOfSelectionStart += positionOfStartWithinText.lineNumber - 1; + offsetOfSelectionEnd += positionOfEndWithinText.lineNumber - 1; + } + const positionOfSelectionStart = model.getPositionAt(offsetOfSelectionStart); + const positionOfSelectionEnd = model.getPositionAt(offsetOfSelectionEnd); + const newSelection = Selection.fromPositions(positionOfSelectionStart, positionOfSelectionEnd); + viewController.setSelection(newSelection); + }); } } diff --git a/src/vs/editor/browser/controller/editContext/native/nativeEditContextRegistry.ts b/src/vs/editor/browser/controller/editContext/native/nativeEditContextRegistry.ts new file mode 100644 index 00000000000..eda35288125 --- /dev/null +++ b/src/vs/editor/browser/controller/editContext/native/nativeEditContextRegistry.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { NativeEditContext } from './nativeEditContext.js'; + +class NativeEditContextRegistryImpl { + + private _nativeEditContextMapping: Map = new Map(); + + register(ownerID: string, nativeEditContext: NativeEditContext): IDisposable { + this._nativeEditContextMapping.set(ownerID, nativeEditContext); + return { + dispose: () => { + this._nativeEditContextMapping.delete(ownerID); + } + }; + } + + get(ownerID: string): NativeEditContext | undefined { + return this._nativeEditContextMapping.get(ownerID); + } +} + +export const NativeEditContextRegistry = new NativeEditContextRegistryImpl(); diff --git a/src/vs/editor/browser/controller/editContext/native/nativeEditContextUtils.ts b/src/vs/editor/browser/controller/editContext/native/nativeEditContextUtils.ts index e55e1521fac..b3166de0437 100644 --- a/src/vs/editor/browser/controller/editContext/native/nativeEditContextUtils.ts +++ b/src/vs/editor/browser/controller/editContext/native/nativeEditContextUtils.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { addDisposableListener } from '../../../../../base/browser/dom.js'; +import { addDisposableListener, getActiveWindow } from '../../../../../base/browser/dom.js'; import { IDisposable, Disposable } from '../../../../../base/common/lifecycle.js'; export interface ITypeData { @@ -40,6 +40,11 @@ export class FocusTracker extends Disposable { this._domNode.focus(); } + public refreshFocusState(): void { + const focused = this._domNode === getActiveWindow().document.activeElement; + this._handleFocusedChanged(focused); + } + get isFocused(): boolean { return this._isFocused; } diff --git a/src/vs/editor/browser/controller/editContext/native/screenReaderSupport.ts b/src/vs/editor/browser/controller/editContext/native/screenReaderSupport.ts index bea87b94bf8..769bb020d72 100644 --- a/src/vs/editor/browser/controller/editContext/native/screenReaderSupport.ts +++ b/src/vs/editor/browser/controller/editContext/native/screenReaderSupport.ts @@ -6,7 +6,7 @@ import { getActiveWindow } from '../../../../../base/browser/dom.js'; import { FastDomNode } from '../../../../../base/browser/fastDomNode.js'; import { localize } from '../../../../../nls.js'; -import { AccessibilitySupport } from '../../../../../platform/accessibility/common/accessibility.js'; +import { AccessibilitySupport, IAccessibilityService } from '../../../../../platform/accessibility/common/accessibility.js'; import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; import { FontInfo } from '../../../../common/config/fontInfo.js'; @@ -17,7 +17,8 @@ import { EndOfLinePreference } from '../../../../common/model.js'; import { ViewConfigurationChangedEvent, ViewCursorStateChangedEvent } from '../../../../common/viewEvents.js'; import { ViewContext } from '../../../../common/viewModel/viewContext.js'; import { applyFontInfo } from '../../../config/domFontInfo.js'; -import { RestrictedRenderingContext, RenderingContext } from '../../../view/renderingContext.js'; +import { IEditorAriaOptions } from '../../../editorBrowser.js'; +import { RestrictedRenderingContext, RenderingContext, HorizontalPosition } from '../../../view/renderingContext.js'; import { ariaLabelForScreenReaderContent, ISimpleModel, newlinecount, PagedScreenReaderStrategy, ScreenReaderContentState } from '../screenReaderUtils.js'; export class ScreenReaderSupport { @@ -25,23 +26,38 @@ export class ScreenReaderSupport { // Configuration values private _contentLeft: number = 1; private _contentWidth: number = 1; + private _contentHeight: number = 1; private _lineHeight: number = 1; - private _fontInfo: FontInfo | undefined; - private _accessibilitySupport: AccessibilitySupport = AccessibilitySupport.Unknown; + private _fontInfo!: FontInfo; private _accessibilityPageSize: number = 1; + private _ignoreSelectionChangeTime: number = 0; private _primarySelection: Selection = new Selection(1, 1, 1, 1); + private _primaryCursorVisibleRange: HorizontalPosition | null = null; private _screenReaderContentState: ScreenReaderContentState | undefined; constructor( private readonly _domNode: FastDomNode, private readonly _context: ViewContext, @IKeybindingService private readonly _keybindingService: IKeybindingService, + @IAccessibilityService private readonly _accessibilityService: IAccessibilityService ) { this._updateConfigurationSettings(); this._updateDomAttributes(); } + public setIgnoreSelectionChangeTime(reason: string): void { + this._ignoreSelectionChangeTime = Date.now(); + } + + public getIgnoreSelectionChangeTime(): number { + return this._ignoreSelectionChangeTime; + } + + public resetSelectionChangeTime(): void { + this._ignoreSelectionChangeTime = 0; + } + public onConfigurationChanged(e: ViewConfigurationChangedEvent): void { this._updateConfigurationSettings(); this._updateDomAttributes(); @@ -55,9 +71,9 @@ export class ScreenReaderSupport { const layoutInfo = options.get(EditorOption.layoutInfo); this._contentLeft = layoutInfo.contentLeft; this._contentWidth = layoutInfo.contentWidth; + this._contentHeight = layoutInfo.height; this._fontInfo = options.get(EditorOption.fontInfo); this._lineHeight = options.get(EditorOption.lineHeight); - this._accessibilitySupport = options.get(EditorOption.accessibilitySupport); this._accessibilityPageSize = options.get(EditorOption.accessibilityPageSize); } @@ -80,51 +96,104 @@ export class ScreenReaderSupport { public prepareRender(ctx: RenderingContext): void { this.writeScreenReaderContent(); + this._primaryCursorVisibleRange = ctx.visibleRangeForPosition(this._primarySelection.getPosition()); } public render(ctx: RestrictedRenderingContext): void { if (!this._screenReaderContentState) { return; } - // For correct alignment of the screen reader content, we need to apply the correct font - applyFontInfo(this._domNode, this._fontInfo!); - const verticalOffsetForPrimaryLineNumber = this._context.viewLayout.getVerticalOffsetForLineNumber(this._primarySelection.positionLineNumber); + if (!this._primaryCursorVisibleRange) { + // The primary cursor is outside the viewport => place textarea to the top left + this._renderAtTopLeft(); + return; + } + + const editorScrollLeft = this._context.viewLayout.getCurrentScrollLeft(); + const left = this._contentLeft + this._primaryCursorVisibleRange.left - editorScrollLeft; + if (left < this._contentLeft || left > this._contentLeft + this._contentWidth) { + // cursor is outside the viewport + this._renderAtTopLeft(); + return; + } + const editorScrollTop = this._context.viewLayout.getCurrentScrollTop(); - const top = verticalOffsetForPrimaryLineNumber - editorScrollTop; + const top = this._context.viewLayout.getVerticalOffsetForLineNumber(this._primarySelection.positionLineNumber) - editorScrollTop; + if (top < 0 || top > this._contentHeight) { + // cursor is outside the viewport + this._renderAtTopLeft(); + return; + } + + this._doRender(top, this._contentLeft, this._contentWidth, this._lineHeight); + this._setScrollTop(); + } + + private _renderAtTopLeft(): void { + this._doRender(0, 0, this._contentWidth, 1); + } + + private _doRender(top: number, left: number, width: number, height: number): void { + // For correct alignment of the screen reader content, we need to apply the correct font + applyFontInfo(this._domNode, this._fontInfo); this._domNode.setTop(top); - this._domNode.setLeft(this._contentLeft); - this._domNode.setWidth(this._contentWidth); - this._domNode.setHeight(this._lineHeight); + this._domNode.setLeft(left); + this._domNode.setWidth(width); + this._domNode.setHeight(height); + } + private _setScrollTop(): void { + if (!this._screenReaderContentState) { + return; + } // Setting position within the screen reader content by modifying scroll position const textContentBeforeSelection = this._screenReaderContentState.value.substring(0, this._screenReaderContentState.selectionStart); const numberOfLinesOfContentBeforeSelection = newlinecount(textContentBeforeSelection); this._domNode.domNode.scrollTop = numberOfLinesOfContentBeforeSelection * this._lineHeight; } - public setAriaOptions(): void { } + public setAriaOptions(options: IEditorAriaOptions): void { + if (options.activeDescendant) { + this._domNode.setAttribute('aria-haspopup', 'true'); + this._domNode.setAttribute('aria-autocomplete', 'list'); + this._domNode.setAttribute('aria-activedescendant', options.activeDescendant); + } else { + this._domNode.setAttribute('aria-haspopup', 'false'); + this._domNode.setAttribute('aria-autocomplete', 'both'); + this._domNode.removeAttribute('aria-activedescendant'); + } + if (options.role) { + this._domNode.setAttribute('role', options.role); + } + } public writeScreenReaderContent(): void { const focusedElement = getActiveWindow().document.activeElement; if (!focusedElement || focusedElement !== this._domNode.domNode) { return; } - this._screenReaderContentState = this._getScreenReaderContentState(); - if (!this._screenReaderContentState) { - return; - } - if (this._domNode.domNode.textContent !== this._screenReaderContentState.value) { - this._domNode.domNode.textContent = this._screenReaderContentState.value; + const isScreenReaderOptimized = this._accessibilityService.isScreenReaderOptimized(); + if (isScreenReaderOptimized) { + this._screenReaderContentState = this._getScreenReaderContentState(); + if (this._domNode.domNode.textContent !== this._screenReaderContentState.value) { + this.setIgnoreSelectionChangeTime('setValue'); + this._domNode.domNode.textContent = this._screenReaderContentState.value; + } + this._setSelectionOfScreenReaderContent(this._screenReaderContentState.selectionStart, this._screenReaderContentState.selectionEnd); + } else { + this._screenReaderContentState = undefined; + this.setIgnoreSelectionChangeTime('setValue'); + this._domNode.domNode.textContent = ''; } - this._setSelectionOfScreenReaderContent(this._screenReaderContentState.selectionStart, this._screenReaderContentState.selectionEnd); } - private _getScreenReaderContentState(): ScreenReaderContentState | undefined { - if (this._accessibilitySupport === AccessibilitySupport.Disabled) { - return; - } + public get screenReaderContentState(): ScreenReaderContentState | undefined { + return this._screenReaderContentState; + } + + private _getScreenReaderContentState(): ScreenReaderContentState { const simpleModel: ISimpleModel = { getLineCount: (): number => { return this._context.viewModel.getLineCount(); @@ -142,7 +211,7 @@ export class ScreenReaderSupport { return this._context.viewModel.modifyPosition(position, offset); } }; - return PagedScreenReaderStrategy.fromEditorSelection(simpleModel, this._primarySelection, this._accessibilityPageSize, this._accessibilitySupport === AccessibilitySupport.Unknown); + return PagedScreenReaderStrategy.fromEditorSelection(simpleModel, this._primarySelection, this._accessibilityPageSize, this._accessibilityService.getAccessibilitySupport() === AccessibilitySupport.Unknown); } private _setSelectionOfScreenReaderContent(selectionOffsetStart: number, selectionOffsetEnd: number): void { @@ -158,6 +227,7 @@ export class ScreenReaderSupport { const range = new globalThis.Range(); range.setStart(textContent, selectionOffsetStart); range.setEnd(textContent, selectionOffsetEnd); + this.setIgnoreSelectionChangeTime('setRange'); activeDocumentSelection.removeAllRanges(); activeDocumentSelection.addRange(range); } diff --git a/src/vs/editor/browser/controller/editContext/screenReaderUtils.ts b/src/vs/editor/browser/controller/editContext/screenReaderUtils.ts index cc2dcd3f371..0a222d04817 100644 --- a/src/vs/editor/browser/controller/editContext/screenReaderUtils.ts +++ b/src/vs/editor/browser/controller/editContext/screenReaderUtils.ts @@ -31,6 +31,9 @@ export interface ScreenReaderContentState { /** the editor range in the view coordinate system that matches the selection inside `value` */ selection: Range; + /** the position of the start of the `value` in the editor */ + startPositionWithinEditor: Position; + /** the visible line count (wrapped, not necessarily matching \n characters) for the text in `value` before `selectionStart` */ newlineCountBeforeSelection: number; } @@ -97,6 +100,7 @@ export class PagedScreenReaderStrategy { selection: selection, selectionStart: pretext.length, selectionEnd: pretext.length + text.length, + startPositionWithinEditor: pretextRange.getStartPosition(), newlineCountBeforeSelection: pretextRange.endLineNumber - pretextRange.startLineNumber, }; } diff --git a/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContext.ts b/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContext.ts index acbc89e9e8e..a3f8b75544b 100644 --- a/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContext.ts +++ b/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContext.ts @@ -34,7 +34,7 @@ import { Color } from '../../../../../base/common/color.js'; import { IME } from '../../../../../base/common/ime.js'; import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; -import { AbstractEditContext } from '../editContextUtils.js'; +import { AbstractEditContext } from '../editContext.js'; import { ICompositionData, IPasteData, ITextAreaInputHost, TextAreaInput, TextAreaWrapper } from './textAreaEditContextInput.js'; import { ariaLabelForScreenReaderContent, ISimpleModel, newlinecount, PagedScreenReaderStrategy } from '../screenReaderUtils.js'; import { ClipboardDataToCopy, getDataToCopy } from '../clipboardUtils.js'; @@ -148,6 +148,7 @@ export class TextAreaEditContext extends AbstractEditContext { constructor( context: ViewContext, + overflowGuardContainer: FastDomNode, viewController: ViewController, visibleRangeProvider: IVisibleRangeProvider, @IKeybindingService private readonly _keybindingService: IKeybindingService, @@ -201,6 +202,9 @@ export class TextAreaEditContext extends AbstractEditContext { this.textAreaCover = createFastDomNode(document.createElement('div')); this.textAreaCover.setPosition('absolute'); + overflowGuardContainer.appendChild(this.textArea); + overflowGuardContainer.appendChild(this.textAreaCover); + const simpleModel: ISimpleModel = { getLineCount: (): number => { return this._context.viewModel.getLineCount(); @@ -466,15 +470,14 @@ export class TextAreaEditContext extends AbstractEditContext { return this.textArea; } - appendTo(overflowGuardContainer: FastDomNode): void { - overflowGuardContainer.appendChild(this.textArea); - overflowGuardContainer.appendChild(this.textAreaCover); - } - public writeScreenReaderContent(reason: string): void { this._textAreaInput.writeNativeTextAreaContent(reason); } + public getTextAreaDomNode(): HTMLTextAreaElement { + return this.textArea.domNode; + } + public override dispose(): void { super.dispose(); this.textArea.domNode.remove(); diff --git a/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContextInput.ts b/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContextInput.ts index 87e99556b33..fc2dc0dd5ea 100644 --- a/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContextInput.ts +++ b/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContextInput.ts @@ -12,14 +12,13 @@ import { RunOnceScheduler } from '../../../../../base/common/async.js'; import { Emitter, Event } from '../../../../../base/common/event.js'; import { KeyCode } from '../../../../../base/common/keyCodes.js'; import { Disposable, IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; -import { Mimes } from '../../../../../base/common/mime.js'; import { OperatingSystem } from '../../../../../base/common/platform.js'; import * as strings from '../../../../../base/common/strings.js'; import { Position } from '../../../../common/core/position.js'; import { Selection } from '../../../../common/core/selection.js'; import { IAccessibilityService } from '../../../../../platform/accessibility/common/accessibility.js'; import { ILogService } from '../../../../../platform/log/common/log.js'; -import { ClipboardDataToCopy, ClipboardStoredMetadata, InMemoryClipboardMetadataManager } from '../clipboardUtils.js'; +import { ClipboardDataToCopy, ClipboardEventUtils, ClipboardStoredMetadata, InMemoryClipboardMetadataManager } from '../clipboardUtils.js'; import { _debugComposition, ITextAreaWrapper, ITypeData, TextAreaState } from './textAreaEditContextState.js'; export namespace TextAreaSyntethicEvents { @@ -580,7 +579,9 @@ export class TextAreaInput extends Disposable { if (!this._hasFocus) { textAreaState = textAreaState.collapseSelection(); } - + if (!textAreaState.isWrittenToTextArea(this._textArea, this._hasFocus)) { + this._logService.trace(`writeTextAreaState(reason: ${reason})`); + } textAreaState.writeToTextArea(reason, this._textArea, this._hasFocus); this._textAreaState = textAreaState; } @@ -591,7 +592,6 @@ export class TextAreaInput extends Disposable { // Do not write to the text area when doing composition return; } - this._logService.trace(`writeTextAreaState(reason: ${reason})`); this._setAndWriteTextAreaState(reason, this._host.getScreenReaderContent()); } @@ -617,41 +617,6 @@ export class TextAreaInput extends Disposable { } } -export const ClipboardEventUtils = { - - getTextData(clipboardData: DataTransfer): [string, ClipboardStoredMetadata | null] { - const text = clipboardData.getData(Mimes.text); - let metadata: ClipboardStoredMetadata | null = null; - const rawmetadata = clipboardData.getData('vscode-editor-data'); - if (typeof rawmetadata === 'string') { - try { - metadata = JSON.parse(rawmetadata); - if (metadata.version !== 1) { - metadata = null; - } - } catch (err) { - // no problem! - } - } - - if (text.length === 0 && metadata === null && clipboardData.files.length > 0) { - // no textual data pasted, generate text from file names - const files: File[] = Array.prototype.slice.call(clipboardData.files, 0); - return [files.map(file => file.name).join('\n'), null]; - } - - return [text, metadata]; - }, - - setTextData(clipboardData: DataTransfer, text: string, html: string | null | undefined, metadata: ClipboardStoredMetadata): void { - clipboardData.setData(Mimes.text, text); - if (typeof html === 'string') { - clipboardData.setData('text/html', html); - } - clipboardData.setData('vscode-editor-data', JSON.stringify(metadata)); - } -}; - export class TextAreaWrapper extends Disposable implements ICompleteTextAreaWrapper { public readonly onKeyDown = this._register(new DomEmitter(this._actual, 'keydown')).event; diff --git a/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContextState.ts b/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContextState.ts index 2e833a1b263..9a158f6d231 100644 --- a/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContextState.ts +++ b/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContextState.ts @@ -68,6 +68,15 @@ export class TextAreaState { return new TextAreaState(this.value, this.value.length, this.value.length, null, undefined); } + public isWrittenToTextArea(textArea: ITextAreaWrapper, select: boolean): boolean { + const valuesEqual = this.value === textArea.getValue(); + if (!select) { + return valuesEqual; + } + const selectionsEqual = this.selectionStart === textArea.getSelectionStart() && this.selectionEnd === textArea.getSelectionEnd(); + return selectionsEqual && valuesEqual; + } + public writeToTextArea(reason: string, textArea: ITextAreaWrapper, select: boolean): void { if (_debugComposition) { console.log(`writeToTextArea ${reason}: ${this.toString()}`); diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index cd9b92c4ed0..b1e326d835a 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -21,11 +21,13 @@ import { ViewEventHandler } from '../../common/viewEventHandler.js'; import { EditorOption } from '../../common/config/editorOptions.js'; import { NavigationCommandRevealType } from '../coreCommands.js'; import { MouseWheelClassifier } from '../../../base/browser/ui/scrollbar/scrollableElement.js'; +import type { ViewLinesGpu } from '../viewParts/viewLinesGpu/viewLinesGpu.js'; export interface IPointerHandlerHelper { viewDomNode: HTMLElement; linesContentDomNode: HTMLElement; viewLinesDomNode: HTMLElement; + viewLinesGpu: ViewLinesGpu | undefined; focusTextArea(): void; dispatchTextAreaEvent(event: CustomEvent): void; diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index 737daf6d294..28b39952393 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -22,6 +22,7 @@ import { PositionAffinity } from '../../common/model.js'; import { InjectedText } from '../../common/modelLineProjectionData.js'; import { Mutable } from '../../../base/common/types.js'; import { Lazy } from '../../../base/common/lazy.js'; +import type { ViewLinesGpu } from '../viewParts/viewLinesGpu/viewLinesGpu.js'; const enum HitTestResultType { Unknown, @@ -238,6 +239,7 @@ export class HitTestContext { public readonly viewModel: IViewModel; public readonly layoutInfo: EditorLayoutInfo; public readonly viewDomNode: HTMLElement; + public readonly viewLinesGpu: ViewLinesGpu | undefined; public readonly lineHeight: number; public readonly stickyTabStops: boolean; public readonly typicalHalfwidthCharacterWidth: number; @@ -251,6 +253,7 @@ export class HitTestContext { const options = context.configuration.options; this.layoutInfo = options.get(EditorOption.layoutInfo); this.viewDomNode = viewHelper.viewDomNode; + this.viewLinesGpu = viewHelper.viewLinesGpu; this.lineHeight = options.get(EditorOption.lineHeight); this.stickyTabStops = options.get(EditorOption.stickyTabStops); this.typicalHalfwidthCharacterWidth = options.get(EditorOption.fontInfo).typicalHalfwidthCharacterWidth; @@ -754,6 +757,32 @@ export class MouseTargetFactory { const pos = new Position(lineNumber, ctx.viewModel.getLineMaxColumn(lineNumber)); return request.fulfillContentEmpty(pos, detail); } + } else { + if (ctx.viewLinesGpu) { + const lineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset); + if (ctx.viewModel.getLineLength(lineNumber) === 0) { + const lineWidth = ctx.getLineWidth(lineNumber); + const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth); + return request.fulfillContentEmpty(new Position(lineNumber, 1), detail); + } + + const lineWidth = ctx.getLineWidth(lineNumber); + if (request.mouseContentHorizontalOffset >= lineWidth) { + // TODO: This is wrong for RTL + const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth); + const pos = new Position(lineNumber, ctx.viewModel.getLineMaxColumn(lineNumber)); + return request.fulfillContentEmpty(pos, detail); + } + + const position = ctx.viewLinesGpu.getPositionAtCoordinate(lineNumber, request.mouseContentHorizontalOffset); + if (position) { + const detail: IMouseTargetContentTextData = { + injectedText: null, + mightBeForeignElement: false + }; + return request.fulfillContentText(position, EditorRange.fromPositions(position, position), detail); + } + } } // Do the hit test (if not already done) diff --git a/src/vs/editor/browser/dnd.ts b/src/vs/editor/browser/dnd.ts index 9f74733b7f3..ef63da81a74 100644 --- a/src/vs/editor/browser/dnd.ts +++ b/src/vs/editor/browser/dnd.ts @@ -7,7 +7,7 @@ import { DataTransfers } from '../../base/browser/dnd.js'; import { createFileDataTransferItem, createStringDataTransferItem, IDataTransferItem, UriList, VSDataTransfer } from '../../base/common/dataTransfer.js'; import { Mimes } from '../../base/common/mime.js'; import { URI } from '../../base/common/uri.js'; -import { CodeDataTransfers, FileAdditionalNativeProperties } from '../../platform/dnd/browser/dnd.js'; +import { CodeDataTransfers, getPathForFile } from '../../platform/dnd/browser/dnd.js'; export function toVSDataTransfer(dataTransfer: DataTransfer) { @@ -28,7 +28,8 @@ export function toVSDataTransfer(dataTransfer: DataTransfer) { } function createFileDataTransferItemFromFile(file: File): IDataTransferItem { - const uri = (file as FileAdditionalNativeProperties).path ? URI.parse((file as FileAdditionalNativeProperties).path!) : undefined; + const path = getPathForFile(file); + const uri = path ? URI.parse(path!) : undefined; return createFileDataTransferItem(file.name, uri, async () => { return new Uint8Array(await file.arrayBuffer()); }); @@ -55,7 +56,7 @@ export function toExternalVSDataTransfer(sourceDataTransfer: DataTransfer, overw for (const item of sourceDataTransfer.items) { const file = item.getAsFile(); if (file) { - const path = (file as FileAdditionalNativeProperties).path; + const path = getPathForFile(file); try { if (path) { editorData.push(URI.file(path).toString()); diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 7401ec5de61..082c14ea1d3 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -204,7 +204,22 @@ export interface IContentWidget { * widget. Is being invoked with the selected position preference * or `null` if not rendered. */ - afterRender?(position: ContentWidgetPositionPreference | null): void; + afterRender?(position: ContentWidgetPositionPreference | null, coordinate: IContentWidgetRenderedCoordinate | null): void; +} + +/** + * Coordinatees passed in {@link IContentWidget.afterRender} + */ +export interface IContentWidgetRenderedCoordinate { + /** + * Top position relative to the editor content. + */ + readonly top: number; + + /** + * Left position relative to the editor content. + */ + readonly left: number; } /** @@ -679,6 +694,10 @@ export interface ICodeEditor extends editorCommon.IEditor { * @internal */ readonly onDidType: Event; + /** + * Boolean indicating whether input is in composition + */ + readonly inComposition: boolean; /** * An event emitted after composition has started. */ diff --git a/src/vs/editor/browser/editorDom.ts b/src/vs/editor/browser/editorDom.ts index 75bd4acfae7..5a3627d8791 100644 --- a/src/vs/editor/browser/editorDom.ts +++ b/src/vs/editor/browser/editorDom.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from '../../base/browser/dom.js'; +import * as domStylesheetsJs from '../../base/browser/domStylesheets.js'; import { GlobalPointerMoveMonitor } from '../../base/browser/globalPointerMoveMonitor.js'; import { StandardMouseEvent } from '../../base/browser/mouseEvent.js'; import { RunOnceScheduler } from '../../base/common/async.js'; @@ -63,7 +64,7 @@ export class EditorPagePosition { } /** - * Coordinates relative to the the (top;left) of the editor that can be used safely with other internal editor metrics. + * Coordinates relative to the (top;left) of the editor that can be used safely with other internal editor metrics. * **NOTE**: This position is obtained by taking page coordinates and transforming them relative to the * editor's (top;left) position in a way in which scale transformations are taken into account. * **NOTE**: These coordinates could be negative if the mouse position is outside the editor. @@ -149,13 +150,13 @@ export class EditorMouseEventFactory { } public onContextMenu(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable { - return dom.addDisposableListener(target, 'contextmenu', (e: MouseEvent) => { + return dom.addDisposableListener(target, dom.EventType.CONTEXT_MENU, (e: MouseEvent) => { callback(this._create(e)); }); } public onMouseUp(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable { - return dom.addDisposableListener(target, 'mouseup', (e: MouseEvent) => { + return dom.addDisposableListener(target, dom.EventType.MOUSE_UP, (e: MouseEvent) => { callback(this._create(e)); }); } @@ -179,7 +180,7 @@ export class EditorMouseEventFactory { } public onMouseMove(target: HTMLElement, callback: (e: EditorMouseEvent) => void): IDisposable { - return dom.addDisposableListener(target, 'mousemove', (e) => callback(this._create(e))); + return dom.addDisposableListener(target, dom.EventType.MOUSE_MOVE, (e) => callback(this._create(e))); } } @@ -368,7 +369,7 @@ class RefCountedCssRule { public readonly properties: CssProperties, ) { this._styleElementDisposables = new DisposableStore(); - this._styleElement = dom.createStyleSheet(_containerElement, undefined, this._styleElementDisposables); + this._styleElement = domStylesheetsJs.createStyleSheet(_containerElement, undefined, this._styleElementDisposables); this._styleElement.textContent = this.getCssText(this.className, this.properties); } diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index b9c67238fd5..3751b26de7d 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -334,11 +334,15 @@ export interface IEditorActionContextMenuOptions { when?: ContextKeyExpression; menuId?: MenuId; } -export interface IActionOptions extends ICommandOptions { +export type IActionOptions = ICommandOptions & { + contextMenuOpts?: IEditorActionContextMenuOptions | IEditorActionContextMenuOptions[]; +} & ({ + label: nls.ILocalizedString; + alias?: string; +} | { label: string; alias: string; - contextMenuOpts?: IEditorActionContextMenuOptions | IEditorActionContextMenuOptions[]; -} +}); export abstract class EditorAction extends EditorCommand { @@ -358,7 +362,7 @@ export abstract class EditorAction extends EditorCommand { item.menuId = MenuId.EditorContext; } if (!item.title) { - item.title = opts.label; + item.title = typeof opts.label === 'string' ? opts.label : opts.label.value; } item.when = ContextKeyExpr.and(opts.precondition, item.when); return item; @@ -379,8 +383,13 @@ export abstract class EditorAction extends EditorCommand { constructor(opts: IActionOptions) { super(EditorAction.convertOptions(opts)); - this.label = opts.label; - this.alias = opts.alias; + if (typeof opts.label === 'string') { + this.label = opts.label; + this.alias = opts.alias ?? opts.label; + } else { + this.label = opts.label.value; + this.alias = opts.alias ?? opts.label.original; + } } public runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void | Promise { @@ -643,6 +652,11 @@ export const UndoCommand = registerCommand(new MultiCommand({ group: '', title: nls.localize('undo', "Undo"), order: 1 + }, { + menuId: MenuId.SimpleEditorContext, + group: '1_do', + title: nls.localize('undo', "Undo"), + order: 1 }] })); @@ -667,6 +681,11 @@ export const RedoCommand = registerCommand(new MultiCommand({ group: '', title: nls.localize('redo', "Redo"), order: 1 + }, { + menuId: MenuId.SimpleEditorContext, + group: '1_do', + title: nls.localize('redo', "Redo"), + order: 2 }] })); @@ -690,5 +709,10 @@ export const SelectAllCommand = registerCommand(new MultiCommand({ group: '', title: nls.localize('selectAll', "Select All"), order: 1 + }, { + menuId: MenuId.SimpleEditorContext, + group: '9_select', + title: nls.localize('selectAll', "Select All"), + order: 1 }] })); diff --git a/src/vs/editor/browser/gpu/atlas/atlas.ts b/src/vs/editor/browser/gpu/atlas/atlas.ts index e9719042705..7ef9da7d5f5 100644 --- a/src/vs/editor/browser/gpu/atlas/atlas.ts +++ b/src/vs/editor/browser/gpu/atlas/atlas.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { ThreeKeyMap } from '../../../../base/common/map.js'; +import type { NKeyMap } from '../../../../base/common/map.js'; import type { IBoundingBox, IRasterizedGlyph } from '../raster/raster.js'; /** @@ -31,6 +31,20 @@ export interface ITextureAtlasPageGlyph { originOffsetX: number; /** The y offset from {@link y} of the glyph's origin. */ originOffsetY: number; + /** + * The distance from the glyph baseline to the top of the highest bounding rectangle of all + * fonts used to render the text. + * + * @see {@link TextMetrics.fontBoundingBoxAscent} + */ + fontBoundingBoxAscent: number; + /** + * The distance from the glyph baseline to the bottom of the bounding rectangle of all fonts + * used to render the text. + * + * @see {@link TextMetrics.fontBoundingBoxDescent} + */ + fontBoundingBoxDescent: number; } /** @@ -92,4 +106,9 @@ export const enum UsagePreviewColors { Restricted = '#FF000088', } -export type GlyphMap = ThreeKeyMap; +export type GlyphMap = NKeyMap; diff --git a/src/vs/editor/browser/gpu/atlas/textureAtlas.ts b/src/vs/editor/browser/gpu/atlas/textureAtlas.ts index e8b8ad73c7a..a4e5865c011 100644 --- a/src/vs/editor/browser/gpu/atlas/textureAtlas.ts +++ b/src/vs/editor/browser/gpu/atlas/textureAtlas.ts @@ -5,15 +5,16 @@ import { getActiveWindow } from '../../../../base/browser/dom.js'; import { CharCode } from '../../../../base/common/charCode.js'; +import { BugIndicatingError } from '../../../../base/common/errors.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { Disposable, dispose, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; -import { ThreeKeyMap } from '../../../../base/common/map.js'; +import { NKeyMap } from '../../../../base/common/map.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { MetadataConsts } from '../../../common/encodedTokenAttributes.js'; import { GlyphRasterizer } from '../raster/glyphRasterizer.js'; import type { IGlyphRasterizer } from '../raster/raster.js'; -import { IdleTaskQueue } from '../taskQueue.js'; +import { IdleTaskQueue, type ITaskQueue } from '../taskQueue.js'; import type { IReadableTextureAtlasPage, ITextureAtlasPageGlyph, GlyphMap } from './atlas.js'; import { AllocatorType, TextureAtlasPage } from './textureAtlasPage.js'; @@ -22,11 +23,17 @@ export interface ITextureAtlasOptions { } export class TextureAtlas extends Disposable { - private _colorMap!: string[]; - private readonly _warmUpTask: MutableDisposable = this._register(new MutableDisposable()); + private _colorMap?: string[]; + private readonly _warmUpTask: MutableDisposable = this._register(new MutableDisposable()); private readonly _warmedUpRasterizers = new Set(); private readonly _allocatorType: AllocatorType; + /** + * The maximum number of texture atlas pages. This is currently a hard static cap that must not + * be reached. + */ + static readonly maximumPageCount = 16; + /** * The main texture atlas pages which are both larger textures and more efficiently packed * relative to the scratch page. The idea is the main pages are drawn to and uploaded to the GPU @@ -43,7 +50,7 @@ export class TextureAtlas extends Disposable { * so it is not guaranteed to be the actual page the glyph is on. But it is guaranteed that all * pages with a lower index do not contain the glyph. */ - private readonly _glyphPageIndex: GlyphMap = new ThreeKeyMap(); + private readonly _glyphPageIndex: GlyphMap = new NKeyMap(); private readonly _onDidDeleteGlyphs = this._register(new Emitter()); readonly onDidDeleteGlyphs = this._onDidDeleteGlyphs.event; @@ -60,7 +67,9 @@ export class TextureAtlas extends Disposable { this._allocatorType = options?.allocatorType ?? 'slab'; this._register(Event.runAndSubscribe(this._themeService.onDidColorThemeChange, () => { - // TODO: Clear entire atlas on theme change + if (this._colorMap) { + this.clear(); + } this._colorMap = this._themeService.getColorTheme().tokenColorMap; })); @@ -79,8 +88,8 @@ export class TextureAtlas extends Disposable { // IMPORTANT: The first glyph on the first page must be an empty glyph such that zeroed out // cells end up rendering nothing // TODO: This currently means the first slab is for 0x0 glyphs and is wasted - const nullRasterizer = new GlyphRasterizer(1, ''); - firstPage.getGlyph(nullRasterizer, '', 0); + const nullRasterizer = new GlyphRasterizer(1, '', 1); + firstPage.getGlyph(nullRasterizer, '', 0, 0); nullRasterizer.dispose(); } @@ -101,10 +110,15 @@ export class TextureAtlas extends Disposable { this._onDidDeleteGlyphs.fire(); } - getGlyph(rasterizer: IGlyphRasterizer, chars: string, metadata: number): Readonly { + getGlyph(rasterizer: IGlyphRasterizer, chars: string, tokenMetadata: number, decorationStyleSetId: number, x: number): Readonly { // TODO: Encode font size and family into key // Ignore metadata that doesn't affect the glyph - metadata &= ~(MetadataConsts.LANGUAGEID_MASK | MetadataConsts.TOKEN_TYPE_MASK | MetadataConsts.BALANCED_BRACKETS_MASK); + tokenMetadata &= ~(MetadataConsts.LANGUAGEID_MASK | MetadataConsts.TOKEN_TYPE_MASK | MetadataConsts.BALANCED_BRACKETS_MASK); + + // Add x offset for sub-pixel rendering to the unused portion or tokenMetadata. This + // converts the decimal part of the x to a range from 0 to 9, where 0 = 0.0px x offset, + // 9 = 0.9px x offset + tokenMetadata |= Math.floor((x % 1) * 10); // Warm up common glyphs if (!this._warmedUpRasterizers.has(rasterizer.id)) { @@ -113,25 +127,27 @@ export class TextureAtlas extends Disposable { } // Try get the glyph, overflowing to a new page if necessary - return this._tryGetGlyph(this._glyphPageIndex.get(chars, metadata, rasterizer.cacheKey) ?? 0, rasterizer, chars, metadata); + return this._tryGetGlyph(this._glyphPageIndex.get(chars, tokenMetadata, decorationStyleSetId, rasterizer.cacheKey) ?? 0, rasterizer, chars, tokenMetadata, decorationStyleSetId); } - private _tryGetGlyph(pageIndex: number, rasterizer: IGlyphRasterizer, chars: string, metadata: number): Readonly { - this._glyphPageIndex.set(chars, metadata, rasterizer.cacheKey, pageIndex); + private _tryGetGlyph(pageIndex: number, rasterizer: IGlyphRasterizer, chars: string, tokenMetadata: number, decorationStyleSetId: number): Readonly { + this._glyphPageIndex.set(pageIndex, chars, tokenMetadata, decorationStyleSetId, rasterizer.cacheKey); return ( - this._pages[pageIndex].getGlyph(rasterizer, chars, metadata) + this._pages[pageIndex].getGlyph(rasterizer, chars, tokenMetadata, decorationStyleSetId) ?? (pageIndex + 1 < this._pages.length - ? this._tryGetGlyph(pageIndex + 1, rasterizer, chars, metadata) + ? this._tryGetGlyph(pageIndex + 1, rasterizer, chars, tokenMetadata, decorationStyleSetId) : undefined) - ?? this._getGlyphFromNewPage(rasterizer, chars, metadata) + ?? this._getGlyphFromNewPage(rasterizer, chars, tokenMetadata, decorationStyleSetId) ); } - private _getGlyphFromNewPage(rasterizer: IGlyphRasterizer, chars: string, metadata: number): Readonly { - // TODO: Support more than 2 pages and the GPU texture layer limit + private _getGlyphFromNewPage(rasterizer: IGlyphRasterizer, chars: string, tokenMetadata: number, decorationStyleSetId: number): Readonly { + if (this._pages.length >= TextureAtlas.maximumPageCount) { + throw new Error(`Attempt to create a texture atlas page past the limit ${TextureAtlas.maximumPageCount}`); + } this._pages.push(this._instantiationService.createInstance(TextureAtlasPage, this._pages.length, this.pageSize, this._allocatorType)); - this._glyphPageIndex.set(chars, metadata, rasterizer.cacheKey, this._pages.length - 1); - return this._pages[this._pages.length - 1].getGlyph(rasterizer, chars, metadata)!; + this._glyphPageIndex.set(this._pages.length - 1, chars, tokenMetadata, decorationStyleSetId, rasterizer.cacheKey); + return this._pages[this._pages.length - 1].getGlyph(rasterizer, chars, tokenMetadata, decorationStyleSetId)!; } public getUsagePreview(): Promise { @@ -147,32 +163,42 @@ export class TextureAtlas extends Disposable { * is distrubuted over multiple idle callbacks to avoid blocking the main thread. */ private _warmUpAtlas(rasterizer: IGlyphRasterizer): void { + const colorMap = this._colorMap; + if (!colorMap) { + throw new BugIndicatingError('Cannot warm atlas without color map'); + } this._warmUpTask.value?.clear(); const taskQueue = this._warmUpTask.value = new IdleTaskQueue(); // Warm up using roughly the larger glyphs first to help optimize atlas allocation // A-Z for (let code = CharCode.A; code <= CharCode.Z; code++) { - taskQueue.enqueue(() => { - for (const fgColor of this._colorMap.keys()) { - this.getGlyph(rasterizer, String.fromCharCode(code), (fgColor << MetadataConsts.FOREGROUND_OFFSET) & MetadataConsts.FOREGROUND_MASK); - } - }); + for (const fgColor of colorMap.keys()) { + taskQueue.enqueue(() => { + for (let x = 0; x < 1; x += 0.1) { + this.getGlyph(rasterizer, String.fromCharCode(code), (fgColor << MetadataConsts.FOREGROUND_OFFSET) & MetadataConsts.FOREGROUND_MASK, 0, x); + } + }); + } } // a-z for (let code = CharCode.a; code <= CharCode.z; code++) { - taskQueue.enqueue(() => { - for (const fgColor of this._colorMap.keys()) { - this.getGlyph(rasterizer, String.fromCharCode(code), (fgColor << MetadataConsts.FOREGROUND_OFFSET) & MetadataConsts.FOREGROUND_MASK); - } - }); + for (const fgColor of colorMap.keys()) { + taskQueue.enqueue(() => { + for (let x = 0; x < 1; x += 0.1) { + this.getGlyph(rasterizer, String.fromCharCode(code), (fgColor << MetadataConsts.FOREGROUND_OFFSET) & MetadataConsts.FOREGROUND_MASK, 0, x); + } + }); + } } // Remaining ascii for (let code = CharCode.ExclamationMark; code <= CharCode.Tilde; code++) { - taskQueue.enqueue(() => { - for (const fgColor of this._colorMap.keys()) { - this.getGlyph(rasterizer, String.fromCharCode(code), (fgColor << MetadataConsts.FOREGROUND_OFFSET) & MetadataConsts.FOREGROUND_MASK); - } - }); + for (const fgColor of colorMap.keys()) { + taskQueue.enqueue(() => { + for (let x = 0; x < 1; x += 0.1) { + this.getGlyph(rasterizer, String.fromCharCode(code), (fgColor << MetadataConsts.FOREGROUND_OFFSET) & MetadataConsts.FOREGROUND_MASK, 0, x); + } + }); + } } } } diff --git a/src/vs/editor/browser/gpu/atlas/textureAtlasPage.ts b/src/vs/editor/browser/gpu/atlas/textureAtlasPage.ts index a661ef64bcc..1548f772e88 100644 --- a/src/vs/editor/browser/gpu/atlas/textureAtlasPage.ts +++ b/src/vs/editor/browser/gpu/atlas/textureAtlasPage.ts @@ -3,9 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from '../../../../base/common/event.js'; import { Disposable, toDisposable } from '../../../../base/common/lifecycle.js'; -import { ThreeKeyMap } from '../../../../base/common/map.js'; +import { NKeyMap } from '../../../../base/common/map.js'; import { ILogService, LogLevel } from '../../../../platform/log/common/log.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import type { IBoundingBox, IGlyphRasterizer } from '../raster/raster.js'; @@ -32,7 +31,7 @@ export class TextureAtlasPage extends Disposable implements IReadableTextureAtla private readonly _canvas: OffscreenCanvas; get source(): OffscreenCanvas { return this._canvas; } - private readonly _glyphMap: GlyphMap = new ThreeKeyMap(); + private readonly _glyphMap: GlyphMap = new NKeyMap(); private readonly _glyphInOrderSet: Set = new Set(); get glyphs(): IterableIterator { return this._glyphInOrderSet.values(); @@ -46,11 +45,12 @@ export class TextureAtlasPage extends Disposable implements IReadableTextureAtla pageSize: number, allocatorType: AllocatorType, @ILogService private readonly _logService: ILogService, - @IThemeService private readonly _themeService: IThemeService, + @IThemeService themeService: IThemeService, ) { super(); this._canvas = new OffscreenCanvas(pageSize, pageSize); + this._colorMap = themeService.getColorTheme().tokenColorMap; switch (allocatorType) { case 'shelf': this._allocator = new TextureAtlasShelfAllocator(this._canvas, textureIndex); break; @@ -58,11 +58,6 @@ export class TextureAtlasPage extends Disposable implements IReadableTextureAtla default: this._allocator = allocatorType(this._canvas, textureIndex); break; } - this._register(Event.runAndSubscribe(this._themeService.onDidColorThemeChange, () => { - // TODO: Clear entire atlas on theme change - this._colorMap = this._themeService.getColorTheme().tokenColorMap; - })); - // Reduce impact of a memory leak if this object is not released this._register(toDisposable(() => { this._canvas.width = 1; @@ -70,29 +65,31 @@ export class TextureAtlasPage extends Disposable implements IReadableTextureAtla })); } - public getGlyph(rasterizer: IGlyphRasterizer, chars: string, metadata: number): Readonly | undefined { + public getGlyph(rasterizer: IGlyphRasterizer, chars: string, tokenMetadata: number, decorationStyleSetId: number): Readonly | undefined { // IMPORTANT: There are intentionally no intermediate variables here to aid in runtime // optimization as it's a very hot function - return this._glyphMap.get(chars, metadata, rasterizer.cacheKey) ?? this._createGlyph(rasterizer, chars, metadata); + return this._glyphMap.get(chars, tokenMetadata, decorationStyleSetId, rasterizer.cacheKey) ?? this._createGlyph(rasterizer, chars, tokenMetadata, decorationStyleSetId); } - private _createGlyph(rasterizer: IGlyphRasterizer, chars: string, metadata: number): Readonly | undefined { + private _createGlyph(rasterizer: IGlyphRasterizer, chars: string, tokenMetadata: number, decorationStyleSetId: number): Readonly | undefined { // Ensure the glyph can fit on the page if (this._glyphInOrderSet.size >= TextureAtlasPage.maximumGlyphCount) { return undefined; } // Rasterize and allocate the glyph - const rasterizedGlyph = rasterizer.rasterizeGlyph(chars, metadata, this._colorMap); + const rasterizedGlyph = rasterizer.rasterizeGlyph(chars, tokenMetadata, decorationStyleSetId, this._colorMap); const glyph = this._allocator.allocate(rasterizedGlyph); // Ensure the glyph was allocated if (glyph === undefined) { + // TODO: undefined here can mean the glyph was too large for a slab on the page, this + // can lead to big problems if we don't handle it properly https://github.com/microsoft/vscode/issues/232984 return undefined; } // Save the glyph - this._glyphMap.set(chars, metadata, rasterizer.cacheKey, glyph); + this._glyphMap.set(glyph, chars, tokenMetadata, decorationStyleSetId, rasterizer.cacheKey); this._glyphInOrderSet.add(glyph); // Update page version and it's tracked used area @@ -103,7 +100,8 @@ export class TextureAtlasPage extends Disposable implements IReadableTextureAtla if (this._logService.getLevel() === LogLevel.Trace) { this._logService.trace('New glyph', { chars, - metadata, + tokenMetadata, + decorationStyleSetId, rasterizedGlyph, glyph }); diff --git a/src/vs/editor/browser/gpu/atlas/textureAtlasShelfAllocator.ts b/src/vs/editor/browser/gpu/atlas/textureAtlasShelfAllocator.ts index 1bbf920997f..c22491a5f1c 100644 --- a/src/vs/editor/browser/gpu/atlas/textureAtlasShelfAllocator.ts +++ b/src/vs/editor/browser/gpu/atlas/textureAtlasShelfAllocator.ts @@ -80,7 +80,9 @@ export class TextureAtlasShelfAllocator implements ITextureAtlasAllocator { w: glyphWidth, h: glyphHeight, originOffsetX: rasterizedGlyph.originOffset.x, - originOffsetY: rasterizedGlyph.originOffset.y + originOffsetY: rasterizedGlyph.originOffset.y, + fontBoundingBoxAscent: rasterizedGlyph.fontBoundingBoxAscent, + fontBoundingBoxDescent: rasterizedGlyph.fontBoundingBoxDescent, }; // Shift current row diff --git a/src/vs/editor/browser/gpu/atlas/textureAtlasSlabAllocator.ts b/src/vs/editor/browser/gpu/atlas/textureAtlasSlabAllocator.ts index b41fed6978c..e6340cac982 100644 --- a/src/vs/editor/browser/gpu/atlas/textureAtlasSlabAllocator.ts +++ b/src/vs/editor/browser/gpu/atlas/textureAtlasSlabAllocator.ts @@ -5,7 +5,7 @@ import { getActiveWindow } from '../../../../base/browser/dom.js'; import { BugIndicatingError } from '../../../../base/common/errors.js'; -import { TwoKeyMap } from '../../../../base/common/map.js'; +import { NKeyMap } from '../../../../base/common/map.js'; import { ensureNonNullable } from '../gpuUtils.js'; import type { IRasterizedGlyph } from '../raster/raster.js'; import { UsagePreviewColors, type ITextureAtlasAllocator, type ITextureAtlasPageGlyph } from './atlas.js'; @@ -29,7 +29,7 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { private readonly _ctx: OffscreenCanvasRenderingContext2D; private readonly _slabs: ITextureAtlasSlab[] = []; - private readonly _activeSlabsByDims: TwoKeyMap = new TwoKeyMap(); + private readonly _activeSlabsByDims: NKeyMap = new NKeyMap(); private readonly _unusedRects: ITextureAtlasSlabUnusedRect[] = []; @@ -55,7 +55,7 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { })); this._slabW = Math.min( - options?.slabW ?? (64 << (Math.floor(getActiveWindow().devicePixelRatio) - 1)), + options?.slabW ?? (64 << Math.max(Math.floor(getActiveWindow().devicePixelRatio) - 1, 0)), this._canvas.width ); this._slabH = Math.min( @@ -243,7 +243,7 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { }); } this._slabs.push(slab); - this._activeSlabsByDims.set(desiredSlabSize.w, desiredSlabSize.h, slab); + this._activeSlabsByDims.set(slab, desiredSlabSize.w, desiredSlabSize.h); } const glyphsPerRow = Math.floor(this._slabW / slab.entryW); @@ -278,7 +278,9 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { w: glyphWidth, h: glyphHeight, originOffsetX: rasterizedGlyph.originOffset.x, - originOffsetY: rasterizedGlyph.originOffset.y + originOffsetY: rasterizedGlyph.originOffset.y, + fontBoundingBoxAscent: rasterizedGlyph.fontBoundingBoxAscent, + fontBoundingBoxDescent: rasterizedGlyph.fontBoundingBoxDescent, }; // Set the glyph diff --git a/src/vs/editor/browser/gpu/contentSegmenter.ts b/src/vs/editor/browser/gpu/contentSegmenter.ts new file mode 100644 index 00000000000..1dab721b53a --- /dev/null +++ b/src/vs/editor/browser/gpu/contentSegmenter.ts @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { safeIntl } from '../../../base/common/date.js'; +import type { GraphemeIterator } from '../../../base/common/strings.js'; +import type { ViewLineRenderingData } from '../../common/viewModel.js'; +import type { ViewLineOptions } from '../viewParts/viewLines/viewLineOptions.js'; + +export interface IContentSegmenter { + /** + * Gets the content segment at an index within the line data's contents. This will be undefined + * when the index should not be rendered, ie. when it's part of an earlier segment like the tail + * end of an emoji, or when the line is not that long. + * @param index The index within the line data's content string. + */ + getSegmentAtIndex(index: number): string | undefined; + getSegmentData(index: number): Intl.SegmentData | undefined; +} + +export function createContentSegmenter(lineData: ViewLineRenderingData, options: ViewLineOptions): IContentSegmenter { + if (lineData.isBasicASCII && options.useMonospaceOptimizations) { + return new AsciiContentSegmenter(lineData); + } + return new GraphemeContentSegmenter(lineData); +} + +class AsciiContentSegmenter implements IContentSegmenter { + private readonly _content: string; + + constructor(lineData: ViewLineRenderingData) { + this._content = lineData.content; + } + + getSegmentAtIndex(index: number): string { + return this._content[index]; + } + + getSegmentData(index: number): Intl.SegmentData | undefined { + return undefined; + } +} + +/** + * This is a more modern version of {@link GraphemeIterator}, relying on browser APIs instead of a + * manual table approach. + */ +class GraphemeContentSegmenter implements IContentSegmenter { + private readonly _segments: (Intl.SegmentData | undefined)[] = []; + + constructor(lineData: ViewLineRenderingData) { + const content = lineData.content; + const segmenter = safeIntl.Segmenter(undefined, { granularity: 'grapheme' }); + const segmentedContent = Array.from(segmenter.segment(content)); + let segmenterIndex = 0; + + for (let x = 0; x < content.length; x++) { + const segment = segmentedContent[segmenterIndex]; + + // No more segments in the string (eg. an emoji is the last segment) + if (!segment) { + break; + } + + // The segment isn't renderable (eg. the tail end of an emoji) + if (segment.index !== x) { + this._segments.push(undefined); + continue; + } + + segmenterIndex++; + this._segments.push(segment); + } + } + + getSegmentAtIndex(index: number): string | undefined { + return this._segments[index]?.segment; + } + + getSegmentData(index: number): Intl.SegmentData | undefined { + return this._segments[index]; + } +} diff --git a/src/vs/editor/browser/gpu/css/decorationCssRuleExtractor.ts b/src/vs/editor/browser/gpu/css/decorationCssRuleExtractor.ts new file mode 100644 index 00000000000..43dbd85c87a --- /dev/null +++ b/src/vs/editor/browser/gpu/css/decorationCssRuleExtractor.ts @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { $, getActiveDocument } from '../../../../base/browser/dom.js'; +import { Disposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import './media/decorationCssRuleExtractor.css'; + +/** + * Extracts CSS rules that would be applied to certain decoration classes. + */ +export class DecorationCssRuleExtractor extends Disposable { + private _container: HTMLElement; + private _dummyElement: HTMLSpanElement; + + private _ruleCache: Map = new Map(); + + constructor() { + super(); + + this._container = $('div.monaco-decoration-css-rule-extractor'); + this._dummyElement = $('span'); + this._container.appendChild(this._dummyElement); + + this._register(toDisposable(() => this._container.remove())); + } + + getStyleRules(canvas: HTMLElement, decorationClassName: string): CSSStyleRule[] { + // Check cache + const existing = this._ruleCache.get(decorationClassName); + if (existing) { + return existing; + } + + // Set up DOM + this._dummyElement.className = decorationClassName; + canvas.appendChild(this._container); + + // Get rules + const rules = this._getStyleRules(decorationClassName); + this._ruleCache.set(decorationClassName, rules); + + // Tear down DOM + canvas.removeChild(this._container); + + return rules; + } + + private _getStyleRules(className: string) { + // Iterate through all stylesheets and imported stylesheets to find matching rules + const rules = []; + const doc = getActiveDocument(); + const stylesheets = [...doc.styleSheets]; + for (let i = 0; i < stylesheets.length; i++) { + const stylesheet = stylesheets[i]; + for (const rule of stylesheet.cssRules) { + if (rule instanceof CSSImportRule) { + if (rule.styleSheet) { + stylesheets.push(rule.styleSheet); + } + } else if (rule instanceof CSSStyleRule) { + // Note that originally `.matches(rule.selectorText)` was used but this would + // not pick up pseudo-classes which are important to determine support of the + // returned styles. + // + // Since a selector could contain a class name lookup that is simple a prefix of + // the class name we are looking for, we need to also check the character after + // it. + const searchTerm = `.${className}`; + const index = rule.selectorText.indexOf(searchTerm); + if (index !== -1) { + const endOfResult = index + searchTerm.length; + if (rule.selectorText.length === endOfResult || rule.selectorText.substring(endOfResult, endOfResult + 1).match(/[ :]/)) { + rules.push(rule); + } + } + } + } + } + + return rules; + } +} diff --git a/src/vs/editor/browser/gpu/css/decorationStyleCache.ts b/src/vs/editor/browser/gpu/css/decorationStyleCache.ts new file mode 100644 index 00000000000..1b1c07df163 --- /dev/null +++ b/src/vs/editor/browser/gpu/css/decorationStyleCache.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { NKeyMap } from '../../../../base/common/map.js'; + +export interface IDecorationStyleSet { + /** + * A 24-bit number representing `color`. + */ + color: number | undefined; + /** + * Whether the text should be rendered in bold. + */ + bold: boolean | undefined; + /** + * A number between 0 and 1 representing the opacity of the text. + */ + opacity: number | undefined; +} + +export interface IDecorationStyleCacheEntry extends IDecorationStyleSet { + /** + * A unique identifier for this set of styles. + */ + id: number; +} + +export class DecorationStyleCache { + + private _nextId = 1; + + private readonly _cacheById = new Map(); + private readonly _cacheByStyle = new NKeyMap(); + + getOrCreateEntry( + color: number | undefined, + bold: boolean | undefined, + opacity: number | undefined + ): number { + if (color === undefined && bold === undefined && opacity === undefined) { + return 0; + } + const result = this._cacheByStyle.get(color ?? 0, bold ? 1 : 0, opacity === undefined ? '' : opacity.toFixed(2)); + if (result) { + return result.id; + } + const id = this._nextId++; + const entry = { + id, + color, + bold, + opacity, + }; + this._cacheById.set(id, entry); + this._cacheByStyle.set(entry, color ?? 0, bold ? 1 : 0, opacity === undefined ? '' : opacity.toFixed(2)); + return id; + } + + getStyleSet(id: number): IDecorationStyleSet | undefined { + if (id === 0) { + return undefined; + } + return this._cacheById.get(id); + } +} diff --git a/src/vs/workbench/workbench.desktop.main.css b/src/vs/editor/browser/gpu/css/media/decorationCssRuleExtractor.css similarity index 77% rename from src/vs/workbench/workbench.desktop.main.css rename to src/vs/editor/browser/gpu/css/media/decorationCssRuleExtractor.css index 06ed8a197b2..900154c64fd 100644 --- a/src/vs/workbench/workbench.desktop.main.css +++ b/src/vs/editor/browser/gpu/css/media/decorationCssRuleExtractor.css @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/* NOTE: THIS FILE WILL BE OVERWRITTEN DURING BUILD TIME, DO NOT EDIT */ - -div.monaco.main.css { -} \ No newline at end of file +.monaco-editor .monaco-decoration-css-rule-extractor { + visibility: hidden; + pointer-events: none; +} diff --git a/src/vs/editor/browser/gpu/fullFileRenderStrategy.ts b/src/vs/editor/browser/gpu/fullFileRenderStrategy.ts deleted file mode 100644 index 561185b7971..00000000000 --- a/src/vs/editor/browser/gpu/fullFileRenderStrategy.ts +++ /dev/null @@ -1,301 +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 { getActiveWindow } from '../../../base/browser/dom.js'; -import { BugIndicatingError } from '../../../base/common/errors.js'; -import { Disposable } from '../../../base/common/lifecycle.js'; -import { EditorOption } from '../../common/config/editorOptions.js'; -import { CursorColumns } from '../../common/core/cursorColumns.js'; -import type { IViewLineTokens } from '../../common/tokens/lineTokens.js'; -import type { ViewportData } from '../../common/viewLayout/viewLinesViewportData.js'; -import type { ViewLineRenderingData } from '../../common/viewModel.js'; -import type { ViewContext } from '../../common/viewModel/viewContext.js'; -import type { ViewLineOptions } from '../viewParts/viewLines/viewLineOptions.js'; -import type { ITextureAtlasPageGlyph } from './atlas/atlas.js'; -import type { TextureAtlas } from './atlas/textureAtlas.js'; -import { fullFileRenderStrategyWgsl } from './fullFileRenderStrategy.wgsl.js'; -import { BindingId, type IGpuRenderStrategy } from './gpu.js'; -import { GPULifecycle } from './gpuDisposable.js'; -import { quadVertices } from './gpuUtils.js'; -import { GlyphRasterizer } from './raster/glyphRasterizer.js'; -import { ViewGpuContext } from './viewGpuContext.js'; - - -const enum Constants { - IndicesPerCell = 6, -} - -const enum CellBufferInfo { - FloatsPerEntry = 6, - BytesPerEntry = CellBufferInfo.FloatsPerEntry * 4, - Offset_X = 0, - Offset_Y = 1, - Offset_Unused1 = 2, - Offset_Unused2 = 3, - GlyphIndex = 4, - TextureIndex = 5, -} - -export class FullFileRenderStrategy extends Disposable implements IGpuRenderStrategy { - - private static _lineCount = 3000; - private static _columnCount = 200; - - readonly wgsl: string = fullFileRenderStrategyWgsl; - - private readonly _glyphRasterizer: GlyphRasterizer; - - private _cellBindBuffer!: GPUBuffer; - - /** - * The cell value buffers, these hold the cells and their glyphs. It's double buffers such that - * the thread doesn't block when one is being uploaded to the GPU. - */ - private _cellValueBuffers!: [ArrayBuffer, ArrayBuffer]; - private _activeDoubleBufferIndex: 0 | 1 = 0; - - private readonly _upToDateLines: [Set, Set] = [new Set(), new Set()]; - private _visibleObjectCount: number = 0; - - private _scrollOffsetBindBuffer!: GPUBuffer; - private _scrollOffsetValueBuffers!: [Float32Array, Float32Array]; - - get bindGroupEntries(): GPUBindGroupEntry[] { - return [ - { binding: BindingId.Cells, resource: { buffer: this._cellBindBuffer } }, - { binding: BindingId.ScrollOffset, resource: { buffer: this._scrollOffsetBindBuffer } } - ]; - } - - constructor( - private readonly _context: ViewContext, - private readonly _device: GPUDevice, - private readonly _canvas: HTMLCanvasElement, - private readonly _atlas: TextureAtlas, - ) { - super(); - - // TODO: Detect when lines have been tokenized and clear _upToDateLines - const fontFamily = this._context.configuration.options.get(EditorOption.fontFamily); - const fontSize = this._context.configuration.options.get(EditorOption.fontSize); - - this._glyphRasterizer = this._register(new GlyphRasterizer(fontSize, fontFamily)); - - const bufferSize = FullFileRenderStrategy._lineCount * FullFileRenderStrategy._columnCount * Constants.IndicesPerCell * Float32Array.BYTES_PER_ELEMENT; - this._cellBindBuffer = this._register(GPULifecycle.createBuffer(this._device, { - label: 'Monaco full file cell buffer', - size: bufferSize, - usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, - })).object; - this._cellValueBuffers = [ - new ArrayBuffer(bufferSize), - new ArrayBuffer(bufferSize), - ]; - - const scrollOffsetBufferSize = 2; - this._scrollOffsetBindBuffer = this._register(GPULifecycle.createBuffer(this._device, { - label: 'Monaco scroll offset buffer', - size: scrollOffsetBufferSize * Float32Array.BYTES_PER_ELEMENT, - usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, - })).object; - this._scrollOffsetValueBuffers = [ - new Float32Array(scrollOffsetBufferSize), - new Float32Array(scrollOffsetBufferSize), - ]; - } - - reset() { - for (const bufferIndex of [0, 1]) { - // Zero out buffer and upload to GPU to prevent stale rows from rendering - const buffer = new Float32Array(this._cellValueBuffers[bufferIndex]); - buffer.fill(0, 0, buffer.length); - this._device.queue.writeBuffer(this._cellBindBuffer, 0, buffer.buffer, 0, buffer.byteLength); - this._upToDateLines[bufferIndex].clear(); - } - this._visibleObjectCount = 0; - } - - update(viewportData: ViewportData, viewLineOptions: ViewLineOptions): number { - // Pre-allocate variables to be shared within the loop - don't trust the JIT compiler to do - // this optimization to avoid additional blocking time in garbage collector - let chars = ''; - let y = 0; - let x = 0; - let screenAbsoluteX = 0; - let screenAbsoluteY = 0; - let zeroToOneX = 0; - let zeroToOneY = 0; - let wgslX = 0; - let wgslY = 0; - let xOffset = 0; - let glyph: Readonly; - let cellIndex = 0; - - let tokenStartIndex = 0; - let tokenEndIndex = 0; - let tokenMetadata = 0; - - let lineData: ViewLineRenderingData; - let content: string = ''; - let fillStartIndex = 0; - let fillEndIndex = 0; - - let tokens: IViewLineTokens; - - const dpr = getActiveWindow().devicePixelRatio; - - // Update scroll offset - const scrollOffsetBuffer = this._scrollOffsetValueBuffers[this._activeDoubleBufferIndex]; - scrollOffsetBuffer[0] = this._context.viewLayout.getCurrentScrollLeft() * dpr; - scrollOffsetBuffer[1] = this._context.viewLayout.getCurrentScrollTop() * dpr; - this._device.queue.writeBuffer(this._scrollOffsetBindBuffer, 0, scrollOffsetBuffer); - - // Update cell data - const cellBuffer = new Float32Array(this._cellValueBuffers[this._activeDoubleBufferIndex]); - const lineIndexCount = FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; - - const upToDateLines = this._upToDateLines[this._activeDoubleBufferIndex]; - let dirtyLineStart = Number.MAX_SAFE_INTEGER; - let dirtyLineEnd = 0; - - for (y = viewportData.startLineNumber; y <= viewportData.endLineNumber; y++) { - - // Only attempt to render lines that the GPU renderer can handle - if (!ViewGpuContext.canRender(viewLineOptions, viewportData, y)) { - continue; - } - - // TODO: Update on dirty lines; is this known by line before rendering? - // if (upToDateLines.has(y)) { - // continue; - // } - dirtyLineStart = Math.min(dirtyLineStart, y); - dirtyLineEnd = Math.max(dirtyLineEnd, y); - - lineData = viewportData.getViewLineRenderingData(y); - content = lineData.content; - xOffset = 0; - - // See ViewLine#renderLine - // const renderLineInput = new RenderLineInput( - // options.useMonospaceOptimizations, - // options.canUseHalfwidthRightwardsArrow, - // lineData.content, - // lineData.continuesWithWrappedLine, - // lineData.isBasicASCII, - // lineData.containsRTL, - // lineData.minColumn - 1, - // lineData.tokens, - // actualInlineDecorations, - // lineData.tabSize, - // lineData.startVisibleColumn, - // options.spaceWidth, - // options.middotWidth, - // options.wsmiddotWidth, - // options.stopRenderingLineAfter, - // options.renderWhitespace, - // options.renderControlCharacters, - // options.fontLigatures !== EditorFontLigatures.OFF, - // selectionsOnLine - // ); - - tokens = lineData.tokens; - tokenStartIndex = lineData.minColumn - 1; - tokenEndIndex = 0; - for (let tokenIndex = 0, tokensLen = tokens.getCount(); tokenIndex < tokensLen; tokenIndex++) { - tokenEndIndex = tokens.getEndOffset(tokenIndex); - if (tokenEndIndex <= tokenStartIndex) { - // The faux indent part of the line should have no token type - continue; - } - - - tokenMetadata = tokens.getMetadata(tokenIndex); - - // console.log(`token: start=${tokenStartIndex}, end=${tokenEndIndex}, fg=${colorMap[tokenFg]}`); - - - for (x = tokenStartIndex; x < tokenEndIndex; x++) { - // HACK: Prevent rendering past the end of the render buffer - // TODO: This needs to move to a dynamic long line rendering strategy - if (x > FullFileRenderStrategy._columnCount) { - break; - } - chars = content.charAt(x); - if (chars === ' ') { - continue; - } - if (chars === '\t') { - xOffset = CursorColumns.nextRenderTabStop(x + xOffset, lineData.tabSize) - x - 1; - continue; - } - - glyph = this._atlas.getGlyph(this._glyphRasterizer, chars, tokenMetadata); - - // TODO: Support non-standard character widths - screenAbsoluteX = Math.round((x + xOffset) * viewLineOptions.spaceWidth * dpr); - screenAbsoluteY = ( - Math.ceil(( - // Top of line including line height - viewportData.relativeVerticalOffset[y - viewportData.startLineNumber] + - // Delta to top of line after line height - Math.floor((viewportData.lineHeight - this._context.configuration.options.get(EditorOption.fontSize)) / 2) - ) * dpr) - ); - zeroToOneX = screenAbsoluteX / this._canvas.width; - zeroToOneY = screenAbsoluteY / this._canvas.height; - wgslX = zeroToOneX * 2 - 1; - wgslY = zeroToOneY * 2 - 1; - - cellIndex = ((y - 1) * FullFileRenderStrategy._columnCount + (x + xOffset)) * Constants.IndicesPerCell; - cellBuffer[cellIndex + CellBufferInfo.Offset_X] = wgslX; - cellBuffer[cellIndex + CellBufferInfo.Offset_Y] = -wgslY; - cellBuffer[cellIndex + CellBufferInfo.GlyphIndex] = glyph.glyphIndex; - cellBuffer[cellIndex + CellBufferInfo.TextureIndex] = glyph.pageIndex; - } - - tokenStartIndex = tokenEndIndex; - } - - // Clear to end of line - fillStartIndex = ((y - 1) * FullFileRenderStrategy._columnCount + (tokenEndIndex + xOffset)) * Constants.IndicesPerCell; - fillEndIndex = (y * FullFileRenderStrategy._columnCount) * Constants.IndicesPerCell; - cellBuffer.fill(0, fillStartIndex, fillEndIndex); - - upToDateLines.add(y); - } - - const visibleObjectCount = (viewportData.endLineNumber - viewportData.startLineNumber + 1) * lineIndexCount; - - // Only write when there is changed data - if (dirtyLineStart <= dirtyLineEnd) { - // Write buffer and swap it out to unblock writes - this._device.queue.writeBuffer( - this._cellBindBuffer, - (dirtyLineStart - 1) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT, - cellBuffer.buffer, - (dirtyLineStart - 1) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT, - (dirtyLineEnd - dirtyLineStart + 1) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT - ); - } - - this._activeDoubleBufferIndex = this._activeDoubleBufferIndex ? 0 : 1; - - this._visibleObjectCount = visibleObjectCount; - return visibleObjectCount; - } - - draw(pass: GPURenderPassEncoder, viewportData: ViewportData): void { - if (this._visibleObjectCount <= 0) { - throw new BugIndicatingError('Attempt to draw 0 objects'); - } - pass.draw( - quadVertices.length / 2, - this._visibleObjectCount, - undefined, - (viewportData.startLineNumber - 1) * FullFileRenderStrategy._columnCount - ); - } -} diff --git a/src/vs/editor/browser/gpu/gpu.ts b/src/vs/editor/browser/gpu/gpu.ts index 3d4f2a28453..7284d275c76 100644 --- a/src/vs/editor/browser/gpu/gpu.ts +++ b/src/vs/editor/browser/gpu/gpu.ts @@ -3,12 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import type { IDisposable } from '../../../base/common/lifecycle.js'; +import type { ViewConfigurationChangedEvent, ViewLinesChangedEvent, ViewLinesDeletedEvent, ViewLinesInsertedEvent, ViewScrollChangedEvent, ViewTokensChangedEvent } from '../../common/viewEvents.js'; import type { ViewportData } from '../../common/viewLayout/viewLinesViewportData.js'; import type { ViewLineOptions } from '../viewParts/viewLines/viewLineOptions.js'; +import type { IGlyphRasterizer } from './raster/raster.js'; export const enum BindingId { - GlyphInfo0, - GlyphInfo1, + GlyphInfo, Cells, TextureSampler, Texture, @@ -17,14 +19,23 @@ export const enum BindingId { ScrollOffset, } -export interface IGpuRenderStrategy { +export interface IGpuRenderStrategy extends IDisposable { + readonly type: string; readonly wgsl: string; readonly bindGroupEntries: GPUBindGroupEntry[]; + readonly glyphRasterizer: IGlyphRasterizer; + + onLinesDeleted(e: ViewLinesDeletedEvent): boolean; + onConfigurationChanged(e: ViewConfigurationChangedEvent): boolean; + onTokensChanged(e: ViewTokensChangedEvent): boolean; + onLinesInserted(e: ViewLinesInsertedEvent): boolean; + onLinesChanged(e: ViewLinesChangedEvent): boolean; + onScrollChanged(e?: ViewScrollChangedEvent): boolean; /** * Resets the render strategy, clearing all data and setting up for a new frame. */ reset(): void; update(viewportData: ViewportData, viewLineOptions: ViewLineOptions): number; - draw?(pass: GPURenderPassEncoder, viewportData: ViewportData): void; + draw(pass: GPURenderPassEncoder, viewportData: ViewportData): void; } diff --git a/src/vs/editor/browser/gpu/raster/glyphRasterizer.ts b/src/vs/editor/browser/gpu/raster/glyphRasterizer.ts index 26010b7e386..d06871cc24d 100644 --- a/src/vs/editor/browser/gpu/raster/glyphRasterizer.ts +++ b/src/vs/editor/browser/gpu/raster/glyphRasterizer.ts @@ -3,13 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getActiveWindow } from '../../../../base/browser/dom.js'; import { memoize } from '../../../../base/common/decorators.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; +import { isMacintosh } from '../../../../base/common/platform.js'; import { StringBuilder } from '../../../common/core/stringBuilder.js'; import { FontStyle, TokenMetadata } from '../../../common/encodedTokenAttributes.js'; import { ensureNonNullable } from '../gpuUtils.js'; -import type { IBoundingBox, IGlyphRasterizer, IRasterizedGlyph } from './raster.js'; +import { ViewGpuContext } from '../viewGpuContext.js'; +import { type IBoundingBox, type IGlyphRasterizer, type IRasterizedGlyph } from './raster.js'; let nextId = 0; @@ -18,12 +19,14 @@ export class GlyphRasterizer extends Disposable implements IGlyphRasterizer { @memoize public get cacheKey(): string { - return `${this._fontFamily}_${this._fontSize}px`; + return `${this.fontFamily}_${this.fontSize}px`; } private _canvas: OffscreenCanvas; private _ctx: OffscreenCanvasRenderingContext2D; + private readonly _textMetrics: TextMetrics; + private _workGlyph: IRasterizedGlyph = { source: null!, boundingBox: { @@ -35,77 +38,110 @@ export class GlyphRasterizer extends Disposable implements IGlyphRasterizer { originOffset: { x: 0, y: 0, - } + }, + fontBoundingBoxAscent: 0, + fontBoundingBoxDescent: 0, }; - private _workGlyphConfig: { chars: string | undefined; metadata: number } = { chars: undefined, metadata: 0 }; + private _workGlyphConfig: { chars: string | undefined; tokenMetadata: number; decorationStyleSetId: number } = { chars: undefined, tokenMetadata: 0, decorationStyleSetId: 0 }; + + // TODO: Support workbench.fontAliasing correctly + private _antiAliasing: 'subpixel' | 'greyscale' = isMacintosh ? 'greyscale' : 'subpixel'; constructor( - private readonly _fontSize: number, - private readonly _fontFamily: string, + readonly fontSize: number, + readonly fontFamily: string, + readonly devicePixelRatio: number ) { super(); - const devicePixelFontSize = Math.ceil(this._fontSize * getActiveWindow().devicePixelRatio); + const devicePixelFontSize = Math.ceil(this.fontSize * devicePixelRatio); this._canvas = new OffscreenCanvas(devicePixelFontSize * 3, devicePixelFontSize * 3); this._ctx = ensureNonNullable(this._canvas.getContext('2d', { - willReadFrequently: true + willReadFrequently: true, + alpha: this._antiAliasing === 'greyscale', })); this._ctx.textBaseline = 'top'; this._ctx.fillStyle = '#FFFFFF'; + this._ctx.font = `${devicePixelFontSize}px ${this.fontFamily}`; + this._textMetrics = this._ctx.measureText('A'); } - // TODO: Support drawing multiple fonts and sizes /** * Rasterizes a glyph. Note that the returned object is reused across different glyphs and * therefore is only safe for synchronous access. */ public rasterizeGlyph( chars: string, - metadata: number, + tokenMetadata: number, + decorationStyleSetId: number, colorMap: string[], ): Readonly { if (chars === '') { return { source: this._canvas, boundingBox: { top: 0, left: 0, bottom: -1, right: -1 }, - originOffset: { x: 0, y: 0 } + originOffset: { x: 0, y: 0 }, + fontBoundingBoxAscent: 0, + fontBoundingBoxDescent: 0, }; } // Check if the last glyph matches the config, reuse if so. This helps avoid unnecessary // work when the rasterizer is called multiple times like when the glyph doesn't fit into a // page. - if (this._workGlyphConfig.chars === chars && this._workGlyphConfig.metadata === metadata) { + if (this._workGlyphConfig.chars === chars && this._workGlyphConfig.tokenMetadata === tokenMetadata && this._workGlyphConfig.decorationStyleSetId === decorationStyleSetId) { return this._workGlyph; } this._workGlyphConfig.chars = chars; - this._workGlyphConfig.metadata = metadata; - return this._rasterizeGlyph(chars, metadata, colorMap); + this._workGlyphConfig.tokenMetadata = tokenMetadata; + this._workGlyphConfig.decorationStyleSetId = decorationStyleSetId; + return this._rasterizeGlyph(chars, tokenMetadata, decorationStyleSetId, colorMap); } public _rasterizeGlyph( chars: string, - metadata: number, + tokenMetadata: number, + decorationStyleSetId: number, colorMap: string[], ): Readonly { - const devicePixelFontSize = Math.ceil(this._fontSize * getActiveWindow().devicePixelRatio); + const devicePixelFontSize = Math.ceil(this.fontSize * this.devicePixelRatio); const canvasDim = devicePixelFontSize * 3; if (this._canvas.width !== canvasDim) { this._canvas.width = canvasDim; this._canvas.height = canvasDim; } - // TODO: Support workbench.fontAliasing - this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); + this._ctx.save(); + + // The sub-pixel x offset is the fractional part of the x pixel coordinate of the cell, this + // is used to improve the spacing between rendered characters. + const xSubPixelXOffset = (tokenMetadata & 0b1111) / 10; + + const bgId = TokenMetadata.getBackground(tokenMetadata); + const bg = colorMap[bgId]; + + const decorationStyleSet = ViewGpuContext.decorationStyleCache.getStyleSet(decorationStyleSetId); + + // When SPAA is used, the background color must be present to get the right glyph + if (this._antiAliasing === 'subpixel') { + this._ctx.fillStyle = bg; + this._ctx.fillRect(0, 0, this._canvas.width, this._canvas.height); + } else { + this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); + } const fontSb = new StringBuilder(200); - const fontStyle = TokenMetadata.getFontStyle(metadata); + const fontStyle = TokenMetadata.getFontStyle(tokenMetadata); if (fontStyle & FontStyle.Italic) { fontSb.appendString('italic '); } - if (fontStyle & FontStyle.Bold) { + if (decorationStyleSet?.bold !== undefined) { + if (decorationStyleSet.bold) { + fontSb.appendString('bold '); + } + } else if (fontStyle & FontStyle.Bold) { fontSb.appendString('bold '); } - fontSb.appendString(`${devicePixelFontSize}px ${this._fontFamily}`); + fontSb.appendString(`${devicePixelFontSize}px ${this.fontFamily}`); this._ctx.font = fontSb.build(); // TODO: Support FontStyle.Strikethrough and FontStyle.Underline text decorations, these @@ -114,13 +150,28 @@ export class GlyphRasterizer extends Disposable implements IGlyphRasterizer { const originX = devicePixelFontSize; const originY = devicePixelFontSize; - this._ctx.fillStyle = colorMap[TokenMetadata.getForeground(metadata)]; - // TODO: This might actually be slower - // const textMetrics = this._ctx.measureText(chars); + if (decorationStyleSet?.color !== undefined) { + this._ctx.fillStyle = `#${decorationStyleSet.color.toString(16).padStart(8, '0')}`; + } else { + this._ctx.fillStyle = colorMap[TokenMetadata.getForeground(tokenMetadata)]; + } this._ctx.textBaseline = 'top'; - this._ctx.fillText(chars, originX, originY); + + if (decorationStyleSet?.opacity !== undefined) { + this._ctx.globalAlpha = decorationStyleSet.opacity; + } + + this._ctx.fillText(chars, originX + xSubPixelXOffset, originY); + this._ctx.restore(); const imageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height); + if (this._antiAliasing === 'subpixel') { + const bgR = parseInt(bg.substring(1, 3), 16); + const bgG = parseInt(bg.substring(3, 5), 16); + const bgB = parseInt(bg.substring(5, 7), 16); + this._clearColor(imageData, bgR, bgG, bgB); + this._ctx.putImageData(imageData, 0, 0); + } this._findGlyphBoundingBox(imageData, this._workGlyph.boundingBox); // const offset = { // x: textMetrics.actualBoundingBoxLeft, @@ -136,6 +187,9 @@ export class GlyphRasterizer extends Disposable implements IGlyphRasterizer { this._workGlyph.source = this._canvas; this._workGlyph.originOffset.x = this._workGlyph.boundingBox.left - originX; this._workGlyph.originOffset.y = this._workGlyph.boundingBox.top - originY; + this._workGlyph.fontBoundingBoxAscent = this._textMetrics.fontBoundingBoxAscent; + this._workGlyph.fontBoundingBoxDescent = this._textMetrics.fontBoundingBoxDescent; + // const result2: IRasterizedGlyph = { // source: this._canvas, // boundingBox: { @@ -173,6 +227,17 @@ export class GlyphRasterizer extends Disposable implements IGlyphRasterizer { return this._workGlyph; } + private _clearColor(imageData: ImageData, r: number, g: number, b: number) { + for (let offset = 0; offset < imageData.data.length; offset += 4) { + // Check exact match + if (imageData.data[offset] === r && + imageData.data[offset + 1] === g && + imageData.data[offset + 2] === b) { + imageData.data[offset + 3] = 0; + } + } + } + // TODO: Does this even need to happen when measure text is used? private _findGlyphBoundingBox(imageData: ImageData, outBoundingBox: IBoundingBox) { const height = this._canvas.height; @@ -237,4 +302,8 @@ export class GlyphRasterizer extends Disposable implements IGlyphRasterizer { } } } + + public getTextMetrics(text: string): TextMetrics { + return this._ctx.measureText(text); + } } diff --git a/src/vs/editor/browser/gpu/raster/raster.ts b/src/vs/editor/browser/gpu/raster/raster.ts index 6eb41e680b2..32bee48f24d 100644 --- a/src/vs/editor/browser/gpu/raster/raster.ts +++ b/src/vs/editor/browser/gpu/raster/raster.ts @@ -21,15 +21,19 @@ export interface IGlyphRasterizer { * Rasterizes a glyph. * @param chars The character(s) to rasterize. This can be a single character, a ligature, an * emoji, etc. - * @param metadata The metadata of the glyph to rasterize. See {@link MetadataConsts} for how - * this works. + * @param tokenMetadata The token metadata of the glyph to rasterize. See {@link MetadataConsts} + * for how this works. + * @param decorationStyleSetId The id of the decoration style sheet. Zero means no decoration. * @param colorMap A theme's color map. */ rasterizeGlyph( chars: string, - metadata: number, + tokenMetadata: number, + decorationStyleSetId: number, colorMap: string[], ): Readonly; + + getTextMetrics(text: string): TextMetrics; } /** @@ -62,4 +66,18 @@ export interface IRasterizedGlyph { * The offset to the glyph's origin (where it should be drawn to). */ originOffset: { x: number; y: number }; + /** + * The distance from the glyph baseline to the top of the highest bounding rectangle of all + * fonts used to render the text. + * + * @see {@link TextMetrics.fontBoundingBoxAscent} + */ + fontBoundingBoxAscent: number; + /** + * The distance from the glyph baseline to the bottom of the bounding rectangle of all fonts + * used to render the text. + * + * @see {@link TextMetrics.fontBoundingBoxDescent} + */ + fontBoundingBoxDescent: number; } diff --git a/src/vs/editor/browser/gpu/rectangleRenderer.ts b/src/vs/editor/browser/gpu/rectangleRenderer.ts index 0859281f48b..5be1db2f162 100644 --- a/src/vs/editor/browser/gpu/rectangleRenderer.ts +++ b/src/vs/editor/browser/gpu/rectangleRenderer.ts @@ -6,6 +6,7 @@ import { getActiveWindow } from '../../../base/browser/dom.js'; import { Event } from '../../../base/common/event.js'; import { IReference, MutableDisposable } from '../../../base/common/lifecycle.js'; +import type { IObservable } from '../../../base/common/observable.js'; import { EditorOption } from '../../common/config/editorOptions.js'; import { ViewEventHandler } from '../../common/viewEventHandler.js'; import type { ViewScrollChangedEvent } from '../../common/viewEvents.js'; @@ -42,7 +43,6 @@ export class RectangleRenderer extends ViewEventHandler { private _scrollOffsetValueBuffer!: Float32Array; private _initialized: boolean = false; - private _scrollChanged: boolean = true; private readonly _shapeCollection: IObjectCollectionBuffer = this._register(createObjectCollectionBuffer([ { name: 'x' }, @@ -57,6 +57,8 @@ export class RectangleRenderer extends ViewEventHandler { constructor( private readonly _context: ViewContext, + private readonly _contentLeft: IObservable, + private readonly _devicePixelRatio: IObservable, private readonly _canvas: HTMLCanvasElement, private readonly _ctx: GPUCanvasContext, device: Promise, @@ -242,29 +244,29 @@ export class RectangleRenderer extends ViewEventHandler { return this._shapeCollection.createEntry({ x, y, width, height, red, green, blue, alpha }); } - // --- begin event handlers + // #region Event handlers public override onScrollChanged(e: ViewScrollChangedEvent): boolean { - this._scrollChanged = true; - return super.onScrollChanged(e); + if (this._device) { + const dpr = getActiveWindow().devicePixelRatio; + this._scrollOffsetValueBuffer[0] = this._context.viewLayout.getCurrentScrollLeft() * dpr; + this._scrollOffsetValueBuffer[1] = this._context.viewLayout.getCurrentScrollTop() * dpr; + this._device.queue.writeBuffer(this._scrollOffsetBindBuffer, 0, this._scrollOffsetValueBuffer); + } + return true; } - // --- end event handlers + // #endregion private _update() { + if (!this._device) { + return; + } const shapes = this._shapeCollection; if (shapes.dirtyTracker.isDirty) { this._device.queue.writeBuffer(this._shapeBindBuffer.value!.object, 0, shapes.buffer, shapes.dirtyTracker.dataOffset, shapes.dirtyTracker.dirtySize! * shapes.view.BYTES_PER_ELEMENT); shapes.dirtyTracker.clear(); } - - // Update scroll offset - if (this._scrollChanged) { - const dpr = getActiveWindow().devicePixelRatio; - this._scrollOffsetValueBuffer[0] = this._context.viewLayout.getCurrentScrollLeft() * dpr; - this._scrollOffsetValueBuffer[1] = this._context.viewLayout.getCurrentScrollTop() * dpr; - this._device.queue.writeBuffer(this._scrollOffsetBindBuffer, 0, this._scrollOffsetValueBuffer); - } } draw(viewportData: ViewportData) { @@ -282,6 +284,10 @@ export class RectangleRenderer extends ViewEventHandler { pass.setVertexBuffer(0, this._vertexBuffer); pass.setBindGroup(0, this._bindGroup); + // Only draw the content area + const contentLeft = Math.ceil(this._contentLeft.get() * this._devicePixelRatio.get()); + pass.setScissorRect(contentLeft, 0, this._canvas.width - contentLeft, this._canvas.height); + pass.draw(quadVertices.length / 2, this._shapeCollection.entryCount); pass.end(); diff --git a/src/vs/editor/browser/gpu/renderStrategy/baseRenderStrategy.ts b/src/vs/editor/browser/gpu/renderStrategy/baseRenderStrategy.ts new file mode 100644 index 00000000000..b5d9fc895cc --- /dev/null +++ b/src/vs/editor/browser/gpu/renderStrategy/baseRenderStrategy.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ViewEventHandler } from '../../../common/viewEventHandler.js'; +import type { ViewportData } from '../../../common/viewLayout/viewLinesViewportData.js'; +import type { ViewContext } from '../../../common/viewModel/viewContext.js'; +import type { ViewLineOptions } from '../../viewParts/viewLines/viewLineOptions.js'; +import type { IGpuRenderStrategy } from '../gpu.js'; +import { GlyphRasterizer } from '../raster/glyphRasterizer.js'; +import type { ViewGpuContext } from '../viewGpuContext.js'; + +export abstract class BaseRenderStrategy extends ViewEventHandler implements IGpuRenderStrategy { + + get glyphRasterizer() { return this._glyphRasterizer.value; } + + abstract type: string; + abstract wgsl: string; + abstract bindGroupEntries: GPUBindGroupEntry[]; + + constructor( + protected readonly _context: ViewContext, + protected readonly _viewGpuContext: ViewGpuContext, + protected readonly _device: GPUDevice, + protected readonly _glyphRasterizer: { value: GlyphRasterizer }, + ) { + super(); + + this._context.addEventHandler(this); + } + + abstract reset(): void; + abstract update(viewportData: ViewportData, viewLineOptions: ViewLineOptions): number; + abstract draw(pass: GPURenderPassEncoder, viewportData: ViewportData): void; +} diff --git a/src/vs/editor/browser/gpu/renderStrategy/fullFileRenderStrategy.ts b/src/vs/editor/browser/gpu/renderStrategy/fullFileRenderStrategy.ts new file mode 100644 index 00000000000..c41efda089b --- /dev/null +++ b/src/vs/editor/browser/gpu/renderStrategy/fullFileRenderStrategy.ts @@ -0,0 +1,548 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { getActiveWindow } from '../../../../base/browser/dom.js'; +import { Color } from '../../../../base/common/color.js'; +import { BugIndicatingError } from '../../../../base/common/errors.js'; +import { CursorColumns } from '../../../common/core/cursorColumns.js'; +import type { IViewLineTokens } from '../../../common/tokens/lineTokens.js'; +import { ViewEventType, type ViewConfigurationChangedEvent, type ViewDecorationsChangedEvent, type ViewLineMappingChangedEvent, type ViewLinesChangedEvent, type ViewLinesDeletedEvent, type ViewLinesInsertedEvent, type ViewScrollChangedEvent, type ViewThemeChangedEvent, type ViewTokensChangedEvent, type ViewZonesChangedEvent } from '../../../common/viewEvents.js'; +import type { ViewportData } from '../../../common/viewLayout/viewLinesViewportData.js'; +import type { InlineDecoration, ViewLineRenderingData } from '../../../common/viewModel.js'; +import type { ViewContext } from '../../../common/viewModel/viewContext.js'; +import type { ViewLineOptions } from '../../viewParts/viewLines/viewLineOptions.js'; +import type { ITextureAtlasPageGlyph } from '../atlas/atlas.js'; +import { createContentSegmenter, type IContentSegmenter } from '../contentSegmenter.js'; +import { fullFileRenderStrategyWgsl } from './fullFileRenderStrategy.wgsl.js'; +import { BindingId } from '../gpu.js'; +import { GPULifecycle } from '../gpuDisposable.js'; +import { quadVertices } from '../gpuUtils.js'; +import { GlyphRasterizer } from '../raster/glyphRasterizer.js'; +import { ViewGpuContext } from '../viewGpuContext.js'; +import { BaseRenderStrategy } from './baseRenderStrategy.js'; + +const enum Constants { + IndicesPerCell = 6, +} + +const enum CellBufferInfo { + FloatsPerEntry = 6, + BytesPerEntry = CellBufferInfo.FloatsPerEntry * 4, + Offset_X = 0, + Offset_Y = 1, + Offset_Unused1 = 2, + Offset_Unused2 = 3, + GlyphIndex = 4, + TextureIndex = 5, +} + +type QueuedBufferEvent = ( + ViewConfigurationChangedEvent | + ViewLineMappingChangedEvent | + ViewLinesDeletedEvent | + ViewZonesChangedEvent +); + +/** + * A render strategy that tracks a large buffer, uploading only dirty lines as they change and + * leveraging heavy caching. This is the most performant strategy but has limitations around long + * lines and too many lines. + */ +export class FullFileRenderStrategy extends BaseRenderStrategy { + + /** + * The hard cap for line count that can be rendered by the GPU renderer. + */ + static readonly maxSupportedLines = 3000; + + /** + * The hard cap for line columns that can be rendered by the GPU renderer. + */ + static readonly maxSupportedColumns = 200; + + readonly type = 'fullfile'; + readonly wgsl: string = fullFileRenderStrategyWgsl; + + private _cellBindBuffer!: GPUBuffer; + + /** + * The cell value buffers, these hold the cells and their glyphs. It's double buffers such that + * the thread doesn't block when one is being uploaded to the GPU. + */ + private _cellValueBuffers!: [ArrayBuffer, ArrayBuffer]; + private _activeDoubleBufferIndex: 0 | 1 = 0; + + private readonly _upToDateLines: [Set, Set] = [new Set(), new Set()]; + private _visibleObjectCount: number = 0; + private _finalRenderedLine: number = 0; + + private _scrollOffsetBindBuffer: GPUBuffer; + private _scrollOffsetValueBuffer: Float32Array; + private _scrollInitialized: boolean = false; + + private readonly _queuedBufferUpdates: [QueuedBufferEvent[], QueuedBufferEvent[]] = [[], []]; + + get bindGroupEntries(): GPUBindGroupEntry[] { + return [ + { binding: BindingId.Cells, resource: { buffer: this._cellBindBuffer } }, + { binding: BindingId.ScrollOffset, resource: { buffer: this._scrollOffsetBindBuffer } } + ]; + } + + constructor( + context: ViewContext, + viewGpuContext: ViewGpuContext, + device: GPUDevice, + glyphRasterizer: { value: GlyphRasterizer }, + ) { + super(context, viewGpuContext, device, glyphRasterizer); + + const bufferSize = FullFileRenderStrategy.maxSupportedLines * FullFileRenderStrategy.maxSupportedColumns * Constants.IndicesPerCell * Float32Array.BYTES_PER_ELEMENT; + this._cellBindBuffer = this._register(GPULifecycle.createBuffer(this._device, { + label: 'Monaco full file cell buffer', + size: bufferSize, + usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, + })).object; + this._cellValueBuffers = [ + new ArrayBuffer(bufferSize), + new ArrayBuffer(bufferSize), + ]; + + const scrollOffsetBufferSize = 2; + this._scrollOffsetBindBuffer = this._register(GPULifecycle.createBuffer(this._device, { + label: 'Monaco scroll offset buffer', + size: scrollOffsetBufferSize * Float32Array.BYTES_PER_ELEMENT, + usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, + })).object; + this._scrollOffsetValueBuffer = new Float32Array(scrollOffsetBufferSize); + } + + // #region Event handlers + + // The primary job of these handlers is to: + // 1. Invalidate the up to date line cache, which will cause the line to be re-rendered when + // it's _within the viewport_. + // 2. Pass relevant events on to the render function so it can force certain line ranges to be + // re-rendered even if they're not in the viewport. For example when a view zone is added, + // there are lines that used to be visible but are no longer, so those ranges must be + // cleared and uploaded to the GPU. + + public override onConfigurationChanged(e: ViewConfigurationChangedEvent): boolean { + this._invalidateAllLines(); + this._queueBufferUpdate(e); + return true; + } + + public override onDecorationsChanged(e: ViewDecorationsChangedEvent): boolean { + this._invalidateAllLines(); + return true; + } + + public override onTokensChanged(e: ViewTokensChangedEvent): boolean { + // TODO: This currently fires for the entire viewport whenever scrolling stops + // https://github.com/microsoft/vscode/issues/233942 + for (const range of e.ranges) { + this._invalidateLineRange(range.fromLineNumber, range.toLineNumber); + } + return true; + } + + public override onLinesDeleted(e: ViewLinesDeletedEvent): boolean { + // TODO: This currently invalidates everything after the deleted line, it could shift the + // line data up to retain some up to date lines + // TODO: This does not invalidate lines that are no longer in the file + this._invalidateLinesFrom(e.fromLineNumber); + this._queueBufferUpdate(e); + return true; + } + + public override onLinesInserted(e: ViewLinesInsertedEvent): boolean { + // TODO: This currently invalidates everything after the deleted line, it could shift the + // line data up to retain some up to date lines + this._invalidateLinesFrom(e.fromLineNumber); + return true; + } + + public override onLinesChanged(e: ViewLinesChangedEvent): boolean { + this._invalidateLineRange(e.fromLineNumber, e.fromLineNumber + e.count); + return true; + } + + public override onScrollChanged(e?: ViewScrollChangedEvent): boolean { + const dpr = getActiveWindow().devicePixelRatio; + this._scrollOffsetValueBuffer[0] = (e?.scrollLeft ?? this._context.viewLayout.getCurrentScrollLeft()) * dpr; + this._scrollOffsetValueBuffer[1] = (e?.scrollTop ?? this._context.viewLayout.getCurrentScrollTop()) * dpr; + this._device.queue.writeBuffer(this._scrollOffsetBindBuffer, 0, this._scrollOffsetValueBuffer); + return true; + } + + public override onThemeChanged(e: ViewThemeChangedEvent): boolean { + this._invalidateAllLines(); + return true; + } + + public override onLineMappingChanged(e: ViewLineMappingChangedEvent): boolean { + this._invalidateAllLines(); + this._queueBufferUpdate(e); + return true; + } + + public override onZonesChanged(e: ViewZonesChangedEvent): boolean { + this._invalidateAllLines(); + this._queueBufferUpdate(e); + + return true; + } + + // #endregion + + private _invalidateAllLines(): void { + this._upToDateLines[0].clear(); + this._upToDateLines[1].clear(); + } + + private _invalidateLinesFrom(lineNumber: number): void { + for (const i of [0, 1]) { + const upToDateLines = this._upToDateLines[i]; + for (const upToDateLine of upToDateLines) { + if (upToDateLine >= lineNumber) { + upToDateLines.delete(upToDateLine); + } + } + } + } + + private _invalidateLineRange(fromLineNumber: number, toLineNumber: number): void { + for (let i = fromLineNumber; i <= toLineNumber; i++) { + this._upToDateLines[0].delete(i); + this._upToDateLines[1].delete(i); + } + } + + reset() { + this._invalidateAllLines(); + for (const bufferIndex of [0, 1]) { + // Zero out buffer and upload to GPU to prevent stale rows from rendering + const buffer = new Float32Array(this._cellValueBuffers[bufferIndex]); + buffer.fill(0, 0, buffer.length); + this._device.queue.writeBuffer(this._cellBindBuffer, 0, buffer.buffer, 0, buffer.byteLength); + } + this._finalRenderedLine = 0; + } + + update(viewportData: ViewportData, viewLineOptions: ViewLineOptions): number { + // IMPORTANT: This is a hot function. Variables are pre-allocated and shared within the + // loop. This is done so we don't need to trust the JIT compiler to do this optimization to + // avoid potential additional blocking time in garbage collector which is a common cause of + // dropped frames. + + let chars = ''; + let segment: string | undefined; + let charWidth = 0; + let y = 0; + let x = 0; + let absoluteOffsetX = 0; + let absoluteOffsetY = 0; + let tabXOffset = 0; + let glyph: Readonly; + let cellIndex = 0; + + let tokenStartIndex = 0; + let tokenEndIndex = 0; + let tokenMetadata = 0; + + let decorationStyleSetBold: boolean | undefined; + let decorationStyleSetColor: number | undefined; + let decorationStyleSetOpacity: number | undefined; + + let lineData: ViewLineRenderingData; + let decoration: InlineDecoration; + let fillStartIndex = 0; + let fillEndIndex = 0; + + let tokens: IViewLineTokens; + + const dpr = getActiveWindow().devicePixelRatio; + let contentSegmenter: IContentSegmenter; + + if (!this._scrollInitialized) { + this.onScrollChanged(); + this._scrollInitialized = true; + } + + // Update cell data + const cellBuffer = new Float32Array(this._cellValueBuffers[this._activeDoubleBufferIndex]); + const lineIndexCount = FullFileRenderStrategy.maxSupportedColumns * Constants.IndicesPerCell; + + const upToDateLines = this._upToDateLines[this._activeDoubleBufferIndex]; + let dirtyLineStart = 3000; + let dirtyLineEnd = 0; + + // Handle any queued buffer updates + const queuedBufferUpdates = this._queuedBufferUpdates[this._activeDoubleBufferIndex]; + while (queuedBufferUpdates.length) { + const e = queuedBufferUpdates.shift()!; + switch (e.type) { + // TODO: Refine these cases so we're not throwing away everything + case ViewEventType.ViewConfigurationChanged: + case ViewEventType.ViewLineMappingChanged: + case ViewEventType.ViewZonesChanged: { + cellBuffer.fill(0); + + dirtyLineStart = 1; + dirtyLineEnd = Math.max(dirtyLineEnd, this._finalRenderedLine); + this._finalRenderedLine = 0; + break; + } + case ViewEventType.ViewLinesDeleted: { + // Shift content below deleted line up + const deletedLineContentStartIndex = (e.fromLineNumber - 1) * FullFileRenderStrategy.maxSupportedColumns * Constants.IndicesPerCell; + const deletedLineContentEndIndex = (e.toLineNumber) * FullFileRenderStrategy.maxSupportedColumns * Constants.IndicesPerCell; + const nullContentStartIndex = (this._finalRenderedLine - (e.toLineNumber - e.fromLineNumber + 1)) * FullFileRenderStrategy.maxSupportedColumns * Constants.IndicesPerCell; + cellBuffer.set(cellBuffer.subarray(deletedLineContentEndIndex), deletedLineContentStartIndex); + + // Zero out content on lines that are no longer valid + cellBuffer.fill(0, nullContentStartIndex); + + // Update dirty lines and final rendered line + dirtyLineStart = Math.min(dirtyLineStart, e.fromLineNumber); + dirtyLineEnd = Math.max(dirtyLineEnd, this._finalRenderedLine); + this._finalRenderedLine -= e.toLineNumber - e.fromLineNumber + 1; + break; + } + } + } + + for (y = viewportData.startLineNumber; y <= viewportData.endLineNumber; y++) { + + // Only attempt to render lines that the GPU renderer can handle + if (!this._viewGpuContext.canRender(viewLineOptions, viewportData, y)) { + fillStartIndex = ((y - 1) * FullFileRenderStrategy.maxSupportedColumns) * Constants.IndicesPerCell; + fillEndIndex = (y * FullFileRenderStrategy.maxSupportedColumns) * Constants.IndicesPerCell; + cellBuffer.fill(0, fillStartIndex, fillEndIndex); + + dirtyLineStart = Math.min(dirtyLineStart, y); + dirtyLineEnd = Math.max(dirtyLineEnd, y); + + continue; + } + + // Skip updating the line if it's already up to date + if (upToDateLines.has(y)) { + continue; + } + + dirtyLineStart = Math.min(dirtyLineStart, y); + dirtyLineEnd = Math.max(dirtyLineEnd, y); + + lineData = viewportData.getViewLineRenderingData(y); + tabXOffset = 0; + + contentSegmenter = createContentSegmenter(lineData, viewLineOptions); + charWidth = viewLineOptions.spaceWidth * dpr; + absoluteOffsetX = 0; + + tokens = lineData.tokens; + tokenStartIndex = lineData.minColumn - 1; + tokenEndIndex = 0; + for (let tokenIndex = 0, tokensLen = tokens.getCount(); tokenIndex < tokensLen; tokenIndex++) { + tokenEndIndex = tokens.getEndOffset(tokenIndex); + if (tokenEndIndex <= tokenStartIndex) { + // The faux indent part of the line should have no token type + continue; + } + + tokenMetadata = tokens.getMetadata(tokenIndex); + + for (x = tokenStartIndex; x < tokenEndIndex; x++) { + // Only render lines that do not exceed maximum columns + if (x > FullFileRenderStrategy.maxSupportedColumns) { + break; + } + segment = contentSegmenter.getSegmentAtIndex(x); + if (segment === undefined) { + continue; + } + chars = segment; + + if (!(lineData.isBasicASCII && viewLineOptions.useMonospaceOptimizations)) { + charWidth = this.glyphRasterizer.getTextMetrics(chars).width; + } + + decorationStyleSetColor = undefined; + decorationStyleSetBold = undefined; + decorationStyleSetOpacity = undefined; + + // Apply supported inline decoration styles to the cell metadata + for (decoration of lineData.inlineDecorations) { + // This is Range.strictContainsPosition except it works at the cell level, + // it's also inlined to avoid overhead. + if ( + (y < decoration.range.startLineNumber || y > decoration.range.endLineNumber) || + (y === decoration.range.startLineNumber && x < decoration.range.startColumn - 1) || + (y === decoration.range.endLineNumber && x >= decoration.range.endColumn - 1) + ) { + continue; + } + + const rules = ViewGpuContext.decorationCssRuleExtractor.getStyleRules(this._viewGpuContext.canvas.domNode, decoration.inlineClassName); + for (const rule of rules) { + for (const r of rule.style) { + const value = rule.styleMap.get(r)?.toString() ?? ''; + switch (r) { + case 'color': { + // TODO: This parsing and error handling should move into canRender so fallback + // to DOM works + const parsedColor = Color.Format.CSS.parse(value); + if (!parsedColor) { + throw new BugIndicatingError('Invalid color format ' + value); + } + decorationStyleSetColor = parsedColor.toNumber32Bit(); + break; + } + case 'font-weight': { + const parsedValue = parseCssFontWeight(value); + if (parsedValue >= 400) { + decorationStyleSetBold = true; + // TODO: Set bold (https://github.com/microsoft/vscode/issues/237584) + } else { + decorationStyleSetBold = false; + // TODO: Set normal (https://github.com/microsoft/vscode/issues/237584) + } + break; + } + case 'opacity': { + const parsedValue = parseCssOpacity(value); + decorationStyleSetOpacity = parsedValue; + break; + } + default: throw new BugIndicatingError('Unexpected inline decoration style'); + } + } + } + } + + if (chars === ' ' || chars === '\t') { + // Zero out glyph to ensure it doesn't get rendered + cellIndex = ((y - 1) * FullFileRenderStrategy.maxSupportedColumns + x) * Constants.IndicesPerCell; + cellBuffer.fill(0, cellIndex, cellIndex + CellBufferInfo.FloatsPerEntry); + // Adjust xOffset for tab stops + if (chars === '\t') { + // Find the pixel offset between the current position and the next tab stop + const offsetBefore = x + tabXOffset; + tabXOffset = CursorColumns.nextRenderTabStop(x + tabXOffset, lineData.tabSize); + absoluteOffsetX += charWidth * (tabXOffset - offsetBefore); + // Convert back to offset excluding x and the current character + tabXOffset -= x + 1; + } else { + absoluteOffsetX += charWidth; + } + continue; + } + + const decorationStyleSetId = ViewGpuContext.decorationStyleCache.getOrCreateEntry(decorationStyleSetColor, decorationStyleSetBold, decorationStyleSetOpacity); + glyph = this._viewGpuContext.atlas.getGlyph(this.glyphRasterizer, chars, tokenMetadata, decorationStyleSetId, absoluteOffsetX); + + absoluteOffsetY = Math.round( + // Top of layout box (includes line height) + viewportData.relativeVerticalOffset[y - viewportData.startLineNumber] * dpr + + + // Delta from top of layout box (includes line height) to top of the inline box (no line height) + Math.floor((viewportData.lineHeight * dpr - (glyph.fontBoundingBoxAscent + glyph.fontBoundingBoxDescent)) / 2) + + + // Delta from top of inline box (no line height) to top of glyph origin. If the glyph was drawn + // with a top baseline for example, this ends up drawing the glyph correctly using the alphabetical + // baseline. + glyph.fontBoundingBoxAscent + ); + + cellIndex = ((y - 1) * FullFileRenderStrategy.maxSupportedColumns + x) * Constants.IndicesPerCell; + cellBuffer[cellIndex + CellBufferInfo.Offset_X] = Math.floor(absoluteOffsetX); + cellBuffer[cellIndex + CellBufferInfo.Offset_Y] = absoluteOffsetY; + cellBuffer[cellIndex + CellBufferInfo.GlyphIndex] = glyph.glyphIndex; + cellBuffer[cellIndex + CellBufferInfo.TextureIndex] = glyph.pageIndex; + + // Adjust the x pixel offset for the next character + absoluteOffsetX += charWidth; + } + + tokenStartIndex = tokenEndIndex; + } + + // Clear to end of line + fillStartIndex = ((y - 1) * FullFileRenderStrategy.maxSupportedColumns + tokenEndIndex) * Constants.IndicesPerCell; + fillEndIndex = (y * FullFileRenderStrategy.maxSupportedColumns) * Constants.IndicesPerCell; + cellBuffer.fill(0, fillStartIndex, fillEndIndex); + + upToDateLines.add(y); + } + + const visibleObjectCount = (viewportData.endLineNumber - viewportData.startLineNumber + 1) * lineIndexCount; + + // Only write when there is changed data + dirtyLineStart = Math.min(dirtyLineStart, FullFileRenderStrategy.maxSupportedLines); + dirtyLineEnd = Math.min(dirtyLineEnd, FullFileRenderStrategy.maxSupportedLines); + if (dirtyLineStart <= dirtyLineEnd) { + // Write buffer and swap it out to unblock writes + this._device.queue.writeBuffer( + this._cellBindBuffer, + (dirtyLineStart - 1) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT, + cellBuffer.buffer, + (dirtyLineStart - 1) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT, + (dirtyLineEnd - dirtyLineStart + 1) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT + ); + } + + this._finalRenderedLine = Math.max(this._finalRenderedLine, dirtyLineEnd); + + this._activeDoubleBufferIndex = this._activeDoubleBufferIndex ? 0 : 1; + + this._visibleObjectCount = visibleObjectCount; + + return visibleObjectCount; + } + + draw(pass: GPURenderPassEncoder, viewportData: ViewportData): void { + if (this._visibleObjectCount <= 0) { + throw new BugIndicatingError('Attempt to draw 0 objects'); + } + pass.draw( + quadVertices.length / 2, + this._visibleObjectCount, + undefined, + (viewportData.startLineNumber - 1) * FullFileRenderStrategy.maxSupportedColumns + ); + } + + /** + * Queue updates that need to happen on the active buffer, not just the cache. This will be + * deferred to when the actual cell buffer is changed since the active buffer could be locked by + * the GPU which would block the main thread. + */ + private _queueBufferUpdate(e: QueuedBufferEvent) { + this._queuedBufferUpdates[0].push(e); + this._queuedBufferUpdates[1].push(e); + } +} + +function parseCssFontWeight(value: string) { + switch (value) { + case 'lighter': + case 'normal': return 400; + case 'bolder': + case 'bold': return 700; + } + return parseInt(value); +} + +function parseCssOpacity(value: string): number { + if (value.endsWith('%')) { + return parseFloat(value.substring(0, value.length - 1)) / 100; + } + if (value.match(/^\d+(?:\.\d*)/)) { + return parseFloat(value); + } + return 1; +} diff --git a/src/vs/editor/browser/gpu/fullFileRenderStrategy.wgsl.ts b/src/vs/editor/browser/gpu/renderStrategy/fullFileRenderStrategy.wgsl.ts similarity index 74% rename from src/vs/editor/browser/gpu/fullFileRenderStrategy.wgsl.ts rename to src/vs/editor/browser/gpu/renderStrategy/fullFileRenderStrategy.wgsl.ts index c5072ffb616..0e105241f11 100644 --- a/src/vs/editor/browser/gpu/fullFileRenderStrategy.wgsl.ts +++ b/src/vs/editor/browser/gpu/renderStrategy/fullFileRenderStrategy.wgsl.ts @@ -3,7 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BindingId } from './gpu.js'; +import { TextureAtlas } from '../atlas/textureAtlas.js'; +import { TextureAtlasPage } from '../atlas/textureAtlasPage.js'; +import { BindingId } from '../gpu.js'; export const fullFileRenderStrategyWgsl = /*wgsl*/ ` struct GlyphInfo { @@ -45,8 +47,7 @@ struct VSOutput { @group(0) @binding(${BindingId.ScrollOffset}) var scrollOffset: ScrollOffset; // Storage buffers -@group(0) @binding(${BindingId.GlyphInfo0}) var glyphInfo0: array; -@group(0) @binding(${BindingId.GlyphInfo1}) var glyphInfo1: array; +@group(0) @binding(${BindingId.GlyphInfo}) var glyphInfo: array, ${TextureAtlas.maximumPageCount}>; @group(0) @binding(${BindingId.Cells}) var cells: array; @vertex fn vs( @@ -55,19 +56,17 @@ struct VSOutput { @builtin(vertex_index) vertexIndex : u32 ) -> VSOutput { let cell = cells[instanceIndex]; - // TODO: Is there a nicer way to init this? - var glyph = glyphInfo0[0]; - let glyphIndex = u32(cell.glyphIndex); - if (u32(cell.textureIndex) == 0) { - glyph = glyphInfo0[glyphIndex]; - } else { - glyph = glyphInfo1[glyphIndex]; - } + var glyph = glyphInfo[u32(cell.textureIndex)][u32(cell.glyphIndex)]; var vsOut: VSOutput; // Multiple vert.position by 2,-2 to get it into clipspace which ranged from -1 to 1 vsOut.position = vec4f( - (((vert.position * vec2f(2, -2)) / layoutInfo.canvasDims)) * glyph.size + cell.position + ((glyph.origin * vec2f(2, -2)) / layoutInfo.canvasDims) + (((layoutInfo.viewportOffset - scrollOffset.offset * vec2(1, -1)) * 2) / layoutInfo.canvasDims), + // Make everything relative to top left instead of center + vec2f(-1, 1) + + ((vert.position * vec2f(2, -2)) / layoutInfo.canvasDims) * glyph.size + + ((cell.position * vec2f(2, -2)) / layoutInfo.canvasDims) + + ((glyph.origin * vec2f(2, -2)) / layoutInfo.canvasDims) + + (((layoutInfo.viewportOffset - scrollOffset.offset * vec2(1, -1)) * 2) / layoutInfo.canvasDims), 0.0, 1.0 ); diff --git a/src/vs/editor/browser/gpu/renderStrategy/viewportRenderStrategy.ts b/src/vs/editor/browser/gpu/renderStrategy/viewportRenderStrategy.ts new file mode 100644 index 00000000000..f35ccc85edc --- /dev/null +++ b/src/vs/editor/browser/gpu/renderStrategy/viewportRenderStrategy.ts @@ -0,0 +1,427 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { getActiveWindow } from '../../../../base/browser/dom.js'; +import { Color } from '../../../../base/common/color.js'; +import { BugIndicatingError } from '../../../../base/common/errors.js'; +import { Emitter } from '../../../../base/common/event.js'; +import { CursorColumns } from '../../../common/core/cursorColumns.js'; +import type { IViewLineTokens } from '../../../common/tokens/lineTokens.js'; +import { type ViewConfigurationChangedEvent, type ViewDecorationsChangedEvent, type ViewLineMappingChangedEvent, type ViewLinesChangedEvent, type ViewLinesDeletedEvent, type ViewLinesInsertedEvent, type ViewScrollChangedEvent, type ViewThemeChangedEvent, type ViewTokensChangedEvent, type ViewZonesChangedEvent } from '../../../common/viewEvents.js'; +import type { ViewportData } from '../../../common/viewLayout/viewLinesViewportData.js'; +import type { InlineDecoration, ViewLineRenderingData } from '../../../common/viewModel.js'; +import type { ViewContext } from '../../../common/viewModel/viewContext.js'; +import type { ViewLineOptions } from '../../viewParts/viewLines/viewLineOptions.js'; +import type { ITextureAtlasPageGlyph } from '../atlas/atlas.js'; +import { createContentSegmenter, type IContentSegmenter } from '../contentSegmenter.js'; +import { BindingId } from '../gpu.js'; +import { GPULifecycle } from '../gpuDisposable.js'; +import { quadVertices } from '../gpuUtils.js'; +import { GlyphRasterizer } from '../raster/glyphRasterizer.js'; +import { ViewGpuContext } from '../viewGpuContext.js'; +import { BaseRenderStrategy } from './baseRenderStrategy.js'; +import { fullFileRenderStrategyWgsl } from './fullFileRenderStrategy.wgsl.js'; + +const enum Constants { + IndicesPerCell = 6, + CellBindBufferCapacityIncrement = 32, + CellBindBufferInitialCapacity = 63, // Will be rounded up to nearest increment +} + +const enum CellBufferInfo { + FloatsPerEntry = 6, + BytesPerEntry = CellBufferInfo.FloatsPerEntry * 4, + Offset_X = 0, + Offset_Y = 1, + Offset_Unused1 = 2, + Offset_Unused2 = 3, + GlyphIndex = 4, + TextureIndex = 5, +} + +/** + * A render strategy that uploads the content of the entire viewport every frame. + */ +export class ViewportRenderStrategy extends BaseRenderStrategy { + /** + * The hard cap for line columns that can be rendered by the GPU renderer. + */ + static readonly maxSupportedColumns = 2000; + + readonly type = 'viewport'; + readonly wgsl: string = fullFileRenderStrategyWgsl; + + private _cellBindBufferLineCapacity = Constants.CellBindBufferInitialCapacity; + private _cellBindBuffer!: GPUBuffer; + + /** + * The cell value buffers, these hold the cells and their glyphs. It's double buffers such that + * the thread doesn't block when one is being uploaded to the GPU. + */ + private _cellValueBuffers!: [ArrayBuffer, ArrayBuffer]; + private _activeDoubleBufferIndex: 0 | 1 = 0; + + private _visibleObjectCount: number = 0; + + private _scrollOffsetBindBuffer: GPUBuffer; + private _scrollOffsetValueBuffer: Float32Array; + private _scrollInitialized: boolean = false; + + get bindGroupEntries(): GPUBindGroupEntry[] { + return [ + { binding: BindingId.Cells, resource: { buffer: this._cellBindBuffer } }, + { binding: BindingId.ScrollOffset, resource: { buffer: this._scrollOffsetBindBuffer } } + ]; + } + + private readonly _onDidChangeBindGroupEntries = this._register(new Emitter()); + readonly onDidChangeBindGroupEntries = this._onDidChangeBindGroupEntries.event; + + constructor( + context: ViewContext, + viewGpuContext: ViewGpuContext, + device: GPUDevice, + glyphRasterizer: { value: GlyphRasterizer }, + ) { + super(context, viewGpuContext, device, glyphRasterizer); + + this._rebuildCellBuffer(this._cellBindBufferLineCapacity); + + const scrollOffsetBufferSize = 2; + this._scrollOffsetBindBuffer = this._register(GPULifecycle.createBuffer(this._device, { + label: 'Monaco scroll offset buffer', + size: scrollOffsetBufferSize * Float32Array.BYTES_PER_ELEMENT, + usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, + })).object; + this._scrollOffsetValueBuffer = new Float32Array(scrollOffsetBufferSize); + } + + private _rebuildCellBuffer(lineCount: number) { + this._cellBindBuffer?.destroy(); + + // Increase in chunks so resizing a window by hand doesn't keep allocating and throwing away + const lineCountWithIncrement = (Math.floor(lineCount / Constants.CellBindBufferCapacityIncrement) + 1) * Constants.CellBindBufferCapacityIncrement; + + const bufferSize = lineCountWithIncrement * ViewportRenderStrategy.maxSupportedColumns * Constants.IndicesPerCell * Float32Array.BYTES_PER_ELEMENT; + this._cellBindBuffer = this._register(GPULifecycle.createBuffer(this._device, { + label: 'Monaco full file cell buffer', + size: bufferSize, + usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, + })).object; + this._cellValueBuffers = [ + new ArrayBuffer(bufferSize), + new ArrayBuffer(bufferSize), + ]; + this._cellBindBufferLineCapacity = lineCountWithIncrement; + + this._onDidChangeBindGroupEntries.fire(); + } + + // #region Event handlers + + // The primary job of these handlers is to: + // 1. Invalidate the up to date line cache, which will cause the line to be re-rendered when + // it's _within the viewport_. + // 2. Pass relevant events on to the render function so it can force certain line ranges to be + // re-rendered even if they're not in the viewport. For example when a view zone is added, + // there are lines that used to be visible but are no longer, so those ranges must be + // cleared and uploaded to the GPU. + + public override onConfigurationChanged(e: ViewConfigurationChangedEvent): boolean { + return true; + } + + public override onDecorationsChanged(e: ViewDecorationsChangedEvent): boolean { + return true; + } + + public override onTokensChanged(e: ViewTokensChangedEvent): boolean { + return true; + } + + public override onLinesDeleted(e: ViewLinesDeletedEvent): boolean { + return true; + } + + public override onLinesInserted(e: ViewLinesInsertedEvent): boolean { + return true; + } + + public override onLinesChanged(e: ViewLinesChangedEvent): boolean { + return true; + } + + public override onScrollChanged(e?: ViewScrollChangedEvent): boolean { + const dpr = getActiveWindow().devicePixelRatio; + this._scrollOffsetValueBuffer[0] = (e?.scrollLeft ?? this._context.viewLayout.getCurrentScrollLeft()) * dpr; + this._scrollOffsetValueBuffer[1] = (e?.scrollTop ?? this._context.viewLayout.getCurrentScrollTop()) * dpr; + this._device.queue.writeBuffer(this._scrollOffsetBindBuffer, 0, this._scrollOffsetValueBuffer); + return true; + } + + public override onThemeChanged(e: ViewThemeChangedEvent): boolean { + return true; + } + + public override onLineMappingChanged(e: ViewLineMappingChangedEvent): boolean { + return true; + } + + public override onZonesChanged(e: ViewZonesChangedEvent): boolean { + return true; + } + + // #endregion + + reset() { + for (const bufferIndex of [0, 1]) { + // Zero out buffer and upload to GPU to prevent stale rows from rendering + const buffer = new Float32Array(this._cellValueBuffers[bufferIndex]); + buffer.fill(0, 0, buffer.length); + this._device.queue.writeBuffer(this._cellBindBuffer, 0, buffer.buffer, 0, buffer.byteLength); + } + } + + update(viewportData: ViewportData, viewLineOptions: ViewLineOptions): number { + // IMPORTANT: This is a hot function. Variables are pre-allocated and shared within the + // loop. This is done so we don't need to trust the JIT compiler to do this optimization to + // avoid potential additional blocking time in garbage collector which is a common cause of + // dropped frames. + + let chars = ''; + let segment: string | undefined; + let charWidth = 0; + let y = 0; + let x = 0; + let absoluteOffsetX = 0; + let absoluteOffsetY = 0; + let tabXOffset = 0; + let glyph: Readonly; + let cellIndex = 0; + + let tokenStartIndex = 0; + let tokenEndIndex = 0; + let tokenMetadata = 0; + + let decorationStyleSetBold: boolean | undefined; + let decorationStyleSetColor: number | undefined; + let decorationStyleSetOpacity: number | undefined; + + let lineData: ViewLineRenderingData; + let decoration: InlineDecoration; + let fillStartIndex = 0; + let fillEndIndex = 0; + + let tokens: IViewLineTokens; + + const dpr = getActiveWindow().devicePixelRatio; + let contentSegmenter: IContentSegmenter; + + if (!this._scrollInitialized) { + this.onScrollChanged(); + this._scrollInitialized = true; + } + + // Zero out cell buffer or rebuild if needed + if (this._cellBindBufferLineCapacity < viewportData.endLineNumber - viewportData.startLineNumber + 1) { + this._rebuildCellBuffer(viewportData.endLineNumber - viewportData.startLineNumber + 1); + } + const cellBuffer = new Float32Array(this._cellValueBuffers[this._activeDoubleBufferIndex]); + cellBuffer.fill(0); + + const lineIndexCount = ViewportRenderStrategy.maxSupportedColumns * Constants.IndicesPerCell; + + for (y = viewportData.startLineNumber; y <= viewportData.endLineNumber; y++) { + + // Only attempt to render lines that the GPU renderer can handle + if (!this._viewGpuContext.canRender(viewLineOptions, viewportData, y)) { + continue; + } + + lineData = viewportData.getViewLineRenderingData(y); + tabXOffset = 0; + + contentSegmenter = createContentSegmenter(lineData, viewLineOptions); + charWidth = viewLineOptions.spaceWidth * dpr; + absoluteOffsetX = 0; + + tokens = lineData.tokens; + tokenStartIndex = lineData.minColumn - 1; + tokenEndIndex = 0; + for (let tokenIndex = 0, tokensLen = tokens.getCount(); tokenIndex < tokensLen; tokenIndex++) { + tokenEndIndex = tokens.getEndOffset(tokenIndex); + if (tokenEndIndex <= tokenStartIndex) { + // The faux indent part of the line should have no token type + continue; + } + + tokenMetadata = tokens.getMetadata(tokenIndex); + + for (x = tokenStartIndex; x < tokenEndIndex; x++) { + // Only render lines that do not exceed maximum columns + if (x > ViewportRenderStrategy.maxSupportedColumns) { + break; + } + segment = contentSegmenter.getSegmentAtIndex(x); + if (segment === undefined) { + continue; + } + chars = segment; + + if (!(lineData.isBasicASCII && viewLineOptions.useMonospaceOptimizations)) { + charWidth = this.glyphRasterizer.getTextMetrics(chars).width; + } + + decorationStyleSetColor = undefined; + decorationStyleSetBold = undefined; + decorationStyleSetOpacity = undefined; + + // Apply supported inline decoration styles to the cell metadata + for (decoration of lineData.inlineDecorations) { + // This is Range.strictContainsPosition except it works at the cell level, + // it's also inlined to avoid overhead. + if ( + (y < decoration.range.startLineNumber || y > decoration.range.endLineNumber) || + (y === decoration.range.startLineNumber && x < decoration.range.startColumn - 1) || + (y === decoration.range.endLineNumber && x >= decoration.range.endColumn - 1) + ) { + continue; + } + + const rules = ViewGpuContext.decorationCssRuleExtractor.getStyleRules(this._viewGpuContext.canvas.domNode, decoration.inlineClassName); + for (const rule of rules) { + for (const r of rule.style) { + const value = rule.styleMap.get(r)?.toString() ?? ''; + switch (r) { + case 'color': { + // TODO: This parsing and error handling should move into canRender so fallback + // to DOM works + const parsedColor = Color.Format.CSS.parse(value); + if (!parsedColor) { + throw new BugIndicatingError('Invalid color format ' + value); + } + decorationStyleSetColor = parsedColor.toNumber32Bit(); + break; + } + case 'font-weight': { + const parsedValue = parseCssFontWeight(value); + if (parsedValue >= 400) { + decorationStyleSetBold = true; + // TODO: Set bold (https://github.com/microsoft/vscode/issues/237584) + } else { + decorationStyleSetBold = false; + // TODO: Set normal (https://github.com/microsoft/vscode/issues/237584) + } + break; + } + case 'opacity': { + const parsedValue = parseCssOpacity(value); + decorationStyleSetOpacity = parsedValue; + break; + } + default: throw new BugIndicatingError('Unexpected inline decoration style'); + } + } + } + } + + if (chars === ' ' || chars === '\t') { + // Zero out glyph to ensure it doesn't get rendered + cellIndex = ((y - 1) * ViewportRenderStrategy.maxSupportedColumns + x) * Constants.IndicesPerCell; + cellBuffer.fill(0, cellIndex, cellIndex + CellBufferInfo.FloatsPerEntry); + // Adjust xOffset for tab stops + if (chars === '\t') { + // Find the pixel offset between the current position and the next tab stop + const offsetBefore = x + tabXOffset; + tabXOffset = CursorColumns.nextRenderTabStop(x + tabXOffset, lineData.tabSize); + absoluteOffsetX += charWidth * (tabXOffset - offsetBefore); + // Convert back to offset excluding x and the current character + tabXOffset -= x + 1; + } else { + absoluteOffsetX += charWidth; + } + continue; + } + + const decorationStyleSetId = ViewGpuContext.decorationStyleCache.getOrCreateEntry(decorationStyleSetColor, decorationStyleSetBold, decorationStyleSetOpacity); + glyph = this._viewGpuContext.atlas.getGlyph(this.glyphRasterizer, chars, tokenMetadata, decorationStyleSetId, absoluteOffsetX); + + absoluteOffsetY = Math.round( + // Top of layout box (includes line height) + viewportData.relativeVerticalOffset[y - viewportData.startLineNumber] * dpr + + + // Delta from top of layout box (includes line height) to top of the inline box (no line height) + Math.floor((viewportData.lineHeight * dpr - (glyph.fontBoundingBoxAscent + glyph.fontBoundingBoxDescent)) / 2) + + + // Delta from top of inline box (no line height) to top of glyph origin. If the glyph was drawn + // with a top baseline for example, this ends up drawing the glyph correctly using the alphabetical + // baseline. + glyph.fontBoundingBoxAscent + ); + + cellIndex = ((y - viewportData.startLineNumber) * ViewportRenderStrategy.maxSupportedColumns + x) * Constants.IndicesPerCell; + cellBuffer[cellIndex + CellBufferInfo.Offset_X] = Math.floor(absoluteOffsetX); + cellBuffer[cellIndex + CellBufferInfo.Offset_Y] = absoluteOffsetY; + cellBuffer[cellIndex + CellBufferInfo.GlyphIndex] = glyph.glyphIndex; + cellBuffer[cellIndex + CellBufferInfo.TextureIndex] = glyph.pageIndex; + + // Adjust the x pixel offset for the next character + absoluteOffsetX += charWidth; + } + + tokenStartIndex = tokenEndIndex; + } + + // Clear to end of line + fillStartIndex = ((y - viewportData.startLineNumber) * ViewportRenderStrategy.maxSupportedColumns + tokenEndIndex) * Constants.IndicesPerCell; + fillEndIndex = ((y - viewportData.startLineNumber) * ViewportRenderStrategy.maxSupportedColumns) * Constants.IndicesPerCell; + cellBuffer.fill(0, fillStartIndex, fillEndIndex); + } + + const visibleObjectCount = (viewportData.endLineNumber - viewportData.startLineNumber + 1) * lineIndexCount; + + // This render strategy always uploads the whole viewport + this._device.queue.writeBuffer( + this._cellBindBuffer, + 0, + cellBuffer.buffer, + 0, + (viewportData.endLineNumber - viewportData.startLineNumber) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT + ); + + this._activeDoubleBufferIndex = this._activeDoubleBufferIndex ? 0 : 1; + + this._visibleObjectCount = visibleObjectCount; + + return visibleObjectCount; + } + + draw(pass: GPURenderPassEncoder, viewportData: ViewportData): void { + if (this._visibleObjectCount <= 0) { + throw new BugIndicatingError('Attempt to draw 0 objects'); + } + pass.draw(quadVertices.length / 2, this._visibleObjectCount); + } +} + +function parseCssFontWeight(value: string) { + switch (value) { + case 'lighter': + case 'normal': return 400; + case 'bolder': + case 'bold': return 700; + } + return parseInt(value); +} + +function parseCssOpacity(value: string): number { + if (value.endsWith('%')) { + return parseFloat(value.substring(0, value.length - 1)) / 100; + } + if (value.match(/^\d+(?:\.\d*)/)) { + return parseFloat(value); + } + return 1; +} diff --git a/src/vs/editor/browser/gpu/taskQueue.ts b/src/vs/editor/browser/gpu/taskQueue.ts index 27d64d01fe2..a6cf28c8c86 100644 --- a/src/vs/editor/browser/gpu/taskQueue.ts +++ b/src/vs/editor/browser/gpu/taskQueue.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { getActiveWindow } from '../../../base/browser/dom.js'; -import { Disposable, toDisposable } from '../../../base/common/lifecycle.js'; +import { Disposable, toDisposable, type IDisposable } from '../../../base/common/lifecycle.js'; /** * Copyright (c) 2022 The xterm.js authors. All rights reserved. * @license MIT */ -interface ITaskQueue { +export interface ITaskQueue extends IDisposable { /** * Adds a task to the queue which will run in a future idle callback. * To avoid perceivable stalls on the mainthread, tasks with heavy workload @@ -134,13 +134,7 @@ export class PriorityTaskQueue extends TaskQueue { } } -/** - * A queue of that runs tasks over several idle callbacks, trying to respect the idle callback's - * deadline given by the environment. The tasks will run in the order they are enqueued, but they - * will run some time later, and care should be taken to ensure they're non-urgent and will not - * introduce race conditions. - */ -export class IdleTaskQueue extends TaskQueue { +class IdleTaskQueueInternal extends TaskQueue { protected _requestCallback(callback: IdleRequestCallback): number { return getActiveWindow().requestIdleCallback(callback); } @@ -150,6 +144,16 @@ export class IdleTaskQueue extends TaskQueue { } } +/** + * A queue of that runs tasks over several idle callbacks, trying to respect the idle callback's + * deadline given by the environment. The tasks will run in the order they are enqueued, but they + * will run some time later, and care should be taken to ensure they're non-urgent and will not + * introduce race conditions. + * + * This reverts to a {@link PriorityTaskQueue} if the environment does not support idle callbacks. + */ +export const IdleTaskQueue = ('requestIdleCallback' in getActiveWindow()) ? IdleTaskQueueInternal : PriorityTaskQueue; + /** * An object that tracks a single debounced task that will run on the next idle frame. When called * multiple times, only the last set task will run. diff --git a/src/vs/editor/browser/gpu/viewGpuContext.ts b/src/vs/editor/browser/gpu/viewGpuContext.ts index 877c1b3a868..181c468f12c 100644 --- a/src/vs/editor/browser/gpu/viewGpuContext.ts +++ b/src/vs/editor/browser/gpu/viewGpuContext.ts @@ -19,17 +19,38 @@ import { GPULifecycle } from './gpuDisposable.js'; import { ensureNonNullable, observeDevicePixelDimensions } from './gpuUtils.js'; import { RectangleRenderer } from './rectangleRenderer.js'; import type { ViewContext } from '../../common/viewModel/viewContext.js'; +import { DecorationCssRuleExtractor } from './css/decorationCssRuleExtractor.js'; +import { Event } from '../../../base/common/event.js'; +import { EditorOption, type IEditorOptions } from '../../common/config/editorOptions.js'; +import { InlineDecorationType } from '../../common/viewModel.js'; +import { DecorationStyleCache } from './css/decorationStyleCache.js'; +import { ViewportRenderStrategy } from './renderStrategy/viewportRenderStrategy.js'; export class ViewGpuContext extends Disposable { + /** + * The hard cap for line columns rendered by the GPU renderer. + */ + readonly maxGpuCols = ViewportRenderStrategy.maxSupportedColumns; + readonly canvas: FastDomNode; readonly ctx: GPUCanvasContext; - readonly device: Promise; + static device: Promise; + static deviceSync: GPUDevice | undefined; readonly rectangleRenderer: RectangleRenderer; - private static _atlas: TextureAtlas | undefined; + private static readonly _decorationCssRuleExtractor = new DecorationCssRuleExtractor(); + static get decorationCssRuleExtractor(): DecorationCssRuleExtractor { + return ViewGpuContext._decorationCssRuleExtractor; + } + private static readonly _decorationStyleCache = new DecorationStyleCache(); + static get decorationStyleCache(): DecorationStyleCache { + return ViewGpuContext._decorationStyleCache; + } + + private static _atlas: TextureAtlas | undefined; /** * The shared texture atlas to use across all views. @@ -54,6 +75,7 @@ export class ViewGpuContext extends Disposable { readonly canvasDevicePixelDimensions: IObservable<{ width: number; height: number }>; readonly devicePixelRatio: IObservable; + readonly contentLeft: IObservable; constructor( context: ViewContext, @@ -66,29 +88,41 @@ export class ViewGpuContext extends Disposable { this.canvas = createFastDomNode(document.createElement('canvas')); this.canvas.setClassName('editorCanvas'); - this.ctx = ensureNonNullable(this.canvas.domNode.getContext('webgpu')); - - this.device = GPULifecycle.requestDevice((message) => { - const choices: IPromptChoice[] = [{ - label: nls.localize('editor.dom.render', "Use DOM-based rendering"), - run: () => this.configurationService.updateValue('editor.experimentalGpuAcceleration', 'off'), - }]; - this._notificationService.prompt(Severity.Warning, message, choices); - }).then(ref => this._register(ref).object); - this.device.then(device => { - if (!ViewGpuContext._atlas) { - ViewGpuContext._atlas = this._instantiationService.createInstance(TextureAtlas, device.limits.maxTextureDimension2D, undefined); - runOnChange(this.devicePixelRatio, () => ViewGpuContext.atlas.clear()); + // Adjust the canvas size to avoid drawing under the scroll bar + this._register(Event.runAndSubscribe(configurationService.onDidChangeConfiguration, e => { + if (!e || e.affectsConfiguration('editor.scrollbar.verticalScrollbarSize')) { + const verticalScrollbarSize = configurationService.getValue('editor').scrollbar?.verticalScrollbarSize ?? 14; + this.canvas.domNode.style.boxSizing = 'border-box'; + this.canvas.domNode.style.paddingRight = `${verticalScrollbarSize}px`; } - }); + })); - this.rectangleRenderer = this._instantiationService.createInstance(RectangleRenderer, context, this.canvas.domNode, this.ctx, this.device); + this.ctx = ensureNonNullable(this.canvas.domNode.getContext('webgpu')); + + // Request the GPU device, we only want to do this a single time per window as it's async + // and can delay the initial render. + if (!ViewGpuContext.device) { + ViewGpuContext.device = GPULifecycle.requestDevice((message) => { + const choices: IPromptChoice[] = [{ + label: nls.localize('editor.dom.render', "Use DOM-based rendering"), + run: () => this.configurationService.updateValue('editor.experimentalGpuAcceleration', 'off'), + }]; + this._notificationService.prompt(Severity.Warning, message, choices); + }).then(ref => { + ViewGpuContext.deviceSync = ref.object; + if (!ViewGpuContext._atlas) { + ViewGpuContext._atlas = this._instantiationService.createInstance(TextureAtlas, ref.object.limits.maxTextureDimension2D, undefined); + } + return ref.object; + }); + } const dprObs = observableValue(this, getActiveWindow().devicePixelRatio); this._register(addDisposableListener(getActiveWindow(), 'resize', () => { dprObs.set(getActiveWindow().devicePixelRatio, undefined); })); this.devicePixelRatio = dprObs; + this._register(runOnChange(this.devicePixelRatio, () => ViewGpuContext.atlas?.clear())); const canvasDevicePixelDimensions = observableValue(this, { width: this.canvas.domNode.width, height: this.canvas.domNode.height }); this._register(observeDevicePixelDimensions( @@ -101,6 +135,14 @@ export class ViewGpuContext extends Disposable { } )); this.canvasDevicePixelDimensions = canvasDevicePixelDimensions; + + const contentLeft = observableValue(this, 0); + this._register(this.configurationService.onDidChangeConfiguration(e => { + contentLeft.set(context.configuration.options.get(EditorOption.layoutInfo).contentLeft, undefined); + })); + this.contentLeft = contentLeft; + + this.rectangleRenderer = this._instantiationService.createInstance(RectangleRenderer, context, this.contentLeft, this.devicePixelRatio, this.canvas.domNode, this.ctx, ViewGpuContext.device); } /** @@ -108,16 +150,119 @@ export class ViewGpuContext extends Disposable { * renderer. Eventually this should trend all lines, except maybe exceptional cases like * decorations that use class names. */ - public static canRender(options: ViewLineOptions, viewportData: ViewportData, lineNumber: number): boolean { + public canRender(options: ViewLineOptions, viewportData: ViewportData, lineNumber: number): boolean { const data = viewportData.getViewLineRenderingData(lineNumber); + + // Check if the line has simple attributes that aren't supported if ( data.containsRTL || - data.maxColumn > 200 || - data.continuesWithWrappedLine || - data.inlineDecorations.length > 0 + data.maxColumn > this.maxGpuCols ) { return false; } + + // Check if all inline decorations are supported + if (data.inlineDecorations.length > 0) { + let supported = true; + for (const decoration of data.inlineDecorations) { + if (decoration.type !== InlineDecorationType.Regular) { + supported = false; + break; + } + const styleRules = ViewGpuContext._decorationCssRuleExtractor.getStyleRules(this.canvas.domNode, decoration.inlineClassName); + supported &&= styleRules.every(rule => { + // Pseudo classes aren't supported currently + if (rule.selectorText.includes(':')) { + return false; + } + for (const r of rule.style) { + if (!supportsCssRule(r, rule.style)) { + return false; + } + } + return true; + }); + if (!supported) { + break; + } + } + return supported; + } + return true; } + + /** + * Like {@link canRender} but returns detailed information about why the line cannot be rendered. + */ + public canRenderDetailed(options: ViewLineOptions, viewportData: ViewportData, lineNumber: number): string[] { + const data = viewportData.getViewLineRenderingData(lineNumber); + const reasons: string[] = []; + if (data.containsRTL) { + reasons.push('containsRTL'); + } + if (data.maxColumn > this.maxGpuCols) { + reasons.push('maxColumn > maxGpuCols'); + } + if (data.inlineDecorations.length > 0) { + let supported = true; + const problemTypes: InlineDecorationType[] = []; + const problemSelectors: string[] = []; + const problemRules: string[] = []; + for (const decoration of data.inlineDecorations) { + if (decoration.type !== InlineDecorationType.Regular) { + problemTypes.push(decoration.type); + supported = false; + continue; + } + const styleRules = ViewGpuContext._decorationCssRuleExtractor.getStyleRules(this.canvas.domNode, decoration.inlineClassName); + supported &&= styleRules.every(rule => { + // Pseudo classes aren't supported currently + if (rule.selectorText.includes(':')) { + problemSelectors.push(rule.selectorText); + return false; + } + for (const r of rule.style) { + if (!supportsCssRule(r, rule.style)) { + problemRules.push(`${r}: ${rule.style[r as any]}`); + return false; + } + } + return true; + }); + if (!supported) { + continue; + } + } + if (problemTypes.length > 0) { + reasons.push(`inlineDecorations with unsupported types (${problemTypes.map(e => `\`${e}\``).join(', ')})`); + } + if (problemRules.length > 0) { + reasons.push(`inlineDecorations with unsupported CSS rules (${problemRules.map(e => `\`${e}\``).join(', ')})`); + } + if (problemSelectors.length > 0) { + reasons.push(`inlineDecorations with unsupported CSS selectors (${problemSelectors.map(e => `\`${e}\``).join(', ')})`); + } + } + return reasons; + } +} + +/** + * A list of supported decoration CSS rules that can be used in the GPU renderer. + */ +const gpuSupportedDecorationCssRules = [ + 'color', + 'font-weight', + 'opacity', +]; + +function supportsCssRule(rule: string, style: CSSStyleDeclaration) { + if (!gpuSupportedDecorationCssRules.includes(rule)) { + return false; + } + // Check for values that aren't supported + switch (rule) { + default: return true; + } } diff --git a/src/vs/editor/browser/observableCodeEditor.ts b/src/vs/editor/browser/observableCodeEditor.ts index 5747a39c438..eb5172693ce 100644 --- a/src/vs/editor/browser/observableCodeEditor.ts +++ b/src/vs/editor/browser/observableCodeEditor.ts @@ -5,14 +5,17 @@ import { equalsIfDefined, itemsEquals } from '../../base/common/equals.js'; import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../base/common/lifecycle.js'; -import { IObservable, ITransaction, TransactionImpl, autorun, autorunOpts, derived, derivedOpts, derivedWithSetter, observableFromEvent, observableSignal, observableValue, observableValueOpts } from '../../base/common/observable.js'; +import { IObservable, IObservableWithChange, ITransaction, TransactionImpl, autorun, autorunOpts, derived, derivedOpts, derivedWithSetter, observableFromEvent, observableSignal, observableValue, observableValueOpts } from '../../base/common/observable.js'; import { EditorOption, FindComputedEditorOptionValueById } from '../common/config/editorOptions.js'; +import { LineRange } from '../common/core/lineRange.js'; +import { OffsetRange } from '../common/core/offsetRange.js'; import { Position } from '../common/core/position.js'; import { Selection } from '../common/core/selection.js'; import { ICursorSelectionChangedEvent } from '../common/cursorEvents.js'; import { IModelDeltaDecoration, ITextModel } from '../common/model.js'; import { IModelContentChangedEvent } from '../common/textModelEvents.js'; -import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from './editorBrowser.js'; +import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent, IOverlayWidget, IOverlayWidgetPosition, IPasteEvent } from './editorBrowser.js'; +import { Point } from './point.js'; /** * Returns a facade for the code editor that provides observables for various states/events. @@ -91,6 +94,16 @@ export class ObservableCodeEditor extends Disposable { } })); + this._register(this.editor.onDidPaste((e) => { + this._beginUpdate(); + try { + this._forceUpdate(); + this.onDidPaste.trigger(this._currentTransaction, e); + } finally { + this._endUpdate(); + } + })); + this._register(this.editor.onDidChangeModelContent(e => { this._beginUpdate(); try { @@ -142,13 +155,13 @@ export class ObservableCodeEditor extends Disposable { public readonly isReadonly = observableFromEvent(this, this.editor.onDidChangeConfiguration, () => this.editor.getOption(EditorOption.readOnly)); private readonly _versionId = observableValueOpts({ owner: this, lazy: true }, this.editor.getModel()?.getVersionId() ?? null); - public readonly versionId: IObservable = this._versionId; + public readonly versionId: IObservableWithChange = this._versionId; private readonly _selections = observableValueOpts( { owner: this, equalsFn: equalsIfDefined(itemsEquals(Selection.selectionsEqual)), lazy: true }, this.editor.getSelections() ?? null ); - public readonly selections: IObservable = this._selections; + public readonly selections: IObservableWithChange = this._selections; public readonly positions = derivedOpts( @@ -167,6 +180,32 @@ export class ObservableCodeEditor extends Disposable { }; }, () => this.editor.hasWidgetFocus()); + public readonly isTextFocused = observableFromEvent(this, e => { + const d1 = this.editor.onDidFocusEditorText(e); + const d2 = this.editor.onDidBlurEditorText(e); + return { + dispose() { + d1.dispose(); + d2.dispose(); + } + }; + }, () => this.editor.hasTextFocus()); + + public readonly inComposition = observableFromEvent(this, e => { + const d1 = this.editor.onDidCompositionStart(() => { + e(undefined); + }); + const d2 = this.editor.onDidCompositionEnd(() => { + e(undefined); + }); + return { + dispose() { + d1.dispose(); + d2.dispose(); + } + }; + }, () => this.editor.inComposition); + public readonly value = derivedWithSetter(this, reader => { this.versionId.read(reader); return this.model.read(reader)?.getValue() ?? ''; }, (value, tx) => { @@ -184,6 +223,7 @@ export class ObservableCodeEditor extends Disposable { public readonly cursorLineNumber = derived(this, reader => this.cursorPosition.read(reader)?.lineNumber ?? null); public readonly onDidType = observableSignal(this); + public readonly onDidPaste = observableSignal(this); public readonly scrollTop = observableFromEvent(this.editor.onDidScrollChange, () => this.editor.getScrollTop()); public readonly scrollLeft = observableFromEvent(this.editor.onDidScrollChange, () => this.editor.getScrollLeft()); @@ -191,6 +231,9 @@ export class ObservableCodeEditor extends Disposable { public readonly layoutInfo = observableFromEvent(this.editor.onDidLayoutChange, () => this.editor.getLayoutInfo()); public readonly layoutInfoContentLeft = this.layoutInfo.map(l => l.contentLeft); public readonly layoutInfoDecorationsLeft = this.layoutInfo.map(l => l.decorationsLeft); + public readonly layoutInfoWidth = this.layoutInfo.map(l => l.width); + public readonly layoutInfoMinimap = this.layoutInfo.map(l => l.minimap); + public readonly layoutInfoVerticalScrollbarWidth = this.layoutInfo.map(l => l.verticalScrollbarWidth); public readonly contentWidth = observableFromEvent(this.editor.onDidContentSizeChange, () => this.editor.getContentWidth()); @@ -215,10 +258,10 @@ export class ObservableCodeEditor extends Disposable { return d; } - private _overlayWidgetCounter = 0; + private _widgetCounter = 0; public createOverlayWidget(widget: IObservableOverlayWidget): IDisposable { - const overlayWidgetId = 'observableOverlayWidget' + (this._overlayWidgetCounter++); + const overlayWidgetId = 'observableOverlayWidget' + (this._widgetCounter++); const w: IOverlayWidget = { getDomNode: () => widget.domNode, getPosition: () => widget.position.get(), @@ -237,6 +280,94 @@ export class ObservableCodeEditor extends Disposable { this.editor.removeOverlayWidget(w); }); } + + public createContentWidget(widget: IObservableContentWidget): IDisposable { + const contentWidgetId = 'observableContentWidget' + (this._widgetCounter++); + const w: IContentWidget = { + getDomNode: () => widget.domNode, + getPosition: () => widget.position.get(), + getId: () => contentWidgetId, + allowEditorOverflow: widget.allowEditorOverflow, + }; + this.editor.addContentWidget(w); + const d = autorun(reader => { + widget.position.read(reader); + this.editor.layoutContentWidget(w); + }); + return toDisposable(() => { + d.dispose(); + this.editor.removeContentWidget(w); + }); + } + + public observeLineOffsetRange(lineRange: IObservable, store: DisposableStore): IObservable { + const start = this.observePosition(lineRange.map(r => new Position(r.startLineNumber, 1)), store); + const end = this.observePosition(lineRange.map(r => new Position(r.endLineNumberExclusive + 1, 1)), store); + + return derived(reader => { + start.read(reader); + end.read(reader); + const range = lineRange.read(reader); + const lineCount = this.model.read(reader)?.getLineCount(); + const s = ( + (typeof lineCount !== 'undefined' && range.startLineNumber > lineCount + ? this.editor.getBottomForLineNumber(lineCount) + : this.editor.getTopForLineNumber(range.startLineNumber) + ) + - this.scrollTop.read(reader) + ); + const e = range.isEmpty ? s : (this.editor.getBottomForLineNumber(range.endLineNumberExclusive - 1) - this.scrollTop.read(reader)); + return new OffsetRange(s, e); + }); + } + + public observePosition(position: IObservable, store: DisposableStore): IObservable { + let pos = position.get(); + const result = observableValueOpts({ owner: this, debugName: () => `topLeftOfPosition${pos?.toString()}`, equalsFn: equalsIfDefined(Point.equals) }, new Point(0, 0)); + const contentWidgetId = `observablePositionWidget` + (this._widgetCounter++); + const domNode = document.createElement('div'); + const w: IContentWidget = { + getDomNode: () => domNode, + getPosition: () => { + return pos ? { preference: [ContentWidgetPositionPreference.EXACT], position: position.get() } : null; + }, + getId: () => contentWidgetId, + allowEditorOverflow: false, + afterRender: (position, coordinate) => { + const model = this._model.get(); + if (model && pos && pos.lineNumber > model.getLineCount()) { + // the position is after the last line + result.set(new Point(0, this.editor.getBottomForLineNumber(model.getLineCount()) - this.scrollTop.get()), undefined); + } else { + result.set(coordinate ? new Point(coordinate.left, coordinate.top) : null, undefined); + } + }, + }; + this.editor.addContentWidget(w); + store.add(autorun(reader => { + pos = position.read(reader); + this.editor.layoutContentWidget(w); + })); + store.add(toDisposable(() => { + this.editor.removeContentWidget(w); + })); + return result; + } + + public readonly openedPeekWidgets = observableValue(this, 0); + + isTargetHovered(predicate: (target: IEditorMouseEvent) => boolean, store: DisposableStore): IObservable { + const isHovered = observableValue('isInjectedTextHovered', false); + store.add(this.editor.onMouseMove(e => { + const val = predicate(e); + isHovered.set(val, undefined); + })); + + store.add(this.editor.onMouseLeave(E => { + isHovered.set(false, undefined); + })); + return isHovered; + } } interface IObservableOverlayWidget { @@ -245,3 +376,9 @@ interface IObservableOverlayWidget { readonly minContentWidthInPx: IObservable; get allowEditorOverflow(): boolean; } + +interface IObservableContentWidget { + get domNode(): HTMLElement; + readonly position: IObservable; + get allowEditorOverflow(): boolean; +} diff --git a/src/vs/editor/browser/point.ts b/src/vs/editor/browser/point.ts new file mode 100644 index 00000000000..8a41c329f18 --- /dev/null +++ b/src/vs/editor/browser/point.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. + *--------------------------------------------------------------------------------------------*/ + +export class Point { + + static equals(a: Point, b: Point): boolean { + return a.x === b.x && a.y === b.y; + } + + constructor( + public readonly x: number, + public readonly y: number + ) { } + + public add(other: Point): Point { + return new Point(this.x + other.x, this.y + other.y); + } + + public deltaX(delta: number): Point { + return new Point(this.x + delta, this.y); + } + + public deltaY(delta: number): Point { + return new Point(this.x, this.y + delta); + } + + public toString() { + return `(${this.x},${this.y})`; + } +} diff --git a/src/vs/editor/browser/rect.ts b/src/vs/editor/browser/rect.ts new file mode 100644 index 00000000000..66c1bd87ebd --- /dev/null +++ b/src/vs/editor/browser/rect.ts @@ -0,0 +1,191 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BugIndicatingError } from '../../base/common/errors.js'; +import { OffsetRange } from '../common/core/offsetRange.js'; +import { Point } from './point.js'; + +export class Rect { + public static fromPoint(point: Point): Rect { + return new Rect(point.x, point.y, point.x, point.y); + } + + public static fromPoints(topLeft: Point, bottomRight: Point): Rect { + return new Rect(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y); + } + + public static fromPointSize(point: Point, size: Point): Rect { + return new Rect(point.x, point.y, point.x + size.x, point.y + size.y); + } + + public static fromLeftTopRightBottom(left: number, top: number, right: number, bottom: number): Rect { + return new Rect(left, top, right, bottom); + } + + public static fromLeftTopWidthHeight(left: number, top: number, width: number, height: number): Rect { + return new Rect(left, top, left + width, top + height); + } + + public static fromRanges(leftRight: OffsetRange, topBottom: OffsetRange): Rect { + return new Rect(leftRight.start, topBottom.start, leftRight.endExclusive, topBottom.endExclusive); + } + + public static hull(rects: Rect[]): Rect { + let left = Number.MAX_SAFE_INTEGER; + let top = Number.MAX_SAFE_INTEGER; + let right = Number.MIN_SAFE_INTEGER; + let bottom = Number.MIN_SAFE_INTEGER; + + for (const rect of rects) { + left = Math.min(left, rect.left); + top = Math.min(top, rect.top); + right = Math.max(right, rect.right); + bottom = Math.max(bottom, rect.bottom); + } + + return new Rect(left, top, right, bottom); + } + + public get width() { return this.right - this.left; } + public get height() { return this.bottom - this.top; } + + constructor( + public readonly left: number, + public readonly top: number, + public readonly right: number, + public readonly bottom: number, + ) { + if (left > right || top > bottom) { + throw new BugIndicatingError('Invalid arguments'); + } + } + + withMargin(margin: number): Rect { + return new Rect(this.left - margin, this.top - margin, this.right + margin, this.bottom + margin); + } + + intersectVertical(range: OffsetRange): Rect { + return new Rect( + this.left, + Math.max(this.top, range.start), + this.right, + Math.min(this.bottom, range.endExclusive), + ); + } + + toString(): string { + return `Rect{(${this.left},${this.top}), (${this.right},${this.bottom})}`; + } + + intersect(parent: Rect): Rect | undefined { + const left = Math.max(this.left, parent.left); + const right = Math.min(this.right, parent.right); + const top = Math.max(this.top, parent.top); + const bottom = Math.min(this.bottom, parent.bottom); + + if (left > right || top > bottom) { + return undefined; + } + + return new Rect(left, top, right, bottom); + } + + union(other: Rect): Rect { + return new Rect( + Math.min(this.left, other.left), + Math.min(this.top, other.top), + Math.max(this.right, other.right), + Math.max(this.bottom, other.bottom), + ); + } + + containsRect(other: Rect): boolean { + return this.left <= other.left + && this.top <= other.top + && this.right >= other.right + && this.bottom >= other.bottom; + } + + containsPoint(point: Point): boolean { + return this.left <= point.x + && this.top <= point.y + && this.right >= point.x + && this.bottom >= point.y; + } + + moveToBeContainedIn(parent: Rect): Rect { + const width = this.width; + const height = this.height; + + let left = this.left; + let top = this.top; + + if (left < parent.left) { + left = parent.left; + } else if (left + width > parent.right) { + left = parent.right - width; + } + + if (top < parent.top) { + top = parent.top; + } else if (top + height > parent.bottom) { + top = parent.bottom - height; + } + + return new Rect(left, top, left + width, top + height); + } + + withWidth(width: number): Rect { + return new Rect(this.left, this.top, this.left + width, this.bottom); + } + + withHeight(height: number): Rect { + return new Rect(this.left, this.top, this.right, this.top + height); + } + + withTop(top: number): Rect { + return new Rect(this.left, top, this.right, this.bottom); + } + + translateX(delta: number): Rect { + return new Rect(this.left + delta, this.top, this.right + delta, this.bottom); + } + + translateY(delta: number): Rect { + return new Rect(this.left, this.top + delta, this.right, this.bottom + delta); + } + + deltaRight(delta: number): Rect { + return new Rect(this.left, this.top, this.right + delta, this.bottom); + } + + deltaTop(delta: number): Rect { + return new Rect(this.left, this.top + delta, this.right, this.bottom); + } + + deltaLeft(delta: number): Rect { + return new Rect(this.left + delta, this.top, this.right, this.bottom); + } + + deltaBottom(delta: number): Rect { + return new Rect(this.left, this.top, this.right, this.bottom + delta); + } + + getLeftBottom(): Point { + return new Point(this.left, this.bottom); + } + + getRightBottom(): Point { + return new Point(this.right, this.bottom); + } + + getLeftTop(): Point { + return new Point(this.left, this.top); + } + + getRightTop(): Point { + return new Point(this.right, this.top); + } +} diff --git a/src/vs/editor/browser/services/abstractCodeEditorService.ts b/src/vs/editor/browser/services/abstractCodeEditorService.ts index d92fc24c9cf..295d5bf1ca1 100644 --- a/src/vs/editor/browser/services/abstractCodeEditorService.ts +++ b/src/vs/editor/browser/services/abstractCodeEditorService.ts @@ -4,8 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from '../../../base/browser/dom.js'; +import * as domStylesheets from '../../../base/browser/domStylesheets.js'; +import * as cssJs from '../../../base/browser/cssValue.js'; import { Emitter, Event } from '../../../base/common/event.js'; -import { IDisposable, DisposableStore, Disposable, toDisposable } from '../../../base/common/lifecycle.js'; +import { IDisposable, DisposableStore, Disposable, toDisposable, DisposableMap } from '../../../base/common/lifecycle.js'; import { LinkedList } from '../../../base/common/linkedList.js'; import * as strings from '../../../base/common/strings.js'; import { URI } from '../../../base/common/uri.js'; @@ -127,7 +129,7 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC } protected _createGlobalStyleSheet(): GlobalStyleSheet { - return new GlobalStyleSheet(dom.createStyleSheet()); + return new GlobalStyleSheet(domStylesheets.createStyleSheet()); } private _getOrCreateStyleSheet(editor: ICodeEditor | undefined): GlobalStyleSheet | RefCountedStyleSheet { @@ -140,7 +142,7 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC } const editorId = editor.getId(); if (!this._editorStyleSheets.has(editorId)) { - const refCountedStyleSheet = new RefCountedStyleSheet(this, editorId, dom.createStyleSheet(domNode)); + const refCountedStyleSheet = new RefCountedStyleSheet(this, editorId, domStylesheets.createStyleSheet(domNode)); this._editorStyleSheets.set(editorId, refCountedStyleSheet); } return this._editorStyleSheets.get(editorId)!; @@ -208,7 +210,7 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC return provider.resolveDecorationCSSRules(); } - private readonly _transientWatchers: { [uri: string]: ModelTransientSettingWatcher } = {}; + private readonly _transientWatchers = this._register(new DisposableMap()); private readonly _modelProperties = new Map>(); public setModelProperty(resource: URI, key: string, value: any): void { @@ -236,12 +238,10 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC public setTransientModelProperty(model: ITextModel, key: string, value: any): void { const uri = model.uri.toString(); - let w: ModelTransientSettingWatcher; - if (this._transientWatchers.hasOwnProperty(uri)) { - w = this._transientWatchers[uri]; - } else { + let w = this._transientWatchers.get(uri); + if (!w) { w = new ModelTransientSettingWatcher(uri, model, this); - this._transientWatchers[uri] = w; + this._transientWatchers.set(uri, w); } const previousValue = w.get(key); @@ -254,25 +254,27 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC public getTransientModelProperty(model: ITextModel, key: string): any { const uri = model.uri.toString(); - if (!this._transientWatchers.hasOwnProperty(uri)) { + const watcher = this._transientWatchers.get(uri); + if (!watcher) { return undefined; } - return this._transientWatchers[uri].get(key); + return watcher.get(key); } public getTransientModelProperties(model: ITextModel): [string, any][] | undefined { const uri = model.uri.toString(); - if (!this._transientWatchers.hasOwnProperty(uri)) { + const watcher = this._transientWatchers.get(uri); + if (!watcher) { return undefined; } - return this._transientWatchers[uri].keys().map(key => [key, this._transientWatchers[uri].get(key)]); + return watcher.keys().map(key => [key, watcher.get(key)]); } _removeWatcher(w: ModelTransientSettingWatcher): void { - delete this._transientWatchers[w.uri]; + this._transientWatchers.deleteAndDispose(w.uri); } abstract getActiveCodeEditor(): ICodeEditor | null; @@ -293,14 +295,16 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC } } -export class ModelTransientSettingWatcher { +export class ModelTransientSettingWatcher extends Disposable { public readonly uri: string; private readonly _values: { [key: string]: any }; constructor(uri: string, model: ITextModel, owner: AbstractCodeEditorService) { + super(); + this.uri = uri; this._values = {}; - model.onWillDispose(() => owner._removeWatcher(this)); + this._register(model.onWillDispose(() => owner._removeWatcher(this))); } public set(key: string, value: any): void { @@ -347,11 +351,11 @@ class RefCountedStyleSheet { } public insertRule(selector: string, rule: string): void { - dom.createCSSRule(selector, rule, this._styleSheet); + domStylesheets.createCSSRule(selector, rule, this._styleSheet); } public removeRulesContainingSelector(ruleName: string): void { - dom.removeCSSRulesContainingSelector(ruleName, this._styleSheet); + domStylesheets.removeCSSRulesContainingSelector(ruleName, this._styleSheet); } } @@ -373,11 +377,11 @@ export class GlobalStyleSheet { } public insertRule(selector: string, rule: string): void { - dom.createCSSRule(selector, rule, this._styleSheet); + domStylesheets.createCSSRule(selector, rule, this._styleSheet); } public removeRulesContainingSelector(ruleName: string): void { - dom.removeCSSRulesContainingSelector(ruleName, this._styleSheet); + domStylesheets.removeCSSRulesContainingSelector(ruleName, this._styleSheet); } } @@ -771,7 +775,7 @@ class DecorationCSSRules { if (typeof opts !== 'undefined') { this.collectBorderSettingsCSSText(opts, cssTextArr); if (typeof opts.contentIconPath !== 'undefined') { - cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, dom.asCSSUrl(URI.revive(opts.contentIconPath)))); + cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, cssJs.asCSSUrl(URI.revive(opts.contentIconPath)))); } if (typeof opts.contentText === 'string') { const truncated = opts.contentText.match(/^.*$/m)![0]; // only take first line @@ -798,7 +802,7 @@ class DecorationCSSRules { const cssTextArr: string[] = []; if (typeof opts.gutterIconPath !== 'undefined') { - cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, dom.asCSSUrl(URI.revive(opts.gutterIconPath)))); + cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, cssJs.asCSSUrl(URI.revive(opts.gutterIconPath)))); if (typeof opts.gutterIconSize !== 'undefined') { cssTextArr.push(strings.format(_CSS_MAP.gutterIconSize, opts.gutterIconSize)); } diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index cbc88fafc2f..fa12a2ab99e 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -55,7 +55,7 @@ export class ResourceTextEdit extends ResourceEdit implements IWorkspaceTextEdit constructor( readonly resource: URI, - readonly textEdit: TextEdit & { insertAsSnippet?: boolean }, + readonly textEdit: TextEdit & { insertAsSnippet?: boolean; keepWhitespace?: boolean }, readonly versionId: number | undefined = undefined, metadata?: WorkspaceEditMetadata, ) { diff --git a/src/vs/editor/browser/services/codeEditorService.ts b/src/vs/editor/browser/services/codeEditorService.ts index b6d66c2cc1b..733018faea7 100644 --- a/src/vs/editor/browser/services/codeEditorService.ts +++ b/src/vs/editor/browser/services/codeEditorService.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - import { Event } from '../../../base/common/event.js'; import { ICodeEditor, IDiffEditor } from '../editorBrowser.js'; import { IDecorationRenderOptions } from '../../common/editorCommon.js'; diff --git a/src/vs/editor/browser/services/hoverService/hover.css b/src/vs/editor/browser/services/hoverService/hover.css index 87ed1d3c84e..8d483a54163 100644 --- a/src/vs/editor/browser/services/hoverService/hover.css +++ b/src/vs/editor/browser/services/hoverService/hover.css @@ -22,10 +22,6 @@ border-bottom: none; } -.monaco-workbench .workbench-hover:not(.skip-fade-in) { - animation: fadein 100ms linear; -} - .monaco-workbench .workbench-hover.compact { font-size: 12px; } @@ -37,12 +33,8 @@ .monaco-workbench .workbench-hover-container.locked .workbench-hover { outline: 1px solid var(--vscode-editorHoverWidget-border); } -.monaco-workbench .workbench-hover-container.locked .workbench-hover:focus, -.monaco-workbench .workbench-hover-lock:focus { - outline: 1px solid var(--vscode-focusBorder); -} -.monaco-workbench .workbench-hover-container.locked .workbench-hover-lock:hover { - background: var(--vscode-toolbar-hoverBackground); +.monaco-workbench .workbench-hover-container:focus-within.locked .workbench-hover { + outline-color: var(--vscode-focusBorder); } .monaco-workbench .workbench-hover-pointer { @@ -61,12 +53,16 @@ border-right: 1px solid var(--vscode-editorHoverWidget-border); border-bottom: 1px solid var(--vscode-editorHoverWidget-border); } -.monaco-workbench .locked .workbench-hover-pointer:after { +.monaco-workbench .workbench-hover-container:not(:focus-within).locked .workbench-hover-pointer:after { width: 4px; height: 4px; border-right-width: 2px; border-bottom-width: 2px; } +.monaco-workbench .workbench-hover-container:focus-within .workbench-hover-pointer:after { + border-right: 1px solid var(--vscode-focusBorder); + border-bottom: 1px solid var(--vscode-focusBorder); +} .monaco-workbench .workbench-hover-pointer.left { left: -3px; } .monaco-workbench .workbench-hover-pointer.right { right: 3px; } diff --git a/src/vs/editor/browser/services/hoverService/hoverService.ts b/src/vs/editor/browser/services/hoverService/hoverService.ts index 249aed774b7..3949c56e506 100644 --- a/src/vs/editor/browser/services/hoverService/hoverService.ts +++ b/src/vs/editor/browser/services/hoverService/hoverService.ts @@ -20,10 +20,15 @@ import { IAccessibilityService } from '../../../../platform/accessibility/common import { ILayoutService } from '../../../../platform/layout/browser/layoutService.js'; import { mainWindow } from '../../../../base/browser/window.js'; import { ContextViewHandler } from '../../../../platform/contextview/browser/contextViewService.js'; -import type { IHoverOptions, IHoverWidget, IManagedHover, IManagedHoverContentOrFactory, IManagedHoverOptions } from '../../../../base/browser/ui/hover/hover.js'; +import type { IHoverLifecycleOptions, IHoverOptions, IHoverWidget, IManagedHover, IManagedHoverContentOrFactory, IManagedHoverOptions } from '../../../../base/browser/ui/hover/hover.js'; import type { IHoverDelegate, IHoverDelegateTarget } from '../../../../base/browser/ui/hover/hoverDelegate.js'; import { ManagedHoverWidget } from './updatableHoverWidget.js'; -import { TimeoutTimer } from '../../../../base/common/async.js'; +import { timeout, TimeoutTimer } from '../../../../base/common/async.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { isNumber } from '../../../../base/common/types.js'; +import { KeyChord, KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; +import { KeybindingsRegistry, KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; +import { EditorContextKeys } from '../../../common/editorContextKeys.js'; export class HoverService extends Disposable implements IHoverService { declare readonly _serviceBrand: undefined; @@ -31,12 +36,19 @@ export class HoverService extends Disposable implements IHoverService { private _contextViewHandler: IContextViewProvider; private _currentHoverOptions: IHoverOptions | undefined; private _currentHover: HoverWidget | undefined; + private _currentDelayedHover: HoverWidget | undefined; + private _currentDelayedHoverWasShown: boolean = false; + private _currentDelayedHoverGroupId: number | string | undefined; private _lastHoverOptions: IHoverOptions | undefined; private _lastFocusedElementBeforeOpen: HTMLElement | undefined; + private readonly _delayedHovers = new Map void }>(); + private readonly _managedHovers = new Map(); + constructor( @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IConfigurationService private readonly _configurationService: IConfigurationService, @IContextMenuService contextMenuService: IContextMenuService, @IKeybindingService private readonly _keybindingService: IKeybindingService, @ILayoutService private readonly _layoutService: ILayoutService, @@ -44,15 +56,137 @@ export class HoverService extends Disposable implements IHoverService { ) { super(); - contextMenuService.onDidShowContextMenu(() => this.hideHover()); + this._register(contextMenuService.onDidShowContextMenu(() => this.hideHover())); this._contextViewHandler = this._register(new ContextViewHandler(this._layoutService)); + + this._register(KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'workbench.action.showHover', + weight: KeybindingWeight.WorkbenchContrib - 1, + when: EditorContextKeys.editorTextFocus.negate(), + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.CtrlCmd | KeyCode.KeyI), + handler: () => { this._showAndFocusHoverForActiveElement(); }, + })); } - showHover(options: IHoverOptions, focus?: boolean, skipLastFocusedUpdate?: boolean): IHoverWidget | undefined { - if (getHoverOptionsIdentity(this._currentHoverOptions) === getHoverOptionsIdentity(options)) { + showHover(options: IHoverOptions, focus?: boolean, skipLastFocusedUpdate?: boolean, dontShow?: boolean): IHoverWidget | undefined { + const hover = this._createHover(options, skipLastFocusedUpdate); + if (!hover) { + return undefined; + } + this._showHover(hover, options, focus); + return hover; + } + + showDelayedHover( + options: IHoverOptions, + lifecycleOptions: Pick, + ): IHoverWidget | undefined { + if (!this._currentDelayedHover || this._currentDelayedHoverWasShown) { + // Current hover is locked, reject + if (this._currentHover?.isLocked) { + return undefined; + } + + // Identity is the same, return current hover + if (getHoverOptionsIdentity(this._currentHoverOptions) === getHoverOptionsIdentity(options)) { + return this._currentHover; + } + + // Check group identity, if it's the same skip the delay and show the hover immediately + if (this._currentHover && !this._currentHover.isDisposed && this._currentDelayedHoverGroupId !== undefined && this._currentDelayedHoverGroupId === lifecycleOptions?.groupId) { + return this.showHover({ + ...options, + appearance: { + ...options.appearance, + skipFadeInAnimation: true + } + }); + } + } + + const hover = this._createHover(options, undefined); + if (!hover) { + this._currentDelayedHover = undefined; + this._currentDelayedHoverWasShown = false; + this._currentDelayedHoverGroupId = undefined; + return undefined; + } + + this._currentDelayedHover = hover; + this._currentDelayedHoverWasShown = false; + this._currentDelayedHoverGroupId = lifecycleOptions?.groupId; + + timeout(this._configurationService.getValue('workbench.hover.delay')).then(() => { + if (hover && !hover.isDisposed) { + this._currentDelayedHoverWasShown = true; + this._currentDelayedHoverWasShown = true; + this._showHover(hover, options); + } + }); + + return hover; + } + + setupDelayedHover( + target: HTMLElement, + options: (() => Omit) | Omit, + lifecycleOptions?: IHoverLifecycleOptions, + ): IDisposable { + const resolveHoverOptions = () => ({ + ...typeof options === 'function' ? options() : options, + target + } satisfies IHoverOptions); + return this._setupDelayedHover(target, resolveHoverOptions, lifecycleOptions); + } + + setupDelayedHoverAtMouse( + target: HTMLElement, + options: (() => Omit) | Omit, + lifecycleOptions?: IHoverLifecycleOptions, + ): IDisposable { + const resolveHoverOptions = (e?: MouseEvent) => ({ + ...typeof options === 'function' ? options() : options, + target: { + targetElements: [target], + x: e !== undefined ? e.x + 10 : undefined, + } + } satisfies IHoverOptions); + return this._setupDelayedHover(target, resolveHoverOptions, lifecycleOptions); + } + + private _setupDelayedHover( + target: HTMLElement, + resolveHoverOptions: ((e?: MouseEvent) => IHoverOptions), + lifecycleOptions?: IHoverLifecycleOptions, + ) { + const store = new DisposableStore(); + store.add(addDisposableListener(target, EventType.MOUSE_OVER, e => { + this.showDelayedHover(resolveHoverOptions(e), { + groupId: lifecycleOptions?.groupId + }); + })); + if (lifecycleOptions?.setupKeyboardEvents) { + store.add(addDisposableListener(target, EventType.KEY_DOWN, e => { + const evt = new StandardKeyboardEvent(e); + if (evt.equals(KeyCode.Space) || evt.equals(KeyCode.Enter)) { + this.showHover(resolveHoverOptions(), true); + } + })); + } + + this._delayedHovers.set(target, { show: (focus: boolean) => { this.showHover(resolveHoverOptions(), focus); } }); + store.add(toDisposable(() => this._delayedHovers.delete(target))); + + return store; + } + + private _createHover(options: IHoverOptions, skipLastFocusedUpdate?: boolean): HoverWidget | undefined { + this._currentDelayedHover = undefined; + + if (this._currentHover?.isLocked) { return undefined; } - if (this._currentHover && this._currentHoverOptions?.persistence?.sticky) { + if (getHoverOptionsIdentity(this._currentHoverOptions) === getHoverOptionsIdentity(options)) { return undefined; } this._currentHoverOptions = options; @@ -69,11 +203,30 @@ export class HoverService extends Disposable implements IHoverService { this._lastFocusedElementBeforeOpen = undefined; } } + + // Set `id` to default if it's undefined + if (options.id === undefined) { + options.id = isHTMLElement(options.content) + ? undefined + : typeof options.content === 'string' + ? options.content.toString() + : options.content.value; + } + const hoverDisposables = new DisposableStore(); const hover = this._instantiationService.createInstance(HoverWidget, options); if (options.persistence?.sticky) { hover.isLocked = true; } + + // Adjust target position when a mouse event is provided as the hover position + if (options.position?.hoverPosition && !isNumber(options.position.hoverPosition)) { + options.target = { + targetElements: isHTMLElement(options.target) ? [options.target] : options.target.targetElements, + x: options.position.hoverPosition.x + 10 + }; + } + hover.onDispose(() => { const hoverWasFocused = this._currentHover?.domNode && isAncestorOfActiveElement(this._currentHover.domNode); if (hoverWasFocused) { @@ -83,8 +236,8 @@ export class HoverService extends Disposable implements IHoverService { // Only clear the current options if it's the current hover, the current options help // reduce flickering when the same hover is shown multiple times - if (this._currentHoverOptions === options) { - this._currentHoverOptions = undefined; + if (getHoverOptionsIdentity(this._currentHoverOptions) === getHoverOptionsIdentity(options)) { + this.doHideHover(); } hoverDisposables.dispose(); }, undefined, hoverDisposables); @@ -94,10 +247,6 @@ export class HoverService extends Disposable implements IHoverService { options.container = this._layoutService.getContainer(getWindow(targetElement)); } - this._contextViewHandler.showContextView( - new HoverContextViewDelegate(hover, focus), - options.container - ); hover.onRequestLayout(() => this._contextViewHandler.layout(), undefined, hoverDisposables); if (options.persistence?.sticky) { hoverDisposables.add(addDisposableListener(getWindow(options.container).document, EventType.MOUSE_DOWN, e => { @@ -135,8 +284,15 @@ export class HoverService extends Disposable implements IHoverService { return hover; } - hideHover(): void { - if (this._currentHover?.isLocked || !this._currentHoverOptions) { + private _showHover(hover: HoverWidget, options: IHoverOptions, focus?: boolean) { + this._contextViewHandler.showContextView( + new HoverContextViewDelegate(hover, focus), + options.container + ); + } + + hideHover(force?: boolean): void { + if ((!force && this._currentHover?.isLocked) || !this._currentHoverOptions) { return; } this.doHideHover(); @@ -162,6 +318,21 @@ export class HoverService extends Disposable implements IHoverService { this.showHover(this._lastHoverOptions, true, true); } + private _showAndFocusHoverForActiveElement(): void { + // TODO: if hover is visible, focus it to avoid flickering + + let activeElement = getActiveElement() as HTMLElement | null; + while (activeElement) { + const hover = this._delayedHovers.get(activeElement) ?? this._managedHovers.get(activeElement); + if (hover) { + hover.show(true); + return; + } + + activeElement = activeElement.parentElement; + } + } + private _keyDown(e: KeyboardEvent, hover: HoverWidget, hideOnKeyDown: boolean) { if (e.key === 'Alt') { hover.isLocked = true; @@ -189,12 +360,9 @@ export class HoverService extends Disposable implements IHoverService { } } - private readonly _managedHovers = new Map(); - // TODO: Investigate performance of this function. There seems to be a lot of content created // and thrown away on start up setupManagedHover(hoverDelegate: IHoverDelegate, targetElement: HTMLElement, content: IManagedHoverContentOrFactory, options?: IManagedHoverOptions | undefined): IManagedHover { - targetElement.setAttribute('custom-hover', 'true'); if (targetElement.title !== '') { @@ -231,25 +399,25 @@ export class HoverService extends Disposable implements IHoverService { }, delay); }; + const store = new DisposableStore(); let isMouseDown = false; - const mouseDownEmitter = addDisposableListener(targetElement, EventType.MOUSE_DOWN, () => { + store.add(addDisposableListener(targetElement, EventType.MOUSE_DOWN, () => { isMouseDown = true; hideHover(true, true); - }, true); - const mouseUpEmitter = addDisposableListener(targetElement, EventType.MOUSE_UP, () => { + }, true)); + store.add(addDisposableListener(targetElement, EventType.MOUSE_UP, () => { isMouseDown = false; - }, true); - const mouseLeaveEmitter = addDisposableListener(targetElement, EventType.MOUSE_LEAVE, (e: MouseEvent) => { + }, true)); + store.add(addDisposableListener(targetElement, EventType.MOUSE_LEAVE, (e: MouseEvent) => { isMouseDown = false; hideHover(false, (e).fromElement === targetElement); - }, true); - - const onMouseOver = (e: MouseEvent) => { + }, true)); + store.add(addDisposableListener(targetElement, EventType.MOUSE_OVER, (e: MouseEvent) => { if (hoverPreparation) { return; } - const toDispose: DisposableStore = new DisposableStore(); + const mouseOverStore: DisposableStore = new DisposableStore(); const target: IHoverDelegateTarget = { targetElements: [targetElement], @@ -263,18 +431,17 @@ export class HoverService extends Disposable implements IHoverService { hideHover(true, true); } }; - toDispose.add(addDisposableListener(targetElement, EventType.MOUSE_MOVE, onMouseMove, true)); + mouseOverStore.add(addDisposableListener(targetElement, EventType.MOUSE_MOVE, onMouseMove, true)); } - hoverPreparation = toDispose; + hoverPreparation = mouseOverStore; if ((isHTMLElement(e.target)) && getHoverTargetElement(e.target as HTMLElement, targetElement) !== targetElement) { return; // Do not show hover when the mouse is over another hover target } - toDispose.add(triggerShowHover(hoverDelegate.delay, false, target)); - }; - const mouseOverDomEmitter = addDisposableListener(targetElement, EventType.MOUSE_OVER, onMouseOver, true); + mouseOverStore.add(triggerShowHover(typeof hoverDelegate.delay === 'function' ? hoverDelegate.delay(content) : hoverDelegate.delay, false, target)); + }, true)); const onFocus = () => { if (isMouseDown || hoverPreparation) { @@ -287,14 +454,13 @@ export class HoverService extends Disposable implements IHoverService { const toDispose: DisposableStore = new DisposableStore(); const onBlur = () => hideHover(true, true); toDispose.add(addDisposableListener(targetElement, EventType.BLUR, onBlur, true)); - toDispose.add(triggerShowHover(hoverDelegate.delay, false, target)); + toDispose.add(triggerShowHover(typeof hoverDelegate.delay === 'function' ? hoverDelegate.delay(content) : hoverDelegate.delay, false, target)); hoverPreparation = toDispose; }; // Do not show hover when focusing an input or textarea - let focusDomEmitter: undefined | IDisposable; if (!isEditableElement(targetElement)) { - focusDomEmitter = addDisposableListener(targetElement, EventType.FOCUS, onFocus, true); + store.add(addDisposableListener(targetElement, EventType.FOCUS, onFocus, true)); } const hover: IManagedHover = { @@ -311,11 +477,7 @@ export class HoverService extends Disposable implements IHoverService { }, dispose: () => { this._managedHovers.delete(targetElement); - mouseOverDomEmitter.dispose(); - mouseLeaveEmitter.dispose(); - mouseDownEmitter.dispose(); - mouseUpEmitter.dispose(); - focusDomEmitter?.dispose(); + store.dispose(); hideHover(true, true); } }; diff --git a/src/vs/editor/browser/services/hoverService/hoverWidget.ts b/src/vs/editor/browser/services/hoverService/hoverWidget.ts index b46758c352f..c33296a34e6 100644 --- a/src/vs/editor/browser/services/hoverService/hoverWidget.ts +++ b/src/vs/editor/browser/services/hoverService/hoverWidget.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import './hover.css'; -import { DisposableStore } from '../../../../base/common/lifecycle.js'; +import { DisposableStore, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { Event, Emitter } from '../../../../base/common/event.js'; import * as dom from '../../../../base/browser/dom.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; @@ -23,6 +23,8 @@ import { isMacintosh } from '../../../../base/common/platform.js'; import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; import { status } from '../../../../base/browser/ui/aria/aria.js'; import type { IHoverOptions, IHoverTarget, IHoverWidget } from '../../../../base/browser/ui/hover/hover.js'; +import { TimeoutTimer } from '../../../../base/common/async.js'; +import { isNumber } from '../../../../base/common/types.js'; const $ = dom.$; type TargetRect = { @@ -110,14 +112,11 @@ export class HoverWidget extends Widget implements IHoverWidget { this._target = 'targetElements' in options.target ? options.target : new ElementHoverTarget(options.target); this._hoverPointer = options.appearance?.showPointer ? $('div.workbench-hover-pointer') : undefined; - this._hover = this._register(new BaseHoverWidget()); - this._hover.containerDomNode.classList.add('workbench-hover', 'fadeIn'); + this._hover = this._register(new BaseHoverWidget(!options.appearance?.skipFadeInAnimation)); + this._hover.containerDomNode.classList.add('workbench-hover'); if (options.appearance?.compact) { this._hover.containerDomNode.classList.add('workbench-hover', 'compact'); } - if (options.appearance?.skipFadeInAnimation) { - this._hover.containerDomNode.classList.add('skip-fade-in'); - } if (options.additionalClasses) { this._hover.containerDomNode.classList.add(...options.additionalClasses); } @@ -128,7 +127,12 @@ export class HoverWidget extends Widget implements IHoverWidget { this._enableFocusTraps = true; } - this._hoverPosition = options.position?.hoverPosition ?? HoverPosition.ABOVE; + // Default to position above when the position is unspecified or a mouse event + this._hoverPosition = options.position?.hoverPosition === undefined + ? HoverPosition.ABOVE + : isNumber(options.position.hoverPosition) + ? options.position.hoverPosition + : HoverPosition.BELOW; // Don't allow mousedown out of the widget, otherwise preventDefault will call and text will // not be selected. @@ -161,7 +165,7 @@ export class HoverWidget extends Widget implements IHoverWidget { { codeBlockFontFamily: this._configurationService.getValue('editor').fontFamily || EDITOR_FONT_DEFAULTS.fontFamily } ); - const { element } = mdRenderer.render(markdown, { + const { element, dispose } = mdRenderer.render(markdown, { actionHandler: { callback: (content) => this._linkHandler(content), disposables: this._messageListeners @@ -174,6 +178,7 @@ export class HoverWidget extends Widget implements IHoverWidget { } }); contentsElement.appendChild(element); + this._register(toDisposable(dispose)); } rowElement.appendChild(contentsElement); this._hover.contentsDomNode.appendChild(rowElement); @@ -184,7 +189,7 @@ export class HoverWidget extends Widget implements IHoverWidget { options.actions.forEach(action => { const keybinding = this._keybindingService.lookupKeybinding(action.commandId); const keybindingLabel = keybinding ? keybinding.getLabel() : null; - HoverAction.render(actionsElement, { + this._register(HoverAction.render(actionsElement, { label: action.label, commandId: action.commandId, run: e => { @@ -192,7 +197,7 @@ export class HoverWidget extends Widget implements IHoverWidget { this.dispose(); }, iconClass: action.iconClass - }, keybindingLabel); + }, keybindingLabel)); }); statusBarElement.appendChild(actionsElement); this._hover.containerDomNode.appendChild(statusBarElement); @@ -395,7 +400,6 @@ export class HoverWidget extends Widget implements IHoverWidget { this.setHoverPointerPosition(targetRect); } - this._hover.onContentsChanged(); } @@ -623,9 +627,9 @@ export class HoverWidget extends Widget implements IHoverWidget { public override dispose(): void { if (!this._isDisposed) { this._onDispose.fire(); + this._target.dispose?.(); this._hoverContainer.remove(); this._messageListeners.dispose(); - this._target.dispose(); super.dispose(); } this._isDisposed = true; @@ -634,43 +638,41 @@ export class HoverWidget extends Widget implements IHoverWidget { class CompositeMouseTracker extends Widget { private _isMouseIn: boolean = true; - private _mouseTimeout: number | undefined; + private readonly _mouseTimer: MutableDisposable = this._register(new MutableDisposable()); private readonly _onMouseOut = this._register(new Emitter()); get onMouseOut(): Event { return this._onMouseOut.event; } get isMouseIn(): boolean { return this._isMouseIn; } + /** + * @param _elements The target elements to track mouse in/out events on. + * @param _eventDebounceDelay The delay in ms to debounce the event firing. This is used to + * allow a short period for the mouse to move into the hover or a nearby target element. For + * example hovering a scroll bar will not hide the hover immediately. + */ constructor( - private _elements: HTMLElement[] + private _elements: HTMLElement[], + private _eventDebounceDelay: number = 200 ) { super(); - this._elements.forEach(n => this.onmouseover(n, () => this._onTargetMouseOver(n))); - this._elements.forEach(n => this.onmouseleave(n, () => this._onTargetMouseLeave(n))); + + for (const element of this._elements) { + this.onmouseover(element, () => this._onTargetMouseOver()); + this.onmouseleave(element, () => this._onTargetMouseLeave()); + } } - private _onTargetMouseOver(target: HTMLElement): void { + private _onTargetMouseOver(): void { this._isMouseIn = true; - this._clearEvaluateMouseStateTimeout(target); + this._mouseTimer.clear(); } - private _onTargetMouseLeave(target: HTMLElement): void { + private _onTargetMouseLeave(): void { this._isMouseIn = false; - this._evaluateMouseState(target); - } - - private _evaluateMouseState(target: HTMLElement): void { - this._clearEvaluateMouseStateTimeout(target); // Evaluate whether the mouse is still outside asynchronously such that other mouse targets // have the opportunity to first their mouse in event. - this._mouseTimeout = dom.getWindow(target).setTimeout(() => this._fireIfMouseOutside(), 0); - } - - private _clearEvaluateMouseStateTimeout(target: HTMLElement): void { - if (this._mouseTimeout) { - dom.getWindow(target).clearTimeout(this._mouseTimeout); - this._mouseTimeout = undefined; - } + this._mouseTimer.value = new TimeoutTimer(() => this._fireIfMouseOutside(), this._eventDebounceDelay); } private _fireIfMouseOutside(): void { diff --git a/src/vs/editor/browser/services/hoverService/updatableHoverWidget.ts b/src/vs/editor/browser/services/hoverService/updatableHoverWidget.ts index c5b656b99d3..19a333e0035 100644 --- a/src/vs/editor/browser/services/hoverService/updatableHoverWidget.ts +++ b/src/vs/editor/browser/services/hoverService/updatableHoverWidget.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { isHTMLElement } from '../../../../base/browser/dom.js'; -import type { IHoverWidget, IManagedHoverContent, IManagedHoverOptions } from '../../../../base/browser/ui/hover/hover.js'; +import { isManagedHoverTooltipMarkdownString, type IHoverWidget, type IManagedHoverContent, type IManagedHoverOptions } from '../../../../base/browser/ui/hover/hover.js'; import type { IHoverDelegate, IHoverDelegateOptions, IHoverDelegateTarget } from '../../../../base/browser/ui/hover/hoverDelegate.js'; import { HoverPosition } from '../../../../base/browser/ui/hover/hoverWidget.js'; import { CancellationTokenSource } from '../../../../base/common/cancellation.js'; @@ -20,8 +20,7 @@ export class ManagedHoverWidget implements IDisposable { private _hoverWidget: IHoverWidget | undefined; private _cancellationTokenSource: CancellationTokenSource | undefined; - constructor(private hoverDelegate: IHoverDelegate, private target: IHoverDelegateTarget | HTMLElement, private fadeInAnimation: boolean) { - } + constructor(private hoverDelegate: IHoverDelegate, private target: IHoverDelegateTarget | HTMLElement, private fadeInAnimation: boolean) { } async update(content: IManagedHoverContent, focus?: boolean, options?: IManagedHoverOptions): Promise { if (this._cancellationTokenSource) { @@ -33,25 +32,37 @@ export class ManagedHoverWidget implements IDisposable { return; } - let resolvedContent; - if (content === undefined || isString(content) || isHTMLElement(content)) { + let resolvedContent: string | HTMLElement | IMarkdownString | undefined; + if (isString(content) || isHTMLElement(content) || content === undefined) { resolvedContent = content; - } else if (!isFunction(content.markdown)) { - resolvedContent = content.markdown ?? content.markdownNotSupportedFallback; } else { // compute the content, potentially long-running - // show 'Loading' if no hover is up yet - if (!this._hoverWidget) { - this.show(localize('iconLabel.loading', "Loading..."), focus, options); + this._cancellationTokenSource = new CancellationTokenSource(); + const token = this._cancellationTokenSource.token; + + let managedContent; + if (isManagedHoverTooltipMarkdownString(content)) { + if (isFunction(content.markdown)) { + managedContent = content.markdown(token).then(resolvedContent => resolvedContent ?? content.markdownNotSupportedFallback); + } else { + managedContent = content.markdown ?? content.markdownNotSupportedFallback; + } + } else { + managedContent = content.element(token); } // compute the content - this._cancellationTokenSource = new CancellationTokenSource(); - const token = this._cancellationTokenSource.token; - resolvedContent = await content.markdown(token); - if (resolvedContent === undefined) { - resolvedContent = content.markdownNotSupportedFallback; + if (managedContent instanceof Promise) { + + // show 'Loading' if no hover is up yet + if (!this._hoverWidget) { + this.show(localize('iconLabel.loading', "Loading..."), focus, options); + } + + resolvedContent = await managedContent; + } else { + resolvedContent = managedContent; } if (this.isDisposed || token.isCancellationRequested) { diff --git a/src/vs/editor/browser/services/inlineDiffService/inlineDiffService.ts b/src/vs/editor/browser/services/inlineDiffService/inlineDiffService.ts deleted file mode 100644 index ece69c5854c..00000000000 --- a/src/vs/editor/browser/services/inlineDiffService/inlineDiffService.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { Disposable } from '../../../../base/common/lifecycle.js'; -import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'; -import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { IModelDeltaDecoration } from '../../../common/model.js'; -import { ICodeEditor, IViewZone } from '../../editorBrowser.js'; -import { IRange } from '../../../common/core/range.js'; -import { EditorOption } from '../../../common/config/editorOptions.js'; - - -// THIS FILE IS OLD + UNUSED!!! - -// SEE inlineDiffsService.ts INSTEAD. - -export interface IInlineDiffService { - readonly _serviceBrand: undefined; - addDiff(editor: ICodeEditor, originalText: string, modifiedRange: IRange): void; - removeDiffs(editor: ICodeEditor): void; -} - -export const IInlineDiffService = createDecorator('inlineDiffServiceOld'); - -class InlineDiffService extends Disposable implements IInlineDiffService { - private readonly _diffDecorations = new Map(); - private readonly _diffZones = new Map(); - _serviceBrand: undefined; - - constructor() { - super(); - } - - initStream() { - - - } - - - public addDiff: IInlineDiffService['addDiff'] = (editor, originalText, modifiedRange) => { - // Clear existing diffs - this.removeDiffs(editor); - - // green decoration and gutter decoration - const greenDecoration: IModelDeltaDecoration[] = [{ - range: modifiedRange, - options: { - className: 'line-insert', // .monaco-editor .line-insert - description: 'line-insert', - isWholeLine: true, - minimap: { - color: { id: 'minimapGutter.addedBackground' }, - position: 2 - }, - overviewRuler: { - color: { id: 'editorOverviewRuler.addedForeground' }, - position: 7 - } - } - }]; - - this._diffDecorations.set(editor, editor.deltaDecorations([], greenDecoration)); - - // red in a view zone - editor.changeViewZones(accessor => { - // Get the editor's font info - const fontInfo = editor.getOption(EditorOption.fontInfo); - - const domNode = document.createElement('div'); - // domNode.className = 'monaco-editor view-zones line-delete monaco-mouse-cursor-text'; - domNode.style.fontSize = `${fontInfo.fontSize}px`; - domNode.style.fontFamily = fontInfo.fontFamily; - domNode.style.lineHeight = `${fontInfo.lineHeight}px`; - - // div - const lineContent = document.createElement('div'); - // lineContent.className = 'view-line'; // .monaco-editor .inline-deleted-text - - // span - const contentSpan = document.createElement('span'); - - // span - const codeSpan = document.createElement('span'); - // codeSpan.className = 'mtk1'; // char-delete - codeSpan.textContent = originalText; - - // Mount - contentSpan.appendChild(codeSpan); - lineContent.appendChild(contentSpan); - domNode.appendChild(lineContent); - - const viewZone: IViewZone = { - afterLineNumber: modifiedRange.startLineNumber - 1, - heightInLines: originalText.split('\n').length + 1, - domNode: domNode, - suppressMouseDown: true, - marginDomNode: this.createGutterElement() - }; - - const zoneId = accessor.addZone(viewZone); - // editor.layout(); - this._diffZones.set(editor, [zoneId]); - }); - } - - // gutter is the thing to the left - private createGutterElement(): HTMLElement { - const gutterDiv = document.createElement('div'); - gutterDiv.className = 'inline-diff-gutter'; - - const minusDiv = document.createElement('div'); - minusDiv.className = 'inline-diff-deleted-gutter'; - // minusDiv.textContent = '-'; - - gutterDiv.appendChild(minusDiv); - return gutterDiv; - } - - public removeDiffs(editor: ICodeEditor): void { - const decorationIds = this._diffDecorations.get(editor) || []; - editor.deltaDecorations(decorationIds, []); - this._diffDecorations.delete(editor); - - editor.changeViewZones(accessor => { - const zoneIds = this._diffZones.get(editor) || []; - zoneIds.forEach(id => accessor.removeZone(id)); - }); - this._diffZones.delete(editor); - } - - override dispose(): void { - super.dispose(); - this._diffDecorations.clear(); - this._diffZones.clear(); - } -} - -registerSingleton(IInlineDiffService, InlineDiffService, InstantiationType.Eager); diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index 870f27bdec7..64765857710 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -73,6 +73,7 @@ class EditorOpener implements IOpener { if (typeof target === 'string') { target = URI.parse(target); } + const { selection, uri } = extractSelection(target); target = uri; @@ -169,13 +170,15 @@ export class OpenerService implements IOpenerService { } async open(target: URI | string, options?: OpenOptions): Promise { + // check with contributed validators - const targetURI = typeof target === 'string' ? URI.parse(target) : target; - // validate against the original URI that this URI resolves to, if one exists - const validationTarget = this._resolvedUriTargets.get(targetURI) ?? target; - for (const validator of this._validators) { - if (!(await validator.shouldOpen(validationTarget, options))) { - return false; + if (!options?.skipValidation) { + const targetURI = typeof target === 'string' ? URI.parse(target) : target; + const validationTarget = this._resolvedUriTargets.get(targetURI) ?? target; // validate against the original URI that this URI resolves to, if one exists + for (const validator of this._validators) { + if (!(await validator.shouldOpen(validationTarget, options))) { + return false; + } } } diff --git a/src/vs/editor/browser/services/treeSitter/treeSitterParserService.ts b/src/vs/editor/browser/services/treeSitter/treeSitterParserService.ts deleted file mode 100644 index a83446f2d27..00000000000 --- a/src/vs/editor/browser/services/treeSitter/treeSitterParserService.ts +++ /dev/null @@ -1,502 +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 type { Parser } from '@vscode/tree-sitter-wasm'; -import { AppResourcePath, FileAccess, nodeModulesAsarUnpackedPath, nodeModulesPath } from '../../../../base/common/network.js'; -import { EDITOR_EXPERIMENTAL_PREFER_TREESITTER, ITreeSitterParserService, ITreeSitterParseResult } from '../../../common/services/treeSitterParserService.js'; -import { IModelService } from '../../../common/services/model.js'; -import { Disposable, DisposableMap, DisposableStore, dispose, IDisposable } from '../../../../base/common/lifecycle.js'; -import { ITextModel } from '../../../common/model.js'; -import { IFileService } from '../../../../platform/files/common/files.js'; -import { IModelContentChange } from '../../../common/textModelEvents.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; -import { ILogService } from '../../../../platform/log/common/log.js'; -import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { setTimeout0 } from '../../../../base/common/platform.js'; -import { importAMDNodeModule } from '../../../../amdX.js'; -import { Emitter, Event } from '../../../../base/common/event.js'; -import { CancellationToken, cancelOnDispose } from '../../../../base/common/cancellation.js'; -import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; -import { canASAR } from '../../../../base/common/amd.js'; -import { CancellationError, isCancellationError } from '../../../../base/common/errors.js'; -import { PromiseResult } from '../../../../base/common/observable.js'; -import { Range } from '../../../common/core/range.js'; - -const EDITOR_TREESITTER_TELEMETRY = 'editor.experimental.treeSitterTelemetry'; -const MODULE_LOCATION_SUBPATH = `@vscode/tree-sitter-wasm/wasm`; -const FILENAME_TREESITTER_WASM = `tree-sitter.wasm`; - -function getModuleLocation(environmentService: IEnvironmentService): AppResourcePath { - return `${(canASAR && environmentService.isBuilt) ? nodeModulesAsarUnpackedPath : nodeModulesPath}/${MODULE_LOCATION_SUBPATH}`; -} - -export class TextModelTreeSitter extends Disposable { - private _onDidChangeParseResult: Emitter = this._register(new Emitter()); - public readonly onDidChangeParseResult: Event = this._onDidChangeParseResult.event; - private _parseResult: TreeSitterParseResult | undefined; - - get parseResult(): ITreeSitterParseResult | undefined { return this._parseResult; } - - constructor(readonly model: ITextModel, - private readonly _treeSitterLanguages: TreeSitterLanguages, - private readonly _treeSitterImporter: TreeSitterImporter, - private readonly _logService: ILogService, - private readonly _telemetryService: ITelemetryService - ) { - super(); - this._register(Event.runAndSubscribe(this.model.onDidChangeLanguage, (e => this._onDidChangeLanguage(e ? e.newLanguage : this.model.getLanguageId())))); - } - - private readonly _languageSessionDisposables = this._register(new DisposableStore()); - /** - * Be very careful when making changes to this method as it is easy to introduce race conditions. - */ - private async _onDidChangeLanguage(languageId: string) { - this._languageSessionDisposables.clear(); - this._parseResult = undefined; - - const token = cancelOnDispose(this._languageSessionDisposables); - let language: Parser.Language | undefined; - try { - language = await this._getLanguage(languageId, token); - } catch (e) { - if (isCancellationError(e)) { - return; - } - throw e; - } - - const Parser = await this._treeSitterImporter.getParserClass(); - if (token.isCancellationRequested) { - return; - } - - const treeSitterTree = this._languageSessionDisposables.add(new TreeSitterParseResult(new Parser(), language, this._logService, this._telemetryService)); - this._languageSessionDisposables.add(this.model.onDidChangeContent(e => this._onDidChangeContent(treeSitterTree, e.changes))); - await this._onDidChangeContent(treeSitterTree, []); - if (token.isCancellationRequested) { - return; - } - - this._parseResult = treeSitterTree; - } - - private _getLanguage(languageId: string, token: CancellationToken): Promise { - const language = this._treeSitterLanguages.getOrInitLanguage(languageId); - if (language) { - return Promise.resolve(language); - } - const disposables: IDisposable[] = []; - - return new Promise((resolve, reject) => { - disposables.push(this._treeSitterLanguages.onDidAddLanguage(e => { - if (e.id === languageId) { - dispose(disposables); - resolve(e.language); - } - })); - token.onCancellationRequested(() => { - dispose(disposables); - reject(new CancellationError()); - }, undefined, disposables); - }); - } - - private async _onDidChangeContent(treeSitterTree: TreeSitterParseResult, changes: IModelContentChange[]) { - const diff = await treeSitterTree.onDidChangeContent(this.model, changes); - if (diff && diff.length > 0) { - // Tree sitter is 0 based, text model is 1 based - this._onDidChangeParseResult.fire(diff.map(r => new Range(r.startPosition.row + 1, r.startPosition.column + 1, r.endPosition.row + 1, r.endPosition.column + 1))); - } - } -} - -const enum TelemetryParseType { - Full = 'fullParse', - Incremental = 'incrementalParse' -} - -export class TreeSitterParseResult implements IDisposable, ITreeSitterParseResult { - private _tree: Parser.Tree | undefined; - private _isDisposed: boolean = false; - constructor(public readonly parser: Parser, - public /** exposed for tests **/ readonly language: Parser.Language, - private readonly _logService: ILogService, - private readonly _telemetryService: ITelemetryService) { - this.parser.setTimeoutMicros(50 * 1000); // 50 ms - this.parser.setLanguage(language); - } - dispose(): void { - this._isDisposed = true; - this._tree?.delete(); - this.parser?.delete(); - } - get tree() { return this._tree; } - private set tree(newTree: Parser.Tree | undefined) { - this._tree?.delete(); - this._tree = newTree; - } - get isDisposed() { return this._isDisposed; } - - private _onDidChangeContentQueue: Promise = Promise.resolve(); - public async onDidChangeContent(model: ITextModel, changes: IModelContentChange[]): Promise { - const oldTree = this.tree?.copy(); - this._applyEdits(model, changes); - return new Promise(resolve => { - this._onDidChangeContentQueue = this._onDidChangeContentQueue.then(async () => { - if (this.isDisposed) { - // No need to continue the queue if we are disposed - return; - } - await this._parseAndUpdateTree(model); - resolve(this.tree ? oldTree?.getChangedRanges(this.tree) : undefined); - - }).catch((e) => { - this._logService.error('Error parsing tree-sitter tree', e); - }); - }); - } - - private _newEdits = true; - private _applyEdits(model: ITextModel, changes: IModelContentChange[]) { - for (const change of changes) { - const newEndOffset = change.rangeOffset + change.text.length; - const newEndPosition = model.getPositionAt(newEndOffset); - - this.tree?.edit({ - startIndex: change.rangeOffset, - oldEndIndex: change.rangeOffset + change.rangeLength, - newEndIndex: change.rangeOffset + change.text.length, - startPosition: { row: change.range.startLineNumber - 1, column: change.range.startColumn - 1 }, - oldEndPosition: { row: change.range.endLineNumber - 1, column: change.range.endColumn - 1 }, - newEndPosition: { row: newEndPosition.lineNumber - 1, column: newEndPosition.column - 1 } - }); - this._newEdits = true; - } - } - - private async _parseAndUpdateTree(model: ITextModel) { - const tree = await this._parse(model); - if (!this._newEdits) { - this.tree = tree; - } - } - - private _parse(model: ITextModel): Promise { - let parseType: TelemetryParseType = TelemetryParseType.Full; - if (this.tree) { - parseType = TelemetryParseType.Incremental; - } - return this._parseAndYield(model, parseType); - } - - private async _parseAndYield(model: ITextModel, parseType: TelemetryParseType): Promise { - const language = model.getLanguageId(); - let tree: Parser.Tree | undefined; - let time: number = 0; - let passes: number = 0; - this._newEdits = false; - do { - const timer = performance.now(); - try { - tree = this.parser.parse((index: number, position?: Parser.Point) => this._parseCallback(model, index), this.tree); - } catch (e) { - // parsing can fail when the timeout is reached, will resume upon next loop - } finally { - time += performance.now() - timer; - passes++; - } - - // Even if the model changes and edits are applied, the tree parsing will continue correctly after the await. - await new Promise(resolve => setTimeout0(resolve)); - - if (model.isDisposed() || this.isDisposed) { - return; - } - } while (!tree && !this._newEdits); // exit if there a new edits, as anhy parsing done while there are new edits is throw away work - this.sendParseTimeTelemetry(parseType, language, time, passes); - return tree; - } - - private _parseCallback(textModel: ITextModel, index: number): string | null { - try { - return textModel.getTextBuffer().getNearestChunk(index); - } catch (e) { - this._logService.debug('Error getting chunk for tree-sitter parsing', e); - } - return null; - } - - private sendParseTimeTelemetry(parseType: TelemetryParseType, languageId: string, time: number, passes: number): void { - this._logService.debug(`Tree parsing (${parseType}) took ${time} ms and ${passes} passes.`); - type ParseTimeClassification = { - owner: 'alros'; - comment: 'Used to understand how long it takes to parse a tree-sitter tree'; - languageId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The programming language ID.' }; - time: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The ms it took to parse' }; - passes: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The number of passes it took to parse' }; - }; - if (parseType === TelemetryParseType.Full) { - this._telemetryService.publicLog2<{ languageId: string; time: number; passes: number }, ParseTimeClassification>(`treeSitter.fullParse`, { languageId, time, passes }); - } else { - this._telemetryService.publicLog2<{ languageId: string; time: number; passes: number }, ParseTimeClassification>(`treeSitter.incrementalParse`, { languageId, time, passes }); - } - } -} - -export class TreeSitterLanguages extends Disposable { - private _languages: AsyncCache = new AsyncCache(); - public /*exposed for tests*/ readonly _onDidAddLanguage: Emitter<{ id: string; language: Parser.Language }> = this._register(new Emitter()); - /** - * If you're looking for a specific language, make sure to check if it already exists with `getLanguage` as it will kick off the process to add it if it doesn't exist. - */ - public readonly onDidAddLanguage: Event<{ id: string; language: Parser.Language }> = this._onDidAddLanguage.event; - - constructor(private readonly _treeSitterImporter: TreeSitterImporter, - private readonly _fileService: IFileService, - private readonly _environmentService: IEnvironmentService, - private readonly _registeredLanguages: Map, - ) { - super(); - } - - public getOrInitLanguage(languageId: string): Parser.Language | undefined { - if (this._languages.isCached(languageId)) { - return this._languages.getSyncIfCached(languageId); - } else { - // kick off adding the language, but don't wait - this._addLanguage(languageId); - return undefined; - } - } - - private async _addLanguage(languageId: string): Promise { - const languagePromise = this._languages.get(languageId); - if (!languagePromise) { - this._languages.set(languageId, this._fetchLanguage(languageId)); - const language = await this._languages.get(languageId); - if (!language) { - return undefined; - } - this._onDidAddLanguage.fire({ id: languageId, language }); - } - } - - private async _fetchLanguage(languageId: string): Promise { - const grammarName = this._registeredLanguages.get(languageId); - const languageLocation = this._getLanguageLocation(languageId); - if (!grammarName || !languageLocation) { - return undefined; - } - const wasmPath: AppResourcePath = `${languageLocation}/${grammarName}.wasm`; - const languageFile = await (this._fileService.readFile(FileAccess.asFileUri(wasmPath))); - const Parser = await this._treeSitterImporter.getParserClass(); - return Parser.Language.load(languageFile.value.buffer); - } - - private _getLanguageLocation(languageId: string): AppResourcePath | undefined { - const grammarName = this._registeredLanguages.get(languageId); - if (!grammarName) { - return undefined; - } - return getModuleLocation(this._environmentService); - } -} - -export class TreeSitterImporter { - private _treeSitterImport: typeof import('@vscode/tree-sitter-wasm') | undefined; - private async _getTreeSitterImport() { - if (!this._treeSitterImport) { - this._treeSitterImport = await importAMDNodeModule('@vscode/tree-sitter-wasm', 'wasm/tree-sitter.js'); - } - return this._treeSitterImport; - } - - private _parserClass: typeof Parser | undefined; - public async getParserClass() { - if (!this._parserClass) { - this._parserClass = (await this._getTreeSitterImport()).Parser; - } - return this._parserClass; - } -} - -interface TextModelTreeSitterItem { - dispose(): void; - textModelTreeSitter: TextModelTreeSitter; - disposables: DisposableStore; -} - -export class TreeSitterTextModelService extends Disposable implements ITreeSitterParserService { - readonly _serviceBrand: undefined; - private _init!: Promise; - private _textModelTreeSitters: DisposableMap = this._register(new DisposableMap()); - private readonly _registeredLanguages: Map = new Map(); - private readonly _treeSitterImporter: TreeSitterImporter = new TreeSitterImporter(); - private readonly _treeSitterLanguages: TreeSitterLanguages; - - public readonly onDidAddLanguage: Event<{ id: string; language: Parser.Language }>; - private _onDidUpdateTree: Emitter<{ textModel: ITextModel; ranges: Range[] }> = this._register(new Emitter()); - public readonly onDidUpdateTree: Event<{ textModel: ITextModel; ranges: Range[] }> = this._onDidUpdateTree.event; - - constructor(@IModelService private readonly _modelService: IModelService, - @IFileService fileService: IFileService, - @ITelemetryService private readonly _telemetryService: ITelemetryService, - @ILogService private readonly _logService: ILogService, - @IConfigurationService private readonly _configurationService: IConfigurationService, - @IEnvironmentService private readonly _environmentService: IEnvironmentService - ) { - super(); - this._treeSitterLanguages = this._register(new TreeSitterLanguages(this._treeSitterImporter, fileService, this._environmentService, this._registeredLanguages)); - this.onDidAddLanguage = this._treeSitterLanguages.onDidAddLanguage; - this._register(this._configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(EDITOR_EXPERIMENTAL_PREFER_TREESITTER)) { - this._supportedLanguagesChanged(); - } - })); - this._supportedLanguagesChanged(); - } - - getOrInitLanguage(languageId: string): Parser.Language | undefined { - return this._treeSitterLanguages.getOrInitLanguage(languageId); - } - - getParseResult(textModel: ITextModel): ITreeSitterParseResult | undefined { - const textModelTreeSitter = this._textModelTreeSitters.get(textModel); - return textModelTreeSitter?.textModelTreeSitter.parseResult; - } - - private async _doInitParser() { - const Parser = await this._treeSitterImporter.getParserClass(); - const environmentService = this._environmentService; - await Parser.init({ - locateFile(_file: string, _folder: string) { - return FileAccess.asBrowserUri(`${getModuleLocation(environmentService)}/${FILENAME_TREESITTER_WASM}`).toString(true); - } - }); - return true; - } - - private _hasInit: boolean = false; - private async _initParser(hasLanguages: boolean): Promise { - if (this._hasInit) { - return this._init; - } - - if (hasLanguages) { - this._hasInit = true; - this._init = this._doInitParser(); - - // New init, we need to deal with all the existing text models and set up listeners - this._init.then(() => this._registerModelServiceListeners()); - } else { - this._init = Promise.resolve(false); - } - return this._init; - } - - private async _supportedLanguagesChanged() { - const setting = this._getSetting(); - - let hasLanguages = true; - if (setting.length === 0) { - hasLanguages = false; - } - - if (await this._initParser(hasLanguages)) { - // Eventually, this should actually use an extension point to add tree sitter grammars, but for now they are hard coded in core - if (setting.includes('typescript')) { - this._addGrammar('typescript', 'tree-sitter-typescript'); - } else { - this._removeGrammar('typescript'); - } - } - } - - private _getSetting(): string[] { - const setting = this._configurationService.getValue(EDITOR_EXPERIMENTAL_PREFER_TREESITTER); - if (setting && setting.length > 0) { - return setting; - } else { - const expSetting = this._configurationService.getValue(EDITOR_TREESITTER_TELEMETRY); - if (expSetting) { - return ['typescript']; - } - } - return []; - } - - private async _registerModelServiceListeners() { - this._register(this._modelService.onModelAdded(model => { - this._createTextModelTreeSitter(model); - })); - this._register(this._modelService.onModelRemoved(model => { - this._textModelTreeSitters.deleteAndDispose(model); - })); - this._modelService.getModels().forEach(model => this._createTextModelTreeSitter(model)); - } - - private _createTextModelTreeSitter(model: ITextModel) { - const textModelTreeSitter = new TextModelTreeSitter(model, this._treeSitterLanguages, this._treeSitterImporter, this._logService, this._telemetryService); - const disposables = new DisposableStore(); - disposables.add(textModelTreeSitter); - disposables.add(textModelTreeSitter.onDidChangeParseResult((ranges) => this._onDidUpdateTree.fire({ textModel: model, ranges }))); - this._textModelTreeSitters.set(model, { - textModelTreeSitter, - disposables, - dispose: disposables.dispose - }); - } - - private _addGrammar(languageId: string, grammarName: string) { - if (!this._registeredLanguages.has(languageId)) { - this._registeredLanguages.set(languageId, grammarName); - } - } - - private _removeGrammar(languageId: string) { - if (this._registeredLanguages.has(languageId)) { - this._registeredLanguages.delete('typescript'); - } - } -} - -class PromiseWithSyncAccess { - private _result: PromiseResult | undefined; - /** - * Returns undefined if the promise did not resolve yet. - */ - get result(): PromiseResult | undefined { - return this._result; - } - - constructor(public readonly promise: Promise) { - promise.then(result => { - this._result = new PromiseResult(result, undefined); - }).catch(e => { - this._result = new PromiseResult(undefined, e); - }); - } -} - -class AsyncCache { - private readonly _values = new Map>(); - - set(key: TKey, promise: Promise) { - this._values.set(key, new PromiseWithSyncAccess(promise)); - } - - get(key: TKey): Promise | undefined { - return this._values.get(key)?.promise; - } - - getSyncIfCached(key: TKey): T | undefined { - return this._values.get(key)?.result?.data; - } - - isCached(key: TKey): boolean { - return this._values.get(key)?.result !== undefined; - } -} diff --git a/src/vs/editor/browser/view.ts b/src/vs/editor/browser/view.ts index b55cfa1e0c1..e2a992cd339 100644 --- a/src/vs/editor/browser/view.ts +++ b/src/vs/editor/browser/view.ts @@ -57,10 +57,12 @@ import { IInstantiationService } from '../../platform/instantiation/common/insta import { IColorTheme, getThemeTypeSelector } from '../../platform/theme/common/themeService.js'; import { ViewGpuContext } from './gpu/viewGpuContext.js'; import { ViewLinesGpu } from './viewParts/viewLinesGpu/viewLinesGpu.js'; -import { AbstractEditContext } from './controller/editContext/editContextUtils.js'; +import { AbstractEditContext } from './controller/editContext/editContext.js'; import { IVisibleRangeProvider, TextAreaEditContext } from './controller/editContext/textArea/textAreaEditContext.js'; import { NativeEditContext } from './controller/editContext/native/nativeEditContext.js'; import { RulersGpu } from './viewParts/rulersGpu/rulersGpu.js'; +import { GpuMarkOverlay } from './viewParts/gpuMark/gpuMark.js'; +import { AccessibilitySupport } from '../../platform/accessibility/common/accessibility.js'; export interface IContentWidgetData { @@ -99,6 +101,7 @@ export class View extends ViewEventHandler { private readonly _viewController: ViewController; private _experimentalEditContextEnabled: boolean; + private _accessibilitySupport: AccessibilitySupport; private _editContext: AbstractEditContext; private readonly _pointerHandler: PointerHandler; @@ -110,8 +113,10 @@ export class View extends ViewEventHandler { // Actual mutable state private _shouldRecomputeGlyphMarginLanes: boolean = false; private _renderAnimationFrame: IDisposable | null; + private _ownerID: string; constructor( + ownerID: string, commandDelegate: ICommandDelegate, configuration: IEditorConfiguration, colorTheme: IColorTheme, @@ -121,9 +126,14 @@ export class View extends ViewEventHandler { @IInstantiationService private readonly _instantiationService: IInstantiationService ) { super(); + this._ownerID = ownerID; this._selections = [new Selection(1, 1, 1, 1)]; this._renderAnimationFrame = null; + this._overflowGuardContainer = createFastDomNode(document.createElement('div')); + PartFingerprints.write(this._overflowGuardContainer, PartFingerprint.OverflowGuard); + this._overflowGuardContainer.setClassName('overflow-guard'); + this._viewController = new ViewController(configuration, model, userInputEvents, commandDelegate); // The view context is passed on to most classes (basically to reduce param. counts in ctors) @@ -135,8 +145,9 @@ export class View extends ViewEventHandler { this._viewParts = []; // Keyboard handler - this._experimentalEditContextEnabled = this._context.configuration.options.get(EditorOption.experimentalEditContextEnabled); - this._editContext = this._instantiateEditContext(this._experimentalEditContextEnabled); + this._experimentalEditContextEnabled = this._context.configuration.options.get(EditorOption.effectiveExperimentalEditContextEnabled); + this._accessibilitySupport = this._context.configuration.options.get(EditorOption.accessibilitySupport); + this._editContext = this._instantiateEditContext(); this._viewParts.push(this._editContext); @@ -154,15 +165,11 @@ export class View extends ViewEventHandler { this._viewGpuContext = this._instantiationService.createInstance(ViewGpuContext, this._context); } - this._overflowGuardContainer = createFastDomNode(document.createElement('div')); - PartFingerprints.write(this._overflowGuardContainer, PartFingerprint.OverflowGuard); - this._overflowGuardContainer.setClassName('overflow-guard'); - this._scrollbar = new EditorScrollbar(this._context, this._linesContent, this.domNode, this._overflowGuardContainer); this._viewParts.push(this._scrollbar); // View Lines - this._viewLines = new ViewLines(this._context, this._linesContent); + this._viewLines = new ViewLines(this._context, this._viewGpuContext, this._linesContent); if (this._viewGpuContext) { this._viewLinesGpu = this._instantiationService.createInstance(ViewLinesGpu, this._context, this._viewGpuContext); } @@ -193,6 +200,9 @@ export class View extends ViewEventHandler { marginViewOverlays.addDynamicOverlay(new MarginViewLineDecorationsOverlay(this._context)); marginViewOverlays.addDynamicOverlay(new LinesDecorationsOverlay(this._context)); marginViewOverlays.addDynamicOverlay(new LineNumbersOverlay(this._context)); + if (this._viewGpuContext) { + marginViewOverlays.addDynamicOverlay(new GpuMarkOverlay(this._context, this._viewGpuContext)); + } // Glyph margin widgets this._glyphMarginWidgets = new GlyphMarginWidgets(this._context); @@ -247,7 +257,6 @@ export class View extends ViewEventHandler { this._overflowGuardContainer.appendChild(this._viewGpuContext.canvas); } this._overflowGuardContainer.appendChild(scrollDecoration.getDomNode()); - this._editContext.appendTo(this._overflowGuardContainer); this._overflowGuardContainer.appendChild(this._overlayWidgets.getDomNode()); this._overflowGuardContainer.appendChild(minimap.getDomNode()); this._overflowGuardContainer.appendChild(blockOutline.domNode); @@ -267,23 +276,32 @@ export class View extends ViewEventHandler { this._pointerHandler = this._register(new PointerHandler(this._context, this._viewController, this._createPointerHandlerHelper())); } - private _instantiateEditContext(experimentalEditContextEnabled: boolean): AbstractEditContext { - return this._instantiationService.createInstance(experimentalEditContextEnabled ? NativeEditContext : TextAreaEditContext, this._context, this._viewController, this._createTextAreaHandlerHelper()); + private _instantiateEditContext(): AbstractEditContext { + const usingExperimentalEditContext = this._context.configuration.options.get(EditorOption.effectiveExperimentalEditContextEnabled); + if (usingExperimentalEditContext) { + return this._instantiationService.createInstance(NativeEditContext, this._ownerID, this._context, this._overflowGuardContainer, this._viewController, this._createTextAreaHandlerHelper()); + } else { + return this._instantiationService.createInstance(TextAreaEditContext, this._context, this._overflowGuardContainer, this._viewController, this._createTextAreaHandlerHelper()); + } } private _updateEditContext(): void { - const experimentalEditContextEnabled = this._context.configuration.options.get(EditorOption.experimentalEditContextEnabled); - if (this._experimentalEditContextEnabled === experimentalEditContextEnabled) { + const experimentalEditContextEnabled = this._context.configuration.options.get(EditorOption.effectiveExperimentalEditContextEnabled); + const accessibilitySupport = this._context.configuration.options.get(EditorOption.accessibilitySupport); + if (this._experimentalEditContextEnabled === experimentalEditContextEnabled && this._accessibilitySupport === accessibilitySupport) { return; } this._experimentalEditContextEnabled = experimentalEditContextEnabled; + this._accessibilitySupport = accessibilitySupport; + const isEditContextFocused = this._editContext.isFocused(); + const indexOfEditContext = this._viewParts.indexOf(this._editContext); this._editContext.dispose(); - this._editContext = this._instantiateEditContext(experimentalEditContextEnabled); - this._editContext.appendTo(this._overflowGuardContainer); - // Replace the view parts with the new edit context - const indexOfEditContextHandler = this._viewParts.indexOf(this._editContext); - if (indexOfEditContextHandler !== -1) { - this._viewParts.splice(indexOfEditContextHandler, 1, this._editContext); + this._editContext = this._instantiateEditContext(); + if (isEditContextFocused) { + this._editContext.focus(); + } + if (indexOfEditContext !== -1) { + this._viewParts.splice(indexOfEditContext, 1, this._editContext); } } @@ -324,6 +342,7 @@ export class View extends ViewEventHandler { viewDomNode: this.domNode.domNode, linesContentDomNode: this._linesContent.domNode, viewLinesDomNode: this._viewLines.getDomNode().domNode, + viewLinesGpu: this._viewLinesGpu, focusTextArea: () => { this.focus(); @@ -354,11 +373,18 @@ export class View extends ViewEventHandler { visibleRangeForPosition: (lineNumber: number, column: number) => { this._flushAccumulatedAndRenderNow(); - return this._viewLines.visibleRangeForPosition(new Position(lineNumber, column)); + const position = new Position(lineNumber, column); + return this._viewLines.visibleRangeForPosition(position) ?? this._viewLinesGpu?.visibleRangeForPosition(position) ?? null; }, getLineWidth: (lineNumber: number) => { this._flushAccumulatedAndRenderNow(); + if (this._viewLinesGpu) { + const result = this._viewLinesGpu.getLineWidth(lineNumber); + if (result !== undefined) { + return result; + } + } return this._viewLines.getLineWidth(lineNumber); } }; @@ -437,6 +463,7 @@ export class View extends ViewEventHandler { } this._contentWidgets.overflowingContentWidgetsDomNode.domNode.remove(); + this._overlayWidgets.overflowingOverlayWidgetsDomNode.domNode.remove(); this._context.removeEventHandler(this); this._viewGpuContext?.dispose(); @@ -457,6 +484,10 @@ export class View extends ViewEventHandler { throw new BugIndicatingError(); } if (this._renderAnimationFrame === null) { + // TODO: workaround fix for https://github.com/microsoft/vscode/issues/229825 + if (this._editContext instanceof NativeEditContext) { + this._editContext.setEditContextOnDomNode(); + } const rendering = this._createCoordinatedRendering(); this._renderAnimationFrame = EditorRenderingCoordinator.INSTANCE.scheduleCoordinatedRendering({ window: dom.getWindow(this.domNode?.domNode), @@ -819,3 +850,4 @@ class EditorRenderingCoordinator { } } } + diff --git a/src/vs/editor/browser/view/renderingContext.ts b/src/vs/editor/browser/view/renderingContext.ts index 178f546082e..e5ec4b30eca 100644 --- a/src/vs/editor/browser/view/renderingContext.ts +++ b/src/vs/editor/browser/view/renderingContext.ts @@ -80,7 +80,18 @@ export class RenderingContext extends RestrictedRenderingContext { } public linesVisibleRangesForRange(range: Range, includeNewLines: boolean): LineVisibleRanges[] | null { - return this._viewLines.linesVisibleRangesForRange(range, includeNewLines) ?? this._viewLinesGpu?.linesVisibleRangesForRange(range, includeNewLines) ?? null; + const domRanges = this._viewLines.linesVisibleRangesForRange(range, includeNewLines); + if (!this._viewLinesGpu) { + return domRanges ?? null; + } + const gpuRanges = this._viewLinesGpu.linesVisibleRangesForRange(range, includeNewLines); + if (!domRanges) { + return gpuRanges; + } + if (!gpuRanges) { + return domRanges; + } + return domRanges.concat(gpuRanges).sort((a, b) => a.lineNumber - b.lineNumber); } public visibleRangeForPosition(position: Position): HorizontalPosition | null { diff --git a/src/vs/editor/browser/view/viewLayer.ts b/src/vs/editor/browser/view/viewLayer.ts index 7162a1bb939..4058205be2f 100644 --- a/src/vs/editor/browser/view/viewLayer.ts +++ b/src/vs/editor/browser/view/viewLayer.ts @@ -277,9 +277,20 @@ export class VisibleLinesCollection { return false; } - public onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + public onFlushed(e: viewEvents.ViewFlushedEvent, flushDom?: boolean): boolean { + // No need to clear the dom node because a full .innerHTML will occur in + // ViewLayerRenderer._render, however the fallback mechanism in the + // GPU renderer may cause this to be necessary as the .innerHTML call + // may not happen depending on the new state, leaving stale DOM nodes + // around. + if (flushDom) { + const start = this._linesCollection.getStartLineNumber(); + const end = this._linesCollection.getEndLineNumber(); + for (let i = start; i <= end; i++) { + this._linesCollection.getLine(i).getDomNode()?.remove(); + } + } this._linesCollection.flush(); - // No need to clear the dom node because a full .innerHTML will occur in ViewLayerRenderer._render return true; } diff --git a/src/vs/editor/browser/view/viewPart.ts b/src/vs/editor/browser/view/viewPart.ts index 78467eb2e23..a23bcb11b59 100644 --- a/src/vs/editor/browser/view/viewPart.ts +++ b/src/vs/editor/browser/view/viewPart.ts @@ -37,7 +37,8 @@ export const enum PartFingerprint { ScrollableElement, TextArea, ViewLines, - Minimap + Minimap, + ViewLinesGpu } export class PartFingerprints { diff --git a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts index be7bd39b7f9..6ae696f90dd 100644 --- a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts +++ b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts @@ -5,7 +5,7 @@ import * as dom from '../../../../base/browser/dom.js'; import { FastDomNode, createFastDomNode } from '../../../../base/browser/fastDomNode.js'; -import { ContentWidgetPositionPreference, IContentWidget } from '../../editorBrowser.js'; +import { ContentWidgetPositionPreference, IContentWidget, IContentWidgetRenderedCoordinate } from '../../editorBrowser.js'; import { PartFingerprint, PartFingerprints, ViewPart } from '../../view/viewPart.js'; import { RenderingContext, RestrictedRenderingContext } from '../../view/renderingContext.js'; import { ViewContext } from '../../../common/viewModel/viewContext.js'; @@ -567,7 +567,7 @@ class Widget { } if (typeof this._actual.afterRender === 'function') { - safeInvoke(this._actual.afterRender, this._actual, null); + safeInvoke(this._actual.afterRender, this._actual, null, null); } return; } @@ -588,7 +588,7 @@ class Widget { } if (typeof this._actual.afterRender === 'function') { - safeInvoke(this._actual.afterRender, this._actual, this._renderData.position); + safeInvoke(this._actual.afterRender, this._actual, this._renderData.position, this._renderData.coordinate); } } } @@ -600,7 +600,7 @@ class PositionPair { ) { } } -class Coordinate { +class Coordinate implements IContentWidgetRenderedCoordinate { _coordinateBrand: void = undefined; constructor( diff --git a/src/vs/editor/browser/viewParts/gpuMark/gpuMark.css b/src/vs/editor/browser/viewParts/gpuMark/gpuMark.css new file mode 100644 index 00000000000..988c4bcb24a --- /dev/null +++ b/src/vs/editor/browser/viewParts/gpuMark/gpuMark.css @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .margin-view-overlays .gpu-mark { + position: absolute; + top: 0; + bottom: 0; + left: 0; + width: 100%; + display: inline-block; + border-left: solid 2px var(--vscode-editorWarning-foreground); + opacity: 0.2; + transition: background-color 0.1s linear; +} + +.monaco-editor .margin-view-overlays .gpu-mark:hover { + background-color: var(--vscode-editorWarning-foreground) +} diff --git a/src/vs/editor/browser/viewParts/gpuMark/gpuMark.ts b/src/vs/editor/browser/viewParts/gpuMark/gpuMark.ts new file mode 100644 index 00000000000..733cd8e681a --- /dev/null +++ b/src/vs/editor/browser/viewParts/gpuMark/gpuMark.ts @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as viewEvents from '../../../common/viewEvents.js'; +import { ViewContext } from '../../../common/viewModel/viewContext.js'; +import { ViewGpuContext } from '../../gpu/viewGpuContext.js'; +import { DynamicViewOverlay } from '../../view/dynamicViewOverlay.js'; +import { RenderingContext } from '../../view/renderingContext.js'; +import { ViewLineOptions } from '../viewLines/viewLineOptions.js'; +import './gpuMark.css'; + +/** + * A mark on lines to make identification of GPU-rendered lines vs DOM easier. + */ +export class GpuMarkOverlay extends DynamicViewOverlay { + + public static readonly CLASS_NAME = 'gpu-mark'; + + private readonly _context: ViewContext; + + private _renderResult: string[] | null; + + constructor(context: ViewContext, private readonly _viewGpuContext: ViewGpuContext) { + super(); + this._context = context; + this._renderResult = null; + this._context.addEventHandler(this); + } + + public override dispose(): void { + this._context.removeEventHandler(this); + this._renderResult = null; + super.dispose(); + } + + // --- begin event handlers + + public override onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + return true; + } + public override onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { + return true; + } + public override onFlushed(e: viewEvents.ViewFlushedEvent): boolean { + return true; + } + public override onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { + return true; + } + public override onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { + return true; + } + public override onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { + return true; + } + public override onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { + return e.scrollTopChanged; + } + public override onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { + return true; + } + public override onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { + return true; + } + + // --- end event handlers + + public prepareRender(ctx: RenderingContext): void { + const visibleStartLineNumber = ctx.visibleRange.startLineNumber; + const visibleEndLineNumber = ctx.visibleRange.endLineNumber; + + const viewportData = ctx.viewportData; + const options = new ViewLineOptions(this._context.configuration, this._context.theme.type); + + const output: string[] = []; + for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { + const lineIndex = lineNumber - visibleStartLineNumber; + const cannotRenderReasons = this._viewGpuContext.canRenderDetailed(options, viewportData, lineNumber); + output[lineIndex] = cannotRenderReasons.length ? `
` : ''; + } + + this._renderResult = output; + } + + public render(startLineNumber: number, lineNumber: number): string { + if (!this._renderResult) { + return ''; + } + const lineIndex = lineNumber - startLineNumber; + if (lineIndex < 0 || lineIndex >= this._renderResult.length) { + return ''; + } + return this._renderResult[lineIndex]; + } +} diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index a54d780d996..d57f8a9a28c 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -1068,29 +1068,12 @@ export class Minimap extends ViewPart implements IMinimapModel { } public getMinimapDecorationsInViewport(startLineNumber: number, endLineNumber: number): ViewModelDecoration[] { - const decorations = this._getMinimapDecorationsInViewport(startLineNumber, endLineNumber) + return this._getMinimapDecorationsInViewport(startLineNumber, endLineNumber) .filter(decoration => !decoration.options.minimap?.sectionHeaderStyle); - - if (this._samplingState) { - const result: ViewModelDecoration[] = []; - for (const decoration of decorations) { - if (!decoration.options.minimap) { - continue; - } - const range = decoration.range; - const minimapStartLineNumber = this._samplingState.modelLineToMinimapLine(range.startLineNumber); - const minimapEndLineNumber = this._samplingState.modelLineToMinimapLine(range.endLineNumber); - result.push(new ViewModelDecoration(new Range(minimapStartLineNumber, range.startColumn, minimapEndLineNumber, range.endColumn), decoration.options)); - } - return result; - } - return decorations; } public getSectionHeaderDecorationsInViewport(startLineNumber: number, endLineNumber: number): ViewModelDecoration[] { - const minimapLineHeight = this.options.minimapLineHeight; - const sectionHeaderFontSize = this.options.sectionHeaderFontSize; - const headerHeightInMinimapLines = sectionHeaderFontSize / minimapLineHeight; + const headerHeightInMinimapLines = this.options.sectionHeaderFontSize / this.options.minimapLineHeight; startLineNumber = Math.floor(Math.max(1, startLineNumber - headerHeightInMinimapLines)); return this._getMinimapDecorationsInViewport(startLineNumber, endLineNumber) .filter(decoration => !!decoration.options.minimap?.sectionHeaderStyle); @@ -1105,7 +1088,23 @@ export class Minimap extends ViewPart implements IMinimapModel { } else { visibleRange = new Range(startLineNumber, 1, endLineNumber, this._context.viewModel.getLineMaxColumn(endLineNumber)); } - return this._context.viewModel.getMinimapDecorationsInRange(visibleRange); + const decorations = this._context.viewModel.getMinimapDecorationsInRange(visibleRange); + + if (this._samplingState) { + const result: ViewModelDecoration[] = []; + for (const decoration of decorations) { + if (!decoration.options.minimap) { + continue; + } + const range = decoration.range; + const minimapStartLineNumber = this._samplingState.modelLineToMinimapLine(range.startLineNumber); + const minimapEndLineNumber = this._samplingState.modelLineToMinimapLine(range.endLineNumber); + result.push(new ViewModelDecoration(new Range(minimapStartLineNumber, range.startColumn, minimapEndLineNumber, range.endColumn), decoration.options)); + } + return result; + } + + return decorations; } public getSectionHeaderText(decoration: ViewModelDecoration, fitWidth: (s: string) => string): string | null { diff --git a/src/vs/editor/browser/viewParts/rulersGpu/rulersGpu.ts b/src/vs/editor/browser/viewParts/rulersGpu/rulersGpu.ts index 8db2dc2823e..af0b20eb9a7 100644 --- a/src/vs/editor/browser/viewParts/rulersGpu/rulersGpu.ts +++ b/src/vs/editor/browser/viewParts/rulersGpu/rulersGpu.ts @@ -10,7 +10,7 @@ import * as viewEvents from '../../../common/viewEvents.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; import type { ViewGpuContext } from '../../gpu/viewGpuContext.js'; import type { IObjectCollectionBufferEntry } from '../../gpu/objectCollectionBuffer.js'; -import type { RectangleRendererEntrySpec } from '../../gpu/rectangleRenderer.js'; +import type { RectangleRenderer, RectangleRendererEntrySpec } from '../../gpu/rectangleRenderer.js'; import { Color } from '../../../../base/common/color.js'; import { editorRuler } from '../../../common/core/editorColorRegistry.js'; import { autorun, type IReader } from '../../../../base/common/observable.js'; @@ -57,7 +57,7 @@ export class RulersGpu extends ViewPart { const ruler = rulers[i]; const shape = this._gpuShapes[i]; const color = ruler.color ? Color.fromHex(ruler.color) : this._context.theme.getColor(editorRuler) ?? Color.white; - const rulerData = [ + const rulerData: Parameters = [ ruler.column * typicalHalfwidthCharacterWidth * devicePixelRatio, 0, Math.max(1, Math.ceil(devicePixelRatio)), @@ -66,7 +66,7 @@ export class RulersGpu extends ViewPart { color.rgba.g / 255, color.rgba.b / 255, color.rgba.a, - ] as const; + ]; if (!shape) { this._gpuShapes[i] = this._viewGpuContext.rectangleRenderer.register(...rulerData); } else { diff --git a/src/vs/editor/browser/viewParts/selections/selections.ts b/src/vs/editor/browser/viewParts/selections/selections.ts index ce07d7984fa..a882f80f4f1 100644 --- a/src/vs/editor/browser/viewParts/selections/selections.ts +++ b/src/vs/editor/browser/viewParts/selections/selections.ts @@ -56,6 +56,9 @@ function toStyled(item: LineVisibleRanges): LineVisibleRangesWithStyle { return new LineVisibleRangesWithStyle(item.lineNumber, item.ranges.map(toStyledRange)); } +/** + * This view part displays selected text to the user. Every line has its own selection overlay. + */ export class SelectionsOverlay extends DynamicViewOverlay { private static readonly SELECTION_CLASS_NAME = 'selected-text'; diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts index b69180772cc..b4380373c52 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts @@ -63,7 +63,7 @@ export class ViewCursor { const options = this._context.configuration.options; const fontInfo = options.get(EditorOption.fontInfo); - this._cursorStyle = options.get(EditorOption.cursorStyle); + this._cursorStyle = options.get(EditorOption.effectiveCursorStyle); this._lineHeight = options.get(EditorOption.lineHeight); this._typicalHalfwidthCharacterWidth = fontInfo.typicalHalfwidthCharacterWidth; this._lineCursorWidth = Math.min(options.get(EditorOption.cursorWidth), this._typicalHalfwidthCharacterWidth); @@ -130,7 +130,7 @@ export class ViewCursor { const options = this._context.configuration.options; const fontInfo = options.get(EditorOption.fontInfo); - this._cursorStyle = options.get(EditorOption.cursorStyle); + this._cursorStyle = options.get(EditorOption.effectiveCursorStyle); this._lineHeight = options.get(EditorOption.lineHeight); this._typicalHalfwidthCharacterWidth = fontInfo.typicalHalfwidthCharacterWidth; this._lineCursorWidth = Math.min(options.get(EditorOption.cursorWidth), this._typicalHalfwidthCharacterWidth); diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts index fc8695d3e1f..82c46115a1b 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts @@ -59,9 +59,9 @@ export class ViewCursors extends ViewPart { const options = this._context.configuration.options; this._readOnly = options.get(EditorOption.readOnly); this._cursorBlinking = options.get(EditorOption.cursorBlinking); - this._cursorStyle = options.get(EditorOption.cursorStyle); + this._cursorStyle = options.get(EditorOption.effectiveCursorStyle); this._cursorSmoothCaretAnimation = options.get(EditorOption.cursorSmoothCaretAnimation); - this._experimentalEditContextEnabled = options.get(EditorOption.experimentalEditContextEnabled); + this._experimentalEditContextEnabled = options.get(EditorOption.effectiveExperimentalEditContextEnabled); this._selectionIsEmpty = true; this._isComposingInput = false; @@ -114,9 +114,9 @@ export class ViewCursors extends ViewPart { this._readOnly = options.get(EditorOption.readOnly); this._cursorBlinking = options.get(EditorOption.cursorBlinking); - this._cursorStyle = options.get(EditorOption.cursorStyle); + this._cursorStyle = options.get(EditorOption.effectiveCursorStyle); this._cursorSmoothCaretAnimation = options.get(EditorOption.cursorSmoothCaretAnimation); - this._experimentalEditContextEnabled = options.get(EditorOption.experimentalEditContextEnabled); + this._experimentalEditContextEnabled = options.get(EditorOption.effectiveExperimentalEditContextEnabled); this._updateBlinking(); this._updateDomClassName(); diff --git a/src/vs/editor/browser/viewParts/viewLines/viewLine.ts b/src/vs/editor/browser/viewParts/viewLines/viewLine.ts index 5e555246cea..16229cd928a 100644 --- a/src/vs/editor/browser/viewParts/viewLines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/viewLines/viewLine.ts @@ -54,7 +54,7 @@ export class ViewLine implements IVisibleLine { private _isMaybeInvalid: boolean; private _renderedViewLine: IRenderedViewLine | null; - constructor(options: ViewLineOptions) { + constructor(private readonly _viewGpuContext: ViewGpuContext | undefined, options: ViewLineOptions) { this._options = options; this._isMaybeInvalid = true; this._renderedViewLine = null; @@ -98,7 +98,7 @@ export class ViewLine implements IVisibleLine { } public renderLine(lineNumber: number, deltaTop: number, lineHeight: number, viewportData: ViewportData, sb: StringBuilder): boolean { - if (this._options.useGpu && ViewGpuContext.canRender(this._options, viewportData, lineNumber)) { + if (this._options.useGpu && this._viewGpuContext?.canRender(this._options, viewportData, lineNumber)) { this._renderedViewLine?.domNode?.domNode.remove(); this._renderedViewLine = null; return false; diff --git a/src/vs/editor/browser/viewParts/viewLines/viewLines.ts b/src/vs/editor/browser/viewParts/viewLines/viewLines.ts index b93a0028b0b..78324773a68 100644 --- a/src/vs/editor/browser/viewParts/viewLines/viewLines.ts +++ b/src/vs/editor/browser/viewParts/viewLines/viewLines.ts @@ -25,6 +25,7 @@ import { ViewportData } from '../../../common/viewLayout/viewLinesViewportData.j import { Viewport } from '../../../common/viewModel.js'; import { ViewContext } from '../../../common/viewModel/viewContext.js'; import { ViewLineOptions } from './viewLineOptions.js'; +import type { ViewGpuContext } from '../../gpu/viewGpuContext.js'; class LastRenderedData { @@ -125,7 +126,7 @@ export class ViewLines extends ViewPart implements IViewLines { private _stickyScrollEnabled: boolean; private _maxNumberStickyLines: number; - constructor(context: ViewContext, linesContent: FastDomNode) { + constructor(context: ViewContext, viewGpuContext: ViewGpuContext | undefined, linesContent: FastDomNode) { super(context); const conf = this._context.configuration; @@ -145,7 +146,7 @@ export class ViewLines extends ViewPart implements IViewLines { this._linesContent = linesContent; this._textRangeRestingSpot = document.createElement('div'); this._visibleLines = new VisibleLinesCollection({ - createLine: () => new ViewLine(this._viewLineOptions), + createLine: () => new ViewLine(viewGpuContext, this._viewLineOptions), }); this.domNode = this._visibleLines.domNode; @@ -253,7 +254,7 @@ export class ViewLines extends ViewPart implements IViewLines { return true; } public override onFlushed(e: viewEvents.ViewFlushedEvent): boolean { - const shouldRender = this._visibleLines.onFlushed(e); + const shouldRender = this._visibleLines.onFlushed(e, this._viewLineOptions.useGpu); this._maxLineWidth = 0; return shouldRender; } diff --git a/src/vs/editor/browser/viewParts/viewLinesGpu/viewLinesGpu.ts b/src/vs/editor/browser/viewParts/viewLinesGpu/viewLinesGpu.ts index 693bfc1f5da..a6cabd55951 100644 --- a/src/vs/editor/browser/viewParts/viewLinesGpu/viewLinesGpu.ts +++ b/src/vs/editor/browser/viewParts/viewLinesGpu/viewLinesGpu.ts @@ -5,25 +5,31 @@ import { getActiveWindow } from '../../../../base/browser/dom.js'; import { BugIndicatingError } from '../../../../base/common/errors.js'; -import { autorun } from '../../../../base/common/observable.js'; +import { autorun, runOnChange } from '../../../../base/common/observable.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import type { Position } from '../../../common/core/position.js'; -import type { Range } from '../../../common/core/range.js'; -import type { ViewLinesChangedEvent, ViewScrollChangedEvent } from '../../../common/viewEvents.js'; +import { Position } from '../../../common/core/position.js'; +import { Range } from '../../../common/core/range.js'; import type { ViewportData } from '../../../common/viewLayout/viewLinesViewportData.js'; import type { ViewContext } from '../../../common/viewModel/viewContext.js'; import { TextureAtlasPage } from '../../gpu/atlas/textureAtlasPage.js'; -import { FullFileRenderStrategy } from '../../gpu/fullFileRenderStrategy.js'; import { BindingId, type IGpuRenderStrategy } from '../../gpu/gpu.js'; import { GPULifecycle } from '../../gpu/gpuDisposable.js'; -import { observeDevicePixelDimensions, quadVertices } from '../../gpu/gpuUtils.js'; +import { quadVertices } from '../../gpu/gpuUtils.js'; import { ViewGpuContext } from '../../gpu/viewGpuContext.js'; -import { FloatHorizontalRange, HorizontalPosition, IViewLines, LineVisibleRanges, RenderingContext, RestrictedRenderingContext, VisibleRanges } from '../../view/renderingContext.js'; +import { FloatHorizontalRange, HorizontalPosition, HorizontalRange, IViewLines, LineVisibleRanges, RenderingContext, RestrictedRenderingContext, VisibleRanges } from '../../view/renderingContext.js'; import { ViewPart } from '../../view/viewPart.js'; import { ViewLineOptions } from '../viewLines/viewLineOptions.js'; - +import type * as viewEvents from '../../../common/viewEvents.js'; +import { CursorColumns } from '../../../common/core/cursorColumns.js'; +import { TextureAtlas } from '../../gpu/atlas/textureAtlas.js'; +import { createContentSegmenter, type IContentSegmenter } from '../../gpu/contentSegmenter.js'; +import { ViewportRenderStrategy } from '../../gpu/renderStrategy/viewportRenderStrategy.js'; +import { FullFileRenderStrategy } from '../../gpu/renderStrategy/fullFileRenderStrategy.js'; +import { MutableDisposable } from '../../../../base/common/lifecycle.js'; +import type { ViewLineRenderingData } from '../../../common/viewModel.js'; +import { GlyphRasterizer } from '../../gpu/raster/glyphRasterizer.js'; const enum GlyphStorageBufferInfo { FloatsPerEntry = 2 + 2 + 2, @@ -40,6 +46,7 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { private readonly canvas: HTMLCanvasElement; + private _initViewportData?: ViewportData[]; private _lastViewportData?: ViewportData; private _lastViewLineOptions?: ViewLineOptions; @@ -51,13 +58,15 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { private _vertexBuffer!: GPUBuffer; - private readonly _glyphStorageBuffer: GPUBuffer[] = []; + private _glyphStorageBuffer!: GPUBuffer; private _atlasGpuTexture!: GPUTexture; private readonly _atlasGpuTextureVersions: number[] = []; private _initialized = false; - private _renderStrategy!: IGpuRenderStrategy; + private readonly _glyphRasterizer: MutableDisposable = this._register(new MutableDisposable()); + private readonly _renderStrategy: MutableDisposable = this._register(new MutableDisposable()); + private _rebuildBindGroup?: () => void; constructor( context: ViewContext, @@ -69,9 +78,18 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { this.canvas = this._viewGpuContext.canvas.domNode; + // Re-render the following frame after canvas device pixel dimensions change, provided a + // new render does not occur. this._register(autorun(reader => { - /*const dims = */this._viewGpuContext.canvasDevicePixelDimensions.read(reader); - // TODO: Request render, should this just call renderText with the last viewportData + this._viewGpuContext.canvasDevicePixelDimensions.read(reader); + const lastViewportData = this._lastViewportData; + if (lastViewportData) { + setTimeout(() => { + if (lastViewportData === this._lastViewportData) { + this.renderText(lastViewportData); + } + }); + } })); this.initWebgpu(); @@ -80,7 +98,7 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { async initWebgpu() { // #region General - this._device = await this._viewGpuContext.device; + this._device = ViewGpuContext.deviceSync || await ViewGpuContext.device; if (this._store.isDisposed) { return; @@ -93,7 +111,7 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { this._atlasGpuTextureVersions.length = 0; this._atlasGpuTextureVersions[0] = 0; this._atlasGpuTextureVersions[1] = 0; - this._renderStrategy.reset(); + this._renderStrategy.value!.reset(); })); const presentationFormat = navigator.gpu.getPreferredCanvasFormat(); @@ -144,8 +162,11 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { size: Info.BytesPerEntry, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, }, () => updateBufferValues())).object; - this._register(observeDevicePixelDimensions(this.canvas, getActiveWindow(), (w, h) => { - this._device.queue.writeBuffer(layoutInfoUniformBuffer, 0, updateBufferValues(w, h)); + this._register(runOnChange(this._viewGpuContext.canvasDevicePixelDimensions, ({ width, height }) => { + this._device.queue.writeBuffer(layoutInfoUniformBuffer, 0, updateBufferValues(width, height)); + })); + this._register(runOnChange(this._viewGpuContext.contentLeft, () => { + this._device.queue.writeBuffer(layoutInfoUniformBuffer, 0, updateBufferValues()); })); } @@ -173,16 +194,20 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { // #region Storage buffers - this._renderStrategy = this._register(this._instantiationService.createInstance(FullFileRenderStrategy, this._context, this._device, this.canvas, atlas)); + const fontFamily = this._context.configuration.options.get(EditorOption.fontFamily); + const fontSize = this._context.configuration.options.get(EditorOption.fontSize); + this._glyphRasterizer.value = this._register(new GlyphRasterizer(fontSize, fontFamily, this._viewGpuContext.devicePixelRatio.get())); + this._register(runOnChange(this._viewGpuContext.devicePixelRatio, () => { + this._refreshGlyphRasterizer(); + })); - this._glyphStorageBuffer[0] = this._register(GPULifecycle.createBuffer(this._device, { - label: 'Monaco glyph storage buffer', - size: GlyphStorageBufferInfo.BytesPerEntry * TextureAtlasPage.maximumGlyphCount, - usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, - })).object; - this._glyphStorageBuffer[1] = this._register(GPULifecycle.createBuffer(this._device, { + + this._renderStrategy.value = this._instantiationService.createInstance(FullFileRenderStrategy, this._context, this._viewGpuContext, this._device, this._glyphRasterizer as { value: GlyphRasterizer }); + // this._renderStrategy.value = this._instantiationService.createInstance(ViewportRenderStrategy, this._context, this._viewGpuContext, this._device); + + this._glyphStorageBuffer = this._register(GPULifecycle.createBuffer(this._device, { label: 'Monaco glyph storage buffer', - size: GlyphStorageBufferInfo.BytesPerEntry * TextureAtlasPage.maximumGlyphCount, + size: TextureAtlas.maximumPageCount * (TextureAtlasPage.maximumGlyphCount * GlyphStorageBufferInfo.BytesPerEntry), usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, })).object; this._atlasGpuTextureVersions[0] = 0; @@ -190,8 +215,7 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { this._atlasGpuTexture = this._register(GPULifecycle.createTexture(this._device, { label: 'Monaco atlas texture', format: 'rgba8unorm', - // TODO: Dynamically grow/shrink layer count - size: { width: atlas.pageSize, height: atlas.pageSize, depthOrArrayLayers: 2 }, + size: { width: atlas.pageSize, height: atlas.pageSize, depthOrArrayLayers: TextureAtlas.maximumPageCount }, dimension: '2d', usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | @@ -216,7 +240,7 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { const module = this._device.createShaderModule({ label: 'Monaco shader module', - code: this._renderStrategy.wgsl, + code: this._renderStrategy.value!.wgsl, }); // #endregion Shader module @@ -261,34 +285,75 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { // #region Bind group - this._bindGroup = this._device.createBindGroup({ - label: 'Monaco bind group', - layout: this._pipeline.getBindGroupLayout(0), - entries: [ - // TODO: Pass in generically as array? - { binding: BindingId.GlyphInfo0, resource: { buffer: this._glyphStorageBuffer[0] } }, - { binding: BindingId.GlyphInfo1, resource: { buffer: this._glyphStorageBuffer[1] } }, - { - binding: BindingId.TextureSampler, resource: this._device.createSampler({ - label: 'Monaco atlas sampler', - magFilter: 'nearest', - minFilter: 'nearest', - }) - }, - { binding: BindingId.Texture, resource: this._atlasGpuTexture.createView() }, - { binding: BindingId.LayoutInfoUniform, resource: { buffer: layoutInfoUniformBuffer } }, - { binding: BindingId.AtlasDimensionsUniform, resource: { buffer: atlasInfoUniformBuffer } }, - ...this._renderStrategy.bindGroupEntries - ], - }); + this._rebuildBindGroup = () => { + this._bindGroup = this._device.createBindGroup({ + label: 'Monaco bind group', + layout: this._pipeline.getBindGroupLayout(0), + entries: [ + // TODO: Pass in generically as array? + { binding: BindingId.GlyphInfo, resource: { buffer: this._glyphStorageBuffer } }, + { + binding: BindingId.TextureSampler, resource: this._device.createSampler({ + label: 'Monaco atlas sampler', + magFilter: 'nearest', + minFilter: 'nearest', + }) + }, + { binding: BindingId.Texture, resource: this._atlasGpuTexture.createView() }, + { binding: BindingId.LayoutInfoUniform, resource: { buffer: layoutInfoUniformBuffer } }, + { binding: BindingId.AtlasDimensionsUniform, resource: { buffer: atlasInfoUniformBuffer } }, + ...this._renderStrategy.value!.bindGroupEntries + ], + }); + }; + this._rebuildBindGroup(); // endregion Bind group this._initialized = true; + + // Render the initial viewport immediately after initialization + if (this._initViewportData) { + // HACK: Rendering multiple times in the same frame like this isn't ideal, but there + // isn't an easy way to merge viewport data + for (const viewportData of this._initViewportData) { + this.renderText(viewportData); + } + this._initViewportData = undefined; + } + } + + private _refreshRenderStrategy(viewportData: ViewportData) { + if (this._renderStrategy.value?.type === 'viewport') { + return; + } + if (viewportData.endLineNumber < FullFileRenderStrategy.maxSupportedLines && this._viewportMaxColumn(viewportData) < FullFileRenderStrategy.maxSupportedColumns) { + return; + } + this._logService.trace(`File is larger than ${FullFileRenderStrategy.maxSupportedLines} lines or ${FullFileRenderStrategy.maxSupportedColumns} columns, switching to viewport render strategy`); + const viewportRenderStrategy = this._instantiationService.createInstance(ViewportRenderStrategy, this._context, this._viewGpuContext, this._device, this._glyphRasterizer as { value: GlyphRasterizer }); + this._renderStrategy.value = viewportRenderStrategy; + this._register(viewportRenderStrategy.onDidChangeBindGroupEntries(() => this._rebuildBindGroup?.())); + this._rebuildBindGroup?.(); + } + + private _viewportMaxColumn(viewportData: ViewportData): number { + let maxColumn = 0; + let lineData: ViewLineRenderingData; + for (let i = viewportData.startLineNumber; i <= viewportData.endLineNumber; i++) { + lineData = viewportData.getViewLineRenderingData(i); + maxColumn = Math.max(maxColumn, lineData.maxColumn); + } + return maxColumn; } private _updateAtlasStorageBufferAndTexture() { for (const [layerIndex, page] of ViewGpuContext.atlas.pages.entries()) { + if (layerIndex >= TextureAtlas.maximumPageCount) { + console.log(`Attempt to upload atlas page [${layerIndex}], only ${TextureAtlas.maximumPageCount} are supported currently`); + continue; + } + // Skip the update if it's already the latest version if (page.version === this._atlasGpuTextureVersions[layerIndex]) { continue; @@ -296,9 +361,8 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { this._logService.trace('Updating atlas page[', layerIndex, '] from version ', this._atlasGpuTextureVersions[layerIndex], ' to version ', page.version); - // TODO: Reuse buffer instead of reconstructing each time - // TODO: Dynamically set buffer size - const values = new Float32Array(GlyphStorageBufferInfo.FloatsPerEntry * TextureAtlasPage.maximumGlyphCount); + const entryCount = GlyphStorageBufferInfo.FloatsPerEntry * TextureAtlasPage.maximumGlyphCount; + const values = new Float32Array(entryCount); let entryOffset = 0; for (const glyph of page.glyphs) { values[entryOffset + GlyphStorageBufferInfo.Offset_TexturePosition] = glyph.x; @@ -312,7 +376,13 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { if (entryOffset / GlyphStorageBufferInfo.FloatsPerEntry > TextureAtlasPage.maximumGlyphCount) { throw new Error(`Attempting to write more glyphs (${entryOffset / GlyphStorageBufferInfo.FloatsPerEntry}) than the GPUBuffer can hold (${TextureAtlasPage.maximumGlyphCount})`); } - this._device.queue.writeBuffer(this._glyphStorageBuffer[layerIndex], 0, values); + this._device.queue.writeBuffer( + this._glyphStorageBuffer, + layerIndex * GlyphStorageBufferInfo.FloatsPerEntry * TextureAtlasPage.maximumGlyphCount * Float32Array.BYTES_PER_ELEMENT, + values, + 0, + GlyphStorageBufferInfo.FloatsPerEntry * TextureAtlasPage.maximumGlyphCount + ); if (page.usedArea.right - page.usedArea.left > 0 && page.usedArea.bottom - page.usedArea.top > 0) { this._device.queue.copyExternalImageToTexture( { source: page.source }, @@ -325,8 +395,8 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { } }, { - width: page.usedArea.right - page.usedArea.left, - height: page.usedArea.bottom - page.usedArea.top + width: page.usedArea.right - page.usedArea.left + 1, + height: page.usedArea.bottom - page.usedArea.top + 1 }, ); } @@ -334,12 +404,6 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { } } - public static canRender(options: ViewLineOptions, viewportData: ViewportData, lineNumber: number): boolean { - const d = viewportData.getViewLineRenderingData(lineNumber); - // TODO - return d.content.indexOf('e') !== -1; - } - public prepareRender(ctx: RenderingContext): void { throw new BugIndicatingError('Should not be called'); } @@ -348,19 +412,59 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { throw new BugIndicatingError('Should not be called'); } - override onLinesChanged(e: ViewLinesChangedEvent): boolean { - return true; - } + // #region Event handlers - override onScrollChanged(e: ViewScrollChangedEvent): boolean { + // Since ViewLinesGpu currently coordinates rendering to the canvas, it must listen to all + // changed events that any GPU part listens to. This is because any drawing to the canvas will + // clear it for that frame, so all parts must be rendered every time. + // + // Additionally, since this is intrinsically linked to ViewLines, it must also listen to events + // from that side. Luckily rendering is cheap, it's only when uploaded data changes does it + // start to cost. + + override onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { + this._refreshGlyphRasterizer(); return true; } - - // subscribe to more events + override onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { return true; } + override onDecorationsChanged(e: viewEvents.ViewDecorationsChangedEvent): boolean { return true; } + override onFlushed(e: viewEvents.ViewFlushedEvent): boolean { return true; } + + override onLinesChanged(e: viewEvents.ViewLinesChangedEvent): boolean { return true; } + override onLinesDeleted(e: viewEvents.ViewLinesDeletedEvent): boolean { return true; } + override onLinesInserted(e: viewEvents.ViewLinesInsertedEvent): boolean { return true; } + override onLineMappingChanged(e: viewEvents.ViewLineMappingChangedEvent): boolean { return true; } + override onRevealRangeRequest(e: viewEvents.ViewRevealRangeRequestEvent): boolean { return true; } + override onScrollChanged(e: viewEvents.ViewScrollChangedEvent): boolean { return true; } + override onThemeChanged(e: viewEvents.ViewThemeChangedEvent): boolean { return true; } + override onZonesChanged(e: viewEvents.ViewZonesChangedEvent): boolean { return true; } + + // #endregion + + private _refreshGlyphRasterizer() { + const glyphRasterizer = this._glyphRasterizer.value; + if (!glyphRasterizer) { + return; + } + const fontFamily = this._context.configuration.options.get(EditorOption.fontFamily); + const fontSize = this._context.configuration.options.get(EditorOption.fontSize); + const devicePixelRatio = this._viewGpuContext.devicePixelRatio.get(); + if ( + glyphRasterizer.fontFamily !== fontFamily || + glyphRasterizer.fontSize !== fontSize || + glyphRasterizer.devicePixelRatio !== devicePixelRatio + ) { + this._glyphRasterizer.value = new GlyphRasterizer(fontSize, fontFamily, devicePixelRatio); + } + } public renderText(viewportData: ViewportData): void { if (this._initialized) { + this._refreshRenderStrategy(viewportData); return this._renderText(viewportData); + } else { + this._initViewportData = this._initViewportData ?? []; + this._initViewportData.push(viewportData); } } @@ -369,7 +473,7 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { const options = new ViewLineOptions(this._context.configuration, this._context.theme.type); - const visibleObjectCount = this._renderStrategy.update(viewportData, options); + this._renderStrategy.value!.update(viewportData, options); this._updateAtlasStorageBufferAndTexture(); @@ -380,14 +484,13 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { pass.setPipeline(this._pipeline); pass.setVertexBuffer(0, this._vertexBuffer); + // Only draw the content area + const contentLeft = Math.ceil(this._viewGpuContext.contentLeft.get() * this._viewGpuContext.devicePixelRatio.get()); + pass.setScissorRect(contentLeft, 0, this.canvas.width - contentLeft, this.canvas.height); + pass.setBindGroup(0, this._bindGroup); - if (this._renderStrategy?.draw) { - // TODO: Don't draw lines if ViewLinesGpu.canRender is false - this._renderStrategy.draw(pass, viewportData); - } else { - pass.draw(quadVertices.length / 2, visibleObjectCount); - } + this._renderStrategy.value!.draw(pass, viewportData); pass.end(); @@ -399,8 +502,65 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { this._lastViewLineOptions = options; } - linesVisibleRangesForRange(range: Range, includeNewLines: boolean): LineVisibleRanges[] | null { - return null; + linesVisibleRangesForRange(_range: Range, includeNewLines: boolean): LineVisibleRanges[] | null { + if (!this._lastViewportData) { + return null; + } + const originalEndLineNumber = _range.endLineNumber; + const range = Range.intersectRanges(_range, this._lastViewportData.visibleRange); + if (!range) { + return null; + } + + const rendStartLineNumber = this._lastViewportData.startLineNumber; + const rendEndLineNumber = this._lastViewportData.endLineNumber; + + const viewportData = this._lastViewportData; + const viewLineOptions = this._lastViewLineOptions; + + if (!viewportData || !viewLineOptions) { + return null; + } + + const visibleRanges: LineVisibleRanges[] = []; + + let nextLineModelLineNumber: number = 0; + if (includeNewLines) { + nextLineModelLineNumber = this._context.viewModel.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber; + } + + for (let lineNumber = range.startLineNumber; lineNumber <= range.endLineNumber; lineNumber++) { + + if (lineNumber < rendStartLineNumber || lineNumber > rendEndLineNumber) { + continue; + } + const startColumn = lineNumber === range.startLineNumber ? range.startColumn : 1; + const continuesInNextLine = lineNumber !== range.endLineNumber; + const endColumn = continuesInNextLine ? this._context.viewModel.getLineMaxColumn(lineNumber) : range.endColumn; + + const visibleRangesForLine = this._visibleRangesForLineRange(lineNumber, startColumn, endColumn); + + if (!visibleRangesForLine) { + continue; + } + + if (includeNewLines && lineNumber < originalEndLineNumber) { + const currentLineModelLineNumber = nextLineModelLineNumber; + nextLineModelLineNumber = this._context.viewModel.coordinatesConverter.convertViewPositionToModelPosition(new Position(lineNumber + 1, 1)).lineNumber; + + if (currentLineModelLineNumber !== nextLineModelLineNumber) { + visibleRangesForLine.ranges[visibleRangesForLine.ranges.length - 1].width += viewLineOptions.spaceWidth; + } + } + + visibleRanges.push(new LineVisibleRanges(visibleRangesForLine.outsideRenderedLine, lineNumber, HorizontalRange.from(visibleRangesForLine.ranges), continuesInNextLine)); + } + + if (visibleRanges.length === 0) { + return null; + } + + return visibleRanges; } private _visibleRangesForLineRange(lineNumber: number, startColumn: number, endColumn: number): VisibleRanges | null { @@ -417,10 +577,58 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { return null; } + // Resolve tab widths for this line + const lineData = viewportData.getViewLineRenderingData(lineNumber); + const content = lineData.content; + + let contentSegmenter: IContentSegmenter | undefined; + if (!(lineData.isBasicASCII && viewLineOptions.useMonospaceOptimizations)) { + contentSegmenter = createContentSegmenter(lineData, viewLineOptions); + } + + let chars: string | undefined = ''; + + let resolvedStartColumn = 0; + let resolvedStartCssPixelOffset = 0; + for (let x = 0; x < startColumn - 1; x++) { + if (lineData.isBasicASCII && viewLineOptions.useMonospaceOptimizations) { + chars = content.charAt(x); + } else { + chars = contentSegmenter!.getSegmentAtIndex(x); + if (chars === undefined) { + continue; + } + resolvedStartCssPixelOffset += (this._renderStrategy.value!.glyphRasterizer.getTextMetrics(chars).width / getActiveWindow().devicePixelRatio) - viewLineOptions.spaceWidth; + } + if (chars === '\t') { + resolvedStartColumn = CursorColumns.nextRenderTabStop(resolvedStartColumn, lineData.tabSize); + } else { + resolvedStartColumn++; + } + } + let resolvedEndColumn = resolvedStartColumn; + let resolvedEndCssPixelOffset = 0; + for (let x = startColumn - 1; x < endColumn - 1; x++) { + if (lineData.isBasicASCII && viewLineOptions.useMonospaceOptimizations) { + chars = content.charAt(x); + } else { + chars = contentSegmenter!.getSegmentAtIndex(x); + if (chars === undefined) { + continue; + } + resolvedEndCssPixelOffset += (this._renderStrategy.value!.glyphRasterizer.getTextMetrics(chars).width / getActiveWindow().devicePixelRatio) - viewLineOptions.spaceWidth; + } + if (chars === '\t') { + resolvedEndColumn = CursorColumns.nextRenderTabStop(resolvedEndColumn, lineData.tabSize); + } else { + resolvedEndColumn++; + } + } + // Visible horizontal range in _scaled_ pixels const result = new VisibleRanges(false, [new FloatHorizontalRange( - (startColumn - 1) * viewLineOptions.spaceWidth, - (endColumn - startColumn - 1) * viewLineOptions.spaceWidth) + resolvedStartColumn * viewLineOptions.spaceWidth + resolvedStartCssPixelOffset, + (resolvedEndColumn - resolvedStartColumn) * viewLineOptions.spaceWidth + resolvedEndCssPixelOffset) ]); return result; @@ -433,4 +641,74 @@ export class ViewLinesGpu extends ViewPart implements IViewLines { } return new HorizontalPosition(visibleRanges.outsideRenderedLine, visibleRanges.ranges[0].left); } + + getLineWidth(lineNumber: number): number | undefined { + if (!this._lastViewportData || !this._lastViewLineOptions) { + return undefined; + } + if (!this._viewGpuContext.canRender(this._lastViewLineOptions, this._lastViewportData, lineNumber)) { + return undefined; + } + + const lineData = this._lastViewportData.getViewLineRenderingData(lineNumber); + const lineRange = this._visibleRangesForLineRange(lineNumber, 1, lineData.maxColumn); + const lastRange = lineRange?.ranges.at(-1); + if (lastRange) { + return lastRange.width; + } + + return undefined; + } + + getPositionAtCoordinate(lineNumber: number, mouseContentHorizontalOffset: number): Position | undefined { + if (!this._lastViewportData || !this._lastViewLineOptions) { + return undefined; + } + if (!this._viewGpuContext.canRender(this._lastViewLineOptions, this._lastViewportData, lineNumber)) { + return undefined; + } + const lineData = this._lastViewportData.getViewLineRenderingData(lineNumber); + const content = lineData.content; + const dpr = getActiveWindow().devicePixelRatio; + const mouseContentHorizontalOffsetDevicePixels = mouseContentHorizontalOffset * dpr; + const spaceWidthDevicePixels = this._lastViewLineOptions.spaceWidth * dpr; + const contentSegmenter = createContentSegmenter(lineData, this._lastViewLineOptions); + + let widthSoFar = 0; + let charWidth = 0; + let tabXOffset = 0; + let column = 0; + for (let x = 0; x < content.length; x++) { + const chars = contentSegmenter.getSegmentAtIndex(x); + + // Part of an earlier segment + if (chars === undefined) { + column++; + continue; + } + + // Get the width of the character + if (chars === '\t') { + // Find the pixel offset between the current position and the next tab stop + const offsetBefore = x + tabXOffset; + tabXOffset = CursorColumns.nextRenderTabStop(x + tabXOffset, lineData.tabSize); + charWidth = spaceWidthDevicePixels * (tabXOffset - offsetBefore); + // Convert back to offset excluding x and the current character + tabXOffset -= x + 1; + } else if (lineData.isBasicASCII && this._lastViewLineOptions.useMonospaceOptimizations) { + charWidth = spaceWidthDevicePixels; + } else { + charWidth = this._renderStrategy.value!.glyphRasterizer.getTextMetrics(chars).width; + } + + if (mouseContentHorizontalOffsetDevicePixels < widthSoFar + charWidth / 2) { + break; + } + + widthSoFar += charWidth; + column++; + } + + return new Position(lineNumber, column + 1); + } } diff --git a/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts index 59f027dd434..7e325a7f888 100644 --- a/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts @@ -197,6 +197,9 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE private readonly _onEndUpdate: Emitter = this._register(new Emitter()); public readonly onEndUpdate: Event = this._onEndUpdate.event; + private readonly _onBeforeExecuteEdit = this._register(new Emitter<{ source: string | undefined }>()); + public readonly onBeforeExecuteEdit = this._onBeforeExecuteEdit.event; + //#endregion public get isSimpleWidget(): boolean { @@ -244,6 +247,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE private _dropIntoEditorDecorations: EditorDecorationsCollection = this.createDecorationsCollection(); + public inComposition: boolean = false; + constructor( domElement: HTMLElement, _options: Readonly, @@ -285,6 +290,11 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE })); this._contextKeyService = this._register(contextKeyService.createScoped(this._domElement)); + if (codeEditorWidgetOptions.contextKeyValues) { + for (const [key, value] of Object.entries(codeEditorWidgetOptions.contextKeyValues)) { + this._contextKeyService.createKey(key, value); + } + } this._notificationService = notificationService; this._codeEditorService = codeEditorService; this._commandService = commandService; @@ -1113,6 +1123,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (!this._modelData) { return; } + this.inComposition = true; this._modelData.viewModel.startComposition(); this._onDidCompositionStart.fire(); } @@ -1121,6 +1132,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (!this._modelData) { return; } + this.inComposition = false; this._modelData.viewModel.endComposition(source); this._onDidCompositionEnd.fire(); } @@ -1232,6 +1244,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE cursorStateComputer = endCursorState; } + this._onBeforeExecuteEdit.fire({ source: source ?? undefined }); + this._modelData.viewModel.executeEdits(source, edits, cursorStateComputer); return true; } @@ -1859,6 +1873,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE viewUserInputEvents.onMouseWheel = (e) => this._onMouseWheel.fire(e); const view = new View( + this.getId(), commandDelegate, this._configuration, this._themeService.getColorTheme(), @@ -1978,6 +1993,12 @@ export interface ICodeEditorWidgetOptions { * Defaults to MenuId.SimpleEditorContext or MenuId.EditorContext depending on whether the widget is simple. */ contextMenuId?: MenuId; + + /** + * Define extra context keys that will be defined in the context service + * for the editor. + */ + contextKeyValues?: Record; } class ModelData { @@ -2450,21 +2471,26 @@ registerThemingParticipant((theme, collector) => { const errorForeground = theme.getColor(editorErrorForeground); if (errorForeground) { collector.addRule(`.monaco-editor .${ClassName.EditorErrorDecoration} { background: url("data:image/svg+xml,${getSquigglySVGData(errorForeground)}") repeat-x bottom left; }`); + collector.addRule(`:root { --monaco-editor-error-decoration: url("data:image/svg+xml,${getSquigglySVGData(errorForeground)}"); }`); } const warningForeground = theme.getColor(editorWarningForeground); if (warningForeground) { collector.addRule(`.monaco-editor .${ClassName.EditorWarningDecoration} { background: url("data:image/svg+xml,${getSquigglySVGData(warningForeground)}") repeat-x bottom left; }`); + collector.addRule(`:root { --monaco-editor-warning-decoration: url("data:image/svg+xml,${getSquigglySVGData(warningForeground)}"); }`); } const infoForeground = theme.getColor(editorInfoForeground); if (infoForeground) { collector.addRule(`.monaco-editor .${ClassName.EditorInfoDecoration} { background: url("data:image/svg+xml,${getSquigglySVGData(infoForeground)}") repeat-x bottom left; }`); + collector.addRule(`:root { --monaco-editor-info-decoration: url("data:image/svg+xml,${getSquigglySVGData(infoForeground)}"); }`); } const hintForeground = theme.getColor(editorHintForeground); if (hintForeground) { collector.addRule(`.monaco-editor .${ClassName.EditorHintDecoration} { background: url("data:image/svg+xml,${getDotDotDotSVGData(hintForeground)}") no-repeat bottom left; }`); + collector.addRule(`:root { --monaco-editor-hint-decoration: url("data:image/svg+xml,${getDotDotDotSVGData(hintForeground)}"); }`); } const unnecessaryForeground = theme.getColor(editorUnnecessaryCodeOpacity); if (unnecessaryForeground) { collector.addRule(`.monaco-editor.showUnused .${ClassName.EditorUnnecessaryInlineDecoration} { opacity: ${unnecessaryForeground.rgba.a}; }`); + collector.addRule(`:root { --monaco-editor-unnecessary-decoration-opacity: ${unnecessaryForeground.rgba.a}; }`); } }); diff --git a/src/vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget.ts index b716aa61109..3852374d394 100644 --- a/src/vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget.ts @@ -13,7 +13,7 @@ import { ILanguageFeaturesService } from '../../../common/services/languageFeatu import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; @@ -61,3 +61,11 @@ export class EmbeddedCodeEditorWidget extends CodeEditorWidget { super.updateOptions(this._overwriteOptions); } } + +export function getOuterEditor(accessor: ServicesAccessor): ICodeEditor | null { + const editor = accessor.get(ICodeEditorService).getFocusedCodeEditor(); + if (editor instanceof EmbeddedCodeEditorWidget) { + return editor.getParentEditor(); + } + return editor; +} diff --git a/src/vs/editor/browser/widget/diffEditor/commands.ts b/src/vs/editor/browser/widget/diffEditor/commands.ts index 9e04b2aad05..0d0676fea62 100644 --- a/src/vs/editor/browser/widget/diffEditor/commands.ts +++ b/src/vs/editor/browser/widget/diffEditor/commands.ts @@ -20,6 +20,7 @@ import { KeybindingWeight } from '../../../../platform/keybinding/common/keybind import './registrations.contribution.js'; import { DiffEditorSelectionHunkToolbarContext } from './features/gutterFeature.js'; import { URI } from '../../../../base/common/uri.js'; +import { EditorOption } from '../../../common/config/editorOptions.js'; export class ToggleCollapseUnchangedRegions extends Action2 { constructor() { @@ -255,7 +256,7 @@ export function findFocusedDiffEditor(accessor: ServicesAccessor): IDiffEditor | if (activeElement) { for (const d of diffEditors) { const container = d.getContainerDomNode(); - if (isElementOrParentOf(container, activeElement)) { + if (container.contains(activeElement)) { return d; } } @@ -264,13 +265,24 @@ export function findFocusedDiffEditor(accessor: ServicesAccessor): IDiffEditor | return null; } -function isElementOrParentOf(elementOrParent: Element, element: Element): boolean { - let e: Element | null = element; - while (e) { - if (e === elementOrParent) { - return true; + +/** + * If `editor` is the original or modified editor of a diff editor, it returns it. + * It returns null otherwise. + */ +export function findDiffEditorContainingCodeEditor(accessor: ServicesAccessor, editor: ICodeEditor): IDiffEditor | null { + if (!editor.getOption(EditorOption.inDiffEditor)) { + return null; + } + + const codeEditorService = accessor.get(ICodeEditorService); + + for (const diffEditor of codeEditorService.listDiffEditors()) { + const originalEditor = diffEditor.getOriginalEditor(); + const modifiedEditor = diffEditor.getModifiedEditor(); + if (originalEditor === editor || modifiedEditor === editor) { + return diffEditor; } - e = e.parentElement; } - return false; + return null; } diff --git a/src/vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer.ts b/src/vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer.ts index a5add04022d..8a1c508df03 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer.ts @@ -7,7 +7,6 @@ import { addDisposableListener, addStandardDisposableListener, reset } from '../ import { createTrustedTypesPolicy } from '../../../../../base/browser/trustedTypes.js'; import { ActionBar } from '../../../../../base/browser/ui/actionbar/actionbar.js'; import { DomScrollableElement } from '../../../../../base/browser/ui/scrollbar/scrollableElement.js'; -import { Action } from '../../../../../base/common/actions.js'; import { forEachAdjacent, groupAdjacentBy } from '../../../../../base/common/arrays.js'; import { Codicon } from '../../../../../base/common/codicons.js'; import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; @@ -34,6 +33,7 @@ import { IInstantiationService } from '../../../../../platform/instantiation/com import { registerIcon } from '../../../../../platform/theme/common/iconRegistry.js'; import './accessibleDiffViewer.css'; import { DiffEditorEditors } from './diffEditorEditors.js'; +import { toAction } from '../../../../../base/common/actions.js'; const accessibleDiffViewerInsertIcon = registerIcon('diff-review-insert', Codicon.add, localize('accessibleDiffViewerInsertIcon', 'Icon for \'Insert\' in accessible diff viewer.')); const accessibleDiffViewerRemoveIcon = registerIcon('diff-review-remove', Codicon.remove, localize('accessibleDiffViewerRemoveIcon', 'Icon for \'Remove\' in accessible diff viewer.')); @@ -365,13 +365,13 @@ class View extends Disposable { /** @description update actions */ this._actionBar.clear(); if (this._model.canClose.read(reader)) { - this._actionBar.push(new Action( - 'diffreview.close', - localize('label.close', "Close"), - 'close-diff-review ' + ThemeIcon.asClassName(accessibleDiffViewerCloseIcon), - true, - async () => _model.close() - ), { label: false, icon: true }); + this._actionBar.push(toAction({ + id: 'diffreview.close', + label: localize('label.close', "Close"), + class: 'close-diff-review ' + ThemeIcon.asClassName(accessibleDiffViewerCloseIcon), + enabled: true, + run: async () => _model.close() + }), { label: false, icon: true }); } })); diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts index 5d8f652a485..638c9fe8bd5 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts @@ -19,6 +19,7 @@ import { localize } from '../../../../../nls.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; import { DiffEditorOptions } from '../diffEditorOptions.js'; +import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; export class DiffEditorEditors extends Disposable { public readonly original = this._register(this._createLeftHandSideEditor(this._options.editorOptions.get(), this._argCodeEditorWidgetOptions.originalEditor || {})); @@ -51,6 +52,7 @@ export class DiffEditorEditors extends Disposable { private readonly _options: DiffEditorOptions, private _argCodeEditorWidgetOptions: IDiffCodeEditorWidgetOptions, private readonly _createInnerEditor: (instantiationService: IInstantiationService, container: HTMLElement, options: Readonly, editorWidgetOptions: ICodeEditorWidgetOptions) => CodeEditorWidget, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IKeybindingService private readonly _keybindingService: IKeybindingService ) { @@ -80,14 +82,22 @@ export class DiffEditorEditors extends Disposable { private _createLeftHandSideEditor(options: Readonly, codeEditorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { const leftHandSideOptions = this._adjustOptionsForLeftHandSide(undefined, options); const editor = this._constructInnerEditor(this._instantiationService, this.originalEditorElement, leftHandSideOptions, codeEditorWidgetOptions); - editor.setContextValue('isInDiffLeftEditor', true); + + const isInDiffLeftEditorKey = this._contextKeyService.createKey('isInDiffLeftEditor', editor.hasWidgetFocus()); + this._register(editor.onDidFocusEditorWidget(() => isInDiffLeftEditorKey.set(true))); + this._register(editor.onDidBlurEditorWidget(() => isInDiffLeftEditorKey.set(false))); + return editor; } private _createRightHandSideEditor(options: Readonly, codeEditorWidgetOptions: ICodeEditorWidgetOptions): CodeEditorWidget { const rightHandSideOptions = this._adjustOptionsForRightHandSide(undefined, options); const editor = this._constructInnerEditor(this._instantiationService, this.modifiedEditorElement, rightHandSideOptions, codeEditorWidgetOptions); - editor.setContextValue('isInDiffRightEditor', true); + + const isInDiffRightEditorKey = this._contextKeyService.createKey('isInDiffRightEditor', editor.hasWidgetFocus()); + this._register(editor.onDidFocusEditorWidget(() => isInDiffRightEditorKey.set(true))); + this._register(editor.onDidBlurEditorWidget(() => isInDiffRightEditorKey.set(false))); + return editor; } diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorSash.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorSash.ts index 8abaddcceb9..468e6323c7f 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorSash.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorSash.ts @@ -62,7 +62,7 @@ export class DiffEditorSash extends Disposable { private readonly _domNode: HTMLElement, private readonly _dimensions: { height: IObservable; width: IObservable }, private readonly _enabled: IObservable, - private readonly _boundarySashes: IObservable, + private readonly _boundarySashes: IObservable, public readonly sashLeft: ISettableObservable, private readonly _resetSash: () => void, ) { diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts index 58e3c511825..634939aacee 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts @@ -639,6 +639,6 @@ export function allowsTrueInlineDiffRendering(mapping: DetailedLineRangeMapping) ); } -function rangeIsSingleLine(range: Range): boolean { +export function rangeIsSingleLine(range: Range): boolean { return range.startLineNumber === range.endLineNumber; } diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.ts index fc20d63c8f0..ce0eea0bcaa 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.ts @@ -17,7 +17,7 @@ import { InlineDecoration, ViewLineRenderingData } from '../../../../../common/v const ttPolicy = createTrustedTypesPolicy('diffEditorWidget', { createHTML: value => value }); -export function renderLines(source: LineSource, options: RenderOptions, decorations: InlineDecoration[], domNode: HTMLElement): RenderLinesResult { +export function renderLines(source: LineSource, options: RenderOptions, decorations: InlineDecoration[], domNode: HTMLElement, noExtra = false): RenderLinesResult { applyFontInfo(domNode, options.fontInfo); const hasCharChanges = (decorations.length > 0); @@ -44,7 +44,8 @@ export function renderLines(source: LineSource, options: RenderOptions, decorati source.mightContainNonBasicASCII, source.mightContainRTL, options, - sb + sb, + noExtra, )); renderedLineCount++; lastBreakOffset = breakOffset; @@ -61,6 +62,7 @@ export function renderLines(source: LineSource, options: RenderOptions, decorati source.mightContainRTL, options, sb, + noExtra, )); renderedLineCount++; } @@ -83,9 +85,9 @@ export function renderLines(source: LineSource, options: RenderOptions, decorati export class LineSource { constructor( public readonly lineTokens: LineTokens[], - public readonly lineBreakData: (ModelLineProjectionData | null)[], - public readonly mightContainNonBasicASCII: boolean, - public readonly mightContainRTL: boolean, + public readonly lineBreakData: (ModelLineProjectionData | null)[] = lineTokens.map(t => null), + public readonly mightContainNonBasicASCII: boolean = true, + public readonly mightContainRTL: boolean = true, ) { } } @@ -125,7 +127,25 @@ export class RenderOptions { public readonly renderWhitespace: FindComputedEditorOptionValueById, public readonly renderControlCharacters: boolean, public readonly fontLigatures: FindComputedEditorOptionValueById, + public readonly setWidth = true, ) { } + + public withSetWidth(setWidth: boolean): RenderOptions { + return new RenderOptions( + this.tabSize, + this.fontInfo, + this.disableMonospaceOptimizations, + this.typicalHalfwidthCharacterWidth, + this.scrollBeyondLastColumn, + this.lineHeight, + this.lineDecorationsWidth, + this.stopRenderingLineAfter, + this.renderWhitespace, + this.renderControlCharacters, + this.fontLigatures, + setWidth, + ); + } } export interface RenderLinesResult { @@ -143,16 +163,21 @@ function renderOriginalLine( mightContainRTL: boolean, options: RenderOptions, sb: StringBuilder, + noExtra: boolean, ): number { sb.appendString('
'); + if (options.setWidth) { + sb.appendString('px;width:1000000px;">'); + } else { + sb.appendString('px;">'); + } const lineContent = lineTokens.getLineContent(); const isBasicASCII = ViewLineRenderingData.isBasicASCII(lineContent, mightContainNonBasicASCII); diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts index 0c1a533681f..3d2e483ca15 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorOptions.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IObservable, ISettableObservable, derived, derivedConstOnceDefined, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; +import { IObservable, IObservableWithChange, ISettableObservable, derived, derivedConstOnceDefined, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; import { Constants } from '../../../../base/common/uint.js'; import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; import { diffEditorDefaultOptions } from '../../../common/config/diffEditor.js'; @@ -15,7 +15,7 @@ import { DiffEditorViewModel, DiffState } from './diffEditorViewModel.js'; export class DiffEditorOptions { private readonly _options: ISettableObservable, { changedOptions: IDiffEditorOptions }>; - public get editorOptions(): IObservable { return this._options; } + public get editorOptions(): IObservableWithChange { return this._options; } private readonly _diffEditorWidth = observableValue(this, 0); diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts index da2e28f1c44..046f825dfe8 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts @@ -517,6 +517,7 @@ export class DiffEditorWidget extends DelegatingEditor implements IDiffEditor { this._options.updateOptions(changedOptions); } + getDomNode(): HTMLElement { return this.elements.root; } getContainerDomNode(): HTMLElement { return this._domElement; } getOriginalEditor(): ICodeEditor { return this._editors.original; } getModifiedEditor(): ICodeEditor { return this._editors.modified; } @@ -701,7 +702,7 @@ export class DiffEditorWidget extends DelegatingEditor implements IDiffEditor { } } -function toLineChanges(state: DiffState): ILineChange[] { +export function toLineChanges(state: DiffState): ILineChange[] { return state.mappings.map(x => { const m = x.lineRangeMapping; let originalStartLineNumber: number; diff --git a/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts index 556378aebf3..0d8b665b9e4 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts @@ -50,7 +50,7 @@ export class DiffEditorGutter extends Disposable { private readonly _editors: DiffEditorEditors, private readonly _options: DiffEditorOptions, private readonly _sashLayout: SashLayout, - private readonly _boundarySashes: IObservable, + private readonly _boundarySashes: IObservable, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IMenuService private readonly _menuService: IMenuService, @@ -218,7 +218,7 @@ class DiffToolBar extends Disposable implements IGutterItemView { const hoverDelegate = this._register(instantiationService.createInstance( WorkbenchHoverDelegate, 'element', - true, + { instantHover: true }, { position: { hoverPosition: HoverPosition.RIGHT } } )); @@ -245,7 +245,7 @@ class DiffToolBar extends Disposable implements IGutterItemView { }, overflowBehavior: { maxItems: this._isSmall.read(reader) ? 1 : 3 }, hiddenItemStrategy: HiddenItemStrategy.Ignore, - actionRunner: new ActionRunnerWithContext(() => { + actionRunner: store.add(new ActionRunnerWithContext(() => { const item = this._item.get(); const mapping = item.mapping; return { @@ -254,7 +254,7 @@ class DiffToolBar extends Disposable implements IGutterItemView { originalUri: item.originalUri, modifiedUri: item.modifiedUri, } satisfies DiffEditorSelectionHunkToolbarContext; - }), + })), menuOptions: { shouldForwardArgs: true, }, diff --git a/src/vs/editor/browser/widget/diffEditor/style.css b/src/vs/editor/browser/widget/diffEditor/style.css index f2268db0296..8c027c33e1c 100644 --- a/src/vs/editor/browser/widget/diffEditor/style.css +++ b/src/vs/editor/browser/widget/diffEditor/style.css @@ -362,7 +362,7 @@ left: 50%; width: 1px; - border-left: 2px var(--vscode-menu-border) solid; + border-left: 2px var(--vscode-menu-separatorBackground) solid; } .buttons { @@ -425,5 +425,3 @@ margin: 0 4px; } } - - diff --git a/src/vs/editor/browser/widget/diffEditor/utils.ts b/src/vs/editor/browser/widget/diffEditor/utils.ts index b69c5ff8f8c..47faca2f8cf 100644 --- a/src/vs/editor/browser/widget/diffEditor/utils.ts +++ b/src/vs/editor/browser/widget/diffEditor/utils.ts @@ -7,7 +7,7 @@ import { IDimension } from '../../../../base/browser/dom.js'; import { findLast } from '../../../../base/common/arraysFind.js'; import { CancellationTokenSource } from '../../../../base/common/cancellation.js'; import { Disposable, DisposableStore, IDisposable, IReference, toDisposable } from '../../../../base/common/lifecycle.js'; -import { IObservable, ISettableObservable, autorun, autorunHandleChanges, autorunOpts, autorunWithStore, observableValue, transaction } from '../../../../base/common/observable.js'; +import { IObservable, IObservableWithChange, ISettableObservable, autorun, autorunHandleChanges, autorunOpts, autorunWithStore, observableValue, transaction } from '../../../../base/common/observable.js'; import { ElementSizeObserver } from '../../config/elementSizeObserver.js'; import { ICodeEditor, IOverlayWidget, IViewZone } from '../../editorBrowser.js'; import { Position } from '../../../common/core/position.js'; @@ -126,7 +126,7 @@ export class ObservableElementSizeObserver extends Disposable { } } -export function animatedObservable(targetWindow: Window, base: IObservable, store: DisposableStore): IObservable { +export function animatedObservable(targetWindow: Window, base: IObservableWithChange, store: DisposableStore): IObservable { let targetVal = base.get(); let startVal = targetVal; let curVal = targetVal; @@ -179,7 +179,7 @@ function easeOutExpo(t: number, b: number, c: number, d: number): number { } export function deepMerge(source1: T, source2: Partial): T { - const result = {} as T; + const result = {} as any as T; for (const key in source1) { result[key] = source1[key]; } diff --git a/src/vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer.ts b/src/vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer.ts index 33d1adc99c8..d8f15787616 100644 --- a/src/vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer.ts +++ b/src/vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer.ts @@ -6,17 +6,16 @@ import { MarkdownRenderOptions, MarkedOptions, renderMarkdown } from '../../../../../base/browser/markdownRenderer.js'; import { createTrustedTypesPolicy } from '../../../../../base/browser/trustedTypes.js'; import { onUnexpectedError } from '../../../../../base/common/errors.js'; -import { Emitter } from '../../../../../base/common/event.js'; import { IMarkdownString, MarkdownStringTrustedOptions } from '../../../../../base/common/htmlContent.js'; import { DisposableStore, IDisposable } from '../../../../../base/common/lifecycle.js'; -import './renderedMarkdown.css'; -import { applyFontInfo } from '../../../config/domFontInfo.js'; -import { ICodeEditor } from '../../../editorBrowser.js'; +import { IOpenerService } from '../../../../../platform/opener/common/opener.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; import { ILanguageService } from '../../../../common/languages/language.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../../common/languages/modesRegistry.js'; import { tokenizeToString } from '../../../../common/languages/textToHtmlTokenizer.js'; -import { IOpenerService } from '../../../../../platform/opener/common/opener.js'; +import { applyFontInfo } from '../../../config/domFontInfo.js'; +import { ICodeEditor } from '../../../editorBrowser.js'; +import './renderedMarkdown.css'; export interface IMarkdownRenderResult extends IDisposable { readonly element: HTMLElement; @@ -40,19 +39,12 @@ export class MarkdownRenderer { } }); - private readonly _onDidRenderAsync = new Emitter(); - readonly onDidRenderAsync = this._onDidRenderAsync.event; - constructor( private readonly _options: IMarkdownRendererOptions, @ILanguageService private readonly _languageService: ILanguageService, @IOpenerService private readonly _openerService: IOpenerService, ) { } - dispose(): void { - this._onDidRenderAsync.dispose(); - } - render(markdown: IMarkdownString | undefined, options?: MarkdownRenderOptions, markedOptions?: MarkedOptions): IMarkdownRenderResult { if (!markdown) { const element = document.createElement('span'); @@ -68,7 +60,7 @@ export class MarkdownRenderer { }; } - protected _getRenderOptions(markdown: IMarkdownString, disposables: DisposableStore): MarkdownRenderOptions { + private _getRenderOptions(markdown: IMarkdownString, disposables: DisposableStore): MarkdownRenderOptions { return { codeBlockRenderer: async (languageAlias, value) => { // In markdown, @@ -103,10 +95,9 @@ export class MarkdownRenderer { return element; }, - asyncRenderCallback: () => this._onDidRenderAsync.fire(), actionHandler: { callback: (link) => this.openMarkdownLink(link, markdown), - disposables: disposables + disposables } }; } @@ -116,12 +107,13 @@ export class MarkdownRenderer { } } -export async function openLinkFromMarkdown(openerService: IOpenerService, link: string, isTrusted: boolean | MarkdownStringTrustedOptions | undefined): Promise { +export async function openLinkFromMarkdown(openerService: IOpenerService, link: string, isTrusted: boolean | MarkdownStringTrustedOptions | undefined, skipValidation?: boolean): Promise { try { return await openerService.open(link, { fromUserGesture: true, allowContributedOpeners: true, allowCommands: toAllowCommandsOption(isTrusted), + skipValidation }); } catch (e) { onUnexpectedError(e); diff --git a/src/vs/editor/common/codecs/baseToken.ts b/src/vs/editor/common/codecs/baseToken.ts new file mode 100644 index 00000000000..6430ffb61a5 --- /dev/null +++ b/src/vs/editor/common/codecs/baseToken.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 { IRange, Range } from '../../../editor/common/core/range.js'; + +/** + * Base class for all tokens with a `range` that + * reflects token position in the original data. + */ +export abstract class BaseToken { + constructor( + private _range: Range, + ) { } + + public get range(): Range { + return this._range; + } + + /** + * Return text representation of the token. + */ + public abstract get text(): string; + + /** + * Check if this token has the same range as another one. + */ + public sameRange(other: Range): boolean { + return this.range.equalsRange(other); + } + + /** + * Returns a string representation of the token. + */ + public abstract toString(): string; + + /** + * Check if this token is equal to another one. + */ + public equals(other: T): boolean { + if (!(other instanceof this.constructor)) { + return false; + } + + return this.sameRange(other.range); + } + + /** + * Change `range` of the token with provided range components. + */ + public withRange(components: Partial): this { + this._range = new Range( + components.startLineNumber ?? this.range.startLineNumber, + components.startColumn ?? this.range.startColumn, + components.endLineNumber ?? this.range.endLineNumber, + components.endColumn ?? this.range.endColumn, + ); + + return this; + } +} diff --git a/src/vs/editor/common/codecs/linesCodec/linesDecoder.ts b/src/vs/editor/common/codecs/linesCodec/linesDecoder.ts new file mode 100644 index 00000000000..3bd72e5bd73 --- /dev/null +++ b/src/vs/editor/common/codecs/linesCodec/linesDecoder.ts @@ -0,0 +1,213 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Line } from './tokens/line.js'; +import { Range } from '../../core/range.js'; +import { NewLine } from './tokens/newLine.js'; +import { assert } from '../../../../base/common/assert.js'; +import { CarriageReturn } from './tokens/carriageReturn.js'; +import { VSBuffer } from '../../../../base/common/buffer.js'; +import { assertDefined } from '../../../../base/common/types.js'; +import { BaseDecoder } from '../../../../base/common/codecs/baseDecoder.js'; + +/** + * Tokens produced by the `LinesDecoder`. + */ +export type TLineToken = Line | CarriageReturn | NewLine; + +/** + * The `decoder` part of the `LinesCodec` and is able to transform + * data from a binary stream into a stream of text lines(`Line`). + */ +export class LinesDecoder extends BaseDecoder { + /** + * Buffered received data yet to be processed. + */ + private buffer: VSBuffer = VSBuffer.alloc(0); + + /** + * The last emitted `Line` token, if any. The value is used + * to correctly emit remaining line range in the `onStreamEnd` + * method when underlying input stream ends and `buffer` still + * contains some data that must be emitted as the last line. + */ + private lastEmittedLine?: Line; + + /** + * Process data received from the input stream. + */ + protected override onStreamData(chunk: VSBuffer): void { + this.buffer = VSBuffer.concat([this.buffer, chunk]); + + this.processData(false); + } + + /** + * Process buffered data. + * + * @param streamEnded Flag that indicates if the input stream has ended, + * which means that is the last call of this method. + * @throws If internal logic implementation error is detected. + */ + private processData( + streamEnded: boolean, + ) { + // iterate over each line of the data buffer, emitting each line + // as a `Line` token followed by a `NewLine` token, if applies + while (this.buffer.byteLength > 0) { + // get line number based on a previously emitted line, if any + const lineNumber = this.lastEmittedLine + ? this.lastEmittedLine.range.startLineNumber + 1 + : 1; + + // find the `\r`, `\n`, or `\r\n` tokens in the data + const endOfLineTokens = this.findEndOfLineTokens(lineNumber); + const firstToken = endOfLineTokens[0]; + + // if no end-of-the-line tokens found, stop processing because we + // either (1)need more data to arraive or (2)the stream has ended + // in the case (2) remaining data must be emitted as the last line + if (!firstToken) { + // (2) if `streamEnded`, we need to emit the whole remaining + // data as the last line immediately + if (streamEnded) { + this.emitLine(lineNumber, this.buffer.slice(0)); + } + + break; + } + + // emit the line found in the data as the `Line` token + this.emitLine(lineNumber, this.buffer.slice(0, firstToken.range.startColumn - 1)); + + // must always hold true as the `emitLine` above sets this + assertDefined( + this.lastEmittedLine, + 'No last emitted line found.', + ); + + // emit the end-of-the-line tokens + let startColumn = this.lastEmittedLine.range.endColumn; + for (const token of endOfLineTokens) { + const endColumn = startColumn + token.byte.byteLength; + // emit the token updating its column start/end numbers based on + // the emitted line text length and previous end-of-the-line token + this._onData.fire(token.withRange({ startColumn, endColumn })); + // shorten the data buffer by the length of the token + this.buffer = this.buffer.slice(token.byte.byteLength); + // update the start column for the next token + startColumn = endColumn; + } + } + + // if the stream has ended, assert that the input data buffer is now empty + // otherwise we have a logic error and leaving some buffered data behind + if (streamEnded) { + assert( + this.buffer.byteLength === 0, + 'Expected the input data buffer to be empty when the stream ends.', + ); + } + } + + /** + * Find the end of line tokens in the data buffer. + * Can return: + * - [`\r`, `\n`] tokens if the sequence is found + * - [`\r`] token if only the carriage return is found + * - [`\n`] token if only the newline is found + * - an `empty array` if no end of line tokens found + */ + private findEndOfLineTokens( + lineNumber: number, + ): (CarriageReturn | NewLine)[] { + const result = []; + + // find the first occurrence of the carriage return and newline tokens + const carriageReturnIndex = this.buffer.indexOf(CarriageReturn.byte); + const newLineIndex = this.buffer.indexOf(NewLine.byte); + + // if the `\r` comes before the `\n`(if `\n` present at all) + if (carriageReturnIndex >= 0 && (carriageReturnIndex < newLineIndex || newLineIndex === -1)) { + // add the carriage return token first + result.push( + new CarriageReturn(new Range( + lineNumber, + (carriageReturnIndex + 1), + lineNumber, + (carriageReturnIndex + 1) + CarriageReturn.byte.byteLength, + )), + ); + + // if the `\r\n` sequence + if (newLineIndex === carriageReturnIndex + 1) { + // add the newline token to the result + result.push( + new NewLine(new Range( + lineNumber, + (newLineIndex + 1), + lineNumber, + (newLineIndex + 1) + NewLine.byte.byteLength, + )), + ); + } + + if (this.buffer.byteLength > carriageReturnIndex + 1) { + // either `\r` or `\r\n` cases found + return result; + } + + return []; + } + + // no `\r`, but there is `\n` + if (newLineIndex >= 0) { + result.push( + new NewLine(new Range( + lineNumber, + (newLineIndex + 1), + lineNumber, + (newLineIndex + 1) + NewLine.byte.byteLength, + )), + ); + } + + // neither `\r` nor `\n` found, no end of line found at all + return result; + } + + /** + * Emit a provided line as the `Line` token to the output stream. + */ + private emitLine( + lineNumber: number, // Note! 1-based indexing + lineBytes: VSBuffer, + ): void { + + const line = new Line(lineNumber, lineBytes.toString()); + this._onData.fire(line); + + // store the last emitted line so we can use it when we need + // to send the remaining line in the `onStreamEnd` method + this.lastEmittedLine = line; + + // shorten the data buffer by the length of the line emitted + this.buffer = this.buffer.slice(lineBytes.byteLength); + } + + /** + * Handle the end of the input stream - if the buffer still has some data, + * emit it as the last available line token before firing the `onEnd` event. + */ + protected override onStreamEnd(): void { + // if the input data buffer is not empty when the input stream ends, emit + // the remaining data as the last line before firing the `onEnd` event + if (this.buffer.byteLength > 0) { + this.processData(true); + } + + super.onStreamEnd(); + } +} diff --git a/src/vs/editor/common/codecs/linesCodec/tokens/carriageReturn.ts b/src/vs/editor/common/codecs/linesCodec/tokens/carriageReturn.ts new file mode 100644 index 00000000000..a509940bc4e --- /dev/null +++ b/src/vs/editor/common/codecs/linesCodec/tokens/carriageReturn.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Line } from './line.js'; +import { BaseToken } from '../../baseToken.js'; +import { Range } from '../../../core/range.js'; +import { Position } from '../../../core/position.js'; +import { VSBuffer } from '../../../../../base/common/buffer.js'; + +/** + * Token that represent a `carriage return` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class CarriageReturn extends BaseToken { + /** + * The underlying symbol of the token. + */ + public static readonly symbol: string = '\r'; + + /** + * The byte representation of the {@link symbol}. + */ + public static readonly byte = VSBuffer.fromString(CarriageReturn.symbol); + + /** + * The byte representation of the token. + */ + public get byte() { + return CarriageReturn.byte; + } + + /** + * Return text representation of the token. + */ + public get text(): string { + return CarriageReturn.symbol; + } + + /** + * Create new `CarriageReturn` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): CarriageReturn { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new CarriageReturn(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `carriage-return${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/linesCodec/tokens/line.ts b/src/vs/editor/common/codecs/linesCodec/tokens/line.ts new file mode 100644 index 00000000000..6669169967f --- /dev/null +++ b/src/vs/editor/common/codecs/linesCodec/tokens/line.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 { BaseToken } from '../../baseToken.js'; +import { assert } from '../../../../../base/common/assert.js'; +import { Range } from '../../../../../editor/common/core/range.js'; + +/** + * Token representing a line of text with a `range` which + * reflects the line's position in the original data. + */ +export class Line extends BaseToken { + constructor( + // the line index + // Note! 1-based indexing + lineNumber: number, + // the line contents + public readonly text: string, + ) { + assert( + !isNaN(lineNumber), + `The line number must not be a NaN.`, + ); + + assert( + lineNumber > 0, + `The line number must be >= 1, got "${lineNumber}".`, + ); + + super( + new Range( + lineNumber, + 1, + lineNumber, + text.length + 1, + ), + ); + } + + /** + * Check if this token is equal to another one. + */ + public override equals(other: T): boolean { + if (!super.equals(other)) { + return false; + } + + if (!(other instanceof Line)) { + return false; + } + + return this.text === other.text; + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `line("${this.text}")${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/linesCodec/tokens/newLine.ts b/src/vs/editor/common/codecs/linesCodec/tokens/newLine.ts new file mode 100644 index 00000000000..fb826b759ca --- /dev/null +++ b/src/vs/editor/common/codecs/linesCodec/tokens/newLine.ts @@ -0,0 +1,65 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Line } from './line.js'; +import { BaseToken } from '../../baseToken.js'; +import { VSBuffer } from '../../../../../base/common/buffer.js'; +import { Range } from '../../../../../editor/common/core/range.js'; +import { Position } from '../../../../../editor/common/core/position.js'; + +/** + * A token that represent a `new line` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class NewLine extends BaseToken { + /** + * The underlying symbol of the `NewLine` token. + */ + public static readonly symbol: string = '\n'; + + /** + * The byte representation of the {@link symbol}. + */ + public static readonly byte = VSBuffer.fromString(NewLine.symbol); + + /** + * Return text representation of the token. + */ + public get text(): string { + return NewLine.symbol; + } + + /** + * The byte representation of the token. + */ + public get byte() { + return NewLine.byte; + } + + /** + * Create new `NewLine` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): NewLine { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new NewLine( + Range.fromPositions(startPosition, endPosition), + ); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `newline${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/markdownCodec/markdownDecoder.ts b/src/vs/editor/common/codecs/markdownCodec/markdownDecoder.ts new file mode 100644 index 00000000000..3703fa1df29 --- /dev/null +++ b/src/vs/editor/common/codecs/markdownCodec/markdownDecoder.ts @@ -0,0 +1,301 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { MarkdownLink } from './tokens/markdownLink.js'; +import { NewLine } from '../linesCodec/tokens/newLine.js'; +import { assert } from '../../../../base/common/assert.js'; +import { FormFeed } from '../simpleCodec/tokens/formFeed.js'; +import { VSBuffer } from '../../../../base/common/buffer.js'; +import { VerticalTab } from '../simpleCodec/tokens/verticalTab.js'; +import { ReadableStream } from '../../../../base/common/stream.js'; +import { CarriageReturn } from '../linesCodec/tokens/carriageReturn.js'; +import { BaseDecoder } from '../../../../base/common/codecs/baseDecoder.js'; +import { LeftBracket, RightBracket } from '../simpleCodec/tokens/brackets.js'; +import { SimpleDecoder, TSimpleToken } from '../simpleCodec/simpleDecoder.js'; +import { ParserBase, TAcceptTokenResult } from '../simpleCodec/parserBase.js'; +import { LeftParenthesis, RightParenthesis } from '../simpleCodec/tokens/parentheses.js'; + +/** + * Tokens handled by this decoder. + */ +export type TMarkdownToken = MarkdownLink | TSimpleToken; + +/** + * List of characters that stop a markdown link sequence. + */ +const MARKDOWN_LINK_STOP_CHARACTERS: readonly string[] = [CarriageReturn, NewLine, VerticalTab, FormFeed] + .map((token) => { return token.symbol; }); + +/** + * The parser responsible for parsing a `markdown link caption` part of a markdown + * link (e.g., the `[caption text]` part of the `[caption text](./some/path)` link). + * + * The parsing process starts with single `[` token and collects all tokens until + * the first `]` token is encountered. In this successful case, the parser transitions + * into the {@linkcode MarkdownLinkCaption} parser type which continues the general + * parsing process of the markdown link. + * + * Otherwise, if one of the stop characters defined in the {@linkcode MARKDOWN_LINK_STOP_CHARACTERS} + * is encountered before the `]` token, the parsing process is aborted which is communicated to + * the caller by returning a `failure` result. In this case, the caller is assumed to be responsible + * for re-emitting the {@link tokens} accumulated so far as standalone entities since they are no + * longer represent a coherent token entity of a larger size. + */ +class PartialMarkdownLinkCaption extends ParserBase { + constructor(token: LeftBracket) { + super([token]); + } + + public accept(token: TSimpleToken): TAcceptTokenResult { + // any of stop characters is are breaking a markdown link caption sequence + if (MARKDOWN_LINK_STOP_CHARACTERS.includes(token.text)) { + return { + result: 'failure', + wasTokenConsumed: false, + }; + } + + // the `]` character ends the caption of a markdown link + if (token instanceof RightBracket) { + return { + result: 'success', + nextParser: new MarkdownLinkCaption([...this.tokens, token]), + wasTokenConsumed: true, + }; + } + + // otherwise, include the token in the sequence + // and keep the current parser object instance + this.currentTokens.push(token); + return { + result: 'success', + nextParser: this, + wasTokenConsumed: true, + }; + } +} + +/** + * The parser responsible for transitioning from a {@linkcode PartialMarkdownLinkCaption} + * parser to the {@link PartialMarkdownLink} one, therefore serves a parser glue between + * the `[caption]` and the `(./some/path)` parts of the `[caption](./some/path)` link. + * + * The only successful case of this parser is the `(` token that initiated the process + * of parsing the `reference` part of a markdown link and in this case the parser + * transitions into the `PartialMarkdownLink` parser type. + * + * Any other character is considered a failure result. In this case, the caller is assumed + * to be responsible for re-emitting the {@link tokens} accumulated so far as standalone + * entities since they are no longer represent a coherent token entity of a larger size. + */ +class MarkdownLinkCaption extends ParserBase { + public accept(token: TSimpleToken): TAcceptTokenResult { + // the `(` character starts the link part of a markdown link + // that is the only character that can follow the caption + if (token instanceof LeftParenthesis) { + return { + result: 'success', + wasTokenConsumed: true, + nextParser: new PartialMarkdownLink([...this.tokens], token), + }; + } + + return { + result: 'failure', + wasTokenConsumed: false, + }; + } +} + +/** + * The parser responsible for parsing a `link reference` part of a markdown link + * (e.g., the `(./some/path)` part of the `[caption text](./some/path)` link). + * + * The parsing process starts with tokens that represent the `[caption]` part of a markdown + * link, followed by the `(` token. The parser collects all subsequent tokens until final closing + * parenthesis (`)`) is encountered (*\*see [1] below*). In this successful case, the parser object + * transitions into the {@linkcode MarkdownLink} token type which signifies the end of the entire + * parsing process of the link text. + * + * Otherwise, if one of the stop characters defined in the {@linkcode MARKDOWN_LINK_STOP_CHARACTERS} + * is encountered before the final `)` token, the parsing process is aborted which is communicated to + * the caller by returning a `failure` result. In this case, the caller is assumed to be responsible + * for re-emitting the {@link tokens} accumulated so far as standalone entities since they are no + * longer represent a coherent token entity of a larger size. + * + * `[1]` The `reference` part of the markdown link can contain any number of nested parenthesis, e.g., + * `[caption](/some/p(th/file.md)` is a valid markdown link and a valid folder name, hence number + * of open parenthesis must match the number of closing ones and the path sequence is considered + * to be complete as soon as this requirement is met. Therefore the `final` word is used in + * the description comments above to highlight this important detail. + */ +class PartialMarkdownLink extends ParserBase { + /** + * Number of open parenthesis in the sequence. + * See comment in the {@linkcode accept} method for more details. + */ + private openParensCount: number = 1; + + constructor( + protected readonly captionTokens: TSimpleToken[], + token: LeftParenthesis, + ) { + super([token]); + } + + public override get tokens(): readonly TSimpleToken[] { + return [...this.captionTokens, ...this.currentTokens]; + } + + public accept(token: TSimpleToken): TAcceptTokenResult { + // markdown links allow for nested parenthesis inside the link reference part, but + // the number of open parenthesis must match the number of closing parenthesis, e.g.: + // - `[caption](/some/p()th/file.md)` is a valid markdown link + // - `[caption](/some/p(th/file.md)` is an invalid markdown link + // hence we use the `openParensCount` variable to keep track of the number of open + // parenthesis encountered so far; then upon encountering a closing parenthesis we + // decrement the `openParensCount` and if it reaches 0 - we consider the link reference + // to be complete + + if (token instanceof LeftParenthesis) { + this.openParensCount += 1; + } + + if (token instanceof RightParenthesis) { + this.openParensCount -= 1; + + // sanity check! this must alway hold true because we return a complete markdown + // link as soon as we encounter matching number of closing parenthesis, hence + // we must never have `openParensCount` that is less than 0 + assert( + this.openParensCount >= 0, + `Unexpected right parenthesis token encountered: '${token}'.`, + ); + + // the markdown link is complete as soon as we get the same number of closing parenthesis + if (this.openParensCount === 0) { + const { startLineNumber, startColumn } = this.captionTokens[0].range; + + // create link caption string + const caption = this.captionTokens + .map((token) => { return token.text; }) + .join(''); + + // create link reference string + this.currentTokens.push(token); + const reference = this.currentTokens + .map((token) => { return token.text; }).join(''); + + // return complete markdown link object + return { + result: 'success', + wasTokenConsumed: true, + nextParser: new MarkdownLink( + startLineNumber, + startColumn, + caption, + reference, + ), + }; + } + } + + // any of stop characters is are breaking a markdown link reference sequence + if (MARKDOWN_LINK_STOP_CHARACTERS.includes(token.text)) { + return { + result: 'failure', + wasTokenConsumed: false, + }; + } + + // the rest of the tokens can be included in the sequence + this.currentTokens.push(token); + return { + result: 'success', + nextParser: this, + wasTokenConsumed: true, + }; + } +} + +/** + * Decoder capable of parsing markdown entities (e.g., links) from a sequence of simplier tokens. + */ +export class MarkdownDecoder extends BaseDecoder { + /** + * Current parser object that is responsible for parsing a sequence of tokens + * into some markdown entity. + */ + private current?: PartialMarkdownLinkCaption | MarkdownLinkCaption | PartialMarkdownLink; + + constructor( + stream: ReadableStream, + ) { + super(new SimpleDecoder(stream)); + } + + protected override onStreamData(token: TSimpleToken): void { + // markdown links start with `[` character, so here we can + // initiate the process of parsing a markdown link + if (token instanceof LeftBracket && !this.current) { + this.current = new PartialMarkdownLinkCaption(token); + + return; + } + + // if current parser was not initiated before, - we are not inside a + // sequence of tokens we care about, therefore re-emit the token + // immediately and continue to the next one + if (!this.current) { + this._onData.fire(token); + return; + } + + // if there is a current parser object, submit the token to it + // so it can progress with parsing the tokens sequence + const parseResult = this.current.accept(token); + if (parseResult.result === 'success') { + // if got a parsed out `MarkdownLink` back, emit it + // then reset the current parser object + if (parseResult.nextParser instanceof MarkdownLink) { + this._onData.fire(parseResult.nextParser); + delete this.current; + } else { + // otherwise, update the current parser object + this.current = parseResult.nextParser; + } + } else { + // if failed to parse a sequence of a tokens as a single markdown + // entity (e.g., a link), re-emit the tokens accumulated so far + // then reset the current parser object + for (const token of this.current.tokens) { + this._onData.fire(token); + delete this.current; + } + } + + // if token was not consumed by the parser, call `onStreamData` again + // so the token is properly handled by the decoder in the case when a + // new sequence starts with this token + if (!parseResult.wasTokenConsumed) { + this.onStreamData(token); + } + } + + protected override onStreamEnd(): void { + // if the stream has ended and there is a current incomplete parser + // object present, then re-emit its tokens as standalone entities + if (this.current) { + const { tokens } = this.current; + delete this.current; + + for (const token of [...tokens]) { + this._onData.fire(token); + } + } + + super.onStreamEnd(); + } +} diff --git a/src/vs/editor/common/codecs/markdownCodec/tokens/markdownLink.ts b/src/vs/editor/common/codecs/markdownCodec/tokens/markdownLink.ts new file mode 100644 index 00000000000..b4c8947a213 --- /dev/null +++ b/src/vs/editor/common/codecs/markdownCodec/tokens/markdownLink.ts @@ -0,0 +1,136 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BaseToken } from '../../baseToken.js'; +import { MarkdownToken } from './markdownToken.js'; +import { IRange, Range } from '../../../core/range.js'; +import { assert } from '../../../../../base/common/assert.js'; + +/** + * A token that represent a `markdown link` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class MarkdownLink extends MarkdownToken { + /** + * Check if this `markdown link` points to a valid URL address. + */ + public readonly isURL: boolean; + + constructor( + /** + * The starting line number of the link (1-based indexing). + */ + lineNumber: number, + /** + * The starting column number of the link (1-based indexing). + */ + columnNumber: number, + /** + * The caption of the link, including the square brackets. + */ + private readonly caption: string, + /** + * The reference of the link, including the parentheses. + */ + private readonly reference: string, + ) { + assert( + !isNaN(lineNumber), + `The line number must not be a NaN.`, + ); + + assert( + lineNumber > 0, + `The line number must be >= 1, got "${lineNumber}".`, + ); + + assert( + columnNumber > 0, + `The column number must be >= 1, got "${columnNumber}".`, + ); + + assert( + caption[0] === '[' && caption[caption.length - 1] === ']', + `The caption must be enclosed in square brackets, got "${caption}".`, + ); + + assert( + reference[0] === '(' && reference[reference.length - 1] === ')', + `The reference must be enclosed in parentheses, got "${reference}".`, + ); + + super( + new Range( + lineNumber, + columnNumber, + lineNumber, + columnNumber + caption.length + reference.length, + ), + ); + + // set up the `isURL` flag based on the current + try { + new URL(this.path); + this.isURL = true; + } catch { + this.isURL = false; + } + } + + public override get text(): string { + return `${this.caption}${this.reference}`; + } + + /** + * Returns the `reference` part of the link without enclosing parentheses. + */ + public get path(): string { + return this.reference.slice(1, this.reference.length - 1); + } + + /** + * Check if this token is equal to another one. + */ + public override equals(other: T): boolean { + if (!super.sameRange(other.range)) { + return false; + } + + if (!(other instanceof MarkdownLink)) { + return false; + } + + return this.text === other.text; + } + + /** + * Get the range of the `link part` of the token. + */ + public get linkRange(): IRange | undefined { + if (this.path.length === 0) { + return undefined; + } + + const { range } = this; + + // note! '+1' for openning `(` of the link + const startColumn = range.startColumn + this.caption.length + 1; + const endColumn = startColumn + this.path.length; + + return new Range( + range.startLineNumber, + startColumn, + range.endLineNumber, + endColumn, + ); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `md-link("${this.text}")${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/markdownCodec/tokens/markdownToken.ts b/src/vs/editor/common/codecs/markdownCodec/tokens/markdownToken.ts new file mode 100644 index 00000000000..fc1935d081b --- /dev/null +++ b/src/vs/editor/common/codecs/markdownCodec/tokens/markdownToken.ts @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BaseToken } from '../../baseToken.js'; + +/** + * Common base token that all `markdown` tokens should + * inherit from. + */ +export abstract class MarkdownToken extends BaseToken { } diff --git a/src/vs/editor/common/codecs/simpleCodec/parserBase.ts b/src/vs/editor/common/codecs/simpleCodec/parserBase.ts new file mode 100644 index 00000000000..9e864177f9f --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/parserBase.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BaseToken } from '../baseToken.js'; + +/** + * Common interface for a result of accepting a next token + * in a sequence. + */ +export interface IAcceptTokenResult { + /** + * The result type of accepting a next token in a sequence. + */ + result: 'success' | 'failure'; + + /** + * Whether the token to accept was consumed by the parser + * during the accept operation. + */ + wasTokenConsumed: boolean; +} + +/** + * Successful result of accepting a next token in a sequence. + */ +export interface IAcceptTokenSuccess extends IAcceptTokenResult { + result: 'success'; + nextParser: T; +} + +/** + * Failure result of accepting a next token in a sequence. + */ +export interface IAcceptTokenFailure extends IAcceptTokenResult { + result: 'failure'; +} + +/** + * The result of operation of accepting a next token in a sequence. + */ +export type TAcceptTokenResult = IAcceptTokenSuccess | IAcceptTokenFailure; + +/** + * An abstract parser class that is able to parse a sequence of + * tokens into a new single entity. + */ +export abstract class ParserBase { + constructor( + /** + * Set of tokens that were accumulated so far. + */ + protected readonly currentTokens: TToken[] = [], + ) { } + + /** + * Get the tokens that were accumulated so far. + */ + public get tokens(): readonly TToken[] { + return this.currentTokens; + } + + /** + * Accept a new token returning parsing result: + * - successful result must include the next parser object or a fully parsed out token + * - failure result must indicate that the token was not consumed + * + * @param token The token to accept. + * @returns The parsing result. + */ + public abstract accept(token: TToken): TAcceptTokenResult; +} diff --git a/src/vs/editor/common/codecs/simpleCodec/simpleDecoder.ts b/src/vs/editor/common/codecs/simpleCodec/simpleDecoder.ts new file mode 100644 index 00000000000..88ad1298501 --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/simpleDecoder.ts @@ -0,0 +1,103 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Hash } from './tokens/hash.js'; +import { Colon } from './tokens/colon.js'; +import { FormFeed } from './tokens/formFeed.js'; +import { Tab } from '../simpleCodec/tokens/tab.js'; +import { Word } from '../simpleCodec/tokens/word.js'; +import { VerticalTab } from './tokens/verticalTab.js'; +import { Space } from '../simpleCodec/tokens/space.js'; +import { NewLine } from '../linesCodec/tokens/newLine.js'; +import { VSBuffer } from '../../../../base/common/buffer.js'; +import { LeftBracket, RightBracket } from './tokens/brackets.js'; +import { ReadableStream } from '../../../../base/common/stream.js'; +import { CarriageReturn } from '../linesCodec/tokens/carriageReturn.js'; +import { LinesDecoder, TLineToken } from '../linesCodec/linesDecoder.js'; +import { BaseDecoder } from '../../../../base/common/codecs/baseDecoder.js'; +import { LeftParenthesis, RightParenthesis } from './tokens/parentheses.js'; + +/** + * A token type that this decoder can handle. + */ +export type TSimpleToken = Word | Space | Tab | VerticalTab | NewLine | FormFeed | CarriageReturn | LeftBracket + | RightBracket | LeftParenthesis | RightParenthesis | Colon | Hash; + +/** + * List of well-known distinct tokens that this decoder emits (excluding + * the word stop characters defined below). Everything else is considered + * an arbitrary "text" sequence and is emitted as a single `Word` token. + */ +const WELL_KNOWN_TOKENS = [ + Space, Tab, VerticalTab, FormFeed, LeftBracket, RightBracket, + LeftParenthesis, RightParenthesis, Colon, Hash, +]; + +/** + * Characters that stop a "word" sequence. + * Note! the `\r` and `\n` are excluded from the list because this decoder based on `LinesDecoder` which + * already handles the `carriagereturn`/`newline` cases and emits lines that don't contain them. + */ +const WORD_STOP_CHARACTERS = [ + Space.symbol, Tab.symbol, VerticalTab.symbol, FormFeed.symbol, + LeftBracket.symbol, RightBracket.symbol, LeftParenthesis.symbol, + RightParenthesis.symbol, Colon.symbol, Hash.symbol, +]; + +/** + * A decoder that can decode a stream of `Line`s into a stream + * of simple token, - `Word`, `Space`, `Tab`, `NewLine`, etc. + */ +export class SimpleDecoder extends BaseDecoder { + constructor( + stream: ReadableStream, + ) { + super(new LinesDecoder(stream)); + } + + protected override onStreamData(token: TLineToken): void { + // re-emit new line tokens immediately + if (token instanceof CarriageReturn || token instanceof NewLine) { + this._onData.fire(token); + + return; + } + + // loop through the text separating it into `Word` and `Space` tokens + let i = 0; + while (i < token.text.length) { + // index is 0-based, but column numbers are 1-based + const columnNumber = i + 1; + + // check if the current character is a well-known token + const tokenConstructor = WELL_KNOWN_TOKENS + .find((wellKnownToken) => { + return wellKnownToken.symbol === token.text[i]; + }); + + // if it is a well-known token, emit it and continue to the next one + if (tokenConstructor) { + this._onData.fire(tokenConstructor.newOnLine(token, columnNumber)); + + i++; + continue; + } + + // otherwise, it is an arbitrary "text" sequence of characters, + // that needs to be collected into a single `Word` token, hence + // read all the characters until a stop character is encountered + let word = ''; + while (i < token.text.length && !(WORD_STOP_CHARACTERS.includes(token.text[i]))) { + word += token.text[i]; + i++; + } + + // emit a "text" sequence of characters as a single `Word` token + this._onData.fire( + Word.newOnLine(word, token, columnNumber), + ); + } + } +} diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/brackets.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/brackets.ts new file mode 100644 index 00000000000..5c6c1e46a5d --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/brackets.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 { BaseToken } from '../../baseToken.js'; +import { Range } from '../../../core/range.js'; +import { Position } from '../../../core/position.js'; +import { Line } from '../../linesCodec/tokens/line.js'; + +/** + * A token that represent a `[` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class LeftBracket extends BaseToken { + /** + * The underlying symbol of the `LeftBracket` token. + */ + public static readonly symbol: string = '['; + + /** + * Return text representation of the token. + */ + public get text(): string { + return LeftBracket.symbol; + } + + /** + * Create new `LeftBracket` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): LeftBracket { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + // the tab token length is 1, hence `+ 1` + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new LeftBracket(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `left-bracket${this.range}`; + } +} + +/** + * A token that represent a `]` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class RightBracket extends BaseToken { + /** + * The underlying symbol of the `RightBracket` token. + */ + public static readonly symbol: string = ']'; + + /** + * Return text representation of the token. + */ + public get text(): string { + return RightBracket.symbol; + } + + /** + * Create new `RightBracket` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): RightBracket { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + // the tab token length is 1, hence `+ 1` + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new RightBracket(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `right-bracket${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/colon.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/colon.ts new file mode 100644 index 00000000000..2c4b89d9ce5 --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/colon.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 { BaseToken } from '../../baseToken.js'; +import { Range } from '../../../core/range.js'; +import { Position } from '../../../core/position.js'; +import { Line } from '../../linesCodec/tokens/line.js'; + +/** + * A token that represent a `:` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class Colon extends BaseToken { + /** + * The underlying symbol of the `LeftBracket` token. + */ + public static readonly symbol: string = ':'; + + /** + * Return text representation of the token. + */ + public get text(): string { + return Colon.symbol; + } + + /** + * Create new `LeftBracket` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): Colon { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + // the tab token length is 1, hence `+ 1` + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new Colon(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `colon${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/formFeed.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/formFeed.ts new file mode 100644 index 00000000000..35f55dd8a2a --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/formFeed.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 { BaseToken } from '../../baseToken.js'; +import { Line } from '../../linesCodec/tokens/line.js'; +import { Range } from '../../../../../editor/common/core/range.js'; +import { Position } from '../../../../../editor/common/core/position.js'; + +/** + * Token that represent a `form feed` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class FormFeed extends BaseToken { + /** + * The underlying symbol of the token. + */ + public static readonly symbol: string = '\f'; + + /** + * Return text representation of the token. + */ + public get text(): string { + return FormFeed.symbol; + } + + /** + * Create new `FormFeed` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): FormFeed { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new FormFeed(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `formfeed${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/hash.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/hash.ts new file mode 100644 index 00000000000..372e0b2ee3d --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/hash.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 { BaseToken } from '../../baseToken.js'; +import { Range } from '../../../core/range.js'; +import { Position } from '../../../core/position.js'; +import { Line } from '../../linesCodec/tokens/line.js'; + +/** + * A token that represent a `#` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class Hash extends BaseToken { + /** + * The underlying symbol of the `LeftBracket` token. + */ + public static readonly symbol: string = '#'; + + /** + * Return text representation of the token. + */ + public get text(): string { + return Hash.symbol; + } + + /** + * Create new `LeftBracket` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): Hash { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + // the tab token length is 1, hence `+ 1` + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new Hash(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `hash${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/parentheses.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/parentheses.ts new file mode 100644 index 00000000000..b67f4e10f5c --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/parentheses.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 { BaseToken } from '../../baseToken.js'; +import { Range } from '../../../core/range.js'; +import { Position } from '../../../core/position.js'; +import { Line } from '../../linesCodec/tokens/line.js'; + +/** + * A token that represent a `(` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class LeftParenthesis extends BaseToken { + /** + * The underlying symbol of the `LeftParenthesis` token. + */ + public static readonly symbol: string = '('; + + /** + * Return text representation of the token. + */ + public get text(): string { + return LeftParenthesis.symbol; + } + + /** + * Create new `LeftParenthesis` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): LeftParenthesis { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + // the tab token length is 1, hence `+ 1` + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new LeftParenthesis(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `left-parenthesis${this.range}`; + } +} + +/** + * A token that represent a `)` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class RightParenthesis extends BaseToken { + /** + * The underlying symbol of the `RightParenthesis` token. + */ + public static readonly symbol: string = ')'; + + /** + * Return text representation of the token. + */ + public get text(): string { + return RightParenthesis.symbol; + } + + /** + * Create new `RightParenthesis` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): RightParenthesis { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + // the tab token length is 1, hence `+ 1` + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new RightParenthesis(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `right-parenthesis${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/space.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/space.ts new file mode 100644 index 00000000000..18a5dff4a0a --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/space.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 { BaseToken } from '../../baseToken.js'; +import { Line } from '../../linesCodec/tokens/line.js'; +import { Range } from '../../../../../editor/common/core/range.js'; +import { Position } from '../../../../../editor/common/core/position.js'; + +/** + * A token that represent a `space` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class Space extends BaseToken { + /** + * The underlying symbol of the `Space` token. + */ + public static readonly symbol: string = ' '; + + /** + * Return text representation of the token. + */ + public get text(): string { + return Space.symbol; + } + + /** + * Create new `Space` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): Space { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new Space(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `space${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts new file mode 100644 index 00000000000..7f511c2626b --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/tab.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 { BaseToken } from '../../baseToken.js'; +import { Line } from '../../linesCodec/tokens/line.js'; +import { Range } from '../../../../../editor/common/core/range.js'; +import { Position } from '../../../../../editor/common/core/position.js'; + +/** + * A token that represent a `tab` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class Tab extends BaseToken { + /** + * The underlying symbol of the `Tab` token. + */ + public static readonly symbol: string = '\t'; + + /** + * Return text representation of the token. + */ + public get text(): string { + return Tab.symbol; + } + + /** + * Create new `Tab` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): Tab { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + // the tab token length is 1, hence `+ 1` + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new Tab(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `tab${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/verticalTab.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/verticalTab.ts new file mode 100644 index 00000000000..c6b87db0e37 --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/verticalTab.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 { BaseToken } from '../../baseToken.js'; +import { Line } from '../../linesCodec/tokens/line.js'; +import { Range } from '../../../../../editor/common/core/range.js'; +import { Position } from '../../../../../editor/common/core/position.js'; + +/** + * Token that represent a `vertical tab` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class VerticalTab extends BaseToken { + /** + * The underlying symbol of the `VerticalTab` token. + */ + public static readonly symbol: string = '\v'; + + /** + * Return text representation of the token. + */ + public get text(): string { + return VerticalTab.symbol; + } + + /** + * Create new `VerticalTab` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): VerticalTab { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new VerticalTab(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `vtab${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/word.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/word.ts new file mode 100644 index 00000000000..fc3cefa79be --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/word.ts @@ -0,0 +1,72 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BaseToken } from '../../baseToken.js'; +import { Line } from '../../linesCodec/tokens/line.js'; +import { Range } from '../../../../../editor/common/core/range.js'; +import { Position } from '../../../../../editor/common/core/position.js'; + +/** + * A token that represent a word - a set of continuous + * characters without stop characters, like a `space`, + * a `tab`, or a `new line`. + */ +export class Word extends BaseToken { + constructor( + /** + * The word range. + */ + range: Range, + + /** + * The string value of the word. + */ + public readonly text: string, + ) { + super(range); + } + + /** + * Create new `Word` token with the given `text` and the range + * inside the given `Line` at the specified `column number`. + */ + public static newOnLine( + text: string, + line: Line, + atColumnNumber: number, + ): Word { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + const endPosition = new Position(range.startLineNumber, atColumnNumber + text.length); + + return new Word( + Range.fromPositions(startPosition, endPosition), + text, + ); + } + + /** + * Check if this token is equal to another one. + */ + public override equals(other: T): boolean { + if (!super.equals(other)) { + return false; + } + + if (!(other instanceof Word)) { + return false; + } + + return this.text === other.text; + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `word("${this.text.slice(0, 8)}")${this.range}`; + } +} diff --git a/src/vs/editor/common/commands/replaceCommand.ts b/src/vs/editor/common/commands/replaceCommand.ts index f4beabe78a6..779dfd9a7b9 100644 --- a/src/vs/editor/common/commands/replaceCommand.ts +++ b/src/vs/editor/common/commands/replaceCommand.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Position } from '../core/position.js'; import { Range } from '../core/range.js'; import { Selection, SelectionDirection } from '../core/selection.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../editorCommon.js'; @@ -31,6 +32,38 @@ export class ReplaceCommand implements ICommand { } } +export class ReplaceOvertypeCommand implements ICommand { + + private readonly _range: Range; + private readonly _text: string; + public readonly insertsAutoWhitespace: boolean; + + constructor(range: Range, text: string, insertsAutoWhitespace: boolean = false) { + this._range = range; + this._text = text; + this.insertsAutoWhitespace = insertsAutoWhitespace; + } + + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { + const intialStartPosition = this._range.getStartPosition(); + const initialEndPosition = this._range.getEndPosition(); + const initialEndLineNumber = initialEndPosition.lineNumber; + const offsetDelta = this._text.length + (this._range.isEmpty() ? 0 : -1); + let endPosition = addPositiveOffsetToModelPosition(model, initialEndPosition, offsetDelta); + if (endPosition.lineNumber > initialEndLineNumber) { + endPosition = new Position(initialEndLineNumber, model.getLineMaxColumn(initialEndLineNumber)); + } + const replaceRange = Range.fromPositions(intialStartPosition, endPosition); + builder.addTrackedEditOperation(replaceRange, this._text); + } + + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { + const inverseEditOperations = helper.getInverseEditOperations(); + const srcRange = inverseEditOperations[0].range; + return Selection.fromPositions(srcRange.getEndPosition()); + } +} + export class ReplaceCommandThatSelectsText implements ICommand { private readonly _range: Range; @@ -102,6 +135,33 @@ export class ReplaceCommandWithOffsetCursorState implements ICommand { } } +export class ReplaceOvertypeCommandOnCompositionEnd implements ICommand { + + private readonly _range: Range; + + constructor(range: Range) { + this._range = range; + } + + public getEditOperations(model: ITextModel, builder: IEditOperationBuilder): void { + const text = model.getValueInRange(this._range); + const initialEndPosition = this._range.getEndPosition(); + const initialEndLineNumber = initialEndPosition.lineNumber; + let endPosition = addPositiveOffsetToModelPosition(model, initialEndPosition, text.length); + if (endPosition.lineNumber > initialEndLineNumber) { + endPosition = new Position(initialEndLineNumber, model.getLineMaxColumn(initialEndLineNumber)); + } + const replaceRange = Range.fromPositions(initialEndPosition, endPosition); + builder.addTrackedEditOperation(replaceRange, ''); + } + + public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { + const inverseEditOperations = helper.getInverseEditOperations(); + const srcRange = inverseEditOperations[0].range; + return Selection.fromPositions(srcRange.getEndPosition()); + } +} + export class ReplaceCommandThatPreservesSelection implements ICommand { private readonly _range: Range; @@ -127,3 +187,29 @@ export class ReplaceCommandThatPreservesSelection implements ICommand { return helper.getTrackedSelection(this._selectionId!); } } + +function addPositiveOffsetToModelPosition(model: ITextModel, position: Position, offset: number): Position { + if (offset < 0) { + throw new Error('Unexpected negative delta'); + } + const lineCount = model.getLineCount(); + let endPosition = new Position(lineCount, model.getLineMaxColumn(lineCount)); + for (let lineNumber = position.lineNumber; lineNumber <= lineCount; lineNumber++) { + if (lineNumber === position.lineNumber) { + const futureOffset = offset - model.getLineMaxColumn(position.lineNumber) + position.column; + if (futureOffset <= 0) { + endPosition = new Position(position.lineNumber, position.column + offset); + break; + } + offset = futureOffset; + } else { + const futureOffset = offset - model.getLineMaxColumn(lineNumber); + if (futureOffset <= 0) { + endPosition = new Position(lineNumber, offset); + break; + } + offset = futureOffset; + } + } + return endPosition; +} diff --git a/src/vs/editor/common/config/editorConfigurationSchema.ts b/src/vs/editor/common/config/editorConfigurationSchema.ts index e89269b9f21..38aedd4868e 100644 --- a/src/vs/editor/common/config/editorConfigurationSchema.ts +++ b/src/vs/editor/common/config/editorConfigurationSchema.ts @@ -25,6 +25,7 @@ const editorConfiguration: IConfigurationNode = { type: 'number', default: EDITOR_MODEL_DEFAULTS.tabSize, minimum: 1, + maximum: 16, markdownDescription: nls.localize('tabSize', "The number of spaces a tab is equal to. This setting is overridden based on the file contents when {0} is on.", '`#editor.detectIndentation#`') }, 'editor.indentSize': { @@ -113,7 +114,19 @@ const editorConfiguration: IConfigurationNode = { type: 'boolean', default: false, markdownDescription: nls.localize('editor.experimental.treeSitterTelemetry', "Controls whether tree sitter parsing should be turned on and telemetry collected. Setting `editor.experimental.preferTreeSitter` for specific languages will take precedence."), - tags: ['experimental'] + tags: ['experimental', 'onExP'] + }, + 'editor.experimental.preferTreeSitter.typescript': { + type: 'boolean', + default: false, + markdownDescription: nls.localize('editor.experimental.preferTreeSitter.typescript', "Controls whether tree sitter parsing should be turned on for typescript. This will take precedence over `editor.experimental.treeSitterTelemetry` for typescript."), + tags: ['experimental', 'onExP'] + }, + 'editor.experimental.preferTreeSitter.ini': { + type: 'boolean', + default: false, + markdownDescription: nls.localize('editor.experimental.preferTreeSitter.ini', "Controls whether tree sitter parsing should be turned on for ini. This will take precedence over `editor.experimental.treeSitterTelemetry` for ini."), + tags: ['experimental', 'onExP'] }, 'editor.language.brackets': { type: ['array', 'null'], @@ -218,8 +231,7 @@ const editorConfiguration: IConfigurationNode = { markdownEnumDescriptions: [ nls.localize('diffAlgorithm.legacy', "Uses the legacy diffing algorithm."), nls.localize('diffAlgorithm.advanced', "Uses the advanced diffing algorithm."), - ], - tags: ['experimental'], + ] }, 'diffEditor.hideUnchangedRegions.enabled': { type: 'boolean', diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 9c5f5a34d4f..3fdecc8717e 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -16,6 +16,7 @@ import { USUAL_WORD_SEPARATORS } from '../core/wordHelper.js'; import * as nls from '../../../nls.js'; import { AccessibilitySupport } from '../../../platform/accessibility/common/accessibility.js'; import { IConfigurationPropertySchema } from '../../../platform/configuration/common/configurationRegistry.js'; +import product from '../../../platform/product/common/product.js'; //#region typed options @@ -238,10 +239,19 @@ export interface IEditorOptions { */ cursorSmoothCaretAnimation?: 'off' | 'explicit' | 'on'; /** - * Control the cursor style, either 'block' or 'line'. + * Control the cursor style in insert mode. * Defaults to 'line'. */ cursorStyle?: 'line' | 'block' | 'underline' | 'line-thin' | 'block-outline' | 'underline-thin'; + /** + * Control the cursor style in overtype mode. + * Defaults to 'block'. + */ + overtypeCursorStyle?: 'line' | 'block' | 'underline' | 'line-thin' | 'block-outline' | 'underline-thin'; + /** + * Controls whether paste in overtype mode should overwrite or insert. + */ + overtypeOnPaste?: boolean; /** * Control the width of the cursor when cursorStyle is set to 'line' */ @@ -259,7 +269,7 @@ export interface IEditorOptions { /** * Controls whether to use default color decorations or not using the default document color provider */ - defaultColorDecorators?: boolean; + defaultColorDecorators?: 'auto' | 'always' | 'never'; /** * Disable the use of `transform: translate3d(0px, 0px, 0px)` for the editor margin and lines layers. * The usage of `transform: translate3d(0px, 0px, 0px)` acts as a hint for browsers to create an extra layer. @@ -435,7 +445,6 @@ export interface IEditorOptions { */ suggest?: ISuggestOptions; inlineSuggest?: IInlineSuggestOptions; - experimentalInlineEdit?: IInlineEditOptions; /** * Smart select options. */ @@ -573,6 +582,13 @@ export interface IEditorOptions { * 'multiFile' triggers occurrence highlighting across valid open documents */ occurrencesHighlight?: 'off' | 'singleFile' | 'multiFile'; + /** + * Controls delay for occurrences highlighting + * Defaults to 250. + * Minimum value is 0 + * Maximum value is 2000 + */ + occurrencesHighlightDelay?: number; /** * Show code lens * Defaults to true. @@ -967,6 +983,7 @@ export interface IEnvironmentalOptions { readonly emptySelectionClipboard: boolean; readonly pixelRatio: number; readonly tabFocusMode: boolean; + readonly inputMode: 'insert' | 'overtype'; readonly accessibilitySupport: AccessibilitySupport; readonly glyphMarginDecorationLaneCount: number; } @@ -1649,6 +1666,16 @@ export interface IEditorFindOptions { * Controls whether the search result and diff result automatically restarts from the beginning (or the end) when no further matches can be found */ loop?: boolean; + /** + * @internal + * Controls how the find widget search history should be stored + */ + history?: 'never' | 'workspace'; + /** + * @internal + * Controls how the replace widget search history should be stored + */ + replaceHistory?: 'never' | 'workspace'; } /** @@ -1665,7 +1692,9 @@ class EditorFind extends BaseEditorOption(input.history, this.defaultValue.history, ['never', 'workspace']), + replaceHistory: stringSet<'never' | 'workspace'>(input.replaceHistory, this.defaultValue.replaceHistory, ['never', 'workspace']), }; } } @@ -1867,6 +1917,39 @@ class EditorFontInfo extends ComputedEditorOption { + + constructor() { + super(EditorOption.effectiveCursorStyle); + } + + public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions, _: TextEditorCursorStyle): TextEditorCursorStyle { + return env.inputMode === 'overtype' ? + options.get(EditorOption.overtypeCursorStyle) : + options.get(EditorOption.cursorStyle); + } +} + +//#endregion + +//#region effectiveExperimentalEditContext + +class EffectiveExperimentalEditContextEnabled extends ComputedEditorOption { + + constructor() { + super(EditorOption.effectiveExperimentalEditContextEnabled); + } + + public compute(env: IEnvironmentalOptions, options: IComputedEditorOptions): boolean { + const editContextSupported = typeof (globalThis as any).EditContext === 'function'; + return editContextSupported && env.accessibilitySupport !== AccessibilitySupport.Enabled && options.get(EditorOption.experimentalEditContextEnabled); + } +} + +//#endregion + //#region fontSize class EditorFontSize extends SimpleEditorOption { @@ -2821,7 +2904,6 @@ class EditorLightbulb extends BaseEditorOption.+)) that encapsulates the section header. + * Optionally can include another match group named 'separator'. + * To match multi-line headers like: + * // ========== + * // My Section + * // ========== + * Use a pattern like: ^={3,}\n^\/\/ *(?