diff --git a/.eslintignore b/.eslintignore index ec701c488888c..112cdef4d2578 100644 --- a/.eslintignore +++ b/.eslintignore @@ -17,7 +17,3 @@ **/extensions/typescript-basics/test/colorize-fixtures/** **/extensions/**/dist/** **/extensions/typescript-language-features/test-workspace/** - -# These files are not linted by `yarn eslint`, so we exclude them from being linted in the editor. -# This ensures that if we add new rules and they pass CI, there are also no errors in the editor. -/resources/web/code-web.js diff --git a/.eslintrc.json b/.eslintrc.json index 70012db4e74e4..06eeba25e8d6c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -16,6 +16,7 @@ "eqeqeq": "warn", "no-buffer-constructor": "warn", "no-caller": "warn", + "no-case-declarations": "warn", "no-debugger": "warn", "no-duplicate-case": "warn", "no-duplicate-imports": "warn", @@ -91,947 +92,425 @@ ], "code-import-patterns": [ "warn", - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // !!! Do not relax these rules !!! - // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - { - "target": "**/vs/base/common/**", - "restrictions": [ - "vs/nls", - "**/vs/base/common/**" - ] - }, - { - "target": "**/vs/base/test/common/**", - "restrictions": [ - "assert", - "sinon", - "sinon-test", - "vs/nls", - "**/vs/base/common/**", - "**/vs/base/test/common/**" - ] - }, - { - "target": "**/vs/base/browser/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/{common,browser}/**" - ] - }, - { - "target": "**/vs/base/electron-sandbox/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/{common,browser,electron-sandbox}/**" - ] - }, - { - "target": "**/vs/base/node/**", - "restrictions": [ - "vs/nls", - "**/vs/base/{common,node}/**", - "@vscode/*", - "@parcel/*", - "*" // node modules - ] - }, - { - // vs/base/test/browser contains tests for vs/base/browser - "target": "**/vs/base/test/browser/**", - "restrictions": [ - "assert", - "sinon", - "sinon-test", - "vs/nls", - "**/vs/base/{common,browser}/**", - "**/vs/base/test/{common,browser}/**" - ] - }, - { - "target": "**/vs/base/parts/*/common/**", - "restrictions": [ - "vs/nls", - "**/vs/base/common/**", - "**/vs/base/parts/*/common/**" - ] - }, - { - "target": "**/vs/base/parts/*/browser/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/{common,browser}/**", - "**/vs/base/parts/*/{common,browser}/**" - ] - }, { - "target": "**/vs/base/parts/*/node/**", - "restrictions": [ - "vs/nls", - "**/vs/base/{common,node}/**", - "**/vs/base/parts/*/{common,node}/**", - "@vscode/*", - "@parcel/*", - "*" // node modules - ] - }, - { - "target": "**/vs/base/parts/*/electron-sandbox/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/{common,browser,electron-sandbox}/**", - "**/vs/base/parts/*/{common,browser,electron-sandbox}/**" - ] - }, - { - "target": "**/vs/base/parts/*/electron-browser/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/base/parts/*/{common,browser,node,electron-sandbox,electron-browser}/**", - "@vscode/*", - "@parcel/*", - "*" // node modules - ] - }, - { - "target": "**/vs/base/parts/*/electron-main/**", - "restrictions": [ - "vs/nls", - "**/vs/base/{common,node,electron-main}/**", - "**/vs/base/parts/*/{common,node,electron-main}/**", - "@vscode/*", - "@parcel/*", - "*" // node modules - ] - }, - { - "target": "**/vs/platform/*/common/**", - "restrictions": [ - "vs/nls", - "**/vs/base/common/**", - "**/vs/base/parts/*/common/**", - "**/vs/platform/*/common/**", - "tas-client-umd" - ] - }, - { - "target": "**/vs/platform/*/test/common/**", - "restrictions": [ + // imports that are allowed in all files of layers: + // - browser + // - electron-sandbox + // - electron-browser + "when": "hasBrowser", + "allow": [ + "vs/css!./**/*" + ] + }, + { + // imports that are allowed in all files of layers: + // - node + // - electron-browser + // - electron-main + "when": "hasNode", + "allow": [ + "@microsoft/applicationinsights-web", + "@parcel/watcher", + "@vscode/sqlite3", + "@vscode/vscode-languagedetection", + "@vscode/ripgrep", + "@vscode/iconv-lite-umd", + "applicationinsights", "assert", - "sinon", - "sinon-test", - "vs/nls", - "**/vs/base/common/**", - "**/vs/base/parts/*/common/**", - "**/vs/base/test/common/**", - "**/vs/platform/*/common/**", - "**/vs/platform/*/test/common/**" + "child_process", + "console", + "cookie", + "crypto", + "electron", + "events", + "fs", + "graceful-fs", + "http", + "https", + "minimist", + "native-keymap", + "native-watchdog", + "net", + "node-pty", + "os", + "path", + "perf_hooks", + "spdlog", + "stream", + "string_decoder", + "tas-client-umd", + "tls", + "url", + "util", + "v8-inspect-profiler", + "vscode-proxy-agent", + "vscode-regexpp", + "vscode-textmate", + "windows-process-tree", + "worker_threads", + "xterm", + "xterm-addon-search", + "xterm-addon-serialize", + "xterm-addon-unicode11", + "xterm-addon-webgl", + "xterm-headless", + "yauzl", + "yazl", + "zlib" ] }, { - "target": "**/vs/platform/*/test/browser/**", - "restrictions": [ + // imports that are allowed in all /test/ files + "when": "test", + "allow": [ "assert", "sinon", - "sinon-test", - "vs/nls", - "**/vs/base/{common,browser}/**", - "**/vs/base/parts/*/{common,browser}/**", - "**/vs/base/test/{common,browser}/**", - "**/vs/platform/*/{common,browser}/**", - "**/vs/platform/*/test/{common,browser}/**" - ] - }, - { - "target": "**/vs/platform/*/browser/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/{common,browser}/**", - "**/vs/base/parts/*/{common,browser}/**", - "**/vs/platform/*/{common,browser}/**" - ] - }, - { - "target": "**/vs/platform/*/node/**", - "restrictions": [ - "vs/nls", - "**/vs/base/{common,node}/**", - "**/vs/base/parts/*/{common,node}/**", - "**/vs/platform/*/{common,node}/**", - "@vscode/*", - "@parcel/*", - "*" // node modules - ] - }, - { - "target": "**/vs/platform/*/electron-sandbox/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/{common,browser,electron-sandbox}/**", - "**/vs/base/parts/*/{common,browser,electron-sandbox}/**", - "**/vs/platform/*/{common,browser,electron-sandbox}/**" - ] - }, - { - "target": "**/vs/platform/*/electron-browser/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/base/parts/*/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/platform/*/{common,browser,node,electron-sandbox,electron-browser}/**", - "@vscode/*", - "@parcel/*", - "*" // node modules - ] - }, - { - "target": "**/vs/platform/*/electron-main/**", - "restrictions": [ - "vs/nls", - "**/vs/base/{common,node,electron-main}/**", - "**/vs/base/parts/*/{common,node,electron-main}/**", - "**/vs/platform/*/{common,node,electron-main}/**", - "@vscode/*", - "@parcel/*", - "*" // node modules + "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-browser + // - 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-browser + // - 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": "**/vs/platform/*/test/browser/**", + "target": "src/vs/base/~", "restrictions": [ - "assert", - "sinon", - "sinon-test", - "vs/nls", - "**/vs/base/{common,browser}/**", - "**/vs/base/parts/*/{common,browser}/**", - "**/vs/platform/*/{common,browser}/**", - "**/vs/platform/*/test/{common,browser}/**" + "vs/base/~" ] }, { - "target": "**/vs/editor/common/**", + "target": "src/vs/base/parts/*/~", "restrictions": [ - "vs/nls", - "**/vs/base/common/**", - "**/vs/base/worker/**", - "**/vs/platform/*/common/**", - "**/vs/editor/common/**" + "vs/base/~", + "vs/base/parts/*/~" ] }, { - "target": "**/vs/editor/test/common/**", + "target": "src/vs/platform/*/~", "restrictions": [ - "assert", - "sinon", - "sinon-test", - "vs/nls", - "**/vs/base/common/**", - "**/vs/platform/*/common/**", - "**/vs/platform/*/test/common/**", - "**/vs/editor/common/**", - "**/vs/editor/test/common/**" + "vs/base/~", + "vs/base/parts/*/~", + "vs/platform/*/~", + "tas-client-umd" // node module allowed even in /common/ ] }, { - "target": "**/vs/editor/browser/**", + "target": "src/vs/editor/~", "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/{common,browser}/**", - "**/vs/platform/*/{common,browser}/**", - "**/vs/editor/{common,browser}/**" + "vs/base/~", + "vs/base/parts/*/~", + "vs/platform/*/~", + "vs/editor/~" ] }, { - "target": "**/vs/editor/test/browser/**", + "target": "src/vs/editor/contrib/*/~", "restrictions": [ - "assert", - "sinon", - "sinon-test", - "vs/nls", - "**/vs/base/{common,browser}/**", - "**/vs/platform/*/{common,browser}/**", - "**/vs/platform/*/test/{common,browser}/**", - "**/vs/editor/{common,browser}/**", - "**/vs/editor/test/{common,browser}/**" + "vs/base/~", + "vs/base/parts/*/~", + "vs/platform/*/~", + "vs/editor/~", + "vs/editor/contrib/*/~" ] }, { - "target": "**/vs/editor/standalone/common/**", + "target": "src/vs/editor/standalone/~", "restrictions": [ - "vs/nls", - "**/vs/base/common/**", - "**/vs/platform/*/common/**", - "**/vs/editor/common/**", - "**/vs/editor/standalone/common/**" + "vs/base/~", + "vs/base/parts/*/~", + "vs/platform/*/~", + "vs/editor/~", + "vs/editor/contrib/*/~", + "vs/editor/standalone/~" ] }, { - "target": "**/vs/editor/standalone/test/common/**", + "target": "src/vs/editor/editor.all.ts", + "layer": "browser", "restrictions": [ - "assert", - "sinon", - "sinon-test", - "vs/nls", - "**/vs/base/common/**", - "**/vs/platform/*/common/**", - "**/vs/platform/*/test/common/**", - "**/vs/editor/common/**", - "**/vs/editor/test/common/**" + "vs/base/~", + "vs/base/parts/*/~", + "vs/platform/*/~", + "vs/editor/~", + "vs/editor/contrib/*/~" ] }, { - "target": "**/vs/editor/standalone/browser/**", + "target": "src/vs/editor/editor.worker.ts", + "layer": "worker", "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/{common,browser}/**", - "**/vs/base/parts/*/{common,browser}/**", - "**/vs/platform/*/{common,browser}/**", - "**/vs/editor/{common,browser}/**", - "**/vs/editor/contrib/**", - "**/vs/editor/standalone/{common,browser}/**" + "vs/base/~", + "vs/base/parts/*/~", + "vs/platform/*/~", + "vs/editor/~" ] }, { - "target": "**/vs/editor/standalone/test/browser/**", + "target": "src/vs/editor/{editor.api.ts,editor.main.ts}", + "layer": "browser", "restrictions": [ - "assert", - "sinon", - "sinon-test", - "vs/nls", - "**/vs/base/{common,browser}/**", - "**/vs/platform/*/{common,browser}/**", - "**/vs/platform/*/test/{common,browser}/**", - "**/vs/editor/{common,browser}/**", - "**/vs/editor/standalone/{common,browser}/**", - "**/vs/editor/test/{common,browser}/**" + "vs/base/~", + "vs/base/parts/*/~", + "vs/editor/~", + "vs/editor/contrib/*/~", + "vs/editor/standalone/~", + "vs/editor/*" ] }, { - "target": "**/vs/editor/contrib/*/test/**", + "target": "src/vs/workbench/~", "restrictions": [ + "vs/base/~", + "vs/base/parts/*/~", + "vs/platform/*/~", + "vs/editor/~", + "vs/editor/contrib/*/~", + "vs/workbench/~", + "vs/workbench/services/*/~", "assert", - "sinon", - "sinon-test", - "vs/nls", - "**/vs/base/{common,browser}/**", - "**/vs/base/test/{common,browser}/**", - "**/vs/base/parts/*/{common,browser}/**", - "**/vs/platform/*/{common,browser}/**", - "**/vs/platform/*/test/{common,browser}/**", - "**/vs/editor/{common,browser}/**", - "**/vs/editor/test/{common,browser}/**", - "**/vs/editor/contrib/**" - ] - }, - { - "target": "**/vs/editor/contrib/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/{common,browser}/**", - "**/vs/base/parts/*/{common,browser}/**", - "**/vs/platform/{common,browser}/**", - "**/vs/platform/*/{common,browser}/**", - "**/vs/editor/{common,browser}/**", - "**/vs/editor/contrib/**" - ] - }, - { - "target": "**/vs/workbench/common/**", - "restrictions": [ - "vs/nls", - "**/vs/base/common/**", - "**/vs/base/parts/*/common/**", - "**/vs/platform/*/common/**", - "**/vs/editor/common/**", - "**/vs/editor/contrib/*/common/**", - "**/vs/workbench/common/**", - "**/vs/workbench/services/*/common/**", - "assert" + { "when": "test", "pattern": "vs/workbench/contrib/*/~" } // TODO@layers ] }, { - "target": "**/vs/workbench/browser/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/{common,browser}/**", - "**/vs/base/parts/*/{common,browser}/**", - "**/vs/platform/*/{common,browser}/**", - "**/vs/editor/{common,browser}/**", - "**/vs/editor/contrib/**", // editor/contrib is equivalent to /browser/ by convention - "**/vs/workbench/workbench.web.api", - "**/vs/workbench/{common,browser}/**", - "**/vs/workbench/services/*/{common,browser}/**", - "assert" - ] - }, - { - "target": "**/vs/workbench/api/common/**", + "target": "src/vs/workbench/api/~", "restrictions": [ "vscode", - "vs/nls", - "**/vs/base/common/**", - "**/vs/platform/*/common/**", - "**/vs/editor/common/**", - "**/vs/editor/contrib/*/common/**", - "**/vs/workbench/api/common/**", - "**/vs/workbench/common/**", - "**/vs/workbench/services/*/common/**", - "**/vs/workbench/contrib/*/common/**" + "vs/base/~", + "vs/base/parts/*/~", + "vs/platform/*/~", + "vs/editor/~", + "vs/editor/contrib/*/~", + "vs/workbench/api/~", + "vs/workbench/~", + "vs/workbench/services/*/~", + "vs/workbench/contrib/*/~" ] }, { - "target": "**/vs/workbench/api/worker/**", + "target": "src/vs/workbench/services/*/~", "restrictions": [ - "vscode", - "vs/nls", - "**/vs/**/{common,worker}/**" + "vs/base/~", + "vs/base/parts/*/~", + "vs/platform/*/~", + "vs/editor/~", + "vs/editor/contrib/*/~", + "vs/workbench/~", + "vs/workbench/api/~", + "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/ + "@microsoft/applicationinsights-web" // node module allowed even in /common/ ] }, { - "target": "**/vs/workbench/electron-sandbox/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/{common,browser,electron-sandbox}/**", - "**/vs/base/parts/*/{common,browser,electron-sandbox}/**", - "**/vs/platform/*/{common,browser,electron-sandbox}/**", - "**/vs/editor/{common,browser,electron-sandbox}/**", - "**/vs/editor/contrib/**", // editor/contrib is equivalent to /browser/ by convention - "**/vs/workbench/{common,browser,electron-sandbox}/**", - "**/vs/workbench/api/{common,browser,electron-sandbox}/**", - "**/vs/workbench/services/*/{common,browser,electron-sandbox}/**" + "target": "src/vs/workbench/contrib/*/~", + "restrictions": [ + "vs/base/~", + "vs/base/parts/*/~", + "vs/platform/*/~", + "vs/editor/~", + "vs/editor/contrib/*/~", + "vs/workbench/~", + "vs/workbench/api/~", + "vs/workbench/services/*/~", + "vs/workbench/contrib/*/~", + { "when": "hasBrowser", "pattern": "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": "**/vs/workbench/electron-browser/**", + "target": "src/vs/code/~", "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/base/parts/*/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/platform/*/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/editor/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/editor/contrib/**", // editor/contrib is equivalent to /browser/ by convention - "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/workbench/api/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/workbench/services/*/{common,browser,node,electron-sandbox,electron-browser}/**", - "@vscode/*", - "@parcel/*", - "*" // node modules + "vs/base/~", + "vs/base/parts/*/~", + "vs/platform/*/~", + "vs/editor/~", + "vs/editor/contrib/*/~", + "vs/code/~", + { "when": "hasBrowser", "pattern": "vs/workbench/workbench.web.main" } ] }, { - "target": "**/vs/workbench/services/**/test/**", + "target": "src/vs/server/~", "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/**", - "**/vs/platform/**", - "**/vs/editor/**", - "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**", - "vs/workbench/contrib/files/browser/editors/fileEditorInput", - "**/vs/workbench/services/**", - "**/vs/workbench/test/**", - "@vscode/*", - "@parcel/*", - "*" // node modules + "vs/base/~", + "vs/base/parts/*/~", + "vs/platform/*/~", + "vs/workbench/~", + "vs/workbench/api/~", + "vs/workbench/services/*/~", + "vs/workbench/contrib/*/~", + "vs/server/~" ] }, { - "target": "**/vs/workbench/services/**/common/**", + "target": "src/vs/workbench/workbench.common.main.ts", + "layer": "browser", "restrictions": [ - "vs/nls", - "**/vs/base/**/common/**", - "**/vs/platform/**/common/**", - "**/vs/editor/common/**", - "**/vs/workbench/workbench.web.api", - "**/vs/workbench/common/**", - "**/vs/workbench/services/**/common/**", - "**/vs/workbench/api/**/common/**", - "vscode-textmate", - "vscode-oniguruma", - "iconv-lite-umd", - "tas-client-umd", - "jschardet" + "vs/base/~", + "vs/base/parts/*/~", + "vs/platform/*/~", + "vs/editor/~", + "vs/editor/contrib/*/~", + "vs/editor/editor.all", + "vs/workbench/~", + "vs/workbench/api/~", + "vs/workbench/services/*/~", + "vs/workbench/contrib/*/~" ] }, { - "target": "**/vs/workbench/services/**/worker/**", + "target": "src/vs/workbench/workbench.web.main.ts", + "layer": "browser", "restrictions": [ - "vs/nls", - "**/vs/base/**/common/**", - "**/vs/platform/**/common/**", - "**/vs/editor/common/**", - "**/vs/workbench/**/common/**", - "**/vs/workbench/**/worker/**", - "**/vs/workbench/services/**/common/**", - "vscode" + "vs/base/~", + "vs/base/parts/*/~", + "vs/platform/*/~", + "vs/editor/~", + "vs/editor/contrib/*/~", + "vs/editor/editor.all", + "vs/workbench/~", + "vs/workbench/api/~", + "vs/workbench/services/*/~", + "vs/workbench/contrib/*/~", + "vs/workbench/workbench.common.main" ] }, { - "target": "**/vs/workbench/services/**/browser/**", + "target": "src/vs/workbench/{workbench.sandbox.main.ts,workbench.desktop.sandbox.main.ts}", + "layer": "electron-sandbox", "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/**/{common,browser,worker}/**", - "**/vs/platform/**/{common,browser}/**", - "**/vs/editor/{common,browser}/**", - "**/vs/workbench/workbench.web.api", - "**/vs/workbench/{common,browser}/**", - "**/vs/workbench/api/{common,browser}/**", - "**/vs/workbench/services/**/{common,browser}/**", - "vscode-textmate", - "vscode-oniguruma", - "iconv-lite-umd", - "jschardet", - "@vscode/vscode-languagedetection", - "@microsoft/applicationinsights-web" + "vs/base/*/~", + "vs/base/parts/*/~", + "vs/platform/*/~", + "vs/editor/~", + "vs/editor/contrib/*/~", + "vs/editor/editor.all", + "vs/workbench/~", + "vs/workbench/api/~", + "vs/workbench/services/*/~", + "vs/workbench/contrib/*/~", + "vs/workbench/workbench.common.main", + "vs/workbench/workbench.sandbox.main" ] }, { - "target": "**/vs/workbench/services/**/node/**", + "target": "src/vs/workbench/workbench.desktop.main.ts", + "layer": "electron-browser", "restrictions": [ - "vs/nls", - "**/vs/base/**/{common,node}/**", - "**/vs/platform/**/{common,node}/**", - "**/vs/editor/{common,node}/**", - "**/vs/workbench/{common,node}/**", - "**/vs/workbench/api/{common,node}/**", - "**/vs/workbench/services/**/{common,node}/**", - "@vscode/*", - "@parcel/*", - "*" // node modules - ] - }, - { - "target": "**/vs/workbench/services/**/electron-sandbox/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/**/{common,browser,worker,electron-sandbox}/**", - "**/vs/platform/**/{common,browser,electron-sandbox}/**", - "**/vs/editor/**", - "**/vs/workbench/{common,browser,electron-sandbox}/**", - "**/vs/workbench/api/{common,browser,electron-sandbox}/**", - "**/vs/workbench/services/**/{common,browser,electron-sandbox}/**", - "vscode-textmate", - "vscode-oniguruma", - "iconv-lite-umd", - "jschardet" - ] - }, - { - "target": "**/vs/workbench/services/**/electron-browser/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/**/{common,browser,worker,node,electron-sandbox,electron-browser}/**", - "**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/editor/**", - "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/workbench/api/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/workbench/services/**/{common,browser,node,electron-sandbox,electron-browser}/**", - "@vscode/*", - "@parcel/*", - "*" // node modules - ] - }, - { - "target": "**/vs/workbench/contrib/**/test/**", - "restrictions": [ - "assert", - "vs/nls", - "vs/css!./**/*", - "**/vs/base/**", - "**/vs/platform/**", - "**/vs/editor/**", - "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/workbench/services/**", - "**/vs/workbench/contrib/**", - "**/vs/workbench/test/**", - "*" - ] - }, - { - "target": "**/vs/workbench/contrib/terminal/browser/**", - "restrictions": [ - // xterm and its addons are strictly browser-only components - "xterm", - "xterm-addon-*", - "vs/nls", - "vs/css!./**/*", - "**/vs/base/**/{common,browser}/**", - "**/vs/platform/**/{common,browser}/**", - "**/vs/editor/**", - "**/vs/workbench/{common,browser}/**", - "**/vs/workbench/contrib/**/{common,browser}/**", - "**/vs/workbench/services/**/{common,browser}/**" - ] - }, - { - "target": "**/vs/workbench/contrib/extensions/browser/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/**/{common,browser}/**", - "**/vs/platform/**/{common,browser}/**", - "**/vs/editor/**", - "**/vs/workbench/{common,browser}/**", - "**/vs/workbench/contrib/**/{common,browser}/**", - "**/vs/workbench/services/**/{common,browser}/**" - ] - }, - { - "target": "**/vs/workbench/contrib/update/browser/update.ts", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/**/{common,browser}/**", - "**/vs/platform/**/{common,browser}/**", - "**/vs/editor/**", - "**/vs/workbench/{common,browser}/**", - "**/vs/workbench/contrib/**/{common,browser}/**", - "**/vs/workbench/services/**/{common,browser}/**" - ] - }, - { - "target": "**/vs/workbench/contrib/notebook/common/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/**/{common,worker}/**", - "**/vs/platform/**/common/**", - "**/vs/editor/**", - "**/vs/workbench/common/**", - "**/vs/workbench/api/common/**", - "**/vs/workbench/services/**/common/**", - "**/vs/workbench/contrib/**/common/**" - ] - }, - { - "target": "**/vs/workbench/contrib/**/common/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/**/common/**", - "**/vs/platform/**/common/**", - "**/vs/editor/**", - "**/vs/workbench/common/**", - "**/vs/workbench/api/common/**", - "**/vs/workbench/services/**/common/**", - "**/vs/workbench/contrib/**/common/**" + "vs/base/*/~", + "vs/base/parts/*/~", + "vs/platform/*/~", + "vs/editor/~", + "vs/editor/contrib/*/~", + "vs/editor/editor.all", + "vs/workbench/~", + "vs/workbench/api/~", + "vs/workbench/services/*/~", + "vs/workbench/contrib/*/~", + "vs/workbench/workbench.common.main", + "vs/workbench/workbench.sandbox.main" ] }, { - "target": "**/vs/workbench/contrib/**/browser/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/**/{common,browser}/**", - "**/vs/platform/**/{common,browser}/**", - "**/vs/editor/**", - "**/vs/workbench/{common,browser}/**", - "**/vs/workbench/api/{common,browser}/**", - "**/vs/workbench/services/**/{common,browser}/**", - "**/vs/workbench/contrib/**/{common,browser}/**", - "vscode-textmate", - "vscode-oniguruma", - "iconv-lite-umd", - "jschardet" - ] + "target": "src/vs/workbench/{workbench.desktop.main.nls.js,workbench.web.main.nls.js}", + "restrictions": [] }, { - "target": "**/vs/workbench/contrib/**/node/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/**/{common,node}/**", - "**/vs/platform/**/{common,node}/**", - "**/vs/editor/**/common/**", - "**/vs/workbench/{common,node}/**", - "**/vs/workbench/api/{common,node}/**", - "**/vs/workbench/services/**/{common,node}/**", - "**/vs/workbench/contrib/**/{common,node}/**", - "@vscode/*", - "@parcel/*", - "*" // node modules - ] + "target": "src/vs/{css.d.ts,monaco.d.ts,nls.d.ts,nls.mock.ts}", + "restrictions": [] }, { - "target": "**/vs/workbench/contrib/**/electron-sandbox/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/**/{common,browser,worker,electron-sandbox}/**", - "**/vs/platform/**/{common,browser,electron-sandbox}/**", - "**/vs/editor/**", - "**/vs/workbench/{common,browser,electron-sandbox}/**", - "**/vs/workbench/api/{common,browser,electron-sandbox}/**", - "**/vs/workbench/services/**/{common,browser,electron-sandbox}/**", - "**/vs/workbench/contrib/**/{common,browser,electron-sandbox}/**", - "vscode-textmate", - "vscode-oniguruma", - "iconv-lite-umd", - "jschardet" - ] + "target": "src/vscode-dts/**", + "restrictions": [] }, { - "target": "**/vs/workbench/contrib/**/electron-browser/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/**/{common,browser,worker,node,electron-sandbox,electron-browser}/**", - "**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/editor/**", - "**/vs/workbench/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/workbench/api/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/workbench/services/**/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/workbench/contrib/**/{common,browser,node,electron-sandbox,electron-browser}/**", - "@vscode/*", - "@parcel/*", - "*" // node modules - ] + "target": "src/{bootstrap-amd.js,bootstrap-fork.js,bootstrap-node.js,bootstrap-window.js,bootstrap.js,cli.js,main.js,server-cli.js,server-main.js}", + "restrictions": [] }, { - "target": "**/vs/code/browser/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/**/{common,browser}/**", - "**/vs/base/parts/**/{common,browser}/**", - "**/vs/platform/**/{common,browser}/**", - "**/vs/code/**/{common,browser}/**", - "**/vs/workbench/workbench.web.api" - ] - }, - { - "target": "**/vs/code/node/**", - "restrictions": [ - "vs/nls", - "**/vs/base/**/{common,node}/**", - "**/vs/base/parts/**/{common,node}/**", - "**/vs/platform/**/{common,node}/**", - "**/vs/code/**/{common,node}/**", - "@vscode/*", - "@parcel/*", - "*" // node modules - ] - }, - { - "target": "**/vs/code/electron-browser/**", - "restrictions": [ - "vs/nls", - "vs/css!./**/*", - "**/vs/base/**/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/base/parts/**/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/code/**/{common,browser,node,electron-sandbox,electron-browser}/**", - "@vscode/*", - "@parcel/*", - "*" // node modules - ] - }, - { - "target": "**/vs/code/electron-main/**", - "restrictions": [ - "vs/nls", - "**/vs/base/**/{common,node,electron-main}/**", - "**/vs/base/parts/**/{common,node,electron-main}/**", - "**/vs/platform/**/{common,node,electron-main}/**", - "**/vs/code/**/{common,node,electron-main}/**", - "@vscode/*", - "@parcel/*", - "*" // node modules - ] - }, - { - "target": "**/vs/server/**", - "restrictions": [ - "vs/nls", - "**/vs/base/**/{common,node}/**", - "**/vs/base/parts/**/{common,node}/**", - "**/vs/platform/**/{common,node}/**", - "**/vs/workbench/**/{common,node}/**", - "**/vs/server/**", - "@vscode/*", - "@parcel/*", - "*" // node modules - ] - }, - { - "target": "**/src/vs/workbench/workbench.common.main.ts", - "restrictions": [ - "vs/nls", - "**/vs/base/**/{common,browser}/**", - "**/vs/base/parts/**/{common,browser}/**", - "**/vs/platform/**/{common,browser}/**", - "**/vs/editor/**", - "**/vs/workbench/**/{common,browser}/**" - ] - }, - { - "target": "**/src/vs/workbench/workbench.web.main.ts", - "restrictions": [ - "vs/nls", - "**/vs/base/**/{common,browser}/**", - "**/vs/base/parts/**/{common,browser}/**", - "**/vs/platform/**/{common,browser}/**", - "**/vs/editor/**", - "**/vs/workbench/**/{common,browser}/**", - "**/vs/workbench/workbench.common.main" - ] - }, - { - "target": "**/src/vs/workbench/workbench.web.api.ts", - "restrictions": [ - "vs/nls", - "**/vs/base/**/{common,browser}/**", - "**/vs/base/parts/**/{common,browser}/**", - "**/vs/platform/**/{common,browser}/**", - "**/vs/editor/**", - "**/vs/workbench/**/{common,browser}/**", - "**/vs/workbench/workbench.web.main" - ] - }, - { - "target": "**/src/vs/workbench/workbench.sandbox.main.ts", - "restrictions": [ - "vs/nls", - "**/vs/base/**/{common,browser,electron-sandbox}/**", - "**/vs/base/parts/**/{common,browser,electron-sandbox}/**", - "**/vs/platform/**/{common,browser,electron-sandbox}/**", - "**/vs/editor/**", - "**/vs/workbench/**/{common,browser,electron-sandbox}/**", - "**/vs/workbench/workbench.common.main" - ] - }, - { - "target": "**/src/vs/workbench/workbench.desktop.main.ts", - "restrictions": [ - "vs/nls", - "**/vs/base/**/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/base/parts/**/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/platform/**/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/editor/**", - "**/vs/workbench/**/{common,browser,node,electron-sandbox,electron-browser}/**", - "**/vs/workbench/workbench.common.main", - "**/vs/workbench/workbench.sandbox.main" - ] - }, - { - "target": "**/extensions/**", + "target": "extensions/**", "restrictions": "**/*" }, { - "target": "**/test/smoke/**", + "target": "test/smoke/**", "restrictions": [ - "**/test/smoke/**", + "test/automation", + "test/smoke/**", "@vscode/*", "@parcel/*", + "@playwright/*", "*" // node modules ] }, { - "target": "**/test/automation/**", + "target": "test/automation/**", "restrictions": [ - "**/test/automation/**", + "test/automation/**", "@vscode/*", "@parcel/*", "playwright-core/**", + "@playwright/*", "*" // node modules ] }, { - "target": "**/test/integration/**", + "target": "test/integration/**", "restrictions": [ - "**/test/integration/**", + "test/integration/**", "@vscode/*", "@parcel/*", + "@playwright/*", "*" // node modules ] }, { - "target": "**/test/monaco/**", + "target": "test/monaco/**", "restrictions": [ - "**/test/monaco/**", + "test/monaco/**", "@vscode/*", "@parcel/*", + "@playwright/*", "*" // node modules ] }, { - "target": "**/api/**.test.ts", - "restrictions": [ - "**/vs/**", - "assert", - "sinon", - "sinon-test", - "crypto", - "vscode" - ] - }, - { - "target": "**/{node,electron-browser,electron-main}/**/*.test.ts", - "restrictions": [ - "**/vs/**", - "@vscode/*", - "@parcel/*", - "*" // node modules - ] - }, - { - "target": "**/{node,electron-browser,electron-main}/**/test/**", - "restrictions": [ - "**/vs/**", - "@vscode/*", - "@parcel/*", - "*" // node modules - ] - }, - { - "target": "**/test/{node,electron-browser,electron-main}/**", - "restrictions": [ - "**/vs/**", - "@vscode/*", - "@parcel/*", - "*" // node modules - ] - }, - { - "target": "**/**.test.ts", - "restrictions": [ - "**/vs/**", - "assert", - "sinon", - "sinon-test", - "crypto", - "xterm*" - ] + "target": "build/**", + "restrictions": "**/*" }, { - "target": "**/test/**", - "restrictions": [ - "**/vs/**", - "assert", - "sinon", - "sinon-test", - "crypto", - "xterm*" - ] + "target": "**/{buildfile.js,buildfile.desktop.js,buildfile.web.js}", + "restrictions": "**/*" } ], "header/header": [ diff --git a/.github/classifier.json b/.github/classifier.json index d7b302ac0a025..69b60eeae1c18 100644 --- a/.github/classifier.json +++ b/.github/classifier.json @@ -7,7 +7,7 @@ "labels": { "L10N": {"assign": ["TylerLeonhardt", "csigs"]}, "VIM": {"assign": []}, - "accessibility": { "assign": ["sana-ajani"]}, + "accessibility": { "assign": ["isidorn"]}, "api": {"assign": ["jrieken"]}, "api-finalization": {"assign": []}, "api-proposal": {"assign": ["jrieken"]}, diff --git a/.github/commands.json b/.github/commands.json index 21e452e25f00f..68a50baae9577 100644 --- a/.github/commands.json +++ b/.github/commands.json @@ -15,7 +15,7 @@ "type": "label", "name": "*question", "action": "close", - "comment": "We closed this issue because it is a question about using VS Code rather than an issue or feature request. Please search for help on [StackOverflow](https://aka.ms/vscodestackoverflow), where the community has already answered thousands of similar questions. You may find their [guide on asking a new question](https://aka.ms/vscodestackoverflowquestion) helpful if your question has not already been asked. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" + "comment": "We closed this issue because it is a question about using VS Code rather than an issue or feature request. Please search for help on [StackOverflow](https://aka.ms/vscodestackoverflow), where the community has already answered thousands of similar questions. You may find their [guide on asking a new question](https://aka.ms/vscodestackoverflowquestion) helpful if your question has not already been asked. See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting).\n\nHappy Coding!" }, { "type": "label", @@ -27,19 +27,19 @@ "type": "label", "name": "*extension-candidate", "action": "close", - "comment": "We try to keep VS Code lean and we think the functionality you're asking for is great for a VS Code extension. Maybe you can already find one that suits you in the [VS Code Marketplace](https://aka.ms/vscodemarketplace). Just in case, in a few simple steps you can get started [writing your own extension](https://aka.ms/vscodewritingextensions). See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" + "comment": "We try to keep VS Code lean and we think the functionality you're asking for is great for a VS Code extension. Maybe you can already find one that suits you in the [VS Code Marketplace](https://aka.ms/vscodemarketplace). Just in case, in a few simple steps you can get started [writing your own extension](https://aka.ms/vscodewritingextensions). See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting).\n\nHappy Coding!" }, { "type": "label", "name": "*not-reproducible", "action": "close", - "comment": "We closed this issue because we are unable to reproduce the problem with the steps you describe. Chances are we've already fixed your problem in a recent version of VS Code. If not, please ask us to reopen the issue and provide us with more detail. Our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines might help you with that.\n\nHappy Coding!" + "comment": "We closed this issue because we are unable to reproduce the problem with the steps you describe. Chances are we've already fixed your problem in a recent version of VS Code. If not, please ask us to reopen the issue and provide us with more detail. Our [issue reporting guidelines](https://aka.ms/vscodeissuereporting) might help you with that.\n\nHappy Coding!" }, { "type": "label", "name": "*out-of-scope", "action": "close", - "comment": "We closed this issue because we don't plan to address it in the foreseeable future. You can find more detailed information about our decision-making process [here](https://aka.ms/vscode-out-of-scope). If you disagree and feel that this issue is crucial: We are happy to listen and to reconsider.\n\nIf you wonder what we are up to, please see our [roadmap](https://aka.ms/vscoderoadmap) and [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nThanks for your understanding and happy coding!" + "comment": "We closed this issue because we [don't plan to address it](https://aka.ms/vscode-out-of-scope) in the foreseeable future. If you disagree and feel that this issue is crucial: we are happy to listen and to reconsider.\n\nIf you wonder what we are up to, please see our [roadmap](https://aka.ms/vscoderoadmap) and [issue reporting guidelines](https://aka.ms/vscodeissuereporting).\n\nThanks for your understanding, and happy coding!" }, { "type": "comment", @@ -57,13 +57,13 @@ "type": "label", "name": "*caused-by-extension", "action": "close", - "comment": "This issue is caused by an extension, please file it with the repository (or contact) the extension has linked in its overview in VS Code or the [marketplace](https://aka.ms/vscodemarketplace) for VS Code. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" + "comment": "This issue is caused by an extension, please file it with the repository (or contact) the extension has linked in its overview in VS Code or the [marketplace](https://aka.ms/vscodemarketplace) for VS Code. See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting).\n\nHappy Coding!" }, { "type": "label", "name": "*as-designed", "action": "close", - "comment": "The described behavior is how it is expected to work. If you disagree, please explain what is expected and what is not in more detail. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" + "comment": "The described behavior is how it is expected to work. If you disagree, please explain what is expected and what is not in more detail. See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting).\n\nHappy Coding!" }, { "type": "label", @@ -104,7 +104,7 @@ "type": "label", "name": "*duplicate", "action": "close", - "comment": "Thanks for creating this issue! We figured it's covering the same as another one we already have. Thus, we closed this one as a duplicate. You can search for existing issues [here](${duplicateQuery}). See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" + "comment": "Thanks for creating this issue! We figured it's covering the same as another one we already have. Thus, we closed this one as a duplicate. You can search for [similar existing issues](${duplicateQuery}). See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting).\n\nHappy Coding!" }, { "type": "comment", @@ -193,7 +193,7 @@ "action": "updateLabels", "addLabel": "needs more info", "removeLabel": "~needs more info", - "comment": "Thanks for creating this issue! We figured it's missing some basic information or in some other way doesn't follow our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines. Please take the time to review these and update the issue.\n\nHappy Coding!" + "comment": "Thanks for creating this issue! We figured it's missing some basic information or in some other way doesn't follow our [issue reporting guidelines](https://aka.ms/vscodeissuereporting). Please take the time to review these and update the issue.\n\nHappy Coding!" }, { "type": "label", @@ -201,7 +201,7 @@ "action": "updateLabels", "addLabel": "needs more info", "removeLabel": "~needs version info", - "comment": "Thanks for creating this issue! We figured it's missing some basic information, such as a version number, or in some other way doesn't follow our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines. Please take the time to review these and update the issue.\n\nHappy Coding!" + "comment": "Thanks for creating this issue! We figured it's missing some basic information, such as a version number, or in some other way doesn't follow our [issue reporting guidelines](https://aka.ms/vscodeissuereporting). Please take the time to review these and update the issue.\n\nHappy Coding!" }, { "type": "comment", @@ -222,7 +222,7 @@ "type": "label", "name": "*off-topic", "action": "close", - "comment": "Thanks for creating this issue. We think this issue is unactionable or unrelated to the goals of this project. Please follow our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines.\n\nHappy Coding!" + "comment": "Thanks for creating this issue. We think this issue is unactionable or unrelated to the goals of this project. Please follow our [issue reporting guidelines](https://aka.ms/vscodeissuereporting).\n\nHappy Coding!" }, { "type": "comment", @@ -235,7 +235,7 @@ ], "action": "close", "addLabel": "*caused-by-extension", - "comment": "It looks like this is caused by the Python extension. Please file it with the repository [here](https://github.com/microsoft/vscode-python). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + "comment": "It looks like this is caused by the Python extension. Please file the issue to the [Python extension repository](https://github.com/microsoft/vscode-python). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting) for more information.\n\nHappy Coding!" }, { "type": "comment", @@ -248,7 +248,7 @@ ], "action": "close", "addLabel": "*caused-by-extension", - "comment": "It looks like this is caused by the Jupyter extension. Please file it with the repository [here](https://github.com/microsoft/vscode-jupyter). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + "comment": "It looks like this is caused by the Jupyter extension. Please file the issue to the [Jupyter extension repository](https://github.com/microsoft/vscode-jupyter). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting) for more information.\n\nHappy Coding!" }, { "type": "comment", @@ -261,7 +261,7 @@ ], "action": "close", "addLabel": "*caused-by-extension", - "comment": "It looks like this is caused by the C extension. Please file it with the repository [here](https://github.com/microsoft/vscode-cpptools). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + "comment": "It looks like this is caused by the C extension. Please file the issue to the [C extension repository](https://github.com/microsoft/vscode-cpptools). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting) for more information.\n\nHappy Coding!" }, { "type": "comment", @@ -274,7 +274,7 @@ ], "action": "close", "addLabel": "*caused-by-extension", - "comment": "It looks like this is caused by the C++ extension. Please file it with the repository [here](https://github.com/microsoft/vscode-cpptools). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + "comment": "It looks like this is caused by the C++ extension. Please file the issue to the [C++ extension repository](https://github.com/microsoft/vscode-cpptools). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting) for more information.\n\nHappy Coding!" }, { "type": "comment", @@ -287,7 +287,7 @@ ], "action": "close", "addLabel": "*caused-by-extension", - "comment": "It looks like this is caused by the C++ extension. Please file it with the repository [here](https://github.com/microsoft/vscode-cpptools). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + "comment": "It looks like this is caused by the C++ extension. Please file the issue to the [C++ extension repository](https://github.com/microsoft/vscode-cpptools). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting) for more information.\n\nHappy Coding!" }, { "type": "comment", @@ -300,7 +300,7 @@ ], "action": "close", "addLabel": "*caused-by-extension", - "comment": "It looks like this is caused by the TypeScript language service. Please file it with the repository [here](https://github.com/microsoft/TypeScript/). Make sure to check their [contributing guidelines](https://github.com/microsoft/TypeScript/blob/master/CONTRIBUTING.md) and provide relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + "comment": "It looks like this is caused by the TypeScript language service. Please file the issue to the [TypeScript repository](https://github.com/microsoft/TypeScript/). Make sure to check their [contributing guidelines](https://github.com/microsoft/TypeScript/blob/master/CONTRIBUTING.md) and provide relevant information such as the extension version you're using. See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting) for more information.\n\nHappy Coding!" }, { "type": "comment", @@ -313,7 +313,7 @@ ], "action": "close", "addLabel": "*caused-by-extension", - "comment": "It looks like this is caused by the TypeScript/JavaScript language service. Please file it with the repository [here](https://github.com/microsoft/TypeScript/). Make sure to check their [contributing guidelines](https://github.com/microsoft/TypeScript/blob/master/CONTRIBUTING.md) and provide relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + "comment": "It looks like this is caused by the TypeScript/JavaScript language service. Please file the issue to the [TypeScript repository](https://github.com/microsoft/TypeScript/). Make sure to check their [contributing guidelines](https://github.com/microsoft/TypeScript/blob/master/CONTRIBUTING.md) and provide relevant information such as the extension version you're using. See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting) for more information.\n\nHappy Coding!" }, { "type": "comment", @@ -326,7 +326,7 @@ ], "action": "close", "addLabel": "*caused-by-extension", - "comment": "It looks like this is caused by the C# extension. Please file it with the repository [here](https://github.com/OmniSharp/omnisharp-vscode.git). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + "comment": "It looks like this is caused by the C# extension. Please file the issue to the [C# extension repository](https://github.com/OmniSharp/omnisharp-vscode.git). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting) for more information.\n\nHappy Coding!" }, { "type": "comment", @@ -339,7 +339,7 @@ ], "action": "close", "addLabel": "*caused-by-extension", - "comment": "It looks like this is caused by the Go extension. Please file it with the repository [here](https://github.com/golang/vscode-go). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + "comment": "It looks like this is caused by the Go extension. Please file the issue to the [Go extension repository](https://github.com/golang/vscode-go). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting) for more information.\n\nHappy Coding!" }, { "type": "comment", @@ -352,7 +352,7 @@ ], "action": "close", "addLabel": "*caused-by-extension", - "comment": "It looks like this is caused by the PowerShell extension. Please file it with the repository [here](https://github.com/PowerShell/vscode-powershell). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + "comment": "It looks like this is caused by the PowerShell extension. Please file the issue to the [PowerShell extension repository](https://github.com/PowerShell/vscode-powershell). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting) for more information.\n\nHappy Coding!" }, { "type": "comment", @@ -365,7 +365,7 @@ ], "action": "close", "addLabel": "*caused-by-extension", - "comment": "It looks like this is caused by the LiveShare extension. Please file it with the repository [here](https://github.com/MicrosoftDocs/live-share). Make sure to check their [contributing guidelines](https://github.com/MicrosoftDocs/live-share/blob/master/CONTRIBUTING.md) and provide relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + "comment": "It looks like this is caused by the LiveShare extension. Please file the issue to the [LiveShare repository](https://github.com/MicrosoftDocs/live-share). Make sure to check their [contributing guidelines](https://github.com/MicrosoftDocs/live-share/blob/master/CONTRIBUTING.md) and provide relevant information such as the extension version you're using. See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting) for more information.\n\nHappy Coding!" }, { "type": "comment", @@ -378,7 +378,7 @@ ], "action": "close", "addLabel": "*caused-by-extension", - "comment": "It looks like this is caused by the Docker extension. Please file it with the repository [here](https://github.com/microsoft/vscode-docker). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + "comment": "It looks like this is caused by the Docker extension. Please file the issue to the [Docker extension repository](https://github.com/microsoft/vscode-docker). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting) for more information.\n\nHappy Coding!" }, { "type": "comment", @@ -391,7 +391,7 @@ ], "action": "close", "addLabel": "*caused-by-extension", - "comment": "It looks like this is caused by the Java extension. Please file it with the repository [here](https://github.com/redhat-developer/vscode-java). Make sure to check their [troubleshooting instructions](https://github.com/redhat-developer/vscode-java/wiki/Troubleshooting) and provide relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + "comment": "It looks like this is caused by the Java extension. Please file the issue to the [Java extension repository](https://github.com/redhat-developer/vscode-java). Make sure to check their [troubleshooting instructions](https://github.com/redhat-developer/vscode-java/wiki/Troubleshooting) and provide relevant information such as the extension version you're using. See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting) for more information.\n\nHappy Coding!" }, { "type": "comment", @@ -404,7 +404,7 @@ ], "action": "close", "addLabel": "*caused-by-extension", - "comment": "It looks like this is caused by the Java Debugger extension. Please file it with the repository [here](https://github.com/microsoft/vscode-java-debug). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines for more information.\n\nHappy Coding!" + "comment": "It looks like this is caused by the Java Debugger extension. Please file the issue to the [Java Debugger repository](https://github.com/microsoft/vscode-java-debug). Make sure to check their issue reporting template and provide them relevant information such as the extension version you're using. See also our [issue reporting guidelines](https://aka.ms/vscodeissuereporting) for more information.\n\nHappy Coding!" }, { "type": "comment", diff --git a/.github/workflows/build-chat.yml b/.github/workflows/build-chat.yml deleted file mode 100644 index 03f3bd42cafbb..0000000000000 --- a/.github/workflows/build-chat.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: "Build Chat" - -on: - workflow_run: - workflows: - - CI - types: - - completed - branches: - - main - - release/* - -jobs: - main: - runs-on: ubuntu-latest - steps: - - name: Checkout Actions - uses: actions/checkout@v2 - with: - repository: "microsoft/vscode-github-triage-actions" - path: ./actions - - name: Install Actions - run: npm install --production --prefix ./actions - - name: Install Additional Dependencies - # Pulls in a bunch of other packages that arent needed for the rest of the actions - run: npm install @azure/storage-blob@12.1.1 - - name: Build Chat - uses: ./actions/build-chat - with: - token: ${{ secrets.GITHUB_TOKEN }} - slack_token: ${{ secrets.SLACK_TOKEN }} - storage_connection_string: ${{ secrets.BUILD_CHAT_STORAGE_CONNECTION_STRING }} - workflow_run_url: ${{ github.event.workflow_run.url }} - notification_channel: build - log_channel: bot-log diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 312852c4eeb56..3dc90355056c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: windows: name: Windows runs-on: windows-latest - timeout-minutes: 30 + timeout-minutes: 60 env: CHILD_CONCURRENCY: "1" GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -69,33 +69,33 @@ jobs: - name: Compile and Download run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions + - name: Compile Integration Tests + run: yarn --cwd test/integration/browser compile + - name: Run Unit Tests (Electron) run: .\scripts\test.bat - name: Run Unit Tests (node.js) run: yarn test-node - - name: Run Unit Tests (Browser) - run: yarn test-browser --browser chromium - - - name: Compile Integration Tests - run: yarn --cwd test/integration/browser compile + - name: Run Unit Tests (Browser, Chromium) + run: yarn test-browser-no-install --browser chromium - name: Run Integration Tests (Electron) run: .\scripts\test-integration.bat - - name: Run Integration Tests (Browser) - timeout-minutes: 10 - run: .\resources\server\test\test-web-integration.bat --browser firefox + - name: Run Integration Tests (Browser, Firefox) + timeout-minutes: 20 + run: .\scripts\test-web-integration.bat --browser firefox - name: Run Integration Tests (Remote) - timeout-minutes: 10 - run: .\resources\server\test\test-remote-integration.bat + timeout-minutes: 20 + run: .\scripts\test-remote-integration.bat linux: name: Linux runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 40 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: @@ -146,6 +146,9 @@ jobs: - name: Compile and Download run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions + - name: Compile Integration Tests + run: yarn --cwd test/integration/browser compile + - name: Run Unit Tests (Electron) id: electron-unit-tests run: DISPLAY=:10 ./scripts/test.sh @@ -154,30 +157,27 @@ jobs: id: nodejs-unit-tests run: yarn test-node - - name: Run Unit Tests (Browser) + - name: Run Unit Tests (Browser, Chromium) id: browser-unit-tests - run: DISPLAY=:10 yarn test-browser --browser chromium - - - name: Compile Integration Tests - run: yarn --cwd test/integration/browser compile + run: DISPLAY=:10 yarn test-browser-no-install --browser chromium - name: Run Integration Tests (Electron) id: electron-integration-tests run: DISPLAY=:10 ./scripts/test-integration.sh - - name: Run Integration Tests (Browser) + - name: Run Integration Tests (Browser, Chromium) id: browser-integration-tests - run: DISPLAY=:10 ./resources/server/test/test-web-integration.sh --browser chromium + run: DISPLAY=:10 ./scripts/test-web-integration.sh --browser chromium - name: Run Integration Tests (Remote) id: electron-remote-integration-tests - timeout-minutes: 7 - run: DISPLAY=:10 ./resources/server/test/test-remote-integration.sh + timeout-minutes: 15 + run: DISPLAY=:10 ./scripts/test-remote-integration.sh darwin: name: macOS runs-on: macos-latest - timeout-minutes: 30 + timeout-minutes: 40 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: @@ -218,6 +218,9 @@ jobs: - name: Compile and Download run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions + - name: Compile Integration Tests + run: yarn --cwd test/integration/browser compile + # This is required for keytar unittests, otherwise we hit # https://github.com/atom/node-keytar/issues/76 - name: Create temporary keychain @@ -232,26 +235,23 @@ jobs: - name: Run Unit Tests (node.js) run: yarn test-node - - name: Run Unit Tests (Browser) - run: DISPLAY=:10 yarn test-browser --browser chromium - - - name: Compile Integration Tests - run: yarn --cwd test/integration/browser compile + - name: Run Unit Tests (Browser, Chromium) + run: DISPLAY=:10 yarn test-browser-no-install --browser chromium - name: Run Integration Tests (Electron) run: DISPLAY=:10 ./scripts/test-integration.sh - - name: Run Integration Tests (Browser) - run: DISPLAY=:10 ./resources/server/test/test-web-integration.sh --browser webkit + - name: Run Integration Tests (Browser, Webkit) + run: DISPLAY=:10 ./scripts/test-web-integration.sh --browser webkit - name: Run Integration Tests (Remote) - timeout-minutes: 7 - run: DISPLAY=:10 ./resources/server/test/test-remote-integration.sh + timeout-minutes: 15 + run: DISPLAY=:10 ./scripts/test-remote-integration.sh hygiene: name: Hygiene, Layering and Monaco Editor runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 40 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: @@ -289,15 +289,15 @@ jobs: ELECTRON_SKIP_BINARY_DOWNLOAD: 1 run: yarn --frozen-lockfile --network-timeout 180000 + - name: Download Playwright + run: yarn playwright-install + - name: Run Hygiene Checks run: yarn gulp hygiene - name: Run Valid Layers Checks run: yarn valid-layers-check - - name: Compile /build/ - run: yarn --cwd build compile - - name: Run eslint run: yarn eslint @@ -313,6 +313,10 @@ jobs: - name: Editor Distro & ESM Bundle run: yarn gulp editor-esm-bundle + - name: Editor ESM sources check + working-directory: ./test/monaco + run: yarn run esm-check + - name: Typings validation prep run: | mkdir typings-test @@ -325,17 +329,14 @@ jobs: echo "import '../out-monaco-editor-core';" > a.ts ../node_modules/.bin/tsc --noEmit - - name: Webpack Editor + - name: Package Editor with Webpack working-directory: ./test/monaco - run: yarn run bundle + run: yarn run bundle-webpack - name: Compile Editor Tests working-directory: ./test/monaco run: yarn run compile - - name: Download Playwright - run: yarn playwright-install - - name: Run Editor Tests timeout-minutes: 5 working-directory: ./test/monaco diff --git a/.github/workflows/deep-classifier-assign-monitor.yml b/.github/workflows/deep-classifier-assign-monitor.yml new file mode 100644 index 0000000000000..1740c739294ac --- /dev/null +++ b/.github/workflows/deep-classifier-assign-monitor.yml @@ -0,0 +1,24 @@ +name: "Deep Classifier: Assign Monitor" +on: + issues: + types: [assigned] + +jobs: + main: + runs-on: ubuntu-latest + if: ${{ contains(github.event.issue.labels.*.name, 'triage-needed') }} + steps: + - name: Checkout Actions + uses: actions/checkout@v2 + with: + repository: "microsoft/vscode-github-triage-actions" + ref: stable + path: ./actions + - name: Install Actions + run: npm install --production --prefix ./actions + - name: "Run Classifier: Monitor" + uses: ./actions/classifier-deep/monitor + with: + botName: vscode-triage-bot + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} diff --git a/.github/workflows/deep-classifier-runner.yml b/.github/workflows/deep-classifier-runner.yml index 19a8a705971ab..20288f2e7e2b5 100644 --- a/.github/workflows/deep-classifier-runner.yml +++ b/.github/workflows/deep-classifier-runner.yml @@ -2,6 +2,7 @@ name: "Deep Classifier: Runner" on: schedule: - cron: 0 * * * * + workflow_dispatch: repository_dispatch: types: [trigger-deep-classifier-runner] @@ -19,12 +20,12 @@ jobs: run: npm install --production --prefix ./actions - name: Install Additional Dependencies # Pulls in a bunch of other packages that arent needed for the rest of the actions - run: npm install @azure/storage-blob@12.1.1 + run: npm install @azure/storage-blob@12.1.1 mongodb@2.2.31 - name: "Run Classifier: Scraper" uses: ./actions/classifier-deep/apply/fetch-sources with: # slightly overlapping to protect against issues slipping through the cracks if a run is delayed - from: 80 + from: 10000 # ~7 days until: 5 configPath: classifier blobContainerName: vscode-issue-classifier @@ -45,6 +46,7 @@ jobs: uses: ./actions/classifier-deep/apply/apply-labels with: configPath: classifier - allowLabels: "needs more info|new release" + allowLabels: "needs more info|new release|error-telemetry|*english-please|translation-required" appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + manifestDbConnectionString: ${{secrets.MANIFEST_DB_CONNECTION_STRING}} token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} diff --git a/.github/workflows/deep-classifier-scraper.yml b/.github/workflows/deep-classifier-scraper.yml index 270e9ceb4309c..04498d4b7ce95 100644 --- a/.github/workflows/deep-classifier-scraper.yml +++ b/.github/workflows/deep-classifier-scraper.yml @@ -2,6 +2,7 @@ name: "Deep Classifier: Scraper" on: schedule: - cron: 0 0 15 * * # 15th of the month + workflow_dispatch: repository_dispatch: types: [trigger-deep-classifier-scraper] diff --git a/.github/workflows/deep-classifier-monitor.yml b/.github/workflows/deep-classifier-unassign-monitor.yml similarity index 84% rename from .github/workflows/deep-classifier-monitor.yml rename to .github/workflows/deep-classifier-unassign-monitor.yml index 106266d0cff4a..31deb19989b0f 100644 --- a/.github/workflows/deep-classifier-monitor.yml +++ b/.github/workflows/deep-classifier-unassign-monitor.yml @@ -1,4 +1,4 @@ -name: "Deep Classifier: Monitor" +name: "Deep Classifier: Unassign Monitor" on: issues: types: [unassigned] @@ -6,6 +6,7 @@ on: jobs: main: runs-on: ubuntu-latest + if: ${{ ! contains(github.event.issue.labels.*.name, 'triage-needed') }} steps: - name: Checkout Actions uses: actions/checkout@v2 diff --git a/.github/workflows/on-open.yml b/.github/workflows/on-open.yml index e5dc42ac44af7..064b8d86145e4 100644 --- a/.github/workflows/on-open.yml +++ b/.github/workflows/on-open.yml @@ -23,13 +23,14 @@ jobs: token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} owner: JacksonKearl repo: testissues - - name: Run CopyCat (chrmarti/testissues) - uses: ./actions/copycat - with: - appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} - token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} - owner: chrmarti - repo: testissues + + # - name: Run CopyCat (chrmarti/testissues) + # uses: ./actions/copycat + # with: + # appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} + # token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + # owner: chrmarti + # repo: testissues - name: Run New Release uses: ./actions/new-release diff --git a/.github/workflows/rich-navigation.yml b/.github/workflows/rich-navigation.yml index 0d386dc4c2fed..f5fd32493f111 100644 --- a/.github/workflows/rich-navigation.yml +++ b/.github/workflows/rich-navigation.yml @@ -36,6 +36,6 @@ jobs: with: languages: typescript repo-token: ${{ secrets.GITHUB_TOKEN }} - typescriptVersion: 0.6.0-next.19 + typescriptVersion: 0.6.0-next.21 configFiles: .lsifrc.json continue-on-error: true diff --git a/.vscode/launch.json b/.vscode/launch.json index 6c5dda1ce3d6b..84affe5f9207f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -244,10 +244,16 @@ } }, { - "type": "node", + "type": "pwa-node", "request": "launch", - "name": "VS Code (Web)", - "program": "${workspaceFolder}/resources/web/code-web.js", + "name": "VS Code Server (Web)", + "runtimeExecutable": "${workspaceFolder}/scripts/code-server.sh", + "windows": { + "runtimeExecutable": "${workspaceFolder}/scripts/code-server.bat", + }, + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], "presentation": { "group": "0_vscode", "order": 2 @@ -279,9 +285,9 @@ "request": "launch", "outFiles": [], "perScriptSourcemaps": "yes", - "name": "VS Code (Web, Chrome)", - "url": "http://localhost:8080", - "preLaunchTask": "Run web", + "name": "VS Code Server (Web, Chrome)", + "url": "http://localhost:8080?tkn=dev-token", + "preLaunchTask": "Run code server", "presentation": { "group": "0_vscode", "order": 3 @@ -292,10 +298,10 @@ "request": "launch", "outFiles": [], "perScriptSourcemaps": "yes", - "name": "VS Code (Web, Edge)", - "url": "http://localhost:8080", + "name": "VS Code Server (Web, Edge)", + "url": "http://localhost:8080?tkn=dev-token", "pauseForSourceMap": false, - "preLaunchTask": "Run web", + "preLaunchTask": "Run code server", "presentation": { "group": "0_vscode", "order": 3 diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index cf580ac17be3e..613a3dd3ce955 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:\"November 2021\"" + "value": "$repo=repo:microsoft/vscode\n$milestone=milestone:\"January 2022\"" }, { "kind": 1, diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index a242c4053ae62..90355cedb24c0 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/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-remotehub repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-unpkg\n\n$MILESTONE=milestone:\"October 2021\"" + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-remotehub repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-unpkg\n\n$MILESTONE=milestone:\"January 2022\"" }, { "kind": 1, diff --git a/.vscode/notebooks/inbox.github-issues b/.vscode/notebooks/inbox.github-issues index be6afc784c7e3..ad451a6f91ad5 100644 --- a/.vscode/notebooks/inbox.github-issues +++ b/.vscode/notebooks/inbox.github-issues @@ -7,7 +7,12 @@ { "kind": 2, "language": "github-issues", - "value": "$inbox -label:\"needs more info\" sort:created-asc" + "value": "$inbox -label:\"needs more info\" sort:created-desc" + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode label:triage-needed is:open" }, { "kind": 1, diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index f8a9349d7f1c4..c814eb91c54a1 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/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remotehub repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal\n\n$MILESTONE=milestone:\"October 2021\"\n\n$MINE=assignee:@me" + "value": "$REPOS=repo:microsoft/vscode repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-js-debug repo:microsoft/vscode-remote-release repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remotehub repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-livepreview repo:microsoft/vscode-python repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal\n\n$MILESTONE=milestone:\"January 2022\"\n\n$MINE=assignee:@me" }, { "kind": 1, @@ -157,7 +157,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed sort:updated-asc label:bug -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:bamurtaugh -author:bpasero -author:btholt -author:chrisdias -author:chrmarti -author:Chuxel -author:claudiaregio -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:DonJayamanne -author:dynamicwebpaige -author:eamodio -author:egamma -author:fiveisprime -author:greazer -author:gregvanl -author:hediet -author:IanMatthewHuff -author:isidorn -author:ItalyPaleAle -author:JacksonKearl -author:joaomoreno -author:joyceerhl -author:jrieken -author:karrtikr-author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:ornellaalt -author:orta -author:rchiodo -author:rebornix -author:RMacfarlane -author:roblourens -author:rzhao271 -author:sana-ajani -author:sandy081 -author:sbatten -author:stevencl -author:TylerLeonhardt -author:Tyriar -author:weinand " + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed sort:updated-asc label:bug -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -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:dynamicwebpaige -author:eamodio -author:egamma -author:fiveisprime -author:greazer -author:gregvanl -author:hediet -author:IanMatthewHuff -author:isidorn -author:ItalyPaleAle -author:JacksonKearl -author:joaomoreno -author:joyceerhl -author:jrieken -author:karrtikr-author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:ornellaalt -author:orta -author:rchiodo -author:rebornix -author:roblourens -author:rzhao271 -author:sana-ajani -author:sandy081 -author:sbatten -author:stevencl -author:tanhakabir -author:TylerLeonhardt -author:Tyriar -author:weinand " }, { "kind": 1, diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index f207cf5e4b575..1d40f14308224 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/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github\n\n// current milestone name\n$milestone=milestone:\"November 2021\"" + "value": "// list of repos we work in\n$repos=repo:microsoft/vscode repo:microsoft/vscode-remote-release repo:microsoft/vscode-js-debug repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-dev repo:microsoft/vscode-references-view repo:microsoft/vscode-anycode repo:microsoft/vscode-hexeditor repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-livepreview repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-remote-repositories-github\n\n// current milestone name\n$milestone=milestone:\"January 2022\"" }, { "kind": 1, @@ -74,6 +74,16 @@ "language": "markdown", "value": "### Personal Inbox\n" }, + { + "kind": 1, + "language": "markdown", + "value": "#### Triage Needed" + }, + { + "kind": 2, + "language": "github-issues", + "value": "repo:microsoft/vscode is:open assignee:@me label:triage-needed" + }, { "kind": 1, "language": "markdown", diff --git a/.vscode/notebooks/vscode-dev.github-issues b/.vscode/notebooks/vscode-dev.github-issues index 42a1b2e7309f7..2178fa29d596e 100644 --- a/.vscode/notebooks/vscode-dev.github-issues +++ b/.vscode/notebooks/vscode-dev.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode-dev milestone:\"November 2021\" is:open" + "value": "repo:microsoft/vscode-dev milestone:\"December 2021\" is:open" }, { "kind": 2, @@ -32,11 +32,11 @@ { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode-remote-repositories-github milestone:\"November 2021\" is:open" + "value": "repo:microsoft/vscode-remote-repositories-github milestone:\"December 2021\" is:open" }, { "kind": 2, "language": "github-issues", - "value": "repo:microsoft/vscode-remotehub milestone:\"November 2021\" is:open" + "value": "repo:microsoft/vscode-remotehub milestone:\"December 2021\" is:open" } -] \ No newline at end of file +] diff --git a/.vscode/searches/TrustedTypes.code-search b/.vscode/searches/TrustedTypes.code-search deleted file mode 100644 index 85707534c1e1c..0000000000000 --- a/.vscode/searches/TrustedTypes.code-search +++ /dev/null @@ -1,101 +0,0 @@ -# Query: .innerHTML = -# Flags: CaseSensitive WordMatch -# Including: src/vs/**/*.{t,j}s -# Excluding: *.test.ts, **/test/** -# ContextLines: 3 - -12 results - 9 files - -src/vs/base/browser/dom.ts: - 1359 ); - 1360 - 1361 const html = _ttpSafeInnerHtml?.createHTML(value, options) ?? insane(value, options); - 1362: node.innerHTML = html as unknown as string; - 1363 } - -src/vs/base/browser/markdownRenderer.ts: - 272 }; - 273 - 274 if (_ttpInsane) { - 275: element.innerHTML = _ttpInsane.createHTML(renderedMarkdown, insaneOptions) as unknown as string; - 276 } else { - 277: element.innerHTML = insane(renderedMarkdown, insaneOptions); - 278 } - 279 - 280 // signal that async code blocks can be now be inserted - -src/vs/editor/browser/core/markdownRenderer.ts: - 88 - 89 const element = document.createElement('span'); - 90 - 91: element.innerHTML = MarkdownRenderer._ttpTokenizer - 92 ? MarkdownRenderer._ttpTokenizer.createHTML(value, tokenization) as unknown as string - 93 : tokenizeToString(value, tokenization); - 94 - -src/vs/editor/browser/view/domLineBreaksComputer.ts: - 107 allCharOffsets[i] = tmp[0]; - 108 allVisibleColumns[i] = tmp[1]; - 109 } - 110: containerDomNode.innerHTML = sb.build(); - 111 - 112 containerDomNode.style.position = 'absolute'; - 113 containerDomNode.style.top = '10000'; - -src/vs/editor/browser/view/viewLayer.ts: - 512 } - 513 const lastChild = this.domNode.lastChild; - 514 if (domNodeIsEmpty || !lastChild) { - 515: this.domNode.innerHTML = newLinesHTML; - 516 } else { - 517 lastChild.insertAdjacentHTML('afterend', newLinesHTML); - 518 } - - 533 if (ViewLayerRenderer._ttPolicy) { - 534 invalidLinesHTML = ViewLayerRenderer._ttPolicy.createHTML(invalidLinesHTML) as unknown as string; - 535 } - 536: hugeDomNode.innerHTML = invalidLinesHTML; - 537 - 538 for (let i = 0; i < ctx.linesLength; i++) { - 539 const line = ctx.lines[i]; - -src/vs/editor/browser/widget/diffEditorWidget.ts: - 2157 - 2158 let domNode = document.createElement('div'); - 2159 domNode.className = `view-lines line-delete ${MOUSE_CURSOR_TEXT_CSS_CLASS_NAME}`; - 2160: domNode.innerHTML = sb.build(); - 2161 Configuration.applyFontInfoSlow(domNode, fontInfo); - 2162 - 2163 let marginDomNode = document.createElement('div'); - 2164 marginDomNode.className = 'inline-deleted-margin-view-zone'; - 2165: marginDomNode.innerHTML = marginHTML.join(''); - 2166 Configuration.applyFontInfoSlow(marginDomNode, fontInfo); - 2167 - 2168 return { - -src/vs/editor/standalone/browser/colorizer.ts: - 40 let text = domNode.firstChild ? domNode.firstChild.nodeValue : ''; - 41 domNode.className += ' ' + theme; - 42 let render = (str: string) => { - 43: domNode.innerHTML = str; - 44 }; - 45 return this.colorize(modeService, text || '', mimeType, options).then(render, (err) => console.error(err)); - 46 } - -src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts: - 580 const element = DOM.$('div', { style }); - 581 - 582 const linesHtml = this.getRichTextLinesAsHtml(model, modelRange, colorMap); - 583: element.innerHTML = linesHtml as unknown as string; - 584 return element; - 585 } - 586 - -src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts: - 375 addMouseoverListeners(outputNode, outputId); - 376 const content = data.content; - 377 if (content.type === RenderOutputType.Html) { - 378: outputNode.innerHTML = content.htmlContent; - 379 cellOutputContainer.appendChild(outputNode); - 380 domEval(outputNode); - 381 } else if (preloadErrs.some(e => !!e)) { diff --git a/.vscode/settings.json b/.vscode/settings.json index d03741307f0c9..90848fb6dbf92 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -26,7 +26,7 @@ "test/automation/out/**": true, "test/integration/browser/out/**": true, "src/vs/base/test/node/uri.test.data.txt": true, - "src/vs/workbench/test/browser/api/extHostDocumentData.test.perf-data.ts": true + "src/vs/workbench/api/test/browser/extHostDocumentData.test.perf-data.ts": true }, "lcov.path": [ "./.build/coverage/lcov.info", @@ -42,7 +42,9 @@ } ], "eslint.options": { - "rulePaths": ["./build/lib/eslint"] + "rulePaths": [ + "./build/lib/eslint" + ] }, "typescript.tsdk": "node_modules/typescript/lib", "npm.exclude": "**/extensions/**", @@ -52,11 +54,15 @@ "typescript.preferences.quoteStyle": "single", "json.schemas": [ { - "fileMatch": ["cgmanifest.json"], + "fileMatch": [ + "cgmanifest.json" + ], "url": "./.vscode/cgmanifest.schema.json" }, { - "fileMatch": ["cglicenses.json"], + "fileMatch": [ + "cglicenses.json" + ], "url": "./.vscode/cglicenses.schema.json" } ], @@ -88,5 +94,10 @@ "list", "git", "sash" - ] + ], + "explorer.experimental.fileNesting.patterns": { + "*.js": "$(capture).*.js", + "bootstrap.js": "bootstrap-*.js" + }, + "editor.formatOnSave": false, } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index c0b290afa729a..bee1f405a54ac 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -23,8 +23,8 @@ "message": 3 }, "background": { - "beginsPattern": "Starting compilation", - "endsPattern": "Finished compilation" + "beginsPattern": "Starting compilation...", + "endsPattern": "Finished compilation with" } } }, @@ -171,8 +171,12 @@ }, { "type": "shell", - "command": "yarn web --no-launch", - "label": "Run web", + "command": "./scripts/code-server.sh", + "windows": { + "command": ".\\scripts\\code-server.bat" + }, + "args": ["--no-launch", "--connection-token", "dev-token", "--port", "8080"], + "label": "Run code server", "isBackground": true, "problemMatcher": { "pattern": { diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3d6c01e833536..597b1d4591d10 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -69,6 +69,9 @@ Please include the following with each issue: * Errors from the Dev Tools Console (open from the menu: Help > Toggle Developer Tools) +### Creating Pull Requests +* Please refer to the article [here](https://github.com/microsoft/vscode/wiki/How-to-Contribute#pull-requests) for information on how to create Pull Requests and contribute to this project. + ### Final Checklist Please remember to do the following: diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 04fcf3671b517..83a9e45c392e2 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -7,9 +7,9 @@ This project incorporates components from the projects listed below. The origina 1. atom/language-clojure version 0.22.7 (https://github.com/atom/language-clojure) 2. atom/language-coffee-script version 0.49.3 (https://github.com/atom/language-coffee-script) -3. atom/language-css version 0.44.6 (https://github.com/atom/language-css) +3. atom/language-css version 0.45.0 (https://github.com/atom/language-css) 4. atom/language-java version 0.32.1 (https://github.com/atom/language-java) -5. atom/language-sass version 0.62.1 (https://github.com/atom/language-sass) +5. atom/language-sass version 0.61.4 (https://github.com/atom/language-sass) 6. atom/language-shellscript version 0.26.0 (https://github.com/atom/language-shellscript) 7. atom/language-xml version 0.35.2 (https://github.com/atom/language-xml) 8. better-go-syntax version 1.0.0 (https://github.com/jeff-hykin/better-go-syntax/ ) @@ -26,52 +26,53 @@ This project incorporates components from the projects listed below. The origina 19. fadeevab/make.tmbundle (https://github.com/fadeevab/make.tmbundle) 20. freebroccolo/atom-language-swift (https://github.com/freebroccolo/atom-language-swift) 21. HTML 5.1 W3C Working Draft version 08 October 2015 (http://www.w3.org/TR/2015/WD-html51-20151008/) -22. Ikuyadeu/vscode-R version 2.1.0 (https://github.com/Ikuyadeu/vscode-R) +22. Ikuyadeu/vscode-R version 2.3.2 (https://github.com/Ikuyadeu/vscode-R) 23. Ionic documentation version 1.2.4 (https://github.com/ionic-team/ionic-site) 24. ionide/ionide-fsgrammar (https://github.com/ionide/ionide-fsgrammar) 25. James-Yu/LaTeX-Workshop version 8.19.1 (https://github.com/James-Yu/LaTeX-Workshop) 26. jeff-hykin/cpp-textmate-grammar version 1.12.11 (https://github.com/jeff-hykin/cpp-textmate-grammar) 27. jeff-hykin/cpp-textmate-grammar version 1.15.6 (https://github.com/jeff-hykin/cpp-textmate-grammar) -28. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify) -29. JuliaEditorSupport/atom-language-julia version 0.21.1 (https://github.com/JuliaEditorSupport/atom-language-julia) -30. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert) -31. language-docker (https://github.com/moby/moby) -32. language-less version 0.34.2 (https://github.com/atom/language-less) -33. language-php version 0.47.0 (https://github.com/atom/language-php) -34. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython) -35. marked version 3.0.2 (https://github.com/markedjs/marked) -36. mdn-data version 1.1.12 (https://github.com/mdn/data) -37. microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/microsoft/TypeScript-TmLanguage) -38. microsoft/vscode-JSON.tmLanguage (https://github.com/microsoft/vscode-JSON.tmLanguage) -39. microsoft/vscode-markdown-tm-grammar version 1.0.0 (https://github.com/microsoft/vscode-markdown-tm-grammar) -40. microsoft/vscode-mssql version 1.10.1 (https://github.com/microsoft/vscode-mssql) -41. mmims/language-batchfile version 0.7.6 (https://github.com/mmims/language-batchfile) -42. NVIDIA/cuda-cpp-grammar (https://github.com/NVIDIA/cuda-cpp-grammar) -43. PowerShell/EditorSyntax version 1.0.0 (https://github.com/PowerShell/EditorSyntax) -44. rust-syntax version 0.5.0 (https://github.com/dustypomerleau/rust-syntax) -45. semver version 5.5.0 (https://github.com/npm/node-semver) -46. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui) -47. shaders-tmLanguage version 0.1.0 (https://github.com/tgjones/shaders-tmLanguage) -48. textmate/asp.vb.net.tmbundle (https://github.com/textmate/asp.vb.net.tmbundle) -49. textmate/c.tmbundle (https://github.com/textmate/c.tmbundle) -50. textmate/diff.tmbundle (https://github.com/textmate/diff.tmbundle) -51. textmate/git.tmbundle (https://github.com/textmate/git.tmbundle) -52. textmate/groovy.tmbundle (https://github.com/textmate/groovy.tmbundle) -53. textmate/html.tmbundle (https://github.com/textmate/html.tmbundle) -54. textmate/ini.tmbundle (https://github.com/textmate/ini.tmbundle) -55. textmate/javascript.tmbundle (https://github.com/textmate/javascript.tmbundle) -56. textmate/lua.tmbundle (https://github.com/textmate/lua.tmbundle) -57. textmate/markdown.tmbundle (https://github.com/textmate/markdown.tmbundle) -58. textmate/perl.tmbundle (https://github.com/textmate/perl.tmbundle) -59. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle) -60. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) -61. TypeScript-TmLanguage version 0.1.8 (https://github.com/microsoft/TypeScript-TmLanguage) -62. TypeScript-TmLanguage version 1.0.0 (https://github.com/microsoft/TypeScript-TmLanguage) -63. Unicode version 12.0.0 (https://home.unicode.org/) -64. vscode-codicons version 0.0.14 (https://github.com/microsoft/vscode-codicons) -65. vscode-logfile-highlighter version 2.11.0 (https://github.com/emilast/vscode-logfile-highlighter) -66. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) -67. Web Background Synchronization (https://github.com/WICG/background-sync) +28. jlelong/vscode-latex-basics version 1.1.0 (https://github.com/jlelong/vscode-latex-basics) +29. js-beautify version 1.6.8 (https://github.com/beautify-web/js-beautify) +30. JuliaEditorSupport/atom-language-julia version 0.21.2 (https://github.com/JuliaEditorSupport/atom-language-julia) +31. Jxck/assert version 1.0.0 (https://github.com/Jxck/assert) +32. language-docker (https://github.com/moby/moby) +33. language-less version 0.34.2 (https://github.com/atom/language-less) +34. language-php version 0.48.0 (https://github.com/atom/language-php) +35. MagicStack/MagicPython version 1.1.1 (https://github.com/MagicStack/MagicPython) +36. marked version 3.0.2 (https://github.com/markedjs/marked) +37. mdn-data version 1.1.12 (https://github.com/mdn/data) +38. microsoft/TypeScript-TmLanguage version 0.0.1 (https://github.com/microsoft/TypeScript-TmLanguage) +39. microsoft/vscode-JSON.tmLanguage (https://github.com/microsoft/vscode-JSON.tmLanguage) +40. microsoft/vscode-markdown-tm-grammar version 1.0.0 (https://github.com/microsoft/vscode-markdown-tm-grammar) +41. microsoft/vscode-mssql version 1.10.1 (https://github.com/microsoft/vscode-mssql) +42. mmims/language-batchfile version 0.7.6 (https://github.com/mmims/language-batchfile) +43. NVIDIA/cuda-cpp-grammar (https://github.com/NVIDIA/cuda-cpp-grammar) +44. PowerShell/EditorSyntax version 1.0.0 (https://github.com/PowerShell/EditorSyntax) +45. rust-syntax version 0.5.0 (https://github.com/dustypomerleau/rust-syntax) +46. semver version 5.5.0 (https://github.com/npm/node-semver) +47. seti-ui version 0.1.0 (https://github.com/jesseweed/seti-ui) +48. shaders-tmLanguage version 0.1.0 (https://github.com/tgjones/shaders-tmLanguage) +49. textmate/asp.vb.net.tmbundle (https://github.com/textmate/asp.vb.net.tmbundle) +50. textmate/c.tmbundle (https://github.com/textmate/c.tmbundle) +51. textmate/diff.tmbundle (https://github.com/textmate/diff.tmbundle) +52. textmate/git.tmbundle (https://github.com/textmate/git.tmbundle) +53. textmate/groovy.tmbundle (https://github.com/textmate/groovy.tmbundle) +54. textmate/html.tmbundle (https://github.com/textmate/html.tmbundle) +55. textmate/ini.tmbundle (https://github.com/textmate/ini.tmbundle) +56. textmate/javascript.tmbundle (https://github.com/textmate/javascript.tmbundle) +57. textmate/lua.tmbundle (https://github.com/textmate/lua.tmbundle) +58. textmate/markdown.tmbundle (https://github.com/textmate/markdown.tmbundle) +59. textmate/perl.tmbundle (https://github.com/textmate/perl.tmbundle) +60. textmate/ruby.tmbundle (https://github.com/textmate/ruby.tmbundle) +61. textmate/yaml.tmbundle (https://github.com/textmate/yaml.tmbundle) +62. TypeScript-TmLanguage version 0.1.8 (https://github.com/microsoft/TypeScript-TmLanguage) +63. TypeScript-TmLanguage version 1.0.0 (https://github.com/microsoft/TypeScript-TmLanguage) +64. Unicode version 12.0.0 (https://home.unicode.org/) +65. vscode-codicons version 0.0.14 (https://github.com/microsoft/vscode-codicons) +66. vscode-logfile-highlighter version 2.11.0 (https://github.com/emilast/vscode-logfile-highlighter) +67. vscode-swift version 0.0.1 (https://github.com/owensd/vscode-swift) +68. Web Background Synchronization (https://github.com/WICG/background-sync) %% atom/language-clojure NOTICES AND INFORMATION BEGIN HERE @@ -1232,6 +1233,29 @@ SOFTWARE. ========================================= END OF jeff-hykin/cpp-textmate-grammar NOTICES AND INFORMATION +%% jlelong/vscode-latex-basics NOTICES AND INFORMATION BEGIN HERE +========================================= +Copyright (c) vscode-latex-basics authors + +If not otherwise specified (see below), files in this repository fall under the MIT License + + +The file syntaxes/LaTeX.tmLanguage.json is based on https://github.com/textmate/latex.tmbundle/blob/master/Syntaxes/LaTeX.plist +but has been largely modified. The original file falls 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. + +The file syntaxes/markdown-latex-combined.tmLanguage.json is generated from the Markdown grammar +included in VSCode and falls under the license described in markdown-latex-combined-license.txt. + +The file syntaxes/cpp-grammar-bailout.tmLanguage.json is generated from https://github.com/jeff-hykin/better-cpp-syntax +and falls under the license described in cpp-bailout-license.txt. +========================================= +END OF jlelong/vscode-latex-basics NOTICES AND INFORMATION + %% js-beautify NOTICES AND INFORMATION BEGIN HERE ========================================= The MIT License (MIT) diff --git a/build/.moduleignore b/build/.moduleignore index 233ca72a48a73..29b676c1a1bb4 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -21,6 +21,11 @@ fsevents/test/** @vscode/sqlite3/src/** !@vscode/sqlite3/build/Release/*.node +@vscode/windows-registry/binding.gyp +@vscode/windows-registry/src/** +@vscode/windows-registry/build/** +!@vscode/windows-registry/build/Release/*.node + windows-mutex/binding.gyp windows-mutex/build/** windows-mutex/src/** @@ -78,12 +83,6 @@ node-pty/scripts/** !node-pty/build/Release/*.dll !node-pty/build/Release/*.node -vscode-nsfw/binding.gyp -vscode-nsfw/build/** -vscode-nsfw/src/** -vscode-nsfw/includes/** -!vscode-nsfw/build/Release/*.node - @parcel/watcher/binding.gyp @parcel/watcher/build/** @parcel/watcher/prebuilds/** diff --git a/build/.webignore b/build/.webignore index fc1d2067643a5..f55882ba9d446 100644 --- a/build/.webignore +++ b/build/.webignore @@ -32,3 +32,7 @@ xterm-addon-webgl/out/** # This makes sure the model is included in the package !@vscode/vscode-languagedetection/model/** + +@microsoft/applicationinsights*/** +@microsoft/dynamicproto-js/** +!@microsoft/applicationinsights-web/dist/applicationinsights-web.min.js \ No newline at end of file diff --git a/build/azure-pipelines/config/CredScanSuppressions.json b/build/azure-pipelines/config/CredScanSuppressions.json new file mode 100644 index 0000000000000..312a5560cbd6e --- /dev/null +++ b/build/azure-pipelines/config/CredScanSuppressions.json @@ -0,0 +1,11 @@ +{ + "tool": "Credential Scanner", + "suppressions": [ + { + "file": [ + "src/vs/base/test/common/uri.test.ts" + ], + "_justification": "These are not passwords, they are URIs." + } + ] +} diff --git a/build/azure-pipelines/config/tsaoptions.json b/build/azure-pipelines/config/tsaoptions.json new file mode 100644 index 0000000000000..560d0c2513ab2 --- /dev/null +++ b/build/azure-pipelines/config/tsaoptions.json @@ -0,0 +1,12 @@ +{ + "instanceUrl": "https://msazure.visualstudio.com/defaultcollection", + "projectName": "One", + "areaPath": "One\\VSCode\\Client", + "iterationPath": "One", + "notificationAliases": [ + "sbatten@microsoft.com" + ], + "ppe": "false", + "template": "TFSMSAzure", + "codebaseName": "vscode-client" +} diff --git a/build/azure-pipelines/darwin/product-build-darwin-sign.yml b/build/azure-pipelines/darwin/product-build-darwin-sign.yml index 8b5dd741b511e..7d28236e2bf49 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-sign.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-sign.yml @@ -29,9 +29,23 @@ steps: - script: | set -e - yarn --cwd build - yarn --cwd build compile - displayName: Compile build tools + npx https://aka.ms/enablesecurefeed standAlone + timeoutInMinutes: 5 + retryCountOnTaskFailure: 3 + condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true')) + displayName: Switch to Terrapin packages + + - script: | + set -e + for i in {1..3}; do # try 3 times, for Terrapin + yarn --cwd build --frozen-lockfile && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + displayName: Install build dependencies - download: current artifact: unsigned_vscode_client_darwin_$(VSCODE_ARCH)_archive diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 93f575abbeb8b..e97ce7ebbac24 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -37,12 +37,6 @@ steps: git config user.name "VSCode" displayName: Prepare tooling - - script: | - set -e - sudo xcode-select -s /Applications/Xcode_12.2.app - displayName: Switch to Xcode 12 - condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'arm64')) - - script: | set -e git pull --no-rebase https://github.com/$(VSCODE_MIXIN_REPO).git $(node -p "require('./package.json').distro") @@ -77,6 +71,7 @@ steps: set -e npx https://aka.ms/enablesecurefeed standAlone timeoutInMinutes: 5 + retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) displayName: Switch to Terrapin packages @@ -84,7 +79,6 @@ steps: set -e export npm_config_arch=$(VSCODE_ARCH) export npm_config_node_gyp=$(which node-gyp) - export SDKROOT=/Applications/Xcode_12.2.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk for i in {1..3}; do # try 3 times, for Terrapin yarn --frozen-lockfile && break @@ -122,6 +116,11 @@ steps: displayName: Build client condition: and(succeeded(), ne(variables['VSCODE_ARCH'], 'universal')) + - script: | + set -e + node build/azure-pipelines/mixin --server + displayName: Mix in server quality + - script: | set -e VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ @@ -176,21 +175,21 @@ steps: set -e ./scripts/test.sh --build --tfs "Unit Tests" displayName: Run unit tests (Electron) - timeoutInMinutes: 7 + timeoutInMinutes: 15 condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e yarn test-node --build displayName: Run unit tests (node.js) - timeoutInMinutes: 7 + timeoutInMinutes: 15 condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e - yarn test-browser --build --browser chromium --browser webkit --browser firefox --tfs "Browser Unit Tests" - displayName: Run unit tests (Browser) - timeoutInMinutes: 7 + DEBUG=*browser* yarn test-browser-no-install --sequential --build --browser chromium --browser webkit --tfs "Browser Unit Tests" + displayName: Run unit tests (Browser, Chromium & Webkit) + timeoutInMinutes: 30 condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | @@ -204,15 +203,15 @@ steps: VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \ ./scripts/test-integration.sh --build --tfs "Integration Tests" displayName: Run integration tests (Electron) - timeoutInMinutes: 10 + timeoutInMinutes: 20 condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \ - ./resources/server/test/test-web-integration.sh --browser webkit - displayName: Run integration tests (Browser) - timeoutInMinutes: 10 + ./scripts/test-web-integration.sh --browser webkit + displayName: Run integration tests (Browser, Webkit) + timeoutInMinutes: 20 condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | @@ -221,36 +220,36 @@ steps: APP_NAME="`ls $APP_ROOT | head -n 1`" INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME/Contents/MacOS/Electron" \ VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \ - ./resources/server/test/test-remote-integration.sh + ./scripts/test-remote-integration.sh displayName: Run integration tests (Remote) - timeoutInMinutes: 7 + timeoutInMinutes: 20 condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e - APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) - APP_NAME="`ls $APP_ROOT | head -n 1`" - yarn smoketest-no-compile --build "$APP_ROOT/$APP_NAME" --screenshots $(Build.SourcesDirectory)/.build/logs/smoke-tests + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \ + yarn smoketest-no-compile --web --headless timeoutInMinutes: 10 - displayName: Run smoke tests (Electron) + displayName: Run smoke tests (Browser, Chromium) condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) APP_NAME="`ls $APP_ROOT | head -n 1`" - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \ - yarn smoketest-no-compile --build "$APP_ROOT/$APP_NAME" --remote --screenshots $(Build.SourcesDirectory)/.build/logs/smoke-tests-remote + yarn smoketest-no-compile --build "$APP_ROOT/$APP_NAME" timeoutInMinutes: 10 - displayName: Run smoke tests (Remote) + displayName: Run smoke tests (Electron) condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-darwin" \ - yarn smoketest-no-compile --web --headless + APP_ROOT=$(agent.builddirectory)/VSCode-darwin-$(VSCODE_ARCH) + APP_NAME="`ls $APP_ROOT | head -n 1`" + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-darwin" \ + yarn smoketest-no-compile --build "$APP_ROOT/$APP_NAME" --remote timeoutInMinutes: 10 - displayName: Run smoke tests (Browser) + displayName: Run smoke tests (Remote) condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - task: PublishPipelineArtifact@0 @@ -261,6 +260,16 @@ steps: continueOnError: true condition: failed() + # In order to properly symbolify above crash reports + # (if any), we need the compiled native modules too + - task: PublishPipelineArtifact@0 + inputs: + artifactName: node-modules-macos-$(VSCODE_ARCH) + targetPath: node_modules + displayName: "Publish Node Modules" + continueOnError: true + condition: failed() + - task: PublishPipelineArtifact@0 inputs: artifactName: logs-macos-$(VSCODE_ARCH)-$(System.JobAttempt) diff --git a/build/azure-pipelines/linux/alpine/install-dependencies.sh b/build/azure-pipelines/linux/alpine/install-dependencies.sh index 1d2a2325490a4..f849b3acef2cc 100755 --- a/build/azure-pipelines/linux/alpine/install-dependencies.sh +++ b/build/azure-pipelines/linux/alpine/install-dependencies.sh @@ -2,4 +2,4 @@ set -e echo "Installing remote dependencies" -(cd remote && rm -rf node_modules && yarn) \ No newline at end of file +(cd remote && rm -rf node_modules && yarn --verbose) diff --git a/build/azure-pipelines/linux/product-build-alpine.yml b/build/azure-pipelines/linux/product-build-alpine.yml index 0a2dd05f28bda..144f1ace6ec66 100644 --- a/build/azure-pipelines/linux/product-build-alpine.yml +++ b/build/azure-pipelines/linux/product-build-alpine.yml @@ -69,6 +69,7 @@ steps: set -e npx https://aka.ms/enablesecurefeed standAlone timeoutInMinutes: 5 + retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) displayName: Switch to Terrapin packages @@ -100,6 +101,7 @@ steps: - script: | set -e node build/azure-pipelines/mixin + node build/azure-pipelines/mixin --server displayName: Mix in quality - script: docker run --rm --privileged multiarch/qemu-user-static --reset -p yes diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 23c03767a16c1..3c450aa0e5cbc 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -60,14 +60,21 @@ steps: set -e npx https://aka.ms/enablesecurefeed standAlone timeoutInMinutes: 5 + retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) displayName: Switch to Terrapin packages - script: | set -e - yarn --cwd build - yarn --cwd build compile - displayName: Compile build tools + for i in {1..3}; do # try 3 times, for Terrapin + yarn --cwd build --frozen-lockfile && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + displayName: Install build dependencies - script: | set -e @@ -129,6 +136,11 @@ steps: yarn gulp vscode-linux-$(VSCODE_ARCH)-min-ci displayName: Build + - script: | + set -e + node build/azure-pipelines/mixin --server + displayName: Mix in server quality + - script: | set -e VSCODE_MIXIN_PASSWORD="$(github-distro-mixin-password)" \ @@ -159,21 +171,21 @@ steps: set -e ./scripts/test.sh --build --tfs "Unit Tests" displayName: Run unit tests (Electron) - timeoutInMinutes: 7 + timeoutInMinutes: 15 condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e yarn test-node --build displayName: Run unit tests (node.js) - timeoutInMinutes: 7 + timeoutInMinutes: 15 condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e - yarn test-browser --build --browser chromium --tfs "Browser Unit Tests" - displayName: Run unit tests (Browser) - timeoutInMinutes: 7 + DEBUG=*browser* yarn test-browser-no-install --build --browser chromium --tfs "Browser Unit Tests" + displayName: Run unit tests (Browser, Chromium) + timeoutInMinutes: 15 condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | @@ -188,15 +200,15 @@ steps: VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ ./scripts/test-integration.sh --build --tfs "Integration Tests" displayName: Run integration tests (Electron) - timeoutInMinutes: 10 + timeoutInMinutes: 20 condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \ - ./resources/server/test/test-web-integration.sh --browser chromium - displayName: Run integration tests (Browser) - timeoutInMinutes: 10 + ./scripts/test-web-integration.sh --browser chromium + displayName: Run integration tests (Browser, Chromium) + timeoutInMinutes: 20 condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | @@ -206,34 +218,34 @@ steps: INTEGRATION_TEST_APP_NAME="$APP_NAME" \ INTEGRATION_TEST_ELECTRON_PATH="$APP_ROOT/$APP_NAME" \ VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ - ./resources/server/test/test-remote-integration.sh + ./scripts/test-remote-integration.sh displayName: Run integration tests (Remote) - timeoutInMinutes: 7 + timeoutInMinutes: 20 condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e - APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - yarn smoketest-no-compile --build "$APP_PATH" --electronArgs="--disable-dev-shm-usage --use-gl=swiftshader" --screenshots $(Build.SourcesDirectory)/.build/logs/smoke-tests + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \ + yarn smoketest-no-compile --web --headless --electronArgs="--disable-dev-shm-usage --use-gl=swiftshader" timeoutInMinutes: 10 - displayName: Run smoke tests (Electron) + displayName: Run smoke tests (Browser, Chromium) condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ - yarn smoketest-no-compile --build "$APP_PATH" --remote --electronArgs="--disable-dev-shm-usage --use-gl=swiftshader" --screenshots $(Build.SourcesDirectory)/.build/logs/smoke-tests-remote + yarn smoketest-no-compile --build "$APP_PATH" --electronArgs="--disable-dev-shm-usage --use-gl=swiftshader" timeoutInMinutes: 10 - displayName: Run smoke tests (Remote) + displayName: Run smoke tests (Electron) condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - script: | set -e - VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-web-linux-$(VSCODE_ARCH)" \ - yarn smoketest-no-compile --web --headless --electronArgs="--disable-dev-shm-usage --use-gl=swiftshader" + APP_PATH=$(agent.builddirectory)/VSCode-linux-$(VSCODE_ARCH) + VSCODE_REMOTE_SERVER_PATH="$(agent.builddirectory)/vscode-reh-linux-$(VSCODE_ARCH)" \ + yarn smoketest-no-compile --build "$APP_PATH" --remote --electronArgs="--disable-dev-shm-usage --use-gl=swiftshader" timeoutInMinutes: 10 - displayName: Run smoke tests (Browser) + displayName: Run smoke tests (Remote) condition: and(succeeded(), eq(variables['VSCODE_ARCH'], 'x64'), eq(variables['VSCODE_STEP_ON_IT'], 'false')) - task: PublishPipelineArtifact@0 @@ -244,6 +256,16 @@ steps: continueOnError: true condition: failed() + # In order to properly symbolify above crash reports + # (if any), we need the compiled native modules too + - task: PublishPipelineArtifact@0 + inputs: + artifactName: node-modules-linux-$(VSCODE_ARCH) + targetPath: node_modules + displayName: "Publish Node Modules" + continueOnError: true + condition: failed() + - task: PublishPipelineArtifact@0 inputs: artifactName: logs-linux-$(VSCODE_ARCH)-$(System.JobAttempt) @@ -281,13 +303,6 @@ steps: displayName: Download ESRPClient condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - script: | - set -e - yarn --cwd build - yarn --cwd build compile - displayName: Compile build tools - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - script: | set -e node build/azure-pipelines/common/sign "$(esrpclient.toolpath)/$(esrpclient.toolname)" rpm $(ESRP-PKI) $(esrp-aad-username) $(esrp-aad-password) .build/linux/rpm '*.rpm' diff --git a/build/azure-pipelines/mixin.js b/build/azure-pipelines/mixin.js index 62ad40ba09942..ba73cdeb08337 100644 --- a/build/azure-pipelines/mixin.js +++ b/build/azure-pipelines/mixin.js @@ -13,46 +13,74 @@ const fancyLog = require("fancy-log"); const ansiColors = require("ansi-colors"); const fs = require("fs"); const path = require("path"); +async function mixinClient(quality) { + const productJsonFilter = filter(f => f.relative === 'product.json', { restore: true }); + fancyLog(ansiColors.blue('[mixin]'), `Mixing in client:`); + return new Promise((c, e) => { + vfs + .src(`quality/${quality}/**`, { base: `quality/${quality}` }) + .pipe(filter(f => !f.isDirectory())) + .pipe(filter(f => f.relative !== 'product.server.json')) + .pipe(productJsonFilter) + .pipe(buffer()) + .pipe(json((o) => { + const originalProduct = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'product.json'), 'utf8')); + let builtInExtensions = originalProduct.builtInExtensions; + if (Array.isArray(o.builtInExtensions)) { + fancyLog(ansiColors.blue('[mixin]'), 'Overwriting built-in extensions:', o.builtInExtensions.map(e => e.name)); + builtInExtensions = o.builtInExtensions; + } + else if (o.builtInExtensions) { + const include = o.builtInExtensions['include'] || []; + const exclude = o.builtInExtensions['exclude'] || []; + fancyLog(ansiColors.blue('[mixin]'), 'OSS built-in extensions:', builtInExtensions.map(e => e.name)); + fancyLog(ansiColors.blue('[mixin]'), 'Including built-in extensions:', include.map(e => e.name)); + fancyLog(ansiColors.blue('[mixin]'), 'Excluding built-in extensions:', exclude); + builtInExtensions = builtInExtensions.filter(ext => !include.find(e => e.name === ext.name) && !exclude.find(name => name === ext.name)); + builtInExtensions = [...builtInExtensions, ...include]; + fancyLog(ansiColors.blue('[mixin]'), 'Final built-in extensions:', builtInExtensions.map(e => e.name)); + } + else { + fancyLog(ansiColors.blue('[mixin]'), 'Inheriting OSS built-in extensions', builtInExtensions.map(e => e.name)); + } + return Object.assign(Object.assign({ webBuiltInExtensions: originalProduct.webBuiltInExtensions }, o), { builtInExtensions }); + })) + .pipe(productJsonFilter.restore) + .pipe(es.mapSync((f) => { + fancyLog(ansiColors.blue('[mixin]'), f.relative, ansiColors.green('✔︎')); + return f; + })) + .pipe(vfs.dest('.')) + .on('end', () => c()) + .on('error', (err) => e(err)); + }); +} +function mixinServer(quality) { + const serverProductJsonPath = `quality/${quality}/product.server.json`; + if (!fs.existsSync(serverProductJsonPath)) { + fancyLog(ansiColors.blue('[mixin]'), `Server product not found`, serverProductJsonPath); + return; + } + fancyLog(ansiColors.blue('[mixin]'), `Mixing in server:`); + const originalProduct = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'product.json'), 'utf8')); + const serverProductJson = JSON.parse(fs.readFileSync(serverProductJsonPath, 'utf8')); + fs.writeFileSync('product.json', JSON.stringify(Object.assign(Object.assign({}, originalProduct), serverProductJson), undefined, '\t')); + fancyLog(ansiColors.blue('[mixin]'), 'product.json', ansiColors.green('✔︎')); +} function main() { const quality = process.env['VSCODE_QUALITY']; if (!quality) { console.log('Missing VSCODE_QUALITY, skipping mixin'); return; } - const productJsonFilter = filter(f => f.relative === 'product.json', { restore: true }); - fancyLog(ansiColors.blue('[mixin]'), `Mixing in sources:`); - return vfs - .src(`quality/${quality}/**`, { base: `quality/${quality}` }) - .pipe(filter(f => !f.isDirectory())) - .pipe(productJsonFilter) - .pipe(buffer()) - .pipe(json((o) => { - const ossProduct = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'product.json'), 'utf8')); - let builtInExtensions = ossProduct.builtInExtensions; - if (Array.isArray(o.builtInExtensions)) { - fancyLog(ansiColors.blue('[mixin]'), 'Overwriting built-in extensions:', o.builtInExtensions.map(e => e.name)); - builtInExtensions = o.builtInExtensions; - } - else if (o.builtInExtensions) { - const include = o.builtInExtensions['include'] || []; - const exclude = o.builtInExtensions['exclude'] || []; - fancyLog(ansiColors.blue('[mixin]'), 'OSS built-in extensions:', builtInExtensions.map(e => e.name)); - fancyLog(ansiColors.blue('[mixin]'), 'Including built-in extensions:', include.map(e => e.name)); - fancyLog(ansiColors.blue('[mixin]'), 'Excluding built-in extensions:', exclude); - builtInExtensions = builtInExtensions.filter(ext => !include.find(e => e.name === ext.name) && !exclude.find(name => name === ext.name)); - builtInExtensions = [...builtInExtensions, ...include]; - fancyLog(ansiColors.blue('[mixin]'), 'Final built-in extensions:', builtInExtensions.map(e => e.name)); - } - else { - fancyLog(ansiColors.blue('[mixin]'), 'Inheriting OSS built-in extensions', builtInExtensions.map(e => e.name)); - } - return Object.assign(Object.assign({ webBuiltInExtensions: ossProduct.webBuiltInExtensions }, o), { builtInExtensions }); - })) - .pipe(productJsonFilter.restore) - .pipe(es.mapSync(function (f) { - fancyLog(ansiColors.blue('[mixin]'), f.relative, ansiColors.green('✔︎')); - return f; - })) - .pipe(vfs.dest('.')); + if (process.argv[2] === '--server') { + mixinServer(quality); + } + else { + mixinClient(quality).catch(err => { + console.error(err); + process.exit(1); + }); + } } main(); diff --git a/build/azure-pipelines/mixin.ts b/build/azure-pipelines/mixin.ts index b6818d6957db8..35a2d7ae0ec1d 100644 --- a/build/azure-pipelines/mixin.ts +++ b/build/azure-pipelines/mixin.ts @@ -33,6 +33,71 @@ interface Product { readonly webBuiltInExtensions?: IBuiltInExtension[]; } +async function mixinClient(quality: string): Promise { + const productJsonFilter = filter(f => f.relative === 'product.json', { restore: true }); + + fancyLog(ansiColors.blue('[mixin]'), `Mixing in client:`); + + return new Promise((c, e) => { + vfs + .src(`quality/${quality}/**`, { base: `quality/${quality}` }) + .pipe(filter(f => !f.isDirectory())) + .pipe(filter(f => f.relative !== 'product.server.json')) + .pipe(productJsonFilter) + .pipe(buffer()) + .pipe(json((o: Product) => { + const originalProduct = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'product.json'), 'utf8')) as OSSProduct; + let builtInExtensions = originalProduct.builtInExtensions; + + if (Array.isArray(o.builtInExtensions)) { + fancyLog(ansiColors.blue('[mixin]'), 'Overwriting built-in extensions:', o.builtInExtensions.map(e => e.name)); + + builtInExtensions = o.builtInExtensions; + } else if (o.builtInExtensions) { + const include = o.builtInExtensions['include'] || []; + const exclude = o.builtInExtensions['exclude'] || []; + + fancyLog(ansiColors.blue('[mixin]'), 'OSS built-in extensions:', builtInExtensions.map(e => e.name)); + fancyLog(ansiColors.blue('[mixin]'), 'Including built-in extensions:', include.map(e => e.name)); + fancyLog(ansiColors.blue('[mixin]'), 'Excluding built-in extensions:', exclude); + + builtInExtensions = builtInExtensions.filter(ext => !include.find(e => e.name === ext.name) && !exclude.find(name => name === ext.name)); + builtInExtensions = [...builtInExtensions, ...include]; + + fancyLog(ansiColors.blue('[mixin]'), 'Final built-in extensions:', builtInExtensions.map(e => e.name)); + } else { + fancyLog(ansiColors.blue('[mixin]'), 'Inheriting OSS built-in extensions', builtInExtensions.map(e => e.name)); + } + + return { webBuiltInExtensions: originalProduct.webBuiltInExtensions, ...o, builtInExtensions }; + })) + .pipe(productJsonFilter.restore) + .pipe(es.mapSync((f: Vinyl) => { + fancyLog(ansiColors.blue('[mixin]'), f.relative, ansiColors.green('✔︎')); + return f; + })) + .pipe(vfs.dest('.')) + .on('end', () => c()) + .on('error', (err: any) => e(err)); + }); +} + +function mixinServer(quality: string) { + const serverProductJsonPath = `quality/${quality}/product.server.json`; + + if (!fs.existsSync(serverProductJsonPath)) { + fancyLog(ansiColors.blue('[mixin]'), `Server product not found`, serverProductJsonPath); + return; + } + + fancyLog(ansiColors.blue('[mixin]'), `Mixing in server:`); + + const originalProduct = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'product.json'), 'utf8')) as OSSProduct; + const serverProductJson = JSON.parse(fs.readFileSync(serverProductJsonPath, 'utf8')); + fs.writeFileSync('product.json', JSON.stringify({ ...originalProduct, ...serverProductJson }, undefined, '\t')); + fancyLog(ansiColors.blue('[mixin]'), 'product.json', ansiColors.green('✔︎')); +} + function main() { const quality = process.env['VSCODE_QUALITY']; @@ -41,46 +106,14 @@ function main() { return; } - const productJsonFilter = filter(f => f.relative === 'product.json', { restore: true }); - - fancyLog(ansiColors.blue('[mixin]'), `Mixing in sources:`); - return vfs - .src(`quality/${quality}/**`, { base: `quality/${quality}` }) - .pipe(filter(f => !f.isDirectory())) - .pipe(productJsonFilter) - .pipe(buffer()) - .pipe(json((o: Product) => { - const ossProduct = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'product.json'), 'utf8')) as OSSProduct; - let builtInExtensions = ossProduct.builtInExtensions; - - if (Array.isArray(o.builtInExtensions)) { - fancyLog(ansiColors.blue('[mixin]'), 'Overwriting built-in extensions:', o.builtInExtensions.map(e => e.name)); - - builtInExtensions = o.builtInExtensions; - } else if (o.builtInExtensions) { - const include = o.builtInExtensions['include'] || []; - const exclude = o.builtInExtensions['exclude'] || []; - - fancyLog(ansiColors.blue('[mixin]'), 'OSS built-in extensions:', builtInExtensions.map(e => e.name)); - fancyLog(ansiColors.blue('[mixin]'), 'Including built-in extensions:', include.map(e => e.name)); - fancyLog(ansiColors.blue('[mixin]'), 'Excluding built-in extensions:', exclude); - - builtInExtensions = builtInExtensions.filter(ext => !include.find(e => e.name === ext.name) && !exclude.find(name => name === ext.name)); - builtInExtensions = [...builtInExtensions, ...include]; - - fancyLog(ansiColors.blue('[mixin]'), 'Final built-in extensions:', builtInExtensions.map(e => e.name)); - } else { - fancyLog(ansiColors.blue('[mixin]'), 'Inheriting OSS built-in extensions', builtInExtensions.map(e => e.name)); - } - - return { webBuiltInExtensions: ossProduct.webBuiltInExtensions, ...o, builtInExtensions }; - })) - .pipe(productJsonFilter.restore) - .pipe(es.mapSync(function (f: Vinyl) { - fancyLog(ansiColors.blue('[mixin]'), f.relative, ansiColors.green('✔︎')); - return f; - })) - .pipe(vfs.dest('.')); + if (process.argv[2] === '--server') { + mixinServer(quality); + } else { + mixinClient(quality).catch(err => { + console.error(err); + process.exit(1); + }); + } } main(); diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index c0b619cab095c..d07a752e77ef5 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -152,11 +152,11 @@ stages: dependsOn: - Compile pool: - vmImage: VS2017-Win2016 + vmImage: windows-latest jobs: - ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}: - job: Windows - timeoutInMinutes: 90 + timeoutInMinutes: 120 variables: VSCODE_ARCH: x64 steps: @@ -164,7 +164,7 @@ stages: - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_WIN32_32BIT, true)) }}: - job: Windows32 - timeoutInMinutes: 90 + timeoutInMinutes: 120 variables: VSCODE_ARCH: ia32 steps: @@ -254,6 +254,7 @@ stages: - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ALPINE_ARM64, true)) }}: - job: LinuxAlpineArm64 + timeoutInMinutes: 120 variables: VSCODE_ARCH: arm64 steps: diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index addb2995b1258..44364c76beca5 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -50,6 +50,7 @@ steps: set -e npx https://aka.ms/enablesecurefeed standAlone timeoutInMinutes: 5 + retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) displayName: Switch to Terrapin packages diff --git a/build/azure-pipelines/product-onebranch.yml b/build/azure-pipelines/product-onebranch.yml new file mode 100644 index 0000000000000..6241e0c0ee49e --- /dev/null +++ b/build/azure-pipelines/product-onebranch.yml @@ -0,0 +1,46 @@ +trigger: none +pr: none + +variables: + LinuxContainerImage: "onebranch.azurecr.io/linux/ubuntu-2004:latest" + +resources: + repositories: + - repository: templates + type: git + name: OneBranch.Pipelines/GovernedTemplates + ref: refs/heads/main + + - repository: distro + type: github + name: microsoft/vscode-distro + ref: refs/heads/distro + endpoint: Monaco + +extends: + template: v2/OneBranch.NonOfficial.CrossPlat.yml@templates + parameters: + git: + fetchDepth: 1 + lfs: true + retryCount: 3 + + globalSdl: + policheck: + break: true + credscan: + suppressionsFile: $(Build.SourcesDirectory)\build\azure-pipelines\config\CredScanSuppressions.json + + stages: + - stage: Compile + + jobs: + - job: Compile + pool: + type: linux + + variables: + ob_outputDirectory: '$(Build.SourcesDirectory)' + + steps: + - checkout: distro diff --git a/build/azure-pipelines/sdl-scan.yml b/build/azure-pipelines/sdl-scan.yml index d1cd72b3d9544..22b754b4df88c 100644 --- a/build/azure-pipelines/sdl-scan.yml +++ b/build/azure-pipelines/sdl-scan.yml @@ -35,7 +35,7 @@ stages: - stage: Windows condition: eq(variables.SCAN_WINDOWS, 'true') pool: - vmImage: VS2017-Win2016 + vmImage: windows-latest jobs: - job: WindowsJob timeoutInMinutes: 0 @@ -76,6 +76,7 @@ stages: $ErrorActionPreference = "Stop" exec { npx https://aka.ms/enablesecurefeed standAlone } timeoutInMinutes: 5 + retryCountOnTaskFailure: 3 condition: and(succeeded(), eq(variables['ENABLE_TERRAPIN'], 'true')) displayName: Switch to Terrapin packages @@ -168,14 +169,21 @@ stages: set -e npx https://aka.ms/enablesecurefeed standAlone timeoutInMinutes: 5 + retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) displayName: Switch to Terrapin packages - script: | set -e - yarn --cwd build - yarn --cwd build compile - displayName: Compile build tools + for i in {1..3}; do # try 3 times, for Terrapin + yarn --cwd build --frozen-lockfile && break + if [ $i -eq 3 ]; then + echo "Yarn failed too many times" >&2 + exit 1 + fi + echo "Yarn failed $i, trying again..." + done + displayName: Install build dependencies - script: | set -e diff --git a/build/azure-pipelines/upload-cdn.js b/build/azure-pipelines/upload-cdn.js index fe3817c91836d..9d2c3a47ae1d5 100644 --- a/build/azure-pipelines/upload-cdn.js +++ b/build/azure-pipelines/upload-cdn.js @@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); const path = require("path"); const es = require("event-stream"); +const Vinyl = require("vinyl"); const vfs = require("vinyl-fs"); const util = require("../lib/util"); const filter = require("gulp-filter"); @@ -15,25 +16,41 @@ const azure = require('gulp-azure-storage'); const root = path.dirname(path.dirname(__dirname)); const commit = util.getVersion(root); const credential = new identity_1.ClientSecretCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_CLIENT_SECRET']); -function main() { - return new Promise((c, e) => { +async function main() { + const files = []; + const options = { + account: process.env.AZURE_STORAGE_ACCOUNT, + credential, + container: process.env.VSCODE_QUALITY, + prefix: commit + '/', + contentSettings: { + contentEncoding: 'gzip', + cacheControl: 'max-age=31536000, public' + } + }; + await new Promise((c, e) => { vfs.src('**', { cwd: '../vscode-web', base: '../vscode-web', dot: true }) .pipe(filter(f => !f.isDirectory())) .pipe(gzip({ append: false })) .pipe(es.through(function (data) { - console.log('Uploading CDN file:', data.relative); // debug + console.log('Uploading:', data.relative); // debug + files.push(data.relative); this.emit('data', data); })) - .pipe(azure.upload({ - account: process.env.AZURE_STORAGE_ACCOUNT, - credential, - container: process.env.VSCODE_QUALITY, - prefix: commit + '/', - contentSettings: { - contentEncoding: 'gzip', - cacheControl: 'max-age=31536000, public' - } - })) + .pipe(azure.upload(options)) + .on('end', () => c()) + .on('error', (err) => e(err)); + }); + await new Promise((c, e) => { + const listing = new Vinyl({ + path: 'files.txt', + contents: Buffer.from(files.join('\n')), + stat: { mode: 0o666 } + }); + console.log(`Uploading: files.txt (${files.length} files)`); // debug + es.readArray([listing]) + .pipe(gzip({ append: false })) + .pipe(azure.upload(options)) .on('end', () => c()) .on('error', (err) => e(err)); }); diff --git a/build/azure-pipelines/upload-cdn.ts b/build/azure-pipelines/upload-cdn.ts index c35582017d765..c6180507991c1 100644 --- a/build/azure-pipelines/upload-cdn.ts +++ b/build/azure-pipelines/upload-cdn.ts @@ -19,25 +19,44 @@ const root = path.dirname(path.dirname(__dirname)); const commit = util.getVersion(root); const credential = new ClientSecretCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, process.env['AZURE_CLIENT_SECRET']!); -function main(): Promise { - return new Promise((c, e) => { +async function main(): Promise { + const files: string[] = []; + const options = { + account: process.env.AZURE_STORAGE_ACCOUNT, + credential, + container: process.env.VSCODE_QUALITY, + prefix: commit + '/', + contentSettings: { + contentEncoding: 'gzip', + cacheControl: 'max-age=31536000, public' + } + }; + + await new Promise((c, e) => { vfs.src('**', { cwd: '../vscode-web', base: '../vscode-web', dot: true }) .pipe(filter(f => !f.isDirectory())) .pipe(gzip({ append: false })) .pipe(es.through(function (data: Vinyl) { - console.log('Uploading CDN file:', data.relative); // debug + console.log('Uploading:', data.relative); // debug + files.push(data.relative); this.emit('data', data); })) - .pipe(azure.upload({ - account: process.env.AZURE_STORAGE_ACCOUNT, - credential, - container: process.env.VSCODE_QUALITY, - prefix: commit + '/', - contentSettings: { - contentEncoding: 'gzip', - cacheControl: 'max-age=31536000, public' - } - })) + .pipe(azure.upload(options)) + .on('end', () => c()) + .on('error', (err: any) => e(err)); + }); + + await new Promise((c, e) => { + const listing = new Vinyl({ + path: 'files.txt', + contents: Buffer.from(files.join('\n')), + stat: { mode: 0o666 } as any + }); + + console.log(`Uploading: files.txt (${files.length} files)`); // debug + es.readArray([listing]) + .pipe(gzip({ append: false })) + .pipe(azure.upload(options)) .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 cadda5eaf644d..aced8b3076fd3 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -60,6 +60,7 @@ steps: set -e npx https://aka.ms/enablesecurefeed standAlone timeoutInMinutes: 5 + retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) displayName: Switch to Terrapin packages @@ -119,7 +120,7 @@ steps: node build/azure-pipelines/upload-cdn displayName: Upload to CDN - # upload only the workbench.web.api.js source maps because + # 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: | @@ -128,7 +129,7 @@ steps: 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.api.js.map + 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) - script: | diff --git a/build/azure-pipelines/win32/listprocesses.bat b/build/azure-pipelines/win32/listprocesses.bat new file mode 100644 index 0000000000000..e184b37fe5697 --- /dev/null +++ b/build/azure-pipelines/win32/listprocesses.bat @@ -0,0 +1,3 @@ +echo "------------------------------------" +tasklist +echo "------------------------------------" diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 8d3e0aab72b78..94e8584d395cf 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -21,11 +21,11 @@ steps: path: $(Build.ArtifactStagingDirectory) displayName: Download compilation output - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { tar --force-local -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz } + - task: ExtractFiles@1 displayName: Extract compilation output + inputs: + archiveFilePatterns: "$(Build.ArtifactStagingDirectory)/compilation.tar.gz" + cleanDestinationFolder: false - powershell: | . build/azure-pipelines/win32/exec.ps1 @@ -67,6 +67,7 @@ steps: $ErrorActionPreference = "Stop" exec { npx https://aka.ms/enablesecurefeed standAlone } timeoutInMinutes: 5 + retryCountOnTaskFailure: 3 condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), eq(variables['ENABLE_TERRAPIN'], 'true')) displayName: Switch to Terrapin packages @@ -123,6 +124,12 @@ steps: displayName: Prepare Package condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { node build/azure-pipelines/mixin --server } + displayName: Mix in quality + - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" @@ -147,7 +154,7 @@ steps: exec { yarn electron $(VSCODE_ARCH) } exec { .\scripts\test.bat --build --tfs "Unit Tests" } displayName: Run unit tests (Electron) - timeoutInMinutes: 7 + timeoutInMinutes: 15 condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - powershell: | @@ -155,15 +162,15 @@ steps: $ErrorActionPreference = "Stop" exec { yarn test-node --build } displayName: Run unit tests (node.js) - timeoutInMinutes: 7 + timeoutInMinutes: 15 condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - exec { yarn test-browser --build --browser chromium --browser firefox --tfs "Browser Unit Tests" } - displayName: Run unit tests (Browser) - timeoutInMinutes: 7 + exec { yarn test-browser-no-install --sequential --build --browser chromium --browser firefox --tfs "Browser Unit Tests" } + displayName: Run unit tests (Browser, Chromium & Firefox) + timeoutInMinutes: 20 condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - powershell: | @@ -177,15 +184,15 @@ steps: $AppNameShort = $AppProductJson.nameShort exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-integration.bat --build --tfs "Integration Tests" } displayName: Run integration tests (Electron) - timeoutInMinutes: 10 + timeoutInMinutes: 20 condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-web-integration.bat --browser firefox } - displayName: Run integration tests (Browser) - timeoutInMinutes: 10 + exec { $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)"; .\scripts\test-web-integration.bat --browser firefox } + displayName: Run integration tests (Browser, Firefox) + timeoutInMinutes: 20 condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - powershell: | @@ -194,23 +201,25 @@ steps: $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" $AppProductJson = Get-Content -Raw -Path "$AppRoot\resources\app\product.json" | ConvertFrom-Json $AppNameShort = $AppProductJson.nameShort - exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\resources\server\test\test-remote-integration.bat } + exec { $env:INTEGRATION_TEST_ELECTRON_PATH = "$AppRoot\$AppNameShort.exe"; $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)"; .\scripts\test-remote-integration.bat } displayName: Run integration tests (Remote) - timeoutInMinutes: 7 + timeoutInMinutes: 20 condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" - exec { yarn --cwd test/smoke compile } - displayName: Compile smoke tests + $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)" + exec { yarn smoketest-no-compile --web --headless } + displayName: Run smoke tests (Browser, Chromium) + timeoutInMinutes: 10 condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" - exec { yarn smoketest-no-compile --build "$AppRoot" --screenshots $(Build.SourcesDirectory)\.build\logs\smoke-tests } + exec { yarn smoketest-no-compile --build "$AppRoot" } displayName: Run smoke tests (Electron) timeoutInMinutes: 10 condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) @@ -220,19 +229,17 @@ steps: $ErrorActionPreference = "Stop" $AppRoot = "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)" $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-win32-$(VSCODE_ARCH)" - exec { yarn smoketest-no-compile --build "$AppRoot" --remote --screenshots $(Build.SourcesDirectory)\.build\logs\smoke-tests-remote } + exec { yarn smoketest-no-compile --build "$AppRoot" --remote } displayName: Run smoke tests (Remote) timeoutInMinutes: 10 condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) - powershell: | . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - $env:VSCODE_REMOTE_SERVER_PATH = "$(agent.builddirectory)\vscode-reh-web-win32-$(VSCODE_ARCH)" - exec { yarn smoketest-no-compile --web --browser firefox --headless } - displayName: Run smoke tests (Browser) - timeoutInMinutes: 10 - condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false'), ne(variables['VSCODE_ARCH'], 'arm64')) + exec {.\build\azure-pipelines\win32\listprocesses.bat } + displayName: List tasks after smoke test run + continueOnError: true + condition: succeededOrFailed() - task: PublishPipelineArtifact@0 inputs: @@ -242,6 +249,16 @@ steps: continueOnError: true condition: failed() + # In order to properly symbolify above crash reports + # (if any), we need the compiled native modules too + - task: PublishPipelineArtifact@0 + inputs: + artifactName: node-modules-windows-$(VSCODE_ARCH) + targetPath: node_modules + displayName: "Publish Node Modules" + continueOnError: true + condition: failed() + - task: PublishPipelineArtifact@0 inputs: artifactName: logs-windows-$(VSCODE_ARCH)-$(System.JobAttempt) @@ -266,14 +283,6 @@ steps: displayName: Download ESRPClient condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - powershell: | - . build/azure-pipelines/win32/exec.ps1 - $ErrorActionPreference = "Stop" - exec { yarn --cwd build } - exec { yarn --cwd build compile } - displayName: Compile build tools - condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) - - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" diff --git a/build/filters.js b/build/filters.js index 872ed47f9ea09..bff7cad1c20ae 100644 --- a/build/filters.js +++ b/build/filters.js @@ -32,7 +32,7 @@ module.exports.unicodeFilter = [ '!LICENSES.chromium.html', '!**/LICENSE', - '!**/*.{dll,exe,png,bmp,jpg,scpt,cur,ttf,woff,eot,template,ico,icns}', + '!**/*.{dll,exe,png,bmp,jpg,scpt,cur,ttf,woff,eot,template,ico,icns,opus}', '!**/test/**', '!**/*.test.ts', '!**/*.{d.ts,json,md}', @@ -108,7 +108,7 @@ module.exports.indentationFilter = [ '!src/vs/*/**/*.d.ts', '!src/typings/**/*.d.ts', '!extensions/**/*.d.ts', - '!**/*.{svg,exe,png,bmp,jpg,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist}', + '!**/*.{svg,exe,png,bmp,jpg,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,template,yaml,yml,d.ts.recipe,ico,icns,plist,opus}', '!build/{lib,download,linux,darwin}/**/*.js', '!build/**/*.sh', '!build/azure-pipelines/**/*.js', @@ -133,6 +133,7 @@ module.exports.copyrightFilter = [ '!**/*.bat', '!**/*.cmd', '!**/*.ico', + '!**/*.opus', '!**/*.icns', '!**/*.xml', '!**/*.sh', @@ -146,7 +147,6 @@ module.exports.copyrightFilter = [ '!build/linux/libcxx-fetcher.*', '!resources/linux/snap/snapcraft.yaml', '!resources/win32/bin/code.js', - '!resources/web/code-web.js', '!resources/completions/**', '!extensions/configuration-editing/build/inline-allOf.ts', '!extensions/markdown-language-features/media/highlight.css', diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 39b26b2f64610..c8308a0d190b7 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -197,6 +197,44 @@ const compileEditorESMTask = task.define('compile-editor-esm', () => { } }); +/** + * Go over all .js files in `/out-monaco-editor-core/esm/` and make sure that all imports + * use `.js` at the end in order to be ESM compliant. + */ +const appendJSToESMImportsTask = task.define('append-js-to-esm-imports', () => { + const SRC_DIR = path.join(__dirname, '../out-monaco-editor-core/esm'); + const files = util.rreddir(SRC_DIR); + for (const file of files) { + const filePath = path.join(SRC_DIR, file); + if (!/\.js$/.test(filePath)) { + continue; + } + + const contents = fs.readFileSync(filePath).toString(); + const lines = contents.split(/\r\n|\r|\n/g); + const /** @type {string[]} */result = []; + for (const line of lines) { + if (!/^import/.test(line) && !/^export \* from/.test(line)) { + // not an import + result.push(line); + continue; + } + if (/^import '[^']+\.css';/.test(line)) { + // CSS import + result.push(line); + continue; + } + let modifiedLine = ( + line + .replace(/^import(.*)\'([^']+)\'/, `import$1'$2.js'`) + .replace(/^export \* from \'([^']+)\'/, `export * from '$1.js'`) + ); + result.push(modifiedLine); + } + fs.writeFileSync(filePath, result.join('\n')); + } +}); + function toExternalDTS(contents) { let lines = contents.split(/\r\n|\r|\n/); let killNextCloseCurlyBrace = false; @@ -362,7 +400,8 @@ gulp.task('editor-distro', ), task.series( createESMSourcesAndResourcesTask, - compileEditorESMTask + compileEditorESMTask, + appendJSToESMImportsTask ) ), finalEditorResourcesTask @@ -411,6 +450,7 @@ gulp.task('editor-esm-bundle', extractEditorSrcTask, createESMSourcesAndResourcesTask, compileEditorESMTask, + appendJSToESMImportsTask, bundleEditorESMTask, ) ); diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 0dbd4a5f469da..89517f86fef8e 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -29,6 +29,7 @@ const extensionsPath = path.join(path.dirname(__dirname), 'extensions'); // ignore: ['**/out/**', '**/node_modules/**'] // }); const compilations = [ + 'authentication-proxy/tsconfig.json', 'configuration-editing/build/tsconfig.json', 'configuration-editing/tsconfig.json', 'css-language-features/client/tsconfig.json', diff --git a/build/gulpfile.js b/build/gulpfile.js index dc043d7b5a76c..65dda505fa712 100644 --- a/build/gulpfile.js +++ b/build/gulpfile.js @@ -11,25 +11,29 @@ require('events').EventEmitter.defaultMaxListeners = 100; const gulp = require('gulp'); const util = require('./lib/util'); const task = require('./lib/task'); -const compilation = require('./lib/compilation'); +const { compileTask, watchTask, compileApiProposalNamesTask, watchApiProposalNamesTask } = require('./lib/compilation'); const { monacoTypecheckTask/* , monacoTypecheckWatchTask */ } = require('./gulpfile.editor'); const { compileExtensionsTask, watchExtensionsTask, compileExtensionMediaTask } = require('./gulpfile.extensions'); +// API proposal names +gulp.task(compileApiProposalNamesTask); +gulp.task(watchApiProposalNamesTask); + // Fast compile for development time -const compileClientTask = task.define('compile-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), compilation.compileApiProposalNames(), compilation.compileTask('src', 'out', false))); +const compileClientTask = task.define('compile-client', task.series(util.rimraf('out'), util.buildWebNodePaths('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(compilation.watchTask('out', false), compilation.watchApiProposalNames()))); +const watchClientTask = task.define('watch-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), task.parallel(watchTask('out', false), watchApiProposalNamesTask))); gulp.task(watchClientTask); // All -const compileTask = task.define('compile', task.parallel(monacoTypecheckTask, compileClientTask, compileExtensionsTask, compileExtensionMediaTask)); -gulp.task(compileTask); +const _compileTask = task.define('compile', task.parallel(monacoTypecheckTask, compileClientTask, compileExtensionsTask, compileExtensionMediaTask)); +gulp.task(_compileTask); gulp.task(task.define('watch', task.parallel(/* monacoTypecheckWatchTask, */ watchClientTask, watchExtensionsTask))); // Default -gulp.task('default', compileTask); +gulp.task('default', _compileTask); process.on('unhandledRejection', (reason, p) => { console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index ae51405a27234..158e975a82ab6 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -63,15 +63,15 @@ const serverResources = [ 'out-build/vs/base/common/performance.js', // main entry points - 'out-build/vs/server/cli.js', - 'out-build/vs/server/main.js', + 'out-build/server-cli.js', + 'out-build/server-main.js', // Watcher 'out-build/vs/platform/files/**/*.exe', 'out-build/vs/platform/files/**/*.md', // Uri transformer - 'out-build/vs/server/uriTransformer.js', + 'out-build/vs/server/node/uriTransformer.js', // Process monitor 'out-build/vs/base/node/cpuUsage.sh', @@ -91,23 +91,19 @@ const serverWithWebResources = [ const serverEntryPoints = [ { - name: 'vs/server/remoteExtensionHostAgent', + name: 'vs/server/node/server.main', exclude: ['vs/css', 'vs/nls'] }, { - name: 'vs/server/remoteCli', + name: 'vs/server/node/server.cli', exclude: ['vs/css', 'vs/nls'] }, { - name: 'vs/server/remoteExtensionHostProcess', + name: 'vs/workbench/services/extensions/node/extensionHostProcess', exclude: ['vs/css', 'vs/nls'] }, { - name: 'vs/platform/files/node/watcher/nsfw/watcherApp', - exclude: ['vs/css', 'vs/nls'] - }, - { - name: 'vs/platform/files/node/watcher/parcel/watcherApp', + name: 'vs/platform/files/node/watcher/watcherMain', exclude: ['vs/css', 'vs/nls'] }, { @@ -230,6 +226,8 @@ function packageTask(type, platform, arch, sourceFolderName, destinationFolderNa return true; // web: ship all extensions for now } + // Skip shipping UI extensions because the client side will have them anyways + // and they'd just increase the download without being used const manifest = JSON.parse(fs.readFileSync(path.join(REPO_ROOT, extensionPath)).toString()); return !isUIExtension(manifest); }).map((extensionPath) => path.basename(path.dirname(extensionPath))) @@ -255,7 +253,7 @@ function packageTask(type, platform, arch, sourceFolderName, destinationFolderNa const name = product.nameShort; const packageJsonStream = gulp.src(['remote/package.json'], { base: 'remote' }) - .pipe(json({ name, version })); + .pipe(json({ name, version, dependencies: undefined, optionalDependencies: undefined })); const date = new Date().toISOString(); @@ -305,26 +303,28 @@ function packageTask(type, platform, arch, sourceFolderName, destinationFolderNa if (platform === 'win32') { result = es.merge(result, - gulp.src('resources/server/bin/code.cmd', { base: '.' }) + gulp.src('resources/server/bin/remote-cli/code.cmd', { base: '.' }) .pipe(replace('@@VERSION@@', version)) .pipe(replace('@@COMMIT@@', commit)) .pipe(replace('@@APPNAME@@', product.applicationName)) - .pipe(rename(`bin/${product.applicationName}.cmd`)), + .pipe(rename(`bin/remote-cli/${product.applicationName}.cmd`)), gulp.src('resources/server/bin/helpers/browser.cmd', { base: '.' }) .pipe(replace('@@VERSION@@', version)) .pipe(replace('@@COMMIT@@', commit)) .pipe(replace('@@APPNAME@@', product.applicationName)) .pipe(rename(`bin/helpers/browser.cmd`)), - gulp.src('resources/server/bin/server.cmd', { base: '.' }) - .pipe(rename(`server.cmd`)) + gulp.src('resources/server/bin/server-old.cmd', { base: '.' }) + .pipe(rename(`server.cmd`)), + gulp.src('resources/server/bin/code-server.cmd', { base: '.' }) + .pipe(rename(`bin/${product.serverApplicationName}.cmd`)), ); } else if (platform === 'linux' || platform === 'alpine' || platform === 'darwin') { result = es.merge(result, - gulp.src('resources/server/bin/code.sh', { base: '.' }) + gulp.src('resources/server/bin/remote-cli/code.sh', { base: '.' }) .pipe(replace('@@VERSION@@', version)) .pipe(replace('@@COMMIT@@', commit)) .pipe(replace('@@APPNAME@@', product.applicationName)) - .pipe(rename(`bin/${product.applicationName}`)) + .pipe(rename(`bin/remote-cli/${product.applicationName}`)) .pipe(util.setExecutableBit()), gulp.src('resources/server/bin/helpers/browser.sh', { base: '.' }) .pipe(replace('@@VERSION@@', version)) @@ -332,10 +332,17 @@ function packageTask(type, platform, arch, sourceFolderName, destinationFolderNa .pipe(replace('@@APPNAME@@', product.applicationName)) .pipe(rename(`bin/helpers/browser.sh`)) .pipe(util.setExecutableBit()), - gulp.src('resources/server/bin/server.sh', { base: '.' }) - .pipe(rename(`server.sh`)) + gulp.src('resources/server/bin/code-server.sh', { base: '.' }) + .pipe(rename(`bin/${product.serverApplicationName}`)) .pipe(util.setExecutableBit()) ); + if (type !== 'reh-web') { + result = es.merge(result, + gulp.src('resources/server/bin/server-old.sh', { base: '.' }) + .pipe(rename(`server.sh`)) + .pipe(util.setExecutableBit()), + ); + } } return result.pipe(vfs.dest(destination)); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index df089a67b9f88..8a69232956ec5 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -56,7 +56,7 @@ const vscodeResources = [ 'out-build/bootstrap-amd.js', 'out-build/bootstrap-node.js', 'out-build/bootstrap-window.js', - 'out-build/vs/**/*.{svg,png,html,jpg}', + 'out-build/vs/**/*.{svg,png,html,jpg,opus}', '!out-build/vs/code/browser/**/*.html', '!out-build/vs/editor/standalone/**/*.svg', 'out-build/vs/base/common/performance.js', @@ -69,6 +69,8 @@ const vscodeResources = [ '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/browser/media/*.ps1', + 'out-build/vs/workbench/contrib/terminal/browser/media/*.sh', 'out-build/vs/workbench/contrib/webview/browser/pre/*.js', 'out-build/vs/**/markdown.css', 'out-build/vs/workbench/contrib/tasks/**/*.json', @@ -233,7 +235,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op .pipe(jsFilter.restore) .pipe(createAsar(path.join(process.cwd(), 'node_modules'), [ '**/*.node', - '**/vscode-ripgrep/bin/*', + '**/@vscode/ripgrep/bin/*', '**/node-pty/build/Release/*', '**/node-pty/lib/worker/conoutSocketWorker.js', '**/node-pty/lib/shared/conout.js', @@ -321,7 +323,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op .pipe(replace('@@VERSION@@', version)) .pipe(replace('@@COMMIT@@', commit)) .pipe(replace('@@APPNAME@@', product.applicationName)) - .pipe(replace('@@DATAFOLDER@@', product.dataFolderName)) + .pipe(replace('@@SERVERDATAFOLDER@@', product.serverDataFolderName || '.vscode-remote')) .pipe(replace('@@QUALITY@@', quality)) .pipe(rename(function (f) { f.basename = product.applicationName; f.extname = ''; }))); diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index bbc9a03817b17..f3d4166434775 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -17,7 +17,7 @@ const filter = require('gulp-filter'); const _ = require('underscore'); const { getProductionDependencies } = require('./lib/dependencies'); const vfs = require('vinyl-fs'); -const fs = require('fs'); +const replace = require('gulp-replace'); const packageJson = require('../package.json'); const { compileBuildTask } = require('./gulpfile.compile'); const extensions = require('./lib/extensions'); @@ -45,8 +45,7 @@ const vscodeWebResourceIncludes = [ 'out-build/vs/code/browser/workbench/service-worker.js', // Extension Worker - 'out-build/vs/workbench/services/extensions/worker/httpsWebWorkerExtensionHostIframe.html', - 'out-build/vs/workbench/services/extensions/worker/httpWebWorkerExtensionHostIframe.html', + 'out-build/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html', // Web node paths (needed for integration tests) 'out-build/vs/webPackagePaths.js', @@ -68,7 +67,7 @@ const vscodeWebResources = [ const buildfile = require('../src/buildfile'); const vscodeWebEntryPoints = _.flatten([ - buildfile.entrypoint('vs/workbench/workbench.web.api'), + buildfile.entrypoint('vs/workbench/workbench.web.main'), buildfile.base, buildfile.workerExtensionHost, buildfile.workerNotebook, @@ -104,8 +103,7 @@ const createVSCodeWebFileContentMapper = (extensionsRoot) => { // (2) Patch builtin extensions if (path.endsWith('vs/workbench/services/extensionManagement/browser/builtinExtensionsScannerService.js')) { - // Do not inline `vscode-web-playground` even if it has been packed! - const builtinExtensions = JSON.stringify(extensions.scanBuiltinExtensions(extensionsRoot, ['vscode-web-playground'])); + const builtinExtensions = JSON.stringify(extensions.scanBuiltinExtensions(extensionsRoot)); return content.replace('/*BUILD->INSERT_BUILTIN_EXTENSIONS*/', builtinExtensions.substr(1, builtinExtensions.length - 2) /* without [ and ]*/); } @@ -172,6 +170,13 @@ function packageTask(sourceFolderName, destinationFolderName) { gulp.src('resources/server/code-512.png', { base: 'resources/server' }) ); + // TODO@bpasero remove me in Feb + const legacyMain = es.merge( + gulp.src(sourceFolderName + '/vs/workbench/workbench.web.main.js').pipe(rename('out/vs/workbench/workbench.web.api.js')).pipe(replace('workbench.web.main', 'workbench.web.api')), + gulp.src(sourceFolderName + '/vs/workbench/workbench.web.main.css').pipe(rename('out/vs/workbench/workbench.web.api.css')), + gulp.src(sourceFolderName + '/vs/workbench/workbench.web.main.nls.js').pipe(rename('out/vs/workbench/workbench.web.api.nls.js')).pipe(replace('workbench.web.main', 'workbench.web.api')) + ); + let all = es.merge( packageJsonStream, license, @@ -179,7 +184,8 @@ function packageTask(sourceFolderName, destinationFolderName) { deps, favicon, manifest, - pwaicons + pwaicons, + legacyMain ); let result = all diff --git a/build/lib/compilation.js b/build/lib/compilation.js index b81e714fa41de..5c160b7b7aac0 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); -exports.watchApiProposalNames = exports.compileApiProposalNames = exports.watchTask = exports.compileTask = void 0; +exports.watchApiProposalNamesTask = exports.compileApiProposalNamesTask = exports.watchTask = exports.compileTask = void 0; const es = require("event-stream"); const fs = require("fs"); const gulp = require("gulp"); @@ -16,6 +16,8 @@ const util = require("./util"); const fancyLog = require("fancy-log"); const ansiColors = require("ansi-colors"); const os = require("os"); +const File = require("vinyl"); +const task = require("./task"); const watch = require('./watch'); const reporter = (0, reporter_1.createReporter)(); function getTypeScriptCompilerOptions(src) { @@ -175,65 +177,54 @@ class MonacoGenerator { } } } -function apiProposalNamesGenerator() { - const stream = es.through(); - const pattern = /vscode\.proposed\.([a-zA-Z]+)\.d\.ts/; - const dtsFolder = path.join(REPO_SRC_FOLDER, 'vscode-dts'); - const generateFile = () => { - try { - const t1 = Date.now(); - const proposalNames = []; - for (let file of fs.readdirSync(dtsFolder).sort()) { - const match = pattern.exec(file); - if (match) { - proposalNames.push([match[1], `https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/${file}`]); - } - } - const source = [ - '/*---------------------------------------------------------------------------------------------', - ' * Copyright (c) Microsoft Corporation. All rights reserved.', - ' * Licensed under the MIT License. See License.txt in the project root for license information.', - ' *--------------------------------------------------------------------------------------------*/', - '', - '// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY.', - '', - 'export const allApiProposals = Object.freeze({', - `${proposalNames.map(t => `\t${t[0]}: '${t[1]}'`).join(',\n')}`, - '});', - 'export type ApiProposalName = keyof typeof allApiProposals;', - '', - ].join('\n'); - const outFile = path.join(dtsFolder, '../vs/workbench/services/extensions/common/extensionsApiProposals.ts'); - if (fs.readFileSync(outFile).toString() !== source) { - fs.writeFileSync(outFile, source); - console.log(`Generated 'extensionsApiProposals.ts' in ${Date.now() - t1}ms`); - } - } - catch (err) { - stream.emit('error', err); +function generateApiProposalNames() { + const pattern = /vscode\.proposed\.([a-zA-Z]+)\.d\.ts$/; + const proposalNames = new Set(); + const input = es.through(); + const output = input + .pipe(util.filter((f) => pattern.test(f.path))) + .pipe(es.through((f) => { + const name = path.basename(f.path); + const match = pattern.exec(name); + if (match) { + proposalNames.add(match[1]); } - }; - let handle; - stream.on('data', () => { - clearTimeout(handle); - handle = setTimeout(generateFile, 250); - }); - return stream; -} -function compileApiProposalNames() { - return function () { - const srcPipe = gulp.src('src/vscode-dts/**', { base: 'src' }); - const proposals = apiProposalNamesGenerator(); - return srcPipe.pipe(proposals); - }; -} -exports.compileApiProposalNames = compileApiProposalNames; -function watchApiProposalNames() { - return function () { - const watchSrc = watch('src/vscode-dts/**', { base: 'src', readDelay: 200 }); - const proposals = apiProposalNamesGenerator(); - proposals.write(undefined); // send something to trigger initial generate - return watchSrc.pipe(proposals); - }; + }, function () { + const names = [...proposalNames.values()].sort(); + const contents = [ + '/*---------------------------------------------------------------------------------------------', + ' * Copyright (c) Microsoft Corporation. All rights reserved.', + ' * Licensed under the MIT License. See License.txt in the project root for license information.', + ' *--------------------------------------------------------------------------------------------*/', + '', + '// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY.', + '', + 'export const allApiProposals = Object.freeze({', + `${names.map(name => `\t${name}: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.${name}.d.ts'`).join(`,${os.EOL}`)}`, + '});', + 'export type ApiProposalName = keyof typeof allApiProposals;', + '', + ].join(os.EOL); + this.emit('data', new File({ + path: 'vs/workbench/services/extensions/common/extensionsApiProposals.ts', + contents: Buffer.from(contents) + })); + this.emit('end'); + })); + return es.duplex(input, output); } -exports.watchApiProposalNames = watchApiProposalNames; +const apiProposalNamesReporter = (0, reporter_1.createReporter)('api-proposal-names'); +exports.compileApiProposalNamesTask = task.define('compile-api-proposal-names', () => { + return gulp.src('src/vscode-dts/**') + .pipe(generateApiProposalNames()) + .pipe(gulp.dest('src')) + .pipe(apiProposalNamesReporter.end(true)); +}); +exports.watchApiProposalNamesTask = task.define('watch-api-proposal-names', () => { + const task = () => gulp.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')); +}); diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 892acaa421687..a4c8441effc17 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -17,6 +17,8 @@ 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 * as task from './task'; const watch = require('./watch'); @@ -212,26 +214,23 @@ class MonacoGenerator { } } -function apiProposalNamesGenerator() { - const stream = es.through(); +function generateApiProposalNames() { + const pattern = /vscode\.proposed\.([a-zA-Z]+)\.d\.ts$/; + const proposalNames = new Set(); - const pattern = /vscode\.proposed\.([a-zA-Z]+)\.d\.ts/; - const dtsFolder = path.join(REPO_SRC_FOLDER, 'vscode-dts'); + const input = es.through(); + const output = input + .pipe(util.filter((f: File) => pattern.test(f.path))) + .pipe(es.through((f: File) => { + const name = path.basename(f.path); + const match = pattern.exec(name); - const generateFile = () => { - - try { - - const t1 = Date.now(); - const proposalNames: [name: string, url: string][] = []; - for (let file of fs.readdirSync(dtsFolder).sort()) { - const match = pattern.exec(file); - if (match) { - proposalNames.push([match[1], `https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/${file}`]); - } + if (match) { + proposalNames.add(match[1]); } - - const source = [ + }, function () { + const names = [...proposalNames.values()].sort(); + const contents = [ '/*---------------------------------------------------------------------------------------------', ' * Copyright (c) Microsoft Corporation. All rights reserved.', ' * Licensed under the MIT License. See License.txt in the project root for license information.', @@ -240,46 +239,37 @@ function apiProposalNamesGenerator() { '// THIS IS A GENERATED FILE. DO NOT EDIT DIRECTLY.', '', 'export const allApiProposals = Object.freeze({', - `${proposalNames.map(t => `\t${t[0]}: '${t[1]}'`).join(',\n')}`, + `${names.map(name => `\t${name}: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.${name}.d.ts'`).join(`,${os.EOL}`)}`, '});', 'export type ApiProposalName = keyof typeof allApiProposals;', '', - ].join('\n'); + ].join(os.EOL); - const outFile = path.join(dtsFolder, '../vs/workbench/services/extensions/common/extensionsApiProposals.ts'); + this.emit('data', new File({ + path: 'vs/workbench/services/extensions/common/extensionsApiProposals.ts', + contents: Buffer.from(contents) + })); + this.emit('end'); + })); - if (fs.readFileSync(outFile).toString() !== source) { - fs.writeFileSync(outFile, source); - console.log(`Generated 'extensionsApiProposals.ts' in ${Date.now() - t1}ms`); - } + return es.duplex(input, output); +} - } catch (err) { - stream.emit('error', err); - } - }; +const apiProposalNamesReporter = createReporter('api-proposal-names'); - let handle: NodeJS.Timeout; - stream.on('data', () => { - clearTimeout(handle); - handle = setTimeout(generateFile, 250); - }); +export const compileApiProposalNamesTask = task.define('compile-api-proposal-names', () => { + return gulp.src('src/vscode-dts/**') + .pipe(generateApiProposalNames()) + .pipe(gulp.dest('src')) + .pipe(apiProposalNamesReporter.end(true)); +}); - return stream; -} +export const watchApiProposalNamesTask = task.define('watch-api-proposal-names', () => { + const task = () => gulp.src('src/vscode-dts/**') + .pipe(generateApiProposalNames()) + .pipe(apiProposalNamesReporter.end(true)); -export function compileApiProposalNames(): () => NodeJS.ReadWriteStream { - return function () { - const srcPipe = gulp.src('src/vscode-dts/**', { base: 'src' }); - const proposals = apiProposalNamesGenerator(); - return srcPipe.pipe(proposals); - }; -} - -export function watchApiProposalNames(): () => NodeJS.ReadWriteStream { - return function () { - const watchSrc = watch('src/vscode-dts/**', { base: 'src', readDelay: 200 }); - const proposals = apiProposalNamesGenerator(); - proposals.write(undefined); // send something to trigger initial generate - return watchSrc.pipe(proposals); - }; -} + return watch('src/vscode-dts/**', { readDelay: 200 }) + .pipe(util.debounce(task)) + .pipe(gulp.dest('src')); +}); diff --git a/build/lib/electron.js b/build/lib/electron.js index d606479ac7baa..3c4a3a780453f 100644 --- a/build/lib/electron.js +++ b/build/lib/electron.js @@ -77,7 +77,7 @@ exports.config = { version: util.getElectronVersion(), productAppName: product.nameLong, companyName: 'Microsoft Corporation', - copyright: 'Copyright (C) 2021 Microsoft. All rights reserved', + copyright: 'Copyright (C) 2022 Microsoft. All rights reserved', darwinIcon: 'resources/darwin/code.icns', darwinBundleIdentifier: product.darwinBundleIdentifier, darwinApplicationCategoryType: 'public.app-category.developer-tools', diff --git a/build/lib/electron.ts b/build/lib/electron.ts index 420cdad6494b3..12f9013745e6b 100644 --- a/build/lib/electron.ts +++ b/build/lib/electron.ts @@ -93,7 +93,7 @@ export const config = { version: util.getElectronVersion(), productAppName: product.nameLong, companyName: 'Microsoft Corporation', - copyright: 'Copyright (C) 2021 Microsoft. All rights reserved', + copyright: 'Copyright (C) 2022 Microsoft. All rights reserved', darwinIcon: 'resources/darwin/code.icns', darwinBundleIdentifier: product.darwinBundleIdentifier, darwinApplicationCategoryType: 'public.app-category.developer-tools', diff --git a/build/lib/eslint/code-import-patterns.js b/build/lib/eslint/code-import-patterns.js index 97dd154b1d3e8..47cc3063d1c63 100644 --- a/build/lib/eslint/code-import-patterns.js +++ b/build/lib/eslint/code-import-patterns.js @@ -3,44 +3,184 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -const path_1 = require("path"); +const path = require("path"); const minimatch = require("minimatch"); const utils_1 = require("./utils"); +const REPO_ROOT = path.normalize(path.join(__dirname, '../../../')); +function isLayerAllowRule(option) { + return !!(option.when && option.allow); +} +/** + * Returns the filename relative to the project root and using `/` as separators + */ +function getRelativeFilename(context) { + const filename = path.normalize(context.getFilename()); + return filename.substring(REPO_ROOT.length).replace(/\\/g, '/'); +} module.exports = new class { constructor() { this.meta = { messages: { - badImport: 'Imports violates \'{{restrictions}}\' restrictions. See https://github.com/microsoft/vscode/wiki/Source-Code-Organization' + badImport: 'Imports violates \'{{restrictions}}\' restrictions. See https://github.com/microsoft/vscode/wiki/Source-Code-Organization', + badFilename: 'Missing definition in `code-import-patterns` for this file. Define rules at https://github.com/microsoft/vscode/blob/main/.eslintrc.json' }, docs: { url: 'https://github.com/microsoft/vscode/wiki/Source-Code-Organization' } }; + this._optionsCache = new WeakMap(); } create(context) { - const configs = context.options; + const options = context.options; + const configs = this._processOptions(options); + const relativeFilename = getRelativeFilename(context); for (const config of configs) { - if (minimatch(context.getFilename(), config.target)) { + if (minimatch(relativeFilename, config.target)) { return (0, utils_1.createImportRuleListener)((node, value) => this._checkImport(context, config, node, value)); } } + context.report({ + loc: { line: 1, column: 0 }, + messageId: 'badFilename' + }); return {}; } - _checkImport(context, config, node, path) { - // resolve relative paths - if (path[0] === '.') { - path = (0, path_1.join)(context.getFilename(), path); + _processOptions(options) { + if (this._optionsCache.has(options)) { + return this._optionsCache.get(options); + } + function orSegment(variants) { + return (variants.length === 1 ? variants[0] : `{${variants.join(',')}}`); } - let restrictions; - if (typeof config.restrictions === 'string') { - restrictions = [config.restrictions]; + const layerRules = [ + { layer: 'common', deps: orSegment(['common']) }, + { layer: 'worker', deps: orSegment(['common', 'worker']) }, + { layer: 'browser', deps: orSegment(['common', 'browser']), isBrowser: true }, + { layer: 'electron-sandbox', deps: orSegment(['common', 'browser', 'electron-sandbox']), isBrowser: true }, + { layer: 'node', deps: orSegment(['common', 'node']), isNode: true }, + { layer: 'electron-browser', deps: orSegment(['common', 'browser', 'node', 'electron-sandbox', 'electron-browser']), isBrowser: true, isNode: true }, + { layer: 'electron-main', deps: orSegment(['common', 'node', 'electron-main']), isNode: true }, + ]; + let browserAllow = []; + let nodeAllow = []; + let testAllow = []; + for (const option of options) { + if (isLayerAllowRule(option)) { + if (option.when === 'hasBrowser') { + browserAllow = option.allow.slice(0); + } + else if (option.when === 'hasNode') { + nodeAllow = option.allow.slice(0); + } + else if (option.when === 'test') { + testAllow = option.allow.slice(0); + } + } } - else { - restrictions = config.restrictions; + function findLayer(layer) { + for (const layerRule of layerRules) { + if (layerRule.layer === layer) { + return layerRule; + } + } + return null; + } + function generateConfig(layerRule, target, rawRestrictions) { + const restrictions = []; + const testRestrictions = [...testAllow]; + if (layerRule.isBrowser) { + restrictions.push(...browserAllow); + } + if (layerRule.isNode) { + restrictions.push(...nodeAllow); + } + for (const rawRestriction of rawRestrictions) { + let importPattern; + let when = undefined; + if (typeof rawRestriction === 'string') { + importPattern = rawRestriction; + } + else { + importPattern = rawRestriction.pattern; + when = rawRestriction.when; + } + if (typeof when === 'undefined' + || (when === 'hasBrowser' && layerRule.isBrowser) + || (when === 'hasNode' && layerRule.isNode)) { + restrictions.push(importPattern.replace(/\/\~$/, `/${layerRule.deps}/**`)); + testRestrictions.push(importPattern.replace(/\/\~$/, `/test/${layerRule.deps}/**`)); + } + else if (when === 'test') { + testRestrictions.push(importPattern.replace(/\/\~$/, `/${layerRule.deps}/**`)); + testRestrictions.push(importPattern.replace(/\/\~$/, `/test/${layerRule.deps}/**`)); + } + } + testRestrictions.push(...restrictions); + return [ + { + target: target.replace(/\/\~$/, `/${layerRule.layer}/**`), + restrictions: restrictions + }, + { + target: target.replace(/\/\~$/, `/test/${layerRule.layer}/**`), + restrictions: testRestrictions + } + ]; + } + const configs = []; + for (const option of options) { + if (isLayerAllowRule(option)) { + continue; + } + const target = option.target; + const targetIsVS = /^src\/vs\//.test(target); + const restrictions = (typeof option.restrictions === 'string' ? [option.restrictions] : option.restrictions).slice(0); + if (targetIsVS) { + // Always add "vs/nls" + restrictions.push('vs/nls'); + } + if (targetIsVS && option.layer) { + // single layer => simple substitution for /~ + const layerRule = findLayer(option.layer); + if (layerRule) { + const [config, testConfig] = generateConfig(layerRule, target, restrictions); + if (option.test) { + configs.push(testConfig); + } + else { + configs.push(config); + } + } + } + else if (targetIsVS && /\/\~$/.test(target)) { + // generate all layers + for (const layerRule of layerRules) { + const [config, testConfig] = generateConfig(layerRule, target, restrictions); + configs.push(config); + configs.push(testConfig); + } + } + else { + configs.push({ target, restrictions: restrictions.filter(r => typeof r === 'string') }); + } + } + this._optionsCache.set(options, configs); + return configs; + } + _checkImport(context, config, node, importPath) { + // resolve relative paths + if (importPath[0] === '.') { + const relativeFilename = getRelativeFilename(context); + importPath = path.posix.join(path.posix.dirname(relativeFilename), importPath); + if (/^src\/vs\//.test(importPath)) { + // resolve using AMD base url + importPath = importPath.substring('src/'.length); + } } + const restrictions = config.restrictions; let matched = false; for (const pattern of restrictions) { - if (minimatch(path, pattern)) { + if (minimatch(importPath, pattern)) { matched = true; break; } diff --git a/build/lib/eslint/code-import-patterns.ts b/build/lib/eslint/code-import-patterns.ts index c3daadbf59a0d..72b63a45b353d 100644 --- a/build/lib/eslint/code-import-patterns.ts +++ b/build/lib/eslint/code-import-patterns.ts @@ -5,20 +5,46 @@ import * as eslint from 'eslint'; import { TSESTree } from '@typescript-eslint/experimental-utils'; -import { join } from 'path'; +import * as path from 'path'; import * as minimatch from 'minimatch'; import { createImportRuleListener } from './utils'; +const REPO_ROOT = path.normalize(path.join(__dirname, '../../../')); + +interface ConditionalPattern { + when?: 'hasBrowser' | 'hasNode' | 'test'; + pattern: string; +} + +interface RawImportPatternsConfig { + target: string; + layer?: 'common' | 'worker' | 'browser' | 'electron-sandbox' | 'node' | 'electron-browser' | 'electron-main'; + test?: boolean; + restrictions: string | (string | ConditionalPattern)[]; +} + +interface LayerAllowRule { + when: 'hasBrowser' | 'hasNode' | 'test'; + allow: string[]; +} + +type RawOption = RawImportPatternsConfig | LayerAllowRule; + +function isLayerAllowRule(option: RawOption): option is LayerAllowRule { + return !!((option).when && (option).allow); +} + interface ImportPatternsConfig { target: string; - restrictions: string | string[]; + restrictions: string[]; } export = new class implements eslint.Rule.RuleModule { readonly meta: eslint.Rule.RuleMetaData = { messages: { - badImport: 'Imports violates \'{{restrictions}}\' restrictions. See https://github.com/microsoft/vscode/wiki/Source-Code-Organization' + badImport: 'Imports violates \'{{restrictions}}\' restrictions. See https://github.com/microsoft/vscode/wiki/Source-Code-Organization', + badFilename: 'Missing definition in `code-import-patterns` for this file. Define rules at https://github.com/microsoft/vscode/blob/main/.eslintrc.json' }, docs: { url: 'https://github.com/microsoft/vscode/wiki/Source-Code-Organization' @@ -26,35 +52,182 @@ export = new class implements eslint.Rule.RuleModule { }; create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { - - const configs = context.options; + const options = context.options; + const configs = this._processOptions(options); + const relativeFilename = getRelativeFilename(context); for (const config of configs) { - if (minimatch(context.getFilename(), config.target)) { + if (minimatch(relativeFilename, config.target)) { return createImportRuleListener((node, value) => this._checkImport(context, config, node, value)); } } + context.report({ + loc: { line: 1, column: 0 }, + messageId: 'badFilename' + }); + return {}; } - private _checkImport(context: eslint.Rule.RuleContext, config: ImportPatternsConfig, node: TSESTree.Node, path: string) { + private _optionsCache = new WeakMap(); - // resolve relative paths - if (path[0] === '.') { - path = join(context.getFilename(), path); + private _processOptions(options: RawOption[]): ImportPatternsConfig[] { + if (this._optionsCache.has(options)) { + return this._optionsCache.get(options)!; + } + + type Layer = 'common' | 'worker' | 'browser' | 'electron-sandbox' | 'node' | 'electron-browser' | 'electron-main'; + + interface ILayerRule { + layer: Layer; + deps: string; + isBrowser?: boolean; + isNode?: boolean; + } + + function orSegment(variants: Layer[]): string { + return (variants.length === 1 ? variants[0] : `{${variants.join(',')}}`); + } + + const layerRules: ILayerRule[] = [ + { layer: 'common', deps: orSegment(['common']) }, + { layer: 'worker', deps: orSegment(['common', 'worker']) }, + { layer: 'browser', deps: orSegment(['common', 'browser']), isBrowser: true }, + { layer: 'electron-sandbox', deps: orSegment(['common', 'browser', 'electron-sandbox']), isBrowser: true }, + { layer: 'node', deps: orSegment(['common', 'node']), isNode: true }, + { layer: 'electron-browser', deps: orSegment(['common', 'browser', 'node', 'electron-sandbox', 'electron-browser']), isBrowser: true, isNode: true }, + { layer: 'electron-main', deps: orSegment(['common', 'node', 'electron-main']), isNode: true }, + ]; + + let browserAllow: string[] = []; + let nodeAllow: string[] = []; + let testAllow: string[] = []; + for (const option of options) { + if (isLayerAllowRule(option)) { + if (option.when === 'hasBrowser') { + browserAllow = option.allow.slice(0); + } else if (option.when === 'hasNode') { + nodeAllow = option.allow.slice(0); + } else if (option.when === 'test') { + testAllow = option.allow.slice(0); + } + } + } + + function findLayer(layer: Layer): ILayerRule | null { + for (const layerRule of layerRules) { + if (layerRule.layer === layer) { + return layerRule; + } + } + return null; } - let restrictions: string[]; - if (typeof config.restrictions === 'string') { - restrictions = [config.restrictions]; - } else { - restrictions = config.restrictions; + function generateConfig(layerRule: ILayerRule, target: string, rawRestrictions: (string | ConditionalPattern)[]): [ImportPatternsConfig, ImportPatternsConfig] { + const restrictions: string[] = []; + const testRestrictions: string[] = [...testAllow]; + + if (layerRule.isBrowser) { + restrictions.push(...browserAllow); + } + + if (layerRule.isNode) { + restrictions.push(...nodeAllow); + } + + for (const rawRestriction of rawRestrictions) { + let importPattern: string; + let when: 'hasBrowser' | 'hasNode' | 'test' | undefined = undefined; + if (typeof rawRestriction === 'string') { + importPattern = rawRestriction; + } else { + importPattern = rawRestriction.pattern; + when = rawRestriction.when; + } + if (typeof when === 'undefined' + || (when === 'hasBrowser' && layerRule.isBrowser) + || (when === 'hasNode' && layerRule.isNode) + ) { + restrictions.push(importPattern.replace(/\/\~$/, `/${layerRule.deps}/**`)); + testRestrictions.push(importPattern.replace(/\/\~$/, `/test/${layerRule.deps}/**`)); + } else if (when === 'test') { + testRestrictions.push(importPattern.replace(/\/\~$/, `/${layerRule.deps}/**`)); + testRestrictions.push(importPattern.replace(/\/\~$/, `/test/${layerRule.deps}/**`)); + } + } + + testRestrictions.push(...restrictions); + + return [ + { + target: target.replace(/\/\~$/, `/${layerRule.layer}/**`), + restrictions: restrictions + }, + { + target: target.replace(/\/\~$/, `/test/${layerRule.layer}/**`), + restrictions: testRestrictions + } + ]; + } + + const configs: ImportPatternsConfig[] = []; + for (const option of options) { + if (isLayerAllowRule(option)) { + continue; + } + const target = option.target; + const targetIsVS = /^src\/vs\//.test(target); + const restrictions = (typeof option.restrictions === 'string' ? [option.restrictions] : option.restrictions).slice(0); + + if (targetIsVS) { + // Always add "vs/nls" + restrictions.push('vs/nls'); + } + + if (targetIsVS && option.layer) { + // single layer => simple substitution for /~ + const layerRule = findLayer(option.layer); + if (layerRule) { + const [config, testConfig] = generateConfig(layerRule, target, restrictions); + if (option.test) { + configs.push(testConfig); + } else { + configs.push(config); + } + } + } else if (targetIsVS && /\/\~$/.test(target)) { + // generate all layers + for (const layerRule of layerRules) { + const [config, testConfig] = generateConfig(layerRule, target, restrictions); + configs.push(config); + configs.push(testConfig); + } + } else { + configs.push({ target, restrictions: restrictions.filter(r => typeof r === 'string') }); + } + } + this._optionsCache.set(options, configs); + return configs; + } + + private _checkImport(context: eslint.Rule.RuleContext, config: ImportPatternsConfig, node: TSESTree.Node, importPath: string) { + + // resolve relative paths + if (importPath[0] === '.') { + const relativeFilename = getRelativeFilename(context); + importPath = path.posix.join(path.posix.dirname(relativeFilename), importPath); + if (/^src\/vs\//.test(importPath)) { + // resolve using AMD base url + importPath = importPath.substring('src/'.length); + } } + const restrictions = config.restrictions; + let matched = false; for (const pattern of restrictions) { - if (minimatch(path, pattern)) { + if (minimatch(importPath, pattern)) { matched = true; break; } @@ -73,3 +246,10 @@ export = new class implements eslint.Rule.RuleModule { } }; +/** + * Returns the filename relative to the project root and using `/` as separators + */ +function getRelativeFilename(context: eslint.Rule.RuleContext): string { + const filename = path.normalize(context.getFilename()); + return filename.substring(REPO_ROOT.length).replace(/\\/g, '/'); +} diff --git a/build/lib/eslint/code-no-unused-expressions.js b/build/lib/eslint/code-no-unused-expressions.js index d8ee6ca46f3ea..530464f37b5b5 100644 --- a/build/lib/eslint/code-no-unused-expressions.js +++ b/build/lib/eslint/code-no-unused-expressions.js @@ -108,13 +108,16 @@ module.exports = { if (allowTaggedTemplates && node.type === 'TaggedTemplateExpression') { return true; } - return /^(?:Assignment|OptionalCall|Call|New|Update|Yield|Await)Expression$/u.test(node.type) || + if (node.type === 'ExpressionStatement') { + return isValidExpression(node.expression); + } + return /^(?:Assignment|OptionalCall|Call|New|Update|Yield|Await|Chain)Expression$/u.test(node.type) || (node.type === 'UnaryExpression' && ['delete', 'void'].indexOf(node.operator) >= 0); } return { ExpressionStatement(node) { if (!isValidExpression(node.expression) && !isDirective(node, context.getAncestors())) { - context.report({ node: node, message: 'Expected an assignment or function call and instead saw an expression.' }); + context.report({ node: node, message: `Expected an assignment or function call and instead saw an expression. ${node.expression}` }); } } }; diff --git a/build/lib/eslint/code-no-unused-expressions.ts b/build/lib/eslint/code-no-unused-expressions.ts index d130d670da5c6..f5f0728836e20 100644 --- a/build/lib/eslint/code-no-unused-expressions.ts +++ b/build/lib/eslint/code-no-unused-expressions.ts @@ -133,14 +133,18 @@ module.exports = { return true; } - return /^(?:Assignment|OptionalCall|Call|New|Update|Yield|Await)Expression$/u.test(node.type) || + if (node.type === 'ExpressionStatement') { + return isValidExpression(node.expression); + } + + return /^(?:Assignment|OptionalCall|Call|New|Update|Yield|Await|Chain)Expression$/u.test(node.type) || (node.type === 'UnaryExpression' && ['delete', 'void'].indexOf(node.operator) >= 0); } return { ExpressionStatement(node: TSESTree.ExpressionStatement) { if (!isValidExpression(node.expression) && !isDirective(node, context.getAncestors())) { - context.report({ node: node, message: 'Expected an assignment or function call and instead saw an expression.' }); + context.report({ node: node, message: `Expected an assignment or function call and instead saw an expression. ${node.expression}` }); } } }; diff --git a/build/lib/eslint/vscode-dts-event-naming.js b/build/lib/eslint/vscode-dts-event-naming.js index 388ccf2f80401..a1a1c1097f1dd 100644 --- a/build/lib/eslint/vscode-dts-event-naming.js +++ b/build/lib/eslint/vscode-dts-event-naming.js @@ -77,7 +77,7 @@ module.exports = new (_a = class ApiEventNaming { if (def.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { return def; } - else if ((def.type === experimental_utils_1.AST_NODE_TYPES.TSPropertySignature || def.type === experimental_utils_1.AST_NODE_TYPES.ClassProperty) && def.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { + else if ((def.type === experimental_utils_1.AST_NODE_TYPES.TSPropertySignature || def.type === experimental_utils_1.AST_NODE_TYPES.Property) && def.key.type === experimental_utils_1.AST_NODE_TYPES.Identifier) { return def.key; } return this.getIdent(def.parent); diff --git a/build/lib/eslint/vscode-dts-event-naming.ts b/build/lib/eslint/vscode-dts-event-naming.ts index 5ed8818fe44b8..3f91c65049f95 100644 --- a/build/lib/eslint/vscode-dts-event-naming.ts +++ b/build/lib/eslint/vscode-dts-event-naming.ts @@ -88,7 +88,7 @@ export = new class ApiEventNaming implements eslint.Rule.RuleModule { if (def.type === AST_NODE_TYPES.Identifier) { return def; - } else if ((def.type === AST_NODE_TYPES.TSPropertySignature || def.type === AST_NODE_TYPES.ClassProperty) && def.key.type === AST_NODE_TYPES.Identifier) { + } else if ((def.type === AST_NODE_TYPES.TSPropertySignature || def.type === AST_NODE_TYPES.Property) && def.key.type === AST_NODE_TYPES.Identifier) { return def.key; } diff --git a/build/lib/i18n.js b/build/lib/i18n.js index a3c474c884ec0..cf79d4b9d1d3a 100644 --- a/build/lib/i18n.js +++ b/build/lib/i18n.js @@ -15,7 +15,7 @@ const https = require("https"); const gulp = require("gulp"); const fancyLog = require("fancy-log"); const ansiColors = require("ansi-colors"); -const iconv = require("iconv-lite-umd"); +const iconv = require("@vscode/iconv-lite-umd"); const NUMBER_OF_CONCURRENT_DOWNLOADS = 4; function log(message, ...rest) { fancyLog(ansiColors.green('[i18n]'), message, ...rest); diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 0b852934409a6..34cbf9b0f5999 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -243,7 +243,23 @@ "project": "vscode-workbench" }, { - "name": "vs/workbench/contrib/welcome", + "name": "vs/workbench/contrib/welcomeGettingStarted", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/contrib/welcomeOverlay", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/contrib/welcomePage", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/contrib/welcomeViews", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/contrib/welcomeWalkthrough", "project": "vscode-workbench" }, { @@ -262,6 +278,14 @@ "name": "vs/workbench/contrib/languageDetection", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/audioCues", + "project": "vscode-workbench" + }, + { + "name": "vs/workbench/contrib/offline", + "project": "vscode-workbench" + }, { "name": "vs/workbench/services/actions", "project": "vscode-workbench" @@ -335,7 +359,7 @@ "project": "vscode-workbench" }, { - "name": "vs/workbench/services/mode", + "name": "vs/workbench/services/language", "project": "vscode-workbench" }, { diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts index 5e00e9d6c6e66..b3f7e0b518a10 100644 --- a/build/lib/i18n.ts +++ b/build/lib/i18n.ts @@ -14,7 +14,7 @@ import * as https from 'https'; import * as gulp from 'gulp'; import * as fancyLog from 'fancy-log'; import * as ansiColors from 'ansi-colors'; -import * as iconv from 'iconv-lite-umd'; +import * as iconv from '@vscode/iconv-lite-umd'; const NUMBER_OF_CONCURRENT_DOWNLOADS = 4; diff --git a/build/lib/layersChecker.js b/build/lib/layersChecker.js index 813ba66a37f34..7f0637af940a8 100644 --- a/build/lib/layersChecker.js +++ b/build/lib/layersChecker.js @@ -48,9 +48,13 @@ const CORE_TYPES = [ 'encode', 'decode', 'self', + 'trimStart', + 'trimEnd', 'trimLeft', 'trimRight', - 'queueMicrotask' + 'queueMicrotask', + 'MessageChannel', + 'MessagePort' ]; // Types that are defined in a common layer but are known to be only // available in native environments should not be allowed in browser diff --git a/build/lib/layersChecker.ts b/build/lib/layersChecker.ts index e04cda0de6ef9..e62394a405cbe 100644 --- a/build/lib/layersChecker.ts +++ b/build/lib/layersChecker.ts @@ -49,9 +49,13 @@ const CORE_TYPES = [ 'encode', 'decode', 'self', + 'trimStart', + 'trimEnd', 'trimLeft', 'trimRight', - 'queueMicrotask' + 'queueMicrotask', + 'MessageChannel', + 'MessagePort' ]; // Types that are defined in a common layer but are known to be only diff --git a/build/lib/optimize.js b/build/lib/optimize.js index 931d442760cf6..d5b957d119521 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -177,8 +177,10 @@ function minifyTask(src, sourceMapBaseUrl) { const cssnano = require('cssnano'); const postcss = require('gulp-postcss'); const sourcemaps = require('gulp-sourcemaps'); + 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({ entryPoints: [f.path], @@ -195,7 +197,7 @@ function minifyTask(src, sourceMapBaseUrl) { f.sourceMap = JSON.parse(sourceMapFile.text); cb(undefined, f); }, cb); - }), jsFilter.restore, cssFilter, postcss([cssnano({ preset: 'default' })]), cssFilter.restore, sourcemaps.mapSources((sourcePath) => { + }), jsFilter.restore, cssFilter, postcss([cssnano({ preset: 'default' })]), cssFilter.restore, svgFilter, svgmin(), svgFilter.restore, sourcemaps.mapSources((sourcePath) => { if (sourcePath === 'bootstrap-fork.js') { return 'bootstrap-fork.orig.js'; } diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index 96b4e854f6f1a..fc2a2f4266191 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -252,9 +252,11 @@ export function minifyTask(src: string, sourceMapBaseUrl?: string): (cb: any) => const cssnano = require('cssnano') as typeof import('cssnano'); const postcss = require('gulp-postcss') as typeof import('gulp-postcss'); const sourcemaps = require('gulp-sourcemaps') as typeof import('gulp-sourcemaps'); + const svgmin = require('gulp-svgmin') as typeof import('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']), @@ -283,6 +285,9 @@ export function minifyTask(src: string, sourceMapBaseUrl?: string): (cb: any) => cssFilter, postcss([cssnano({ preset: 'default' })]), cssFilter.restore, + svgFilter, + svgmin(), + svgFilter.restore, (sourcemaps).mapSources((sourcePath: string) => { if (sourcePath === 'bootstrap-fork.js') { return 'bootstrap-fork.orig.js'; diff --git a/build/lib/treeshaking.js b/build/lib/treeshaking.js index 41cb33809b04d..1a2da702fda27 100644 --- a/build/lib/treeshaking.js +++ b/build/lib/treeshaking.js @@ -244,6 +244,52 @@ function nodeOrChildIsBlack(node) { function isSymbolWithDeclarations(symbol) { return !!(symbol && symbol.declarations); } +function isVariableStatementWithSideEffects(ts, node) { + if (!ts.isVariableStatement(node)) { + return false; + } + let hasSideEffects = false; + const visitNode = (node) => { + if (hasSideEffects) { + // no need to go on + return; + } + if (ts.isCallExpression(node) || ts.isNewExpression(node)) { + // TODO: assuming `createDecorator` and `refineServiceDecorator` calls are side-effect free + const isSideEffectFree = /(createDecorator|refineServiceDecorator)/.test(node.expression.getText()); + if (!isSideEffectFree) { + hasSideEffects = true; + } + } + node.forEachChild(visitNode); + }; + node.forEachChild(visitNode); + return hasSideEffects; +} +function isStaticMemberWithSideEffects(ts, node) { + if (!ts.isPropertyDeclaration(node)) { + return false; + } + if (!node.modifiers) { + return false; + } + if (!node.modifiers.some(mod => mod.kind === ts.SyntaxKind.StaticKeyword)) { + return false; + } + let hasSideEffects = false; + const visitNode = (node) => { + if (hasSideEffects) { + // no need to go on + return; + } + if (ts.isCallExpression(node) || ts.isNewExpression(node)) { + hasSideEffects = true; + } + node.forEachChild(visitNode); + }; + node.forEachChild(visitNode); + return hasSideEffects; +} function markNodes(ts, languageService, options) { const program = languageService.getProgram(); if (!program) { @@ -282,6 +328,9 @@ function markNodes(ts, languageService, options) { } return; } + if (isVariableStatementWithSideEffects(ts, node)) { + enqueue_black(node); + } if (ts.isExpressionStatement(node) || ts.isIfStatement(node) || ts.isIterationStatement(node, true) @@ -442,6 +491,9 @@ function markNodes(ts, languageService, options) { ) { enqueue_black(member); } + if (isStaticMemberWithSideEffects(ts, member)) { + enqueue_black(member); + } } // queue the heritage clauses if (declaration.heritageClauses) { diff --git a/build/lib/treeshaking.ts b/build/lib/treeshaking.ts index f24b31e26ac90..0ce7e8c0a0187 100644 --- a/build/lib/treeshaking.ts +++ b/build/lib/treeshaking.ts @@ -327,6 +327,54 @@ function isSymbolWithDeclarations(symbol: ts.Symbol | undefined | null): symbol return !!(symbol && symbol.declarations); } +function isVariableStatementWithSideEffects(ts: typeof import('typescript'), node: ts.Node): boolean { + if (!ts.isVariableStatement(node)) { + return false; + } + let hasSideEffects = false; + const visitNode = (node: ts.Node) => { + if (hasSideEffects) { + // no need to go on + return; + } + if (ts.isCallExpression(node) || ts.isNewExpression(node)) { + // TODO: assuming `createDecorator` and `refineServiceDecorator` calls are side-effect free + const isSideEffectFree = /(createDecorator|refineServiceDecorator)/.test(node.expression.getText()); + if (!isSideEffectFree) { + hasSideEffects = true; + } + } + node.forEachChild(visitNode); + }; + node.forEachChild(visitNode); + return hasSideEffects; +} + +function isStaticMemberWithSideEffects(ts: typeof import('typescript'), node: ts.ClassElement | ts.TypeElement): boolean { + if (!ts.isPropertyDeclaration(node)) { + return false; + } + if (!node.modifiers) { + return false; + } + if (!node.modifiers.some(mod => mod.kind === ts.SyntaxKind.StaticKeyword)) { + return false; + } + let hasSideEffects = false; + const visitNode = (node: ts.Node) => { + if (hasSideEffects) { + // no need to go on + return; + } + if (ts.isCallExpression(node) || ts.isNewExpression(node)) { + hasSideEffects = true; + } + node.forEachChild(visitNode); + }; + node.forEachChild(visitNode); + return hasSideEffects; +} + function markNodes(ts: typeof import('typescript'), languageService: ts.LanguageService, options: ITreeShakingOptions) { const program = languageService.getProgram(); if (!program) { @@ -372,6 +420,10 @@ function markNodes(ts: typeof import('typescript'), languageService: ts.Language return; } + if (isVariableStatementWithSideEffects(ts, node)) { + enqueue_black(node); + } + if ( ts.isExpressionStatement(node) || ts.isIfStatement(node) @@ -563,6 +615,10 @@ function markNodes(ts: typeof import('typescript'), languageService: ts.Language ) { enqueue_black(member); } + + if (isStaticMemberWithSideEffects(ts, member)) { + enqueue_black(member); + } } // queue the heritage clauses diff --git a/build/lib/util.js b/build/lib/util.js index e620533e36859..395f30034f7df 100644 --- a/build/lib/util.js +++ b/build/lib/util.js @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); -exports.buildWebNodePaths = exports.createExternalLoaderConfig = exports.acquireWebNodePaths = exports.getElectronVersion = exports.streamToPromise = exports.versionStringToNumber = exports.filter = exports.rebase = exports.getVersion = exports.ensureDir = exports.rreddir = exports.rimraf = exports.rewriteSourceMappingURL = exports.stripSourceMappingURL = exports.loadSourcemaps = exports.cleanNodeModules = exports.skipDirectories = exports.toFileUri = exports.setExecutableBit = exports.fixWin32DirectoryPermissions = exports.incremental = void 0; +exports.buildWebNodePaths = exports.createExternalLoaderConfig = exports.acquireWebNodePaths = exports.getElectronVersion = exports.streamToPromise = exports.versionStringToNumber = exports.filter = exports.rebase = exports.getVersion = exports.ensureDir = exports.rreddir = exports.rimraf = exports.rewriteSourceMappingURL = exports.stripSourceMappingURL = exports.loadSourcemaps = exports.cleanNodeModules = exports.skipDirectories = exports.toFileUri = exports.setExecutableBit = exports.fixWin32DirectoryPermissions = exports.debounce = exports.incremental = void 0; const es = require("event-stream"); -const debounce = require("debounce"); +const _debounce = require("debounce"); const _filter = require("gulp-filter"); const rename = require("gulp-rename"); const path = require("path"); @@ -36,7 +36,7 @@ function incremental(streamProvider, initial, supportsCancellation) { if (initial) { run(initial, false); } - const eventuallyRun = debounce(() => { + const eventuallyRun = _debounce(() => { const paths = Object.keys(buffer); if (paths.length === 0) { return; @@ -54,6 +54,35 @@ function incremental(streamProvider, initial, supportsCancellation) { return es.duplex(input, output); } exports.incremental = incremental; +function debounce(task) { + const input = es.through(); + const output = es.through(); + let state = 'idle'; + const run = () => { + state = 'running'; + task() + .pipe(es.through(undefined, () => { + const shouldRunAgain = state === 'stale'; + state = 'idle'; + if (shouldRunAgain) { + eventuallyRun(); + } + })) + .pipe(output); + }; + run(); + const eventuallyRun = _debounce(() => run(), 500); + input.on('data', () => { + if (state === 'idle') { + eventuallyRun(); + } + else { + state = 'stale'; + } + }); + return es.duplex(input, output); +} +exports.debounce = debounce; function fixWin32DirectoryPermissions() { if (!/win32/.test(process.platform)) { return es.through(); @@ -288,15 +317,25 @@ function acquireWebNodePaths() { let entryPoint = (_a = packageData.browser) !== null && _a !== void 0 ? _a : 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) { - console.warn(`No entry point for ${key} assuming dist/${key}.min.js`); + // 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.substr(2); + entryPoint = entryPoint.substring(2); } else if (entryPoint.startsWith('/')) { - entryPoint = entryPoint.substr(1); + entryPoint = entryPoint.substring(1); + } + // Search for a minified entrypoint as well + if (/(? { + const eventuallyRun = _debounce(() => { const paths = Object.keys(buffer); if (paths.length === 0) { @@ -79,6 +79,41 @@ export function incremental(streamProvider: IStreamProvider, initial: NodeJS.Rea return es.duplex(input, output); } +export function debounce(task: () => NodeJS.ReadWriteStream): NodeJS.ReadWriteStream { + const input = es.through(); + const output = es.through(); + let state = 'idle'; + + const run = () => { + state = 'running'; + + task() + .pipe(es.through(undefined, () => { + const shouldRunAgain = state === 'stale'; + state = 'idle'; + + if (shouldRunAgain) { + eventuallyRun(); + } + })) + .pipe(output); + }; + + run(); + + const eventuallyRun = _debounce(() => run(), 500); + + input.on('data', () => { + if (state === 'idle') { + eventuallyRun(); + } else { + state = 'stale'; + } + }); + + return es.duplex(input, output); +} + export function fixWin32DirectoryPermissions(): NodeJS.ReadWriteStream { if (!/win32/.test(process.platform)) { return es.through(); @@ -347,24 +382,41 @@ 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 nodePaths: { [key: string]: string } = { }; + 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')); - let entryPoint = packageData.browser ?? packageData.main; + let entryPoint: 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) { - console.warn(`No entry point for ${key} assuming dist/${key}.min.js`); + // 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.substr(2); + entryPoint = entryPoint.substring(2); } else if (entryPoint.startsWith('/')) { - entryPoint = entryPoint.substr(1); + entryPoint = entryPoint.substring(1); + } + + // Search for a minified entrypoint as well + if (/(?languages.;editorCommon.=>): -#include(vs/editor/standalone/common/standaloneThemeService): BuiltinTheme, IStandaloneThemeData, IColors -#include(vs/editor/common/modes/supports/tokenization): ITokenThemeRule -#include(vs/editor/common/services/webWorker): MonacoWebWorker, IWebWorkerOptions +#includeAll(vs/editor/standalone/browser/standaloneEditor;languages.Token=>Token): +#include(vs/editor/standalone/common/standaloneTheme): BuiltinTheme, IStandaloneThemeData, IColors +#include(vs/editor/common/languages/supports/tokenization): ITokenThemeRule +#include(vs/editor/browser/services/webWorker): MonacoWebWorker, IWebWorkerOptions #include(vs/editor/standalone/browser/standaloneCodeEditor): IActionDescriptor, IGlobalEditorOptions, IStandaloneEditorConstructionOptions, IStandaloneDiffEditorConstructionOptions, IStandaloneCodeEditor, IStandaloneDiffEditor export interface ICommandHandler { (...args: any[]): void; @@ -69,13 +69,18 @@ export interface ICommandHandler { #include(vs/editor/standalone/browser/colorizer): IColorizerOptions, IColorizerElementOptions #include(vs/base/common/scrollable): ScrollbarVisibility #include(vs/platform/theme/common/themeService): ThemeColor +#include(vs/editor/common/core/editOperation): ISingleEditOperation +#include(vs/editor/common/core/wordHelper): IWordAtPosition #includeAll(vs/editor/common/model): IScrollEvent -#includeAll(vs/editor/common/editorCommon;editorOptions.=>): IScrollEvent -#includeAll(vs/editor/common/model/textModelEvents): -#includeAll(vs/editor/common/controller/cursorEvents): +#include(vs/editor/common/diff/diffComputer): IChange, ICharChange, ILineChange +#include(vs/editor/common/core/dimension): IDimension +#includeAll(vs/editor/common/editorCommon): IScrollEvent +#includeAll(vs/editor/common/textModelEvents): +#includeAll(vs/editor/common/cursor/cursorEvents): #include(vs/platform/accessibility/common/accessibility): AccessibilitySupport #includeAll(vs/editor/common/config/editorOptions): -#includeAll(vs/editor/browser/editorBrowser;editorCommon.=>;editorOptions.=>): +#include(vs/editor/browser/config/editorConfiguration): IEditorConstructionOptions +#includeAll(vs/editor/browser/editorBrowser;editorCommon.=>): #include(vs/editor/common/config/fontInfo): FontInfo, BareFontInfo //compatibility: @@ -85,10 +90,10 @@ export type IModel = ITextModel; declare namespace monaco.languages { -#includeAll(vs/editor/standalone/browser/standaloneLanguages;modes.=>;editorCommon.=>editor.;model.=>editor.;IMarkerData=>editor.IMarkerData): -#includeAll(vs/editor/common/modes/languageConfiguration): -#includeAll(vs/editor/common/modes;editorCommon.IRange=>IRange;editorCommon.IPosition=>IPosition;editorCommon.=>editor.;IMarkerData=>editor.IMarkerData;model.=>editor.): -#include(vs/editor/common/services/modeService): ILanguageExtensionPoint +#includeAll(vs/editor/standalone/browser/standaloneLanguages;languages.=>;editorCommon.=>editor.;model.=>editor.;IMarkerData=>editor.IMarkerData): +#includeAll(vs/editor/common/languages/languageConfiguration): +#includeAll(vs/editor/common/languages;IMarkerData=>editor.IMarkerData;ISingleEditOperation=>editor.ISingleEditOperation;model.=>editor.): Token +#include(vs/editor/common/services/language): ILanguageExtensionPoint #includeAll(vs/editor/standalone/common/monarch/monarchTypes): } diff --git a/build/monaco/package.json b/build/monaco/package.json index 48f2d3a50b3d7..687af0852aafa 100644 --- a/build/monaco/package.json +++ b/build/monaco/package.json @@ -1,7 +1,7 @@ { "name": "monaco-editor-core", "private": true, - "version": "0.31.0", + "version": "0.0.0", "description": "A browser based code editor", "author": "Microsoft Corporation", "license": "MIT", diff --git a/build/npm/gyp/package.json b/build/npm/gyp/package.json new file mode 100644 index 0000000000000..9efc7b787888d --- /dev/null +++ b/build/npm/gyp/package.json @@ -0,0 +1,11 @@ +{ + "name": "code-oss-dev-build", + "version": "1.0.0", + "private": true, + "license": "MIT", + "devDependencies": { + "node-gyp": "^8.4.1" + }, + "scripts": { + } +} diff --git a/build/npm/gyp/yarn.lock b/build/npm/gyp/yarn.lock new file mode 100644 index 0000000000000..ed79f4868b786 --- /dev/null +++ b/build/npm/gyp/yarn.lock @@ -0,0 +1,640 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@gar/promisify@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.2.tgz#30aa825f11d438671d585bd44e7fd564535fc210" + integrity sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw== + +"@npmcli/fs@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-1.0.0.tgz#589612cfad3a6ea0feafcb901d29c63fd52db09f" + integrity sha512-8ltnOpRR/oJbOp8vaGUnipOi3bqkcW+sLHFlyXIr08OGHmVJLB1Hn7QtGXbYcpVtH1gAYZTlmDXtE4YV0+AMMQ== + dependencies: + "@gar/promisify" "^1.0.1" + semver "^7.3.5" + +"@npmcli/move-file@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" + integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" + +"@tootallnate/once@1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" + integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +agent-base@6, agent-base@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + +agentkeepalive@^4.1.3: + version "4.1.4" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.1.4.tgz#d928028a4862cb11718e55227872e842a44c945b" + integrity sha512-+V/rGa3EuU74H6wR04plBb7Ks10FbtUQgRj/FQOG7uUIEuaINI+AiqJR1k6t3SVNs7o7ZjIdus6706qqzVq8jQ== + dependencies: + debug "^4.1.0" + depd "^1.1.2" + humanize-ms "^1.2.1" + +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +"aproba@^1.0.3 || ^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" + integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== + +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +cacache@^15.2.0: + version "15.3.0" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" + integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== + dependencies: + "@npmcli/fs" "^1.0.0" + "@npmcli/move-file" "^1.0.1" + chownr "^2.0.0" + fs-minipass "^2.0.0" + glob "^7.1.4" + infer-owner "^1.0.4" + lru-cache "^6.0.0" + minipass "^3.1.1" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.2" + mkdirp "^1.0.3" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^8.0.1" + tar "^6.0.2" + unique-filename "^1.1.1" + +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +console-control-strings@^1.0.0, console-control-strings@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= + +debug@4, debug@^4.1.0, debug@^4.3.1: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== + dependencies: + ms "2.1.2" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= + +depd@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +encoding@^0.1.12: + version "0.1.13" + resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" + integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== + dependencies: + iconv-lite "^0.6.2" + +env-paths@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + +err-code@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/err-code/-/err-code-2.0.3.tgz#23c2f3b756ffdfc608d30e27c9a941024807e7f9" + integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== + +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +gauge@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.0.tgz#afba07aa0374a93c6219603b1fb83eaa2264d8f8" + integrity sha512-F8sU45yQpjQjxKkm1UOAhf0U/O0aFt//Fl7hsrNVto+patMHjs7dPI9mFOGUKbhrgKm0S3EjW3scMFuQmWSROw== + dependencies: + ansi-regex "^5.0.1" + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.2" + console-control-strings "^1.0.0" + has-unicode "^2.0.1" + signal-exit "^3.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.2" + +glob@^7.1.3, glob@^7.1.4: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +graceful-fs@^4.2.6: + version "4.2.8" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" + integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== + +has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= + +http-cache-semantics@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" + integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== + +http-proxy-agent@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" + integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== + dependencies: + "@tootallnate/once" "1" + agent-base "6" + debug "4" + +https-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" + integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA== + dependencies: + agent-base "6" + debug "4" + +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= + dependencies: + ms "^2.0.0" + +iconv-lite@^0.6.2: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +infer-owner@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-lambda@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" + integrity sha1-PZh3iZ5qU+/AFgUEzeFfgubwYdU= + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +make-fetch-happen@^9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968" + integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg== + dependencies: + agentkeepalive "^4.1.3" + cacache "^15.2.0" + http-cache-semantics "^4.1.0" + http-proxy-agent "^4.0.1" + https-proxy-agent "^5.0.0" + is-lambda "^1.0.1" + lru-cache "^6.0.0" + minipass "^3.1.3" + minipass-collect "^1.0.2" + minipass-fetch "^1.3.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.4" + negotiator "^0.6.2" + promise-retry "^2.0.1" + socks-proxy-agent "^6.0.0" + ssri "^8.0.0" + +minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minipass-collect@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== + dependencies: + minipass "^3.0.0" + +minipass-fetch@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-1.4.1.tgz#d75e0091daac1b0ffd7e9d41629faff7d0c1f1b6" + integrity sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw== + dependencies: + minipass "^3.1.0" + minipass-sized "^1.0.3" + minizlib "^2.0.0" + optionalDependencies: + encoding "^0.1.12" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + +minipass-sized@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/minipass-sized/-/minipass-sized-1.0.3.tgz#70ee5a7c5052070afacfbc22977ea79def353b70" + integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== + dependencies: + minipass "^3.0.0" + +minipass@^3.0.0, minipass@^3.1.0, minipass@^3.1.1, minipass@^3.1.3: + version "3.1.5" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.5.tgz#71f6251b0a33a49c01b3cf97ff77eda030dff732" + integrity sha512-+8NzxD82XQoNKNrl1d/FSi+X8wAEWR+sbYAfIvub4Nz0d22plFG72CEVVaufV8PNf4qSslFTD8VMOxNVhHCjTw== + dependencies: + yallist "^4.0.0" + +minizlib@^2.0.0, minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + +mkdirp@^1.0.3, mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.0.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +negotiator@^0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" + integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + +node-gyp@^8.4.1: + version "8.4.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-8.4.1.tgz#3d49308fc31f768180957d6b5746845fbd429937" + integrity sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w== + dependencies: + env-paths "^2.2.0" + glob "^7.1.4" + graceful-fs "^4.2.6" + make-fetch-happen "^9.1.0" + nopt "^5.0.0" + npmlog "^6.0.0" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.2" + which "^2.0.2" + +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + +npmlog@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.0.tgz#ba9ef39413c3d936ea91553db7be49c34ad0520c" + integrity sha512-03ppFRGlsyUaQFbGC2C8QWJN/C/K7PsfyD9aQdhVKAQIH4sQBc8WASqFBP7O+Ut4d2oo5LoeoboB3cGdBZSp6Q== + dependencies: + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^4.0.0" + set-blocking "^2.0.0" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= + +promise-retry@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-2.0.1.tgz#ff747a13620ab57ba688f5fc67855410c370da22" + integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== + dependencies: + err-code "^2.0.2" + retry "^0.12.0" + +readable-stream@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +retry@^0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" + integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + +set-blocking@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= + +signal-exit@^3.0.0: + version "3.0.6" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" + integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== + +smart-buffer@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" + integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== + +socks-proxy-agent@^6.0.0: + version "6.1.1" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz#e664e8f1aaf4e1fb3df945f09e3d94f911137f87" + integrity sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew== + dependencies: + agent-base "^6.0.2" + debug "^4.3.1" + socks "^2.6.1" + +socks@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e" + integrity sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA== + dependencies: + ip "^1.1.5" + smart-buffer "^4.1.0" + +ssri@^8.0.0, ssri@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" + integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== + dependencies: + minipass "^3.1.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +tar@^6.0.2, tar@^6.1.2: + version "6.1.11" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621" + integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + +unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" + integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== + dependencies: + imurmurhash "^0.1.4" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + +which@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js index 7fb38af7121db..bd698fc168bd9 100644 --- a/build/npm/preinstall.js +++ b/build/npm/preinstall.js @@ -12,6 +12,8 @@ if (majorNodeVersion < 14 || majorNodeVersion >= 17) { err = true; } +const path = require('path'); +const fs = require('fs'); const cp = require('child_process'); const yarnVersion = cp.execSync('yarn -v', { encoding: 'utf8' }).trim(); const parsedYarnVersion = /^(\d+)\.(\d+)\./.exec(yarnVersion); @@ -33,6 +35,9 @@ if (process.platform === 'win32') { console.error('\033[1;31m*** Invalid C/C++ Compiler Toolchain. Please check https://github.com/microsoft/vscode/wiki/How-to-Contribute#prerequisites.\033[0;0m'); err = true; } + if (!err) { + installHeaders(); + } } if (err) { @@ -66,3 +71,55 @@ function hasSupportedVisualStudioVersion() { } return availableVersions.length; } + +function installHeaders() { + const yarn = 'yarn.cmd'; + const opts = { + env: process.env, + cwd: path.join(__dirname, 'gyp'), + stdio: 'inherit' + }; + const yarnResult = cp.spawnSync(yarn, ['install'], opts); + if (yarnResult.error || yarnResult.status !== 0) { + console.error(`Installing node-gyp failed`); + err = true; + return; + } + + const node_gyp = path.join(__dirname, 'gyp', 'node_modules', '.bin', 'node-gyp.cmd'); + const result = cp.execSync(`${node_gyp} list`, { encoding: 'utf8' }); + const versions = new Set(result.split(/\n/g).filter(line => !line.startsWith('gyp info')).map(value => `"${value}"`)); + + const local = getHeaderInfo(path.join(__dirname, '..', '..', '.yarnrc')); + const remote = getHeaderInfo(path.join(__dirname, '..', '..', 'remote', '.yarnrc')); + + if (local !== undefined && !versions.has(local.target)) { + cp.execSync(`${node_gyp} install --dist-url ${local.disturl} ${local.target}`); + } + + if (remote !== undefined && !versions.has(remote.target)) { + cp.execSync(`${node_gyp} install --dist-url ${remote.disturl} ${remote.target}`); + } +} + +/** + * @param {string} rcFile + * @returns {{ disturl: string; target: string } | undefined} + */ +function getHeaderInfo(rcFile) { + const lines = fs.readFileSync(rcFile, 'utf8').split(/\r\n?/g); + let disturl, target; + for (const line of lines) { + let match = line.match(/\s*disturl\s*(.*)$/); + if (match !== null && match.length >= 1) { + disturl = match[1]; + } + match = line.match(/\s*target\s*(.*)$/); + if (match !== null && match.length >= 1) { + target = match[1]; + } + } + return disturl !== undefined && target !== undefined + ? { disturl, target } + : undefined; +} diff --git a/build/package.json b/build/package.json index 7b1131e894c41..456b7e87ed55c 100644 --- a/build/package.json +++ b/build/package.json @@ -41,20 +41,20 @@ "@types/underscore": "^1.8.9", "@types/webpack": "^4.41.25", "@types/xml2js": "0.0.33", - "@typescript-eslint/experimental-utils": "~2.13.0", - "@typescript-eslint/parser": "^3.3.0", - "applicationinsights": "1.0.8", + "@typescript-eslint/experimental-utils": "^5.10.0", + "@typescript-eslint/parser": "^5.10.0", + "@vscode/iconv-lite-umd": "0.7.0", + "applicationinsights": "1.4.2", "byline": "^5.0.0", "colors": "^1.4.0", "commander": "^7.0.0", "debug": "^4.3.2", "electron-osx-sign": "^0.4.16", - "esbuild": "^0.12.6", + "esbuild": "^0.14.2", "extract-zip": "^2.0.1", "fs-extra": "^9.1.0", "got": "11.8.1", "gulp-merge-json": "^2.1.1", - "iconv-lite-umd": "0.6.10", "jsonc-parser": "^2.3.0", "mime": "^1.4.1", "mkdirp": "^1.0.4", @@ -62,14 +62,13 @@ "plist": "^3.0.1", "source-map": "0.6.1", "tmp": "^0.2.1", - "typescript": "^4.6.0-dev.20211115", "vsce": "^1.100.0", "vscode-universal-bundler": "^0.0.2" }, "scripts": { - "compile": "tsc -p tsconfig.build.json", - "watch": "tsc -p tsconfig.build.json --watch", - "npmCheckJs": "tsc --noEmit" + "compile": "../node_modules/.bin/tsc -p tsconfig.build.json", + "watch": "../node_modules/.bin/tsc -p tsconfig.build.json --watch", + "npmCheckJs": "../node_modules/.bin/tsc --noEmit" }, "dependencies": {} } diff --git a/build/yarn.lock b/build/yarn.lock index bf575cab8d4ae..ddb3a8969a42c 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -217,6 +217,27 @@ dependencies: cross-spawn "^7.0.1" +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + "@opentelemetry/api@^1.0.1": version "1.0.3" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.0.3.tgz#13a12ae9e05c2a782f7b5e84c3cbfda4225eaf80" @@ -303,11 +324,6 @@ resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz#b14efa8852b7768d898906613c23f688713e02cd" integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ== -"@types/eslint-visitor-keys@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d" - integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag== - "@types/eslint@4.16.1": version "4.16.1" resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-4.16.1.tgz#19730c9fcb66b6e44742d12b27a603fabfeb2f49" @@ -434,11 +450,16 @@ resolved "https://registry.yarnpkg.com/@types/js-beautify/-/js-beautify-1.8.0.tgz#0369d3d0e1f35a6aec07cb4da2ee2bcda111367c" integrity sha512-/siF86XrwDKLuHe8l7h6NhrAWgLdgqbxmjZv9NvGWmgYRZoTipkjKiWb0SQHy/jcR+ee0GvbG6uGd+LEBMGNvA== -"@types/json-schema@*", "@types/json-schema@^7.0.3": +"@types/json-schema@*": version "7.0.4" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== +"@types/json-schema@^7.0.9": + version "7.0.9" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d" + integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ== + "@types/keyv@*": version "3.1.1" resolved "https://registry.yarnpkg.com/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7" @@ -661,75 +682,107 @@ dependencies: "@types/node" "*" -"@typescript-eslint/experimental-utils@3.10.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-3.10.1.tgz#e179ffc81a80ebcae2ea04e0332f8b251345a686" - integrity sha512-DewqIgscDzmAfd5nOGe4zm6Bl7PKtMG2Ad0KG8CUZAHlXfAKTF9Ol5PXhiMh39yRL2ChRH1cuuUGOcVyyrhQIw== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/types" "3.10.1" - "@typescript-eslint/typescript-estree" "3.10.1" - eslint-scope "^5.0.0" - eslint-utils "^2.0.0" - -"@typescript-eslint/experimental-utils@~2.13.0": - version "2.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.13.0.tgz#958614faa6f77599ee2b241740e0ea402482533d" - integrity sha512-+Hss3clwa6aNiC8ZjA45wEm4FutDV5HsVXPl/rDug1THq6gEtOYRGLqS3JlTk7mSnL5TbJz0LpEbzbPnKvY6sw== - dependencies: - "@types/json-schema" "^7.0.3" - "@typescript-eslint/typescript-estree" "2.13.0" - eslint-scope "^5.0.0" - -"@typescript-eslint/parser@^3.3.0": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-3.10.1.tgz#1883858e83e8b442627e1ac6f408925211155467" - integrity sha512-Ug1RcWcrJP02hmtaXVS3axPPTTPnZjupqhgj+NnZ6BCkwSImWk/283347+x9wN+lqOdK9Eo3vsyiyDHgsmiEJw== - dependencies: - "@types/eslint-visitor-keys" "^1.0.0" - "@typescript-eslint/experimental-utils" "3.10.1" - "@typescript-eslint/types" "3.10.1" - "@typescript-eslint/typescript-estree" "3.10.1" - eslint-visitor-keys "^1.1.0" - -"@typescript-eslint/types@3.10.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-3.10.1.tgz#1d7463fa7c32d8a23ab508a803ca2fe26e758727" - integrity sha512-+3+FCUJIahE9q0lDi1WleYzjCwJs5hIsbugIgnbB+dSCYUxl8L6PwmsyOPFZde2hc1DlTo/xnkOgiTLSyAbHiQ== - -"@typescript-eslint/typescript-estree@2.13.0": - version "2.13.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.13.0.tgz#a2e746867da772c857c13853219fced10d2566bc" - integrity sha512-t21Mg5cc8T3ADEUGwDisHLIubgXKjuNRbkpzDMLb7/JMmgCe/gHM9FaaujokLey+gwTuLF5ndSQ7/EfQqrQx4g== - dependencies: - debug "^4.1.1" - eslint-visitor-keys "^1.1.0" - glob "^7.1.6" - is-glob "^4.0.1" - lodash.unescape "4.0.1" - semver "^6.3.0" - tsutils "^3.17.1" - -"@typescript-eslint/typescript-estree@3.10.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-3.10.1.tgz#fd0061cc38add4fad45136d654408569f365b853" - integrity sha512-QbcXOuq6WYvnB3XPsZpIwztBoquEYLXh2MtwVU+kO8jgYCiv4G5xrSP/1wg4tkvrEE+esZVquIPX/dxPlePk1w== - dependencies: - "@typescript-eslint/types" "3.10.1" - "@typescript-eslint/visitor-keys" "3.10.1" - debug "^4.1.1" - glob "^7.1.6" - is-glob "^4.0.1" - lodash "^4.17.15" - semver "^7.3.2" - tsutils "^3.17.1" - -"@typescript-eslint/visitor-keys@3.10.1": - version "3.10.1" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-3.10.1.tgz#cd4274773e3eb63b2e870ac602274487ecd1e931" - integrity sha512-9JgC82AaQeglebjZMgYR5wgmfUdUc+EitGUUMW8u2nDckaeimzW+VsoLV6FoimPv2id3VQzfjwBxEMVz08ameQ== - dependencies: - eslint-visitor-keys "^1.1.0" +"@typescript-eslint/experimental-utils@^5.10.0": + version "5.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.10.1.tgz#49fa5a7800ed08ea70aef14fccb14fbae85116ab" + integrity sha512-Ryeb8nkJa/1zKl8iujNtJC8tgj6PgaY0sDUnrTqbmC70nrKKkZaHfiRDTcqICmCSCEQyLQcJAoh0AukLaIaGTw== + dependencies: + "@typescript-eslint/utils" "5.10.1" + +"@typescript-eslint/parser@^5.10.0": + version "5.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.10.0.tgz#8f59e036f5f1cffc178cacbd5ccdd02aeb96c91c" + integrity sha512-pJB2CCeHWtwOAeIxv8CHVGJhI5FNyJAIpx5Pt72YkK3QfEzt6qAlXZuyaBmyfOdM62qU0rbxJzNToPTVeJGrQw== + dependencies: + "@typescript-eslint/scope-manager" "5.10.0" + "@typescript-eslint/types" "5.10.0" + "@typescript-eslint/typescript-estree" "5.10.0" + debug "^4.3.2" + +"@typescript-eslint/scope-manager@5.10.0": + version "5.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.10.0.tgz#bb5d872e8b9e36203908595507fbc4d3105329cb" + integrity sha512-tgNgUgb4MhqK6DoKn3RBhyZ9aJga7EQrw+2/OiDk5hKf3pTVZWyqBi7ukP+Z0iEEDMF5FDa64LqODzlfE4O/Dg== + dependencies: + "@typescript-eslint/types" "5.10.0" + "@typescript-eslint/visitor-keys" "5.10.0" + +"@typescript-eslint/scope-manager@5.10.1": + version "5.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.10.1.tgz#f0539c73804d2423506db2475352a4dec36cd809" + integrity sha512-Lyvi559Gvpn94k7+ElXNMEnXu/iundV5uFmCUNnftbFrUbAJ1WBoaGgkbOBm07jVZa682oaBU37ao/NGGX4ZDg== + dependencies: + "@typescript-eslint/types" "5.10.1" + "@typescript-eslint/visitor-keys" "5.10.1" + +"@typescript-eslint/types@5.10.0": + version "5.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.10.0.tgz#beb3cb345076f5b088afe996d57bcd1dfddaa75c" + integrity sha512-wUljCgkqHsMZbw60IbOqT/puLfyqqD5PquGiBo1u1IS3PLxdi3RDGlyf032IJyh+eQoGhz9kzhtZa+VC4eWTlQ== + +"@typescript-eslint/types@5.10.1": + version "5.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.10.1.tgz#dca9bd4cb8c067fc85304a31f38ec4766ba2d1ea" + integrity sha512-ZvxQ2QMy49bIIBpTqFiOenucqUyjTQ0WNLhBM6X1fh1NNlYAC6Kxsx8bRTY3jdYsYg44a0Z/uEgQkohbR0H87Q== + +"@typescript-eslint/typescript-estree@5.10.0": + version "5.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.0.tgz#4be24a3dea0f930bb1397c46187d0efdd955a224" + integrity sha512-x+7e5IqfwLwsxTdliHRtlIYkgdtYXzE0CkFeV6ytAqq431ZyxCFzNMNR5sr3WOlIG/ihVZr9K/y71VHTF/DUQA== + dependencies: + "@typescript-eslint/types" "5.10.0" + "@typescript-eslint/visitor-keys" "5.10.0" + debug "^4.3.2" + globby "^11.0.4" + is-glob "^4.0.3" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/typescript-estree@5.10.1": + version "5.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.10.1.tgz#b268e67be0553f8790ba3fe87113282977adda15" + integrity sha512-PwIGnH7jIueXv4opcwEbVGDATjGPO1dx9RkUl5LlHDSe+FXxPwFL5W/qYd5/NHr7f6lo/vvTrAzd0KlQtRusJQ== + dependencies: + "@typescript-eslint/types" "5.10.1" + "@typescript-eslint/visitor-keys" "5.10.1" + debug "^4.3.2" + globby "^11.0.4" + is-glob "^4.0.3" + semver "^7.3.5" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.10.1": + version "5.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.10.1.tgz#fa682a33af47080ba2c4368ee0ad2128213a1196" + integrity sha512-RRmlITiUbLuTRtn/gcPRi4202niF+q7ylFLCKu4c+O/PcpRvZ/nAUwQ2G00bZgpWkhrNLNnvhZLbDn8Ml0qsQw== + dependencies: + "@types/json-schema" "^7.0.9" + "@typescript-eslint/scope-manager" "5.10.1" + "@typescript-eslint/types" "5.10.1" + "@typescript-eslint/typescript-estree" "5.10.1" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + +"@typescript-eslint/visitor-keys@5.10.0": + version "5.10.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.0.tgz#770215497ad67cd15a572b52089991d5dfe06281" + integrity sha512-GMxj0K1uyrFLPKASLmZzCuSddmjZVbVj3Ouy5QVuIGKZopxvOr24JsS7gruz6C3GExE01mublZ3mIBOaon9zuQ== + dependencies: + "@typescript-eslint/types" "5.10.0" + eslint-visitor-keys "^3.0.0" + +"@typescript-eslint/visitor-keys@5.10.1": + version "5.10.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.10.1.tgz#29102de692f59d7d34ecc457ed59ab5fc558010b" + integrity sha512-NjQ0Xinhy9IL979tpoTRuLKxMc0zJC7QVSdeerXs2/QvOy2yRkzX5dRb10X5woNUdJgU8G3nYRDlI33sq1K4YQ== + dependencies: + "@typescript-eslint/types" "5.10.1" + eslint-visitor-keys "^3.0.0" + +"@vscode/iconv-lite-umd@0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz#d2f1e0664ee6036408f9743fee264ea0699b0e48" + integrity sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg== agent-base@6: version "6.0.2" @@ -765,14 +818,15 @@ anymatch@^3.0.0: normalize-path "^3.0.0" picomatch "^2.0.4" -applicationinsights@1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" - integrity sha512-KzOOGdphOS/lXWMFZe5440LUdFbrLpMvh2SaRxn7BmiI550KAoSb2gIhiq6kJZ9Ir3AxRRztjhzif+e5P5IXIg== +applicationinsights@1.4.2: + version "1.4.2" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.4.2.tgz#2f25f7a3f3e5bf0ab4486b63e42a48a9ec321d52" + integrity sha512-1wE37G9zEMZTsPJVQ8BDrQtsGgG3DGMActLHwPAF8TYHAXkfqqpeZYCH0XV4lUZ7H4MffRMwN2Ln2nEtUmT8HQ== dependencies: + cls-hooked "^4.2.2" + continuation-local-storage "^3.2.1" diagnostic-channel "0.2.0" - diagnostic-channel-publishers "0.2.1" - zone.js "0.7.6" + diagnostic-channel-publishers "^0.3.3" argparse@^1.0.7: version "1.0.10" @@ -791,6 +845,11 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + asar@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/asar/-/asar-3.0.3.tgz#1fef03c2d6d2de0cbad138788e4f7ae03b129c7b" @@ -808,6 +867,21 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +async-hook-jl@^1.7.6: + version "1.7.6" + resolved "https://registry.yarnpkg.com/async-hook-jl/-/async-hook-jl-1.7.6.tgz#4fd25c2f864dbaf279c610d73bf97b1b28595e68" + integrity sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg== + dependencies: + stack-chain "^1.3.7" + +async-listener@^0.6.0: + version "0.6.10" + resolved "https://registry.yarnpkg.com/async-listener/-/async-listener-0.6.10.tgz#a7c97abe570ba602d782273c0de60a51e3e17cbc" + integrity sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw== + dependencies: + semver "^5.3.0" + shimmer "^1.1.0" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -866,6 +940,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +braces@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + buffer-alloc-unsafe@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" @@ -1012,6 +1093,15 @@ cloneable-readable@^1.0.0: process-nextick-args "^2.0.0" readable-stream "^2.3.5" +cls-hooked@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" + integrity sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw== + dependencies: + async-hook-jl "^1.7.6" + emitter-listener "^1.0.1" + semver "^5.4.1" + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -1081,6 +1171,14 @@ config-chain@^1.1.11: ini "^1.3.4" proto-list "~1.2.1" +continuation-local-storage@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz#11f613f74e914fe9b34c92ad2d28fe6ae1db7ffb" + integrity sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA== + dependencies: + async-listener "^0.6.0" + emitter-listener "^1.1.1" + core-js@^3.6.5: version "3.15.2" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.2.tgz#740660d2ff55ef34ce664d7e2455119c5bdd3d61" @@ -1188,10 +1286,10 @@ detect-node@^2.0.4: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== -diagnostic-channel-publishers@0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" - integrity sha1-ji1geottef6IC1SLxYzGvrKIxPM= +diagnostic-channel-publishers@^0.3.3: + version "0.3.5" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.3.5.tgz#a84a05fd6cc1d7619fdd17791c17e540119a7536" + integrity sha512-AOIjw4T7Nxl0G2BoBPhkQ6i7T4bUd9+xvdYizwvG7vVAM1dvr+SDrcUudlmzwH0kbEwdR2V1EcnKT0wAeYLQNQ== diagnostic-channel@0.2.0: version "0.2.0" @@ -1210,6 +1308,13 @@ dir-compare@^2.4.0: commander "2.9.0" minimatch "3.0.4" +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + dom-serializer@^1.0.1, dom-serializer@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" @@ -1264,6 +1369,13 @@ electron-osx-sign@^0.4.16: minimist "^1.2.0" plist "^3.0.1" +emitter-listener@^1.0.1, emitter-listener@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/emitter-listener/-/emitter-listener-1.1.2.tgz#56b140e8f6992375b3d7cb2cab1cc7432d9632e8" + integrity sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ== + dependencies: + shimmer "^1.2.0" + encodeurl@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -1296,10 +1408,113 @@ es6-error@^4.1.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== -esbuild@^0.12.6: - version "0.12.6" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.12.6.tgz#85bc755c7cf3005d4f34b4f10f98049ce0ee67ce" - integrity sha512-RDvVLvAjsq/kIZJoneMiUOH7EE7t2QaW7T3Q7EdQij14+bZbDq5sndb0tTanmHIFSqZVMBMMyqzVHkS3dJobeA== +esbuild-android-arm64@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.2.tgz#256b7cf2f9d382a2a92a4ff4e13187587c9b7c6a" + integrity sha512-hEixaKMN3XXCkoe+0WcexO4CcBVU5DCSUT+7P8JZiWZCbAjSkc9b6Yz2X5DSfQmRCtI/cQRU6TfMYrMQ5NBfdw== + +esbuild-darwin-64@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.2.tgz#891a59ce6bc3aded0265f982469b3eb9571b92f8" + integrity sha512-Uq8t0cbJQkxkQdbUfOl2wZqZ/AtLZjvJulR1HHnc96UgyzG9YlCLSDMiqjM+NANEy7/zzvwKJsy3iNC9wwqLJA== + +esbuild-darwin-arm64@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.2.tgz#ab834fffa9c612b2901ca1e77e4695d4d8aa63a2" + integrity sha512-619MSa17sr7YCIrUj88KzQu2ESA4jKYtIYfLU/smX6qNgxQt3Y/gzM4s6sgJ4fPQzirvmXgcHv1ZNQAs/Xh48A== + +esbuild-freebsd-64@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.2.tgz#f7fc87a83f02de27d5a48472571efa1a432ae86d" + integrity sha512-aP6FE/ZsChZpUV6F3HE3x1Pz0paoYXycJ7oLt06g0G9dhJKknPawXCqQg/WMyD+ldCEZfo7F1kavenPdIT/SGQ== + +esbuild-freebsd-arm64@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.2.tgz#bc8758420431106751f3180293cac0b5bc4ce2ee" + integrity sha512-LSm98WTb1QIhyS83+Po0KTpZNdd2XpVpI9ua5rLWqKWbKeNRFwOsjeiuwBaRNc+O32s9oC2ZMefETxHBV6VNkQ== + +esbuild-linux-32@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.2.tgz#0cc2dcd816d6d66e255bc7aeac139b1d04246812" + integrity sha512-8VxnNEyeUbiGflTKcuVc5JEPTqXfsx2O6ABwUbfS1Hp26lYPRPC7pKQK5Dxa0MBejGc50jy7YZae3EGQUQ8EkQ== + +esbuild-linux-64@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.2.tgz#c790f739aa75b15c153609ea3457153fbe4db93d" + integrity sha512-4bzMS2dNxOJoFIiHId4w+tqQzdnsch71JJV1qZnbnErSFWcR9lRgpSqWnTTFtv6XM+MvltRzSXC5wQ7AEBY6Hg== + +esbuild-linux-arm64@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.2.tgz#96858a1f89ad30274dec780d0e3dd8b5691c6b0c" + integrity sha512-RlIVp0RwJrdtasDF1vTFueLYZ8WuFzxoQ1OoRFZOTyJHCGCNgh7xJIC34gd7B7+RT0CzLBB4LcM5n0LS+hIoww== + +esbuild-linux-arm@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.2.tgz#03e193225afa9b1215d2ec6efe8edf0c03eeed6f" + integrity sha512-PaylahvMHhH8YMfJPMKEqi64qA0Su+d4FNfHKvlKes/2dUe4QxgbwXT9oLVgy8iJdcFMrO7By4R8fS8S0p8aVQ== + +esbuild-linux-mips64le@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.2.tgz#972f218d2cb5125237376d40ad60a6e5356a782c" + integrity sha512-Fdwrq2roFnO5oetIiUQQueZ3+5soCxBSJswg3MvYaXDomj47BN6oAWMZgLrFh1oVrtWrxSDLCJBenYdbm2s+qQ== + +esbuild-linux-ppc64le@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.2.tgz#20b71622ac09142b0e523f633af0829def7fed6b" + integrity sha512-vxptskw8JfCDD9QqpRO0XnsM1osuWeRjPaXX1TwdveLogYsbdFtcuiuK/4FxGiNMUr1ojtnCS2rMPbY8puc5NA== + +esbuild-netbsd-64@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.2.tgz#dbd6a25117902ef67aa11d8779dd9c6bca7fbe82" + integrity sha512-I8+LzYK5iSNpspS9eCV9sW67Rj8FgMHimGri4mKiGAmN0pNfx+hFX146rYtzGtewuxKtTsPywWteHx+hPRLDsw== + +esbuild-openbsd-64@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.2.tgz#3c5f199eed459b2f88865548394c0b77383d9ca4" + integrity sha512-120HgMe9elidWUvM2E6mMf0csrGwx8sYDqUIJugyMy1oHm+/nT08bTAVXuwYG/rkMIqsEO9AlMxuYnwR6En/3Q== + +esbuild-sunos-64@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.2.tgz#900a681db6b76c6a7f60fc28d2bfe5b11698641c" + integrity sha512-Q3xcf9Uyfra9UuCFxoLixVvdigo0daZaKJ97TL2KNA4bxRUPK18wwGUk3AxvgDQZpRmg82w9PnkaNYo7a+24ow== + +esbuild-windows-32@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.2.tgz#61e0ba5bd95b277a55d2b997ac4c04dfe2559220" + integrity sha512-TW7O49tPsrq+N1sW8mb3m24j/iDGa4xzAZH4wHWwoIzgtZAYPKC0hpIhufRRG/LA30bdMChO9pjJZ5mtcybtBQ== + +esbuild-windows-64@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.2.tgz#6ab59ef721ff75c682a1c8ae0570dabb637abddb" + integrity sha512-Rym6ViMNmi1E2QuQMWy0AFAfdY0wGwZD73BnzlsQBX5hZBuy/L+Speh7ucUZ16gwsrMM9v86icZUDrSN/lNBKg== + +esbuild-windows-arm64@0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.2.tgz#aca2a4f83d2f0d1592ad4be832ed0045fc888cda" + integrity sha512-ZrLbhr0vX5Em/P1faMnHucjVVWPS+m3tktAtz93WkMZLmbRJevhiW1y4CbulBd2z0MEdXZ6emDa1zFHq5O5bSA== + +esbuild@^0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.2.tgz#9c1e1a652549cc33e44885eea42ea2cc6267edc2" + integrity sha512-l076A6o/PIgcyM24s0dWmDI/b8RQf41uWoJu9I0M71CtW/YSw5T5NUeXxs5lo2tFQD+O4CW4nBHJXx3OY5NpXg== + optionalDependencies: + esbuild-android-arm64 "0.14.2" + esbuild-darwin-64 "0.14.2" + esbuild-darwin-arm64 "0.14.2" + esbuild-freebsd-64 "0.14.2" + esbuild-freebsd-arm64 "0.14.2" + esbuild-linux-32 "0.14.2" + esbuild-linux-64 "0.14.2" + esbuild-linux-arm "0.14.2" + esbuild-linux-arm64 "0.14.2" + esbuild-linux-mips64le "0.14.2" + esbuild-linux-ppc64le "0.14.2" + esbuild-netbsd-64 "0.14.2" + esbuild-openbsd-64 "0.14.2" + esbuild-sunos-64 "0.14.2" + esbuild-windows-32 "0.14.2" + esbuild-windows-64 "0.14.2" + esbuild-windows-arm64 "0.14.2" escape-string-regexp@^1.0.5: version "1.0.5" @@ -1311,38 +1526,48 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -eslint-scope@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" - integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== dependencies: - esrecurse "^4.1.0" + esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-utils@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" - integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== dependencies: - eslint-visitor-keys "^1.1.0" + eslint-visitor-keys "^2.0.0" -eslint-visitor-keys@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" - integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint-visitor-keys@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.2.0.tgz#6fbb166a6798ee5991358bc2daa1ba76cc1254a1" + integrity sha512-IOzT0X126zn7ALX0dwFiUQEdsfzrm4+ISsQS8nukaJXwEyYKRSnEIIDULYg1mCtGp7UUXgfGl7BIolXREQK+XQ== -esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" - integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: - estraverse "^4.1.0" + estraverse "^5.2.0" -estraverse@^4.1.0, estraverse@^4.1.1: +estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + events@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/events/-/events-3.2.0.tgz#93b87c18f8efcd4202a461aec4dfc0556b639379" @@ -1367,11 +1592,29 @@ extract-zip@^2.0.1: optionalDependencies: "@types/yauzl" "^2.9.1" +fast-glob@^3.2.9: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +fastq@^1.6.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + dependencies: + reusify "^1.0.4" + fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" @@ -1379,10 +1622,17 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + follow-redirects@^1.14.0: - version "1.14.5" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.5.tgz#f09a5848981d3c772b5392309778523f8d85c381" - integrity sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA== + version "1.14.7" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" + integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== form-data@^3.0.0: version "3.0.0" @@ -1454,6 +1704,13 @@ get-stream@^5.1.0: dependencies: pump "^3.0.0" +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + glob@^7.0.6: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" @@ -1508,6 +1765,18 @@ globalthis@^1.0.1: dependencies: define-properties "^1.1.3" +globby@^11.0.4: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + got@11.8.1: version "11.8.1" resolved "https://registry.yarnpkg.com/got/-/got-11.8.1.tgz#df04adfaf2e782babb3daabc79139feec2f7e85d" @@ -1627,10 +1896,10 @@ https-proxy-agent@^5.0.0: agent-base "6" debug "4" -iconv-lite-umd@0.6.10: - version "0.6.10" - resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.10.tgz#faec47521e095b8e3a7175ae08e1b4ae0359a735" - integrity sha512-8NtgTa/m1jVq7vdywmD5+SqIlZsB59wtsjaylQuExyCojMq1tHVQxmHjeqVSYwKwnmQbH4mZ1Dxx1eqDkPgaqA== +ignore@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" + integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== inflight@^1.0.4: version "1.0.6" @@ -1674,6 +1943,18 @@ is-glob@^4.0.1: dependencies: is-extglob "^2.1.1" +is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -1874,11 +2155,6 @@ lodash.once@^4.0.0: resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= -lodash.unescape@4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" - integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= - lodash@^4.17.10, lodash@^4.17.15: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -1924,6 +2200,19 @@ mdurl@^1.0.1: resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + dependencies: + braces "^3.0.1" + picomatch "^2.2.3" + mime-db@1.45.0: version "1.45.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.45.0.tgz#cceeda21ccd7c3a745eba2decd55d4b73e7879ea" @@ -2004,9 +2293,11 @@ node-abort-controller@^1.2.0: integrity sha512-79PYeJuj6S9+yOHirR0JBLFOgjB6sQCir10uN6xRx25iD+ZD4ULqgRn3MwWBRaQGB0vEgReJzWwJo42T1R6YbQ== node-fetch@^2.6.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" normalize-path@^3.0.0: version "3.0.0" @@ -2123,6 +2414,11 @@ path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -2133,6 +2429,11 @@ picomatch@^2.0.4: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== +picomatch@^2.2.3: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" @@ -2221,6 +2522,11 @@ qs@^6.9.1: dependencies: side-channel "^1.0.4" +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + quick-lru@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" @@ -2275,6 +2581,11 @@ responselike@^2.0.0: dependencies: lowercase-keys "^2.0.0" +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + rimraf@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -2294,6 +2605,13 @@ roarr@^2.15.3: semver-compare "^1.0.0" sprintf-js "^1.1.2" +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + safe-buffer@^5.0.1: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -2324,12 +2642,12 @@ semver@^5.1.0, semver@^5.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== -semver@^5.6.0: +semver@^5.4.1, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.2.0, semver@^6.3.0: +semver@^6.2.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -2341,6 +2659,13 @@ semver@^7.3.2: dependencies: lru-cache "^6.0.0" +semver@^7.3.5: + version "7.3.5" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7" + integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + dependencies: + lru-cache "^6.0.0" + serialize-error@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" @@ -2360,6 +2685,11 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +shimmer@^1.1.0, shimmer@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" + integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -2369,6 +2699,11 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -2389,6 +2724,11 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= +stack-chain@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" + integrity sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU= + stoppable@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b" @@ -2439,6 +2779,13 @@ to-readable-stream@^1.0.0: resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + tough-cookie@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" @@ -2448,6 +2795,11 @@ tough-cookie@^4.0.0: punycode "^2.1.1" universalify "^0.1.2" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + tslib@^1.8.1: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" @@ -2463,10 +2815,10 @@ tslib@^2.2.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== -tsutils@^3.17.1: - version "3.17.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" - integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== dependencies: tslib "^1.8.1" @@ -2489,11 +2841,6 @@ typed-rest-client@^1.8.4: tunnel "0.0.6" underscore "^1.12.1" -typescript@^4.6.0-dev.20211115: - version "4.6.0-dev.20211115" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.0-dev.20211115.tgz#215e5d032e77cb83f382dc88e901a0757c02cc53" - integrity sha512-rYdYp/j8OhCRFs97l7GNOX9xGHndwwgY8AcL7LDzmFXgBOXC2VLoQP48nCg8FgVzjK6s0M5V4nijTYHRlwiqGQ== - uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.5.tgz#0c65f15f815aa08b560a61ce8b4db7ffc3f45376" @@ -2592,6 +2939,19 @@ vscode-universal-bundler@^0.0.2: dir-compare "^2.4.0" fs-extra "^9.0.1" +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -2656,8 +3016,3 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - -zone.js@0.7.6: - version "0.7.6" - resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.7.6.tgz#fbbc39d3e0261d0986f1ba06306eb3aeb0d22009" - integrity sha1-+7w50+AmHQmG8boGMG6zrrDSIAk= diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index aabe3c00212d4..a7b71cd05c84f 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -51,9 +51,9 @@ ".devcontainer.json" ], "filenamePatterns": [ - "**/.devcontainer/devcontainer.json", + "**/.devcontainer/devcontainer.json", "**/User/snippets/*.json" - ] + ] } ], "jsonValidation": [ @@ -136,6 +136,10 @@ { "fileMatch": "%APP_SETTINGS_HOME%/globalStorage/ms-vscode-remote.remote-containers/imageConfigs/*.json", "url": "./schemas/attachContainer.schema.json" + }, + { + "fileMatch": "**/quality/*/product.json", + "url": "vscode://schemas/vscode-product" } ] }, diff --git a/extensions/configuration-editing/schemas/devContainer.schema.generated.json b/extensions/configuration-editing/schemas/devContainer.schema.generated.json index b8a6d3cf5a3ce..936ea50ce7a03 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.generated.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.generated.json @@ -109,7 +109,7 @@ }, "name": { "type": "string", - "description": "A name to show for the workspace folder." + "description": "A name for the dev container displayed in the UI." }, "extensions": { "type": "array", @@ -375,7 +375,7 @@ "loginInteractiveShell", "interactiveShell" ], - "description": "User environment probe to run. The default is none." + "description": "User environment probe to run. The default is \"loginInteractiveShell\"." }, "codespaces": { "type": "object", @@ -512,7 +512,7 @@ }, "name": { "type": "string", - "description": "A name to show for the workspace folder." + "description": "A name for the dev container displayed in the UI." }, "extensions": { "type": "array", @@ -778,7 +778,7 @@ "loginInteractiveShell", "interactiveShell" ], - "description": "User environment probe to run. The default is none." + "description": "User environment probe to run. The default is \"loginInteractiveShell\"." }, "codespaces": { "type": "object", @@ -881,7 +881,7 @@ }, "name": { "type": "string", - "description": "A name to show for the workspace folder." + "description": "A name for the dev container displayed in the UI." }, "extensions": { "type": "array", @@ -1147,7 +1147,7 @@ "loginInteractiveShell", "interactiveShell" ], - "description": "User environment probe to run. The default is none." + "description": "User environment probe to run. The default is \"loginInteractiveShell\"." }, "codespaces": { "type": "object", @@ -1224,7 +1224,7 @@ }, "name": { "type": "string", - "description": "A name to show for the workspace folder." + "description": "A name for the dev container displayed in the UI." }, "extensions": { "type": "array", @@ -1490,7 +1490,7 @@ "loginInteractiveShell", "interactiveShell" ], - "description": "User environment probe to run. The default is none." + "description": "User environment probe to run. The default is \"loginInteractiveShell\"." }, "codespaces": { "type": "object", @@ -1532,7 +1532,7 @@ "properties": { "name": { "type": "string", - "description": "A name to show for the workspace folder." + "description": "A name for the dev container displayed in the UI." }, "extensions": { "type": "array", @@ -1798,7 +1798,7 @@ "loginInteractiveShell", "interactiveShell" ], - "description": "User environment probe to run. The default is none." + "description": "User environment probe to run. The default is \"loginInteractiveShell\"." }, "codespaces": { "type": "object", diff --git a/extensions/configuration-editing/schemas/devContainer.schema.src.json b/extensions/configuration-editing/schemas/devContainer.schema.src.json index 7a11e21a01bbc..1e1e6c860aaf5 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.src.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.src.json @@ -9,7 +9,7 @@ "properties": { "name": { "type": "string", - "description": "A name to show for the workspace folder." + "description": "A name for the dev container displayed in the UI." }, "extensions": { "type": "array", @@ -274,7 +274,7 @@ "loginInteractiveShell", "interactiveShell" ], - "description": "User environment probe to run. The default is none." + "description": "User environment probe to run. The default is \"loginInteractiveShell\"." }, "codespaces": { "type": "object", diff --git a/extensions/cpp/cgmanifest.json b/extensions/cpp/cgmanifest.json index f2bf269a5f46d..51ed002f31e8f 100644 --- a/extensions/cpp/cgmanifest.json +++ b/extensions/cpp/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "jeff-hykin/cpp-textmate-grammar", "repositoryUrl": "https://github.com/jeff-hykin/cpp-textmate-grammar", - "commitHash": "db3f4e4a5d8335b2f6d689bec490c23f8313630f" + "commitHash": "0ef79f098ed80ce5a86be4ed40f99ebcdbac4895" } }, "license": "MIT", diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index 88994a67f44aa..bd8db6d02596d 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -849,7 +849,7 @@ "dependencies": { "vscode-languageclient": "^7.0.0", "vscode-nls": "^5.0.0", - "vscode-uri": "^3.0.2" + "vscode-uri": "^3.0.3" }, "devDependencies": { "@types/node": "14.x" diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 0ad75a779bb2e..e3f0a5031f60e 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -10,9 +10,9 @@ "main": "./out/node/cssServerMain", "browser": "./dist/browser/cssServerMain", "dependencies": { - "vscode-css-languageservice": "^5.1.8", + "vscode-css-languageservice": "^5.1.12", "vscode-languageserver": "^7.0.0", - "vscode-uri": "^3.0.2" + "vscode-uri": "^3.0.3" }, "devDependencies": { "@types/mocha": "^8.2.0", diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index 3ca64a8a544dc..18bba8bfc63f2 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -12,10 +12,10 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.43.tgz#26bcbb0595b305400e8ceaf9a127a7f905ae49c8" integrity sha512-3pwDJjp1PWacPTpH0LcfhgjvurQvrZFBrC6xxjaUEZ7ifUtT32jtjPxEMMblpqd2Mvx+k8haqQJLQxolyGN/cQ== -vscode-css-languageservice@^5.1.8: - version "5.1.8" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-5.1.8.tgz#36cb389788ffc2d5e6630ffc84e55ee38f8a2338" - integrity sha512-Si1sMykS8U/p8LYgLGPCfZD1YFT0AtvUJQp9XJGw64DZWhtwYo28G2l64USLS9ge4ZPMZpwdpOK7PfbVKfgiiA== +vscode-css-languageservice@^5.1.12: + version "5.1.12" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-5.1.12.tgz#eed3a6a1bb8a7a70636ef07cd742a034c71635a1" + integrity sha512-293C5C2732Rbhh3opTs+nQBpC5Dd+oYrEA8lc0OWdyt40oYmJ331FV7NMF1SLFSIcOFB5XveLiWUZak2oyc49Q== dependencies: vscode-languageserver-textdocument "^1.0.1" vscode-languageserver-types "^3.16.0" @@ -61,3 +61,8 @@ vscode-uri@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.2.tgz#ecfd1d066cb8ef4c3a208decdbab9a8c23d055d0" integrity sha512-jkjy6pjU1fxUvI51P+gCsxg1u2n8LSt0W6KrCNQceaziKzff74GoWmjVG46KieVzybO1sttPQmYfrwSHey7GUA== + +vscode-uri@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.3.tgz#a95c1ce2e6f41b7549f86279d19f47951e4f4d84" + integrity sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA== diff --git a/extensions/css-language-features/yarn.lock b/extensions/css-language-features/yarn.lock index 2e524319229d7..0875cabea4dca 100644 --- a/extensions/css-language-features/yarn.lock +++ b/extensions/css-language-features/yarn.lock @@ -78,10 +78,10 @@ vscode-nls@^5.0.0: resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== -vscode-uri@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.2.tgz#ecfd1d066cb8ef4c3a208decdbab9a8c23d055d0" - integrity sha512-jkjy6pjU1fxUvI51P+gCsxg1u2n8LSt0W6KrCNQceaziKzff74GoWmjVG46KieVzybO1sttPQmYfrwSHey7GUA== +vscode-uri@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.0.3.tgz#a95c1ce2e6f41b7549f86279d19f47951e4f4d84" + integrity sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA== yallist@^4.0.0: version "4.0.0" diff --git a/extensions/dart/cgmanifest.json b/extensions/dart/cgmanifest.json index 9a778f2ced21e..fb2bd89d625be 100644 --- a/extensions/dart/cgmanifest.json +++ b/extensions/dart/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "dart-lang/dart-syntax-highlight", "repositoryUrl": "https://github.com/dart-lang/dart-syntax-highlight", - "commitHash": "1fa12423de71bcc75f68371ca4debaebdd989c20" + "commitHash": "a93646fe6e552d1984c24fd31b1c07dcb3ce7c21" } }, "licenseDetail": [ diff --git a/extensions/dart/syntaxes/dart.tmLanguage.json b/extensions/dart/syntaxes/dart.tmLanguage.json index cfff8dc963d4f..b491ff8656cff 100644 --- a/extensions/dart/syntaxes/dart.tmLanguage.json +++ b/extensions/dart/syntaxes/dart.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/dart-lang/dart-syntax-highlight/commit/1fa12423de71bcc75f68371ca4debaebdd989c20", + "version": "https://github.com/dart-lang/dart-syntax-highlight/commit/a93646fe6e552d1984c24fd31b1c07dcb3ce7c21", "name": "Dart", "scopeName": "source.dart", "patterns": [ @@ -230,7 +230,7 @@ "class-identifier": { "patterns": [ { - "match": "(?]|, )+>)?|bool\\b|num\\b|int\\b|double\\b|dynamic\\b|(void)\\b)", + "match": "(??]|,\\s*|\\s+extends\\s+)+>)?|bool\\b|num\\b|int\\b|double\\b|dynamic\\b|(void)\\b)", "captures": { "1": { "name": "support.class.dart" @@ -252,7 +252,7 @@ "function-identifier": { "patterns": [ { - "match": "([_$]*[a-z][a-zA-Z0-9_$]*)(<(?:[a-zA-Z0-9_$<>]|, )+>)?(\\(|\\s+=>)", + "match": "([_$]*[a-z][a-zA-Z0-9_$]*)(<(?:[a-zA-Z0-9_$<>?]|,\\s*|\\s+extends\\s+)+>)?(\\(|\\s+=>)", "captures": { "1": { "name": "entity.name.function.dart" @@ -286,7 +286,11 @@ "include": "#class-identifier" }, { - "match": "\\s*,\\s*" + "match": "[\\s,]+" + }, + { + "name": "keyword.declaration.dart", + "match": "extends" } ] }, diff --git a/extensions/diff/cgmanifest.json b/extensions/diff/cgmanifest.json index 04d6573c95ecb..a06c6c7f10eef 100644 --- a/extensions/diff/cgmanifest.json +++ b/extensions/diff/cgmanifest.json @@ -29,4 +29,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index 3dfc882b432af..4b303046a2674 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -508,7 +508,7 @@ "@emmetio/html-matcher": "^0.3.3", "@emmetio/math-expression": "^1.0.4", "@vscode/emmet-helper": "^2.3.0", - "image-size": "^0.5.2", + "image-size": "~1.0.0", "vscode-languageserver-textdocument": "^1.0.1" }, "capabilities": { diff --git a/extensions/emmet/src/imageSizeHelper.ts b/extensions/emmet/src/imageSizeHelper.ts index c761b095b5e4c..b012ab624ee34 100644 --- a/extensions/emmet/src/imageSizeHelper.ts +++ b/extensions/emmet/src/imageSizeHelper.ts @@ -8,16 +8,23 @@ import * as path from 'path'; import * as http from 'http'; import * as https from 'https'; -import { parse as parseUrl } from 'url'; -import * as sizeOf from 'image-size'; +import { URL } from 'url'; +import { imageSize } from 'image-size'; +import { ISizeCalculationResult } from 'image-size/dist/types/interface'; const reUrl = /^https?:/; +export type ImageInfoWithScale = { + realWidth: number, + realHeight: number, + width: number, + height: number +}; /** * Get size of given image file. Supports files from local filesystem, * as well as URLs */ -export function getImageSize(file: string) { +export function getImageSize(file: string): Promise { file = file.replace(/^file:\/\//, ''); return reUrl.test(file) ? getImageSizeFromURL(file) : getImageSizeFromFile(file); } @@ -25,7 +32,7 @@ export function getImageSize(file: string) { /** * Get image size from file on local file system */ -function getImageSizeFromFile(file: string) { +function getImageSizeFromFile(file: string): Promise { return new Promise((resolve, reject) => { const isDataUrl = file.match(/^data:.+?;base64,/); @@ -33,13 +40,13 @@ function getImageSizeFromFile(file: string) { // NB should use sync version of `sizeOf()` for buffers try { const data = Buffer.from(file.slice(isDataUrl[0].length), 'base64'); - return resolve(sizeForFileName('', sizeOf(data))); + return resolve(sizeForFileName('', imageSize(data))); } catch (err) { return reject(err); } } - sizeOf(file, (err: any, size: any) => { + imageSize(file, (err: Error | null, size?: ISizeCalculationResult) => { if (err) { reject(err); } else { @@ -52,9 +59,9 @@ function getImageSizeFromFile(file: string) { /** * Get image size from given remove URL */ -function getImageSizeFromURL(urlStr: string) { +function getImageSizeFromURL(urlStr: string): Promise { return new Promise((resolve, reject) => { - const url = parseUrl(urlStr); + const url = new URL(urlStr); const getTransport = url.protocol === 'https:' ? https.get : http.get; if (!url.pathname) { @@ -62,13 +69,13 @@ function getImageSizeFromURL(urlStr: string) { } const urlPath: string = url.pathname; - getTransport(url as any, resp => { + getTransport(url, resp => { const chunks: Buffer[] = []; let bufSize = 0; const trySize = (chunks: Buffer[]) => { try { - const size = sizeOf(Buffer.concat(chunks, bufSize)); + const size: ISizeCalculationResult = imageSize(Buffer.concat(chunks, bufSize)); resp.removeListener('data', onData); resp.destroy(); // no need to read further resolve(sizeForFileName(path.basename(urlPath), size)); @@ -90,8 +97,7 @@ function getImageSizeFromURL(urlStr: string) { resp.removeListener('data', onData); reject(err); }); - }) - .once('error', reject); + }).once('error', reject); }); } @@ -99,10 +105,14 @@ function getImageSizeFromURL(urlStr: string) { * Returns size object for given file name. If file name contains `@Nx` token, * the final dimentions will be downscaled by N */ -function sizeForFileName(fileName: string, size: any) { +function sizeForFileName(fileName: string, size?: ISizeCalculationResult): ImageInfoWithScale | undefined { const m = fileName.match(/@(\d+)x\./); const scale = m ? +m[1] : 1; + if (!size || !size.width || !size.height) { + return; + } + return { realWidth: size.width, realHeight: size.height, diff --git a/extensions/emmet/src/removeTag.ts b/extensions/emmet/src/removeTag.ts index 5e46eff08e6a3..c43a9a03e7239 100644 --- a/extensions/emmet/src/removeTag.ts +++ b/extensions/emmet/src/removeTag.ts @@ -25,7 +25,7 @@ export function removeTag() { return editor.edit(editBuilder => { finalRangesToRemove.forEach(range => { - editBuilder.replace(range, ''); + editBuilder.delete(range); }); }); } @@ -51,20 +51,79 @@ function getRangesToRemove(document: vscode.TextDocument, rootNode: HtmlFlatNode closeTagRange = offsetRangeToVsRange(document, nodeToUpdate.close.start, nodeToUpdate.close.end); } + if (openTagRange && closeTagRange) { + const innerCombinedRange = new vscode.Range( + openTagRange.end.line, + openTagRange.end.character, + closeTagRange.start.line, + closeTagRange.start.character); + const outerCombinedRange = new vscode.Range( + openTagRange.start.line, + openTagRange.start.character, + closeTagRange.end.line, + closeTagRange.end.character); + // Special case: there is only whitespace in between. + if (document.getText(innerCombinedRange).trim() === '' && nodeToUpdate.name !== 'pre') { + return [outerCombinedRange]; + } + } + let rangesToRemove = []; if (openTagRange) { rangesToRemove.push(openTagRange); if (closeTagRange) { const indentAmountToRemove = calculateIndentAmountToRemove(document, openTagRange, closeTagRange); + let firstInnerNonEmptyLine: number | undefined; + let lastInnerNonEmptyLine: number | undefined; for (let i = openTagRange.start.line + 1; i < closeTagRange.start.line; i++) { - rangesToRemove.push(new vscode.Range(i, 0, i, indentAmountToRemove)); + if (!document.lineAt(i).isEmptyOrWhitespace) { + rangesToRemove.push(new vscode.Range(i, 0, i, indentAmountToRemove)); + if (firstInnerNonEmptyLine === undefined) { + // We found the first non-empty inner line. + firstInnerNonEmptyLine = i; + } + lastInnerNonEmptyLine = i; + } + } + + // Remove the entire last line + empty lines preceding it + // if it is just the tag, otherwise remove just the tag. + if (entireLineIsTag(document, closeTagRange) && lastInnerNonEmptyLine) { + rangesToRemove.push(new vscode.Range( + lastInnerNonEmptyLine, + document.lineAt(lastInnerNonEmptyLine).range.end.character, + closeTagRange.end.line, + closeTagRange.end.character)); + } else { + rangesToRemove.push(closeTagRange); + } + + // Remove the entire first line + empty lines proceding it + // if it is just the tag, otherwise keep on removing just the tag. + if (entireLineIsTag(document, openTagRange) && firstInnerNonEmptyLine) { + rangesToRemove[1] = new vscode.Range( + openTagRange.start.line, + openTagRange.start.character, + firstInnerNonEmptyLine, + document.lineAt(firstInnerNonEmptyLine).firstNonWhitespaceCharacterIndex); + rangesToRemove.shift(); } - rangesToRemove.push(closeTagRange); } } return rangesToRemove; } +function entireLineIsTag(document: vscode.TextDocument, range: vscode.Range): boolean { + if (range.start.line === range.end.line) { + const lineText = document.lineAt(range.start).text; + const tagText = document.getText(range); + if (lineText.trim() === tagText) { + return true; + } + } + return false; +} + /** * Calculates the amount of indent to remove for getRangesToRemove. */ @@ -77,8 +136,11 @@ function calculateIndentAmountToRemove(document: vscode.TextDocument, openRange: let contentIndent: number | undefined; for (let i = startLine + 1; i < endLine; i++) { - const lineIndent = document.lineAt(i).firstNonWhitespaceCharacterIndex; - contentIndent = !contentIndent ? lineIndent : Math.min(contentIndent, lineIndent); + const line = document.lineAt(i); + if (!line.isEmptyOrWhitespace) { + const lineIndent = line.firstNonWhitespaceCharacterIndex; + contentIndent = !contentIndent ? lineIndent : Math.min(contentIndent, lineIndent); + } } let indentAmount = 0; diff --git a/extensions/emmet/src/test/tagActions.test.ts b/extensions/emmet/src/test/tagActions.test.ts index 9406929905a9a..c9f8a7fe4fea2 100644 --- a/extensions/emmet/src/test/tagActions.test.ts +++ b/extensions/emmet/src/test/tagActions.test.ts @@ -27,6 +27,22 @@ suite('Tests for Emmet actions on html tags', () => { `; + const spacedContents = ` +
+
    + +
  • Hello
  • + +
  • There
  • + +
  • Bye
  • + + +
+ +
+ `; + let contentsWithTemplate = ` `; @@ -187,6 +201,29 @@ suite('Tests for Emmet actions on html tags', () => { }); }); }); + + test('remove tag with extra trim', () => { + const expectedContents = ` +
+
  • Hello
  • + +
  • There
  • + +
  • Bye
  • + +
    + `; + return withRandomFileEditor(spacedContents, 'html', (editor, doc) => { + editor.selections = [ + new Selection(2, 4, 2, 4), // cursor inside ul tag + ]; + + return removeTag()!.then(() => { + assert.strictEqual(doc.getText(), expectedContents); + return Promise.resolve(); + }); + }); + }); // #endregion // #region split/join tag diff --git a/extensions/emmet/src/test/testUtils.ts b/extensions/emmet/src/test/testUtils.ts index 30e11ee794b99..01e1e098fead1 100644 --- a/extensions/emmet/src/test/testUtils.ts +++ b/extensions/emmet/src/test/testUtils.ts @@ -8,8 +8,13 @@ import * as fs from 'fs'; import * as os from 'os'; import { join } from 'path'; -function rndName() { - return Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10); +export function rndName() { + let name = ''; + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (let i = 0; i < 10; i++) { + name += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return name; } export function createRandomFile(contents = '', fileExtension = 'txt'): Thenable { diff --git a/extensions/emmet/src/test/updateImageSize.test.ts b/extensions/emmet/src/test/updateImageSize.test.ts index 606f26554b96d..37f5653c574b4 100644 --- a/extensions/emmet/src/test/updateImageSize.test.ts +++ b/extensions/emmet/src/test/updateImageSize.test.ts @@ -12,19 +12,23 @@ import { updateImageSize } from '../updateImageSize'; suite('Tests for Emmet actions on html tags', () => { teardown(closeAllEditors); + const imageUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAHYcAAB2HAY/l8WUAAAATSURBVBhXY/jPwADGDP////8PAB/uBfuDMzhuAAAAAElFTkSuQmCC'; + const imageWidth = 2; + const imageHeight = 2; + test('update image css with multiple cursors in css file', () => { const cssContents = ` .one { margin: 10px; padding: 10px; - background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png); + background-image: url('${imageUrl}'); } .two { - background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png); + background-image: url('${imageUrl}'); height: 42px; } .three { - background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png); + background-image: url('${imageUrl}'); width: 42px; } `; @@ -32,19 +36,19 @@ suite('Tests for Emmet actions on html tags', () => { .one { margin: 10px; padding: 10px; - background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png); - width: 1024px; - height: 1024px; + background-image: url('${imageUrl}'); + width: ${imageWidth}px; + height: ${imageHeight}px; } .two { - background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png); - width: 1024px; - height: 1024px; + background-image: url('${imageUrl}'); + width: ${imageWidth}px; + height: ${imageHeight}px; } .three { - background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png); - height: 1024px; - width: 1024px; + background-image: url('${imageUrl}'); + height: ${imageHeight}px; + width: ${imageWidth}px; } `; return withRandomFileEditor(cssContents, 'css', (editor, doc) => { @@ -68,14 +72,14 @@ suite('Tests for Emmet actions on html tags', () => { .one { margin: 10px; padding: 10px; - background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png); + background-image: url('${imageUrl}'); } .two { - background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png); + background-image: url('${imageUrl}'); height: 42px; } .three { - background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png); + background-image: url('${imageUrl}'); width: 42px; } @@ -87,19 +91,19 @@ suite('Tests for Emmet actions on html tags', () => { .one { margin: 10px; padding: 10px; - background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png); - width: 1024px; - height: 1024px; + background-image: url('${imageUrl}'); + width: ${imageWidth}px; + height: ${imageHeight}px; } .two { - background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png); - width: 1024px; - height: 1024px; + background-image: url('${imageUrl}'); + width: ${imageWidth}px; + height: ${imageHeight}px; } .three { - background-image: url(https://raw.githubusercontent.com/microsoft/vscode/master/resources/linux/code.png); - height: 1024px; - width: 1024px; + background-image: url('${imageUrl}'); + height: ${imageHeight}px; + width: ${imageWidth}px; } @@ -121,16 +125,16 @@ suite('Tests for Emmet actions on html tags', () => { test('update image size in img tag in html file with multiple cursors', () => { const htmlwithimgtag = ` - - - + + + `; const expectedContents = ` - - - + + + `; return withRandomFileEditor(htmlwithimgtag, 'html', (editor, doc) => { diff --git a/extensions/emmet/src/typings/image-size.d.ts b/extensions/emmet/src/typings/image-size.d.ts deleted file mode 100644 index f9a935ea05028..0000000000000 --- a/extensions/emmet/src/typings/image-size.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Type definitions for image-size -// Project: https://github.com/image-size/image-size -// Definitions by: Elisée MAURER -// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped - -/// - -declare module 'image-size' { - interface ImageInfo { - width: number; - height: number; - type: string; - } - - function sizeOf(path: string): ImageInfo; - function sizeOf(path: string, callback: (err: Error, dimensions: ImageInfo) => void): void; - - function sizeOf(buffer: Buffer): ImageInfo; - - namespace sizeOf { } - - export = sizeOf; -} diff --git a/extensions/emmet/src/updateImageSize.ts b/extensions/emmet/src/updateImageSize.ts index dadfe49fd893d..3a9204c0580f3 100644 --- a/extensions/emmet/src/updateImageSize.ts +++ b/extensions/emmet/src/updateImageSize.ts @@ -7,7 +7,7 @@ import { TextEditor, Position, window, TextEdit } from 'vscode'; import * as path from 'path'; -import { getImageSize } from './imageSizeHelper'; +import { getImageSize, ImageInfoWithScale } from './imageSizeHelper'; import { getFlatNode, iterateCSSToken, getCssPropertyFromRule, isStyleSheet, validate, offsetRangeToVsRange } from './util'; import { HtmlNode, CssToken, HtmlToken, Attribute, Property } from 'EmmetFlatNode'; import { locateFile } from './locateFile'; @@ -108,11 +108,11 @@ function updateImageSizeCSS(editor: TextEditor, position: Position, fetchNode: ( return locateFile(path.dirname(editor.document.fileName), src) .then(getImageSize) - .then((size: any): TextEdit[] => { + .then((size: ImageInfoWithScale | undefined): TextEdit[] => { // since this action is asynchronous, we have to ensure that editor wasn't // changed and user didn't moved caret outside node const prop = fetchNode(editor, position); - if (prop && getImageSrcCSS(editor, prop, position) === src) { + if (size && prop && getImageSrcCSS(editor, prop, position) === src) { return updateCSSNode(editor, prop, size.width, size.height); } return []; diff --git a/extensions/emmet/yarn.lock b/extensions/emmet/yarn.lock index 020bf7b07775a..84ad50583e824 100644 --- a/extensions/emmet/yarn.lock +++ b/extensions/emmet/yarn.lock @@ -54,14 +54,14 @@ integrity sha1-Rs/+oRmgoAMxKiHC2bVijLX81EI= "@types/node@14.x": - version "14.17.27" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.27.tgz#5054610d37bb5f6e21342d0e6d24c494231f3b85" - integrity sha512-94+Ahf9IcaDuJTle/2b+wzvjmutxXAEXU6O81JHblYXUg2BDG+dnBy7VxIPHKAyEEDHzCMQydTJuWvrE+Aanzw== + version "14.18.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.0.tgz#98df2397f6936bfbff4f089e40e06fa5dd88d32a" + integrity sha512-0GeIl2kmVMXEnx8tg1SlG6Gg8vkqirrW752KqolYo1PHevhhZN3bhJ67qHj+bQaINhX0Ra3TlWwRvMCd9iEfNQ== "@vscode/emmet-helper@^2.3.0": - version "2.8.2" - resolved "https://registry.yarnpkg.com/@vscode/emmet-helper/-/emmet-helper-2.8.2.tgz#9b2ce4fdd62cf3fda45cf8af67c012cfce55edc9" - integrity sha512-A/+pkBYQq2JTow1A2flfTmEOmiF780KpdkoX7VBjQ7wujeA+CFUPd17YdeIa9aim20+J5Jp7SFujPDwVFiQucQ== + version "2.8.3" + resolved "https://registry.yarnpkg.com/@vscode/emmet-helper/-/emmet-helper-2.8.3.tgz#f7c2b4a4751d03bcf2b421f0fce5b521a0f64a18" + integrity sha512-dkTSL+BaBBS8gFgPm/GMOU+XfxaMyI+Fl1IUYxEi8Iv24RfHf9/q2eCpV2hs7sncLcoKWEbMYe5gv4Ppmp2Oxw== dependencies: emmet "^2.3.0" jsonc-parser "^2.3.0" @@ -71,27 +71,41 @@ vscode-uri "^2.1.2" emmet@^2.3.0: - version "2.3.4" - resolved "https://registry.yarnpkg.com/emmet/-/emmet-2.3.4.tgz#5ba0d7a5569a68c7697dfa890c772e4f3179d123" - integrity sha512-3IqSwmO+N2ZGeuhDyhV/TIOJFUbkChi53bcasSNRE7Yd+4eorbbYz4e53TpMECt38NtYkZNupQCZRlwdAYA42A== + version "2.3.5" + resolved "https://registry.yarnpkg.com/emmet/-/emmet-2.3.5.tgz#7f80f9c3db6831d1ee2b458717b9c36a074b1a47" + integrity sha512-LcWfTamJnXIdMfLvJEC5Ld3hY5/KHXgv1L1bp6I7eEvB0ZhacHZ1kX0BYovJ8FroEsreLcq7n7kZhRMsf6jkXQ== dependencies: "@emmetio/abbreviation" "^2.2.2" "@emmetio/css-abbreviation" "^2.1.4" -image-size@^0.5.2: - version "0.5.5" - resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" - integrity sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w= +image-size@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/image-size/-/image-size-1.0.0.tgz#58b31fe4743b1cec0a0ac26f5c914d3c5b2f0750" + integrity sha512-JLJ6OwBfO1KcA+TvJT+v8gbE6iWbj24LyDNFgFEN0lzegn6cC6a/p3NIDaepMsJjQjlUWqIC7wJv8lBFxPNjcw== + dependencies: + queue "6.0.2" + +inherits@~2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== jsonc-parser@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== +queue@6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/queue/-/queue-6.0.2.tgz#b91525283e2315c7553d2efa18d83e76432fed65" + integrity sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA== + dependencies: + inherits "~2.0.3" + vscode-languageserver-textdocument@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.2.tgz#2f9f6bd5b5eb3d8e21424c0c367009216f016236" - integrity sha512-T7uPC18+f8mYE4lbVZwb3OSmvwTZm3cuFhrdx9Bn2l11lmp3SvSuSVjy2JtvrghzjAo4G6Trqny2m9XGnFnWVA== + version "1.0.3" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.3.tgz#879f2649bfa5a6e07bc8b392c23ede2dfbf43eff" + integrity sha512-ynEGytvgTb6HVSUwPJIAZgiHQmPCx8bZ8w5um5Lz+q5DjP0Zj8wTFhQpyg8xaMvefDytw2+HH5yzqS+FhsR28A== vscode-languageserver-types@^3.15.1: version "3.16.0" diff --git a/extensions/extension-editing/package.json b/extensions/extension-editing/package.json index c57ee402b36bc..dbc18aa142be8 100644 --- a/extensions/extension-editing/package.json +++ b/extensions/extension-editing/package.json @@ -27,7 +27,7 @@ }, "dependencies": { "jsonc-parser": "^2.2.1", - "markdown-it": "^12.0.4", + "markdown-it": "^12.3.2", "parse5": "^3.0.2", "vscode-nls": "^5.0.0" }, diff --git a/extensions/extension-editing/src/extensionLinter.ts b/extensions/extension-editing/src/extensionLinter.ts index 0a66243c6d7e4..d4067e5265ad8 100644 --- a/extensions/extension-editing/src/extensionLinter.ts +++ b/extensions/extension-editing/src/extensionLinter.ts @@ -9,7 +9,7 @@ import { URL } from 'url'; import * as nls from 'vscode-nls'; const localize = nls.loadMessageBundle(); -import { parseTree, findNodeAtLocation, Node as JsonNode } from 'jsonc-parser'; +import { parseTree, findNodeAtLocation, Node as JsonNode, getNodeValue } from 'jsonc-parser'; import * as MarkdownItType from 'markdown-it'; import { languages, workspace, Disposable, TextDocument, Uri, Diagnostic, Range, DiagnosticSeverity, Position, env } from 'vscode'; @@ -17,6 +17,7 @@ import { languages, workspace, Disposable, TextDocument, Uri, Diagnostic, Range, const product = JSON.parse(fs.readFileSync(path.join(env.appRoot, 'product.json'), { encoding: 'utf-8' })); const allowedBadgeProviders: string[] = (product.extensionAllowedBadgeProviders || []).map((s: string) => s.toLowerCase()); const allowedBadgeProvidersRegex: RegExp[] = (product.extensionAllowedBadgeProvidersRegex || []).map((r: string) => new RegExp(r)); +const extensionEnabledApiProposals: Record = product.extensionEnabledApiProposals ?? {}; function isTrustedSVGSource(uri: Uri): boolean { return allowedBadgeProviders.includes(uri.authority.toLowerCase()) || allowedBadgeProvidersRegex.some(r => r.test(uri.toString())); @@ -29,6 +30,7 @@ const dataUrlsNotValid = localize('dataUrlsNotValid', "Data URLs are not a valid const relativeUrlRequiresHttpsRepository = localize('relativeUrlRequiresHttpsRepository', "Relative image URLs require a repository with HTTPS protocol to be specified in the package.json."); const relativeIconUrlRequiresHttpsRepository = localize('relativeIconUrlRequiresHttpsRepository', "An icon requires a repository with HTTPS protocol to be specified in this package.json."); const relativeBadgeUrlRequiresHttpsRepository = localize('relativeBadgeUrlRequiresHttpsRepository', "Relative badge URLs require a repository with HTTPS protocol to be specified in this package.json."); +const apiProposalNotListed = localize('apiProposalNotListed', "This proposal cannot be used because for this extension the product defines a fixed set of API proposals. You can test your extension but before publishing you MUST reach out to the VS Code team."); enum Context { ICON, @@ -75,7 +77,7 @@ export class ExtensionLinter { private queue(document: TextDocument) { const p = document.uri.path; - if (document.languageId === 'json' && endsWith(p, '/package.json')) { + if (document.languageId === 'json' && p.endsWith('/package.json')) { this.packageJsonQ.add(document); this.startTimer(); } @@ -84,7 +86,7 @@ export class ExtensionLinter { private queueReadme(document: TextDocument) { const p = document.uri.path; - if (document.languageId === 'markdown' && (endsWith(p.toLowerCase(), '/readme.md') || endsWith(p.toLowerCase(), '/changelog.md'))) { + if (document.languageId === 'markdown' && (p.toLowerCase().endsWith('/readme.md') || p.toLowerCase().endsWith('/changelog.md'))) { this.readmeQ.add(document); this.startTimer(); } @@ -130,6 +132,23 @@ export class ExtensionLinter { .map(url => this.addDiagnostics(diagnostics, document, url!.offset + 1, url!.offset + url!.length - 1, url!.value, Context.BADGE, info)); } + const publisher = findNodeAtLocation(tree, ['publisher']); + const name = findNodeAtLocation(tree, ['name']); + const enabledApiProposals = findNodeAtLocation(tree, ['enabledApiProposals']); + if (publisher?.type === 'string' && name?.type === 'string' && enabledApiProposals?.type === 'array') { + const extensionId = `${getNodeValue(publisher)}.${getNodeValue(name)}`; + const effectiveProposalNames = extensionEnabledApiProposals[extensionId]; + if (Array.isArray(effectiveProposalNames) && enabledApiProposals.children) { + for (const child of enabledApiProposals.children) { + if (child.type === 'string' && !effectiveProposalNames.includes(getNodeValue(child))) { + const start = document.positionAt(child.offset); + const end = document.positionAt(child.offset + child.length); + diagnostics.push(new Diagnostic(new Range(start, end), apiProposalNotListed, DiagnosticSeverity.Error)); + } + } + } + } + } this.diagnosticsCollection.set(document.uri, diagnostics); }); @@ -329,7 +348,7 @@ export class ExtensionLinter { diagnostics.push(new Diagnostic(range, message, DiagnosticSeverity.Warning)); } - if (endsWith(uri.path.toLowerCase(), '.svg') && !isTrustedSVGSource(uri)) { + if (uri.path.toLowerCase().endsWith('.svg') && !isTrustedSVGSource(uri)) { const range = new Range(document.positionAt(begin), document.positionAt(end)); diagnostics.push(new Diagnostic(range, svgsNotValid, DiagnosticSeverity.Warning)); } @@ -346,17 +365,6 @@ export class ExtensionLinter { } } -function endsWith(haystack: string, needle: string): boolean { - let diff = haystack.length - needle.length; - if (diff > 0) { - return haystack.indexOf(needle, diff) === diff; - } else if (diff === 0) { - return haystack === needle; - } else { - return false; - } -} - function parseUri(src: string, base?: string, retry: boolean = true): Uri | null { try { let url = new URL(src, base); diff --git a/extensions/extension-editing/yarn.lock b/extensions/extension-editing/yarn.lock index ecb53673162a1..171c0baaa6f81 100644 --- a/extensions/extension-editing/yarn.lock +++ b/extensions/extension-editing/yarn.lock @@ -39,10 +39,10 @@ linkify-it@^3.0.1: dependencies: uc.micro "^1.0.1" -markdown-it@^12.0.4: - version "12.0.4" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.0.4.tgz#eec8247d296327eac3ba9746bdeec9cfcc751e33" - integrity sha512-34RwOXZT8kyuOJy25oJNJoulO8L0bTHYWXcdZBYZqFnjIy3NgjeoM3FmPXIOFQ26/lSHYMr8oc62B6adxXcb3Q== +markdown-it@^12.3.2: + version "12.3.2" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-12.3.2.tgz#bf92ac92283fe983fe4de8ff8abfb5ad72cd0c90" + integrity sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg== dependencies: argparse "^2.0.1" entities "~2.1.0" @@ -62,12 +62,7 @@ parse5@^3.0.2: dependencies: "@types/node" "^6.0.46" -uc.micro@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.3.tgz#7ed50d5e0f9a9fb0a573379259f2a77458d50192" - integrity sha1-ftUNXg+an7ClczeSWfKndFjVAZI= - -uc.micro@^1.0.5: +uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== diff --git a/extensions/fsharp/cgmanifest.json b/extensions/fsharp/cgmanifest.json index b898a38669c21..c6bf0cd413978 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": "fc4cac6d9bc1787f54ce48bbc77bcbb1de8160ff" + "commitHash": "bba27391e61090035449b5c1e5c4b9d396bc4c9b" } }, "license": "MIT", diff --git a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json index ca06a19c2c26a..c388ab00750a9 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/fc4cac6d9bc1787f54ce48bbc77bcbb1de8160ff", + "version": "https://github.com/ionide/ionide-fsgrammar/commit/bba27391e61090035449b5c1e5c4b9d396bc4c9b", "name": "fsharp", "scopeName": "source.fsharp", "patterns": [ @@ -527,8 +527,8 @@ "patterns": [ { "name": "comment.block.markdown.fsharp", - "begin": "^\\s*(\\(\\*\\*(?!\\)))(?!\\*\\))$", - "while": "^(?!\\s*\\*\\)$)", + "begin": "^\\s*(\\(\\*\\*(?!\\)))((?!\\*\\)).)*$", + "while": "^(?!\\s*(\\*)+\\)$)", "beginCaptures": { "1": { "name": "comment.block.fsharp" @@ -572,7 +572,7 @@ }, { "name": "comment.block.markdown.fsharp.end", - "match": "(\\*\\))", + "match": "((?('vscode.git-base').exports; + const git = gitBaseExtension.getAPI(1); + ``` diff --git a/extensions/git-base/cgmanifest.json b/extensions/git-base/cgmanifest.json index 256966aba2005..a3786f7edce6a 100644 --- a/extensions/git-base/cgmanifest.json +++ b/extensions/git-base/cgmanifest.json @@ -36,4 +36,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/git/package.json b/extensions/git/package.json index b9284c78b8c7e..ab6a045ed97d4 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -38,7 +38,7 @@ "capabilities": { "virtualWorkspaces": true, "untrustedWorkspaces": { - "supported": true + "supported": false } }, "contributes": { @@ -84,8 +84,7 @@ "command": "git.openChange", "title": "%command.openChange%", "category": "Git", - "icon": "$(compare-changes)", - "enablement": "scmActiveResourceHasChanges" + "icon": "$(compare-changes)" }, { "command": "git.openAllChanges", @@ -495,6 +494,11 @@ "title": "%command.stashDrop%", "category": "Git" }, + { + "command": "git.stashDropAll", + "title": "%command.stashDropAll%", + "category": "Git" + }, { "command": "git.timeline.openDiff", "title": "%command.timelineOpenDiff%", @@ -594,11 +598,11 @@ }, { "command": "git.openFile", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == file && scmActiveResourceHasChanges" }, { "command": "git.openHEADFile", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == file && scmActiveResourceHasChanges" }, { "command": "git.openChange", @@ -674,7 +678,7 @@ }, { "command": "git.rename", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == file" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == file && scmActiveResourceRepository" }, { "command": "git.commit", @@ -874,7 +878,7 @@ }, { "command": "git.ignore", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && resourceScheme == file && scmActiveResourceRepository" }, { "command": "git.stashIncludeUntracked", @@ -904,6 +908,10 @@ "command": "git.stashDrop", "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" }, + { + "command": "git.stashDropAll", + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0" + }, { "command": "git.timeline.openDiff", "when": "false" @@ -1361,7 +1369,7 @@ { "command": "git.openChange", "group": "navigation", - "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && !isInDiffEditor && resourceScheme == file" + "when": "config.git.enabled && !git.missing && gitOpenRepositoryCount != 0 && !isInDiffEditor && resourceScheme == file && scmActiveResourceHasChanges" }, { "command": "git.stageSelectedRanges", @@ -1641,6 +1649,10 @@ { "command": "git.stashDrop", "group": "stash@7" + }, + { + "command": "git.stashDropAll", + "group": "stash@8" } ], "git.tags": [ @@ -2205,6 +2217,34 @@ "scope": "resource", "default": 10000, "description": "%config.statusLimit%" + }, + "git.experimental.installGuide": { + "type": "string", + "enum": [ + "default", + "download" + ], + "tags": ["experimental"], + "scope": "machine", + "description": "%config.experimental.installGuide%", + "default": "default" + }, + "git.repositoryScanIgnoredFolders": { + "type": "array", + "items": { + "type": "string" + }, + "default": [ + "node_modules" + ], + "scope": "resource", + "markdownDescription": "%config.repositoryScanIgnoredFolders%" + }, + "git.repositoryScanMaxDepth": { + "type": "number", + "scope": "resource", + "default": 1, + "markdownDescription": "%config.repositoryScanMaxDepth%" } } }, @@ -2317,10 +2357,30 @@ "contents": "%view.workbench.scm.disabled%", "when": "!config.git.enabled" }, + { + "view": "scm", + "contents": "%view.workbench.scm.missing.guide%", + "when": "config.git.enabled && git.missing && config.git.experimental.installGuide == download" + }, + { + "view": "scm", + "contents": "%view.workbench.scm.missing.guide.mac%", + "when": "config.git.enabled && git.missing && config.git.experimental.installGuide == download && isMac" + }, + { + "view": "scm", + "contents": "%view.workbench.scm.missing.guide.windows%", + "when": "config.git.enabled && git.missing && config.git.experimental.installGuide == download && isWindows" + }, + { + "view": "scm", + "contents": "%view.workbench.scm.missing.guide.windows%", + "when": "config.git.enabled && git.missing && config.git.experimental.installGuide == download && isLinux" + }, { "view": "scm", "contents": "%view.workbench.scm.missing%", - "when": "config.git.enabled && git.missing" + "when": "config.git.enabled && git.missing && config.git.experimental.installGuide == default" }, { "view": "scm", @@ -2367,11 +2427,11 @@ ] }, "dependencies": { + "@vscode/iconv-lite-umd": "0.7.0", "byline": "^5.0.0", "file-type": "^7.2.0", - "iconv-lite-umd": "0.6.10", "jschardet": "3.0.0", - "vscode-extension-telemetry": "0.4.3", + "@vscode/extension-telemetry": "0.4.6", "vscode-nls": "^4.0.0", "vscode-uri": "^2.0.0", "which": "^1.3.0" diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 7b3af1f5ca9c2..7221538835089 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -87,6 +87,7 @@ "command.stashApply": "Apply Stash...", "command.stashApplyLatest": "Apply Latest Stash", "command.stashDrop": "Drop Stash...", + "command.stashDropAll": "Drop All Stashes...", "command.timelineOpenDiff": "Open Changes", "command.timelineCopyCommitId": "Copy Commit ID", "command.timelineCopyCommitMessage": "Copy Commit Message", @@ -193,6 +194,9 @@ "config.showUnpublishedCommitsButton.whenEmpty": "Only shows the action button if there are no other changes and there are unpublished commits.", "config.showUnpublishedCommitsButton.never": "Never shows the action button.", "config.statusLimit": "Controls how to limit the number of changes that can be parsed from Git status command. Can be set to 0 for no limit.", + "config.experimental.installGuide": "Experimental improvements for the git setup flow.", + "config.repositoryScanIgnoredFolders": "List of folders that are ignored while scanning for Git repositories when `#git.autoRepositoryDetection#` is set to `true` or `subFolders`.", + "config.repositoryScanMaxDepth": "Controls the depth used when scanning workspace folders for Git repositories when `#git.autoRepositoryDetection#` is set to `true` or `subFolders`. Can be set to `-1` for no limit.", "submenu.explorer": "Git", "submenu.commit": "Commit", "submenu.commit.amend": "Amend", @@ -214,6 +218,10 @@ "colors.conflict": "Color for resources with conflicts.", "colors.submodule": "Color for submodule resources.", "view.workbench.scm.missing": "A valid git installation was not detected, more details can be found in the [git output](command:git.showOutput).\nPlease [install git](https://git-scm.com/), or learn more about how to use git and source control in VS Code in [our docs](https://aka.ms/vscode-scm).\nIf you're using a different version control system, you can [search the Marketplace](command:workbench.extensions.search?%22%40category%3A%5C%22scm%20providers%5C%22%22) for additional extensions.", + "view.workbench.scm.missing.guide.windows": "[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).", + "view.workbench.scm.missing.guide.mac": "[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).", + "view.workbench.scm.missing.guide.linux": "Source control depends on Git being installed.\n[Download Git for Linux](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).", + "view.workbench.scm.missing.guide": "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.disabled": "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).", "view.workbench.scm.empty": "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).", "view.workbench.scm.folder": "The folder currently open doesn't have a git repository. You can initialize a repository which will enable source control features powered by git.\n[Initialize Repository](command:git.init?%5Btrue%5D)\nTo learn more about how to use git and source control in VS Code [read our docs](https://aka.ms/vscode-scm).", diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index 2117bec4ded84..f6ae052e177ba 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -104,6 +104,10 @@ export class ApiRepository implements Repository { return this._repository.getCommit(ref); } + add(paths: string[]) { + return this._repository.add(paths.map(p => Uri.file(p))); + } + clean(paths: string[]) { return this._repository.clean(paths.map(p => Uri.file(p))); } @@ -174,6 +178,14 @@ export class ApiRepository implements Repository { return this._repository.getMergeBase(ref1, ref2); } + tag(name: string, upstream: string): Promise { + return this._repository.tag(name, upstream); + } + + deleteTag(name: string): Promise { + return this._repository.deleteTag(name); + } + status(): Promise { return this._repository.status(); } diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index 6ab8a38e64c54..c105367696924 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -172,6 +172,7 @@ export interface Repository { show(ref: string, path: string): Promise; getCommit(ref: string): Promise; + add(paths: string[]): Promise; clean(paths: string[]): Promise; apply(patch: string, reverse?: boolean): Promise; @@ -198,6 +199,9 @@ export interface Repository { getMergeBase(ref1: string, ref2: string): Promise; + tag(name: string, upstream: string): Promise; + deleteTag(name: string): Promise; + status(): Promise; checkout(treeish: string): Promise; diff --git a/extensions/git/src/askpass.ts b/extensions/git/src/askpass.ts index e6c21efa9cbe5..f4ba1e84573ce 100644 --- a/extensions/git/src/askpass.ts +++ b/extensions/git/src/askpass.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { window, InputBoxOptions, Uri, OutputChannel, Disposable, workspace } from 'vscode'; -import { IDisposable, EmptyDisposable, toDisposable } from './util'; +import { IDisposable, EmptyDisposable, toDisposable, logTimestamp } from './util'; import * as path from 'path'; import { IIPCHandler, IIPCServer, createIPCServer } from './ipc/ipcServer'; import { CredentialsProvider, Credentials } from './api/git'; @@ -19,7 +19,7 @@ export class Askpass implements IIPCHandler { try { return new Askpass(await createIPCServer(context)); } catch (err) { - outputChannel.appendLine(`[error] Failed to create git askpass IPC: ${err}`); + outputChannel.appendLine(`${logTimestamp()} [error] Failed to create git askpass IPC: ${err}`); return new Askpass(); } } diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 1b6f4aaea4355..913627c892257 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -6,7 +6,7 @@ import * as os from 'os'; import * as path from 'path'; import { Command, commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider } from 'vscode'; -import TelemetryReporter from 'vscode-extension-telemetry'; +import TelemetryReporter from '@vscode/extension-telemetry'; import * as nls from 'vscode-nls'; import { Branch, ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourcePublisher } from './api/git'; import { Git, Stash } from './git'; @@ -14,7 +14,7 @@ import { Model } from './model'; import { Repository, Resource, ResourceGroupType } from './repository'; import { applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineChange, toLineRanges } from './staging'; import { fromGitUri, toGitUri, isGitUri } from './uri'; -import { grep, isDescendant, pathEquals } from './util'; +import { grep, isDescendant, logTimestamp, pathEquals, relativePath } from './util'; import { Log, LogLevel } from './log'; import { GitTimelineItem } from './timelineProvider'; import { ApiRepository } from './api/api1'; @@ -353,7 +353,7 @@ export class CommandCenter { } Log.logLevel = choice.logLevel; - this.outputChannel.appendLine(localize('changed', "Log level changed to: {0}", LogLevel[Log.logLevel])); + this.outputChannel.appendLine(localize('changed', "{0} Log level changed to: {1}", logTimestamp(), LogLevel[Log.logLevel])); } @command('git.refresh', { repository: true }) @@ -686,6 +686,10 @@ export class CommandCenter { } const activeTextEditor = window.activeTextEditor; + // Must extract these now because opening a new document will change the activeTextEditor reference + const previousVisibleRange = activeTextEditor?.visibleRanges[0]; + const previousURI = activeTextEditor?.document.uri; + const previousSelection = activeTextEditor?.selection; for (const uri of uris) { const opts: TextDocumentShowOptions = { @@ -702,18 +706,21 @@ export class CommandCenter { const document = window.activeTextEditor?.document; // If the document doesn't match what we opened then don't attempt to select the range - if (document?.uri.toString() !== uri.toString()) { + // Additioanlly if there was no previous document we don't have information to select a range + if (document?.uri.toString() !== uri.toString() || !activeTextEditor || !previousURI || !previousSelection) { continue; } // Check if active text editor has same path as other editor. we cannot compare via // URI.toString() here because the schemas can be different. Instead we just go by path. - if (activeTextEditor && activeTextEditor.document.uri.path === uri.path && document) { + if (previousURI.path === uri.path && document) { // preserve not only selection but also visible range - opts.selection = activeTextEditor.selection; - const previousVisibleRanges = activeTextEditor.visibleRanges; + opts.selection = previousSelection; const editor = await window.showTextDocument(document, opts); - editor.revealRange(previousVisibleRanges[0]); + // This should always be defined but just in case + if (previousVisibleRange) { + editor.revealRange(previousVisibleRange); + } } } } @@ -796,7 +803,7 @@ export class CommandCenter { return; } - const from = path.relative(repository.root, fromUri.fsPath); + const from = relativePath(repository.root, fromUri.fsPath); let to = await window.showInputBox({ value: from, valueSelection: [from.length - path.basename(from).length, from.length] @@ -813,14 +820,14 @@ export class CommandCenter { @command('git.stage') async stage(...resourceStates: SourceControlResourceState[]): Promise { - this.outputChannel.appendLine(`git.stage ${resourceStates.length}`); + this.outputChannel.appendLine(`${logTimestamp()} git.stage ${resourceStates.length}`); resourceStates = resourceStates.filter(s => !!s); if (resourceStates.length === 0 || (resourceStates[0] && !(resourceStates[0].resourceUri instanceof Uri))) { const resource = this.getSCMResource(); - this.outputChannel.appendLine(`git.stage.getSCMResource ${resource ? resource.resourceUri.toString() : null}`); + this.outputChannel.appendLine(`${logTimestamp()} git.stage.getSCMResource ${resource ? resource.resourceUri.toString() : null}`); if (!resource) { return; @@ -863,7 +870,7 @@ export class CommandCenter { const untracked = selection.filter(s => s.resourceGroupType === ResourceGroupType.Untracked); const scmResources = [...workingTree, ...untracked, ...resolved, ...unresolved]; - this.outputChannel.appendLine(`git.stage.scmResources ${scmResources.length}`); + this.outputChannel.appendLine(`${logTimestamp()} git.stage.scmResources ${scmResources.length}`); if (!scmResources.length) { return; } @@ -2128,7 +2135,7 @@ export class CommandCenter { } const branchName = repository.HEAD.name; - const message = localize('confirm publish branch', "The branch '{0}' has no upstream branch. Would you like to publish this branch?", branchName); + const message = localize('confirm publish branch', "The branch '{0}' has no remote branch. Would you like to publish this branch?", branchName); const yes = localize('ok', "OK"); const pick = await window.showWarningMessage(message, { modal: true }, yes); @@ -2278,7 +2285,7 @@ export class CommandCenter { return; } else if (!HEAD.upstream) { const branchName = HEAD.name; - const message = localize('confirm publish branch', "The branch '{0}' has no upstream branch. Would you like to publish this branch?", branchName); + const message = localize('confirm publish branch', "The branch '{0}' has no remote branch. Would you like to publish this branch?", branchName); const yes = localize('ok', "OK"); const pick = await window.showWarningMessage(message, { modal: true }, yes); @@ -2596,6 +2603,29 @@ export class CommandCenter { await repository.dropStash(stash.index); } + @command('git.stashDropAll', { repository: true }) + async stashDropAll(repository: Repository): Promise { + const stashes = await repository.getStashes(); + + if (stashes.length === 0) { + window.showInformationMessage(localize('no stashes', "There are no stashes in the repository.")); + return; + } + + // request confirmation for the operation + const yes = localize('yes', "Yes"); + const question = stashes.length === 1 ? + localize('drop one stash', "Are you sure you want to drop ALL stashes? There is 1 stash that will be subject to pruning, and MAY BE IMPOSSIBLE TO RECOVER.") : + localize('drop all stashes', "Are you sure you want to drop ALL stashes? There are {0} stashes that will be subject to pruning, and MAY BE IMPOSSIBLE TO RECOVER.", stashes.length); + + const result = await window.showWarningMessage(question, yes); + if (result !== yes) { + return; + } + + await repository.dropStash(); + } + private async pickStash(repository: Repository, placeHolder: string): Promise { const stashes = await repository.getStashes(); @@ -2813,7 +2843,7 @@ export class CommandCenter { type = 'warning'; options.modal = false; break; - case GitErrorCodes.AuthenticationFailed: + case GitErrorCodes.AuthenticationFailed: { const regex = /Authentication failed for '(.*)'/i; const match = regex.exec(err.stderr || String(err)); @@ -2821,12 +2851,13 @@ export class CommandCenter { ? localize('auth failed specific', "Failed to authenticate to git remote:\n\n{0}", match[1]) : localize('auth failed', "Failed to authenticate to git remote."); break; + } case GitErrorCodes.NoUserNameConfigured: case GitErrorCodes.NoUserEmailConfigured: message = localize('missing user info', "Make sure you configure your 'user.name' and 'user.email' in git."); - choices.set(localize('learn more', "Learn More"), () => commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup'))); + choices.set(localize('learn more', "Learn More"), () => commands.executeCommand('vscode.open', Uri.parse('https://aka.ms/vscode-setup-git'))); break; - default: + default: { const hint = (err.stderr || err.message || String(err)) .replace(/^error: /mi, '') .replace(/^> husky.*$/mi, '') @@ -2839,6 +2870,7 @@ export class CommandCenter { : localize('git error', "Git error"); break; + } } if (!message) { @@ -2870,10 +2902,10 @@ export class CommandCenter { private getSCMResource(uri?: Uri): Resource | undefined { uri = uri ? uri : (window.activeTextEditor && window.activeTextEditor.document.uri); - this.outputChannel.appendLine(`git.getSCMResource.uri ${uri && uri.toString()}`); + this.outputChannel.appendLine(`${logTimestamp()} git.getSCMResource.uri ${uri && uri.toString()}`); for (const r of this.model.repositories.map(r => r.root)) { - this.outputChannel.appendLine(`repo root ${r}`); + this.outputChannel.appendLine(`${logTimestamp()} repo root ${r}`); } if (!uri) { diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 7a73b0aeb6e72..76ded13313dc1 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -9,9 +9,9 @@ import * as os from 'os'; import * as cp from 'child_process'; import * as which from 'which'; import { EventEmitter } from 'events'; -import * as iconv from 'iconv-lite-umd'; +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 } from './util'; +import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter, Versions, isWindows } from './util'; import { CancellationToken, Progress, Uri } from 'vscode'; import { detectEncoding } from './encoding'; import { Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, BranchQuery } from './api/git'; @@ -20,7 +20,6 @@ import { StringDecoder } from 'string_decoder'; // https://github.com/microsoft/vscode/issues/65693 const MAX_CLI_LENGTH = 30000; -const isWindows = process.platform === 'win32'; export interface IGit { path: string; @@ -84,7 +83,7 @@ function findGitDarwin(onValidate: (path: string) => boolean): Promise { return e('git not found'); } - const path = gitPathBuffer.toString().replace(/^\s+|\s+$/g, ''); + const path = gitPathBuffer.toString().trim(); function getVersion(path: string) { if (!onValidate(path)) { @@ -531,10 +530,15 @@ export class Git { child.stdin!.end(options.input, 'utf8'); } + const startTime = Date.now(); const bufferResult = await exec(child, options.cancellationToken); - if (options.log !== false && bufferResult.stderr.length > 0) { - this.log(`${bufferResult.stderr}\n`); + if (options.log !== false) { + this.log(`> git ${args.join(' ')} [${Date.now() - startTime}ms]\n`); + + if (bufferResult.stderr.length > 0) { + this.log(`${bufferResult.stderr}\n`); + } } let encoding = options.encoding || 'utf8'; @@ -585,10 +589,6 @@ export class Git { options.cwd = sanitizePath(options.cwd); } - if (options.log !== false) { - this.log(`> git ${args.join(' ')}\n`); - } - return cp.spawn(this.path, args, options); } @@ -1195,7 +1195,7 @@ export class Repository { break; // Rename contains two paths, the second one is what the file is renamed/copied to. - case 'R': + case 'R': { if (index >= entries.length) { break; } @@ -1214,7 +1214,7 @@ export class Repository { }); continue; - + } default: // Unknown status break entriesLoop; @@ -1793,10 +1793,13 @@ export class Repository { } async dropStash(index?: number): Promise { - const args = ['stash', 'drop']; + const args = ['stash']; if (typeof index === 'number') { + args.push('drop'); args.push(`stash@{${index}}`); + } else { + args.push('clear'); } try { @@ -1810,8 +1813,8 @@ export class Repository { } } - getStatus(opts?: { limit?: number, ignoreSubmodules?: boolean }): Promise<{ status: IFileStatus[]; didHitLimit: boolean; }> { - return new Promise<{ status: IFileStatus[]; didHitLimit: boolean; }>((c, e) => { + getStatus(opts?: { limit?: number, ignoreSubmodules?: boolean }): Promise<{ status: IFileStatus[]; statusLength: number; didHitLimit: boolean; }> { + return new Promise<{ status: IFileStatus[]; statusLength: number; didHitLimit: boolean; }>((c, e) => { const parser = new GitStatusParser(); const env = { GIT_OPTIONAL_LOCKS: '0' }; const args = ['status', '-z', '-u']; @@ -1835,10 +1838,10 @@ export class Repository { })); } - c({ status: parser.status, didHitLimit: false }); + c({ status: parser.status, statusLength: parser.status.length, didHitLimit: false }); }; - const limit = opts?.limit ?? 5000; + const limit = opts?.limit ?? 10000; const onStdoutData = (raw: string) => { parser.update(raw); @@ -1847,7 +1850,7 @@ export class Repository { child.stdout!.removeListener('data', onStdoutData); child.kill(); - c({ status: parser.status.slice(0, limit), didHitLimit: true }); + c({ status: parser.status.slice(0, limit), statusLength: parser.status.length, didHitLimit: true }); } }; diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 0aa3193e39475..f31a64d9c33d9 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -13,8 +13,8 @@ import { CommandCenter } from './commands'; import { GitFileSystemProvider } from './fileSystemProvider'; import { GitDecorations } from './decorationProvider'; import { Askpass } from './askpass'; -import { toDisposable, filterEvent, eventToPromise } from './util'; -import TelemetryReporter from 'vscode-extension-telemetry'; +import { toDisposable, filterEvent, eventToPromise, logTimestamp } from './util'; +import TelemetryReporter from '@vscode/extension-telemetry'; import { GitExtension } from './api/git'; import { GitProtocolHandler } from './protocolHandler'; import { GitExtensionImpl } from './api/extension'; @@ -46,7 +46,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann } const info = await findGit(pathHints, gitPath => { - outputChannel.appendLine(localize('validating', "Validating found git in: {0}", gitPath)); + outputChannel.appendLine(localize('validating', "{0} Validating found git in: {1}", logTimestamp(), gitPath)); if (excludes.length === 0) { return true; } @@ -54,7 +54,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann const normalized = path.normalize(gitPath).replace(/[\r\n]+$/, ''); const skip = excludes.some(e => normalized.startsWith(e)); if (skip) { - outputChannel.appendLine(localize('skipped', "Skipped found git in: {0}", gitPath)); + outputChannel.appendLine(localize('skipped', "{0} Skipped found git in: {1}", logTimestamp(), gitPath)); } return !skip; }); @@ -73,7 +73,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann version: info.version, env: environment, }); - const model = new Model(git, askpass, context.globalState, outputChannel); + const model = new Model(git, askpass, context.globalState, outputChannel, telemetryReporter); disposables.push(model); const onRepository = () => commands.executeCommand('setContext', 'gitOpenRepositoryCount', `${model.repositories.length}`); @@ -81,7 +81,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann model.onDidCloseRepository(onRepository, null, disposables); onRepository(); - outputChannel.appendLine(localize('using git', "Using git {0} from {1}", info.version, info.path)); + outputChannel.appendLine(localize('using git', "{0} Using git {1} from {2}", logTimestamp(), info.version, info.path)); const onOutput = (str: string) => { const lines = str.split(/\r?\n/mg); @@ -90,7 +90,7 @@ async function createModel(context: ExtensionContext, outputChannel: OutputChann lines.pop(); } - outputChannel.appendLine(lines.join('\n')); + outputChannel.appendLine(`${logTimestamp()} ${lines.join('\n')}`); }; git.onOutput.addListener('log', onOutput); disposables.push(toDisposable(() => git.onOutput.removeListener('log', onOutput))); @@ -151,7 +151,7 @@ async function warnAboutMissingGit(): Promise { ); if (choice === download) { - commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/')); + commands.executeCommand('vscode.open', Uri.parse('https://aka.ms/vscode-download-git')); } else if (choice === neverShowAgain) { await config.update('ignoreMissingGitWarning', true, true); } @@ -190,7 +190,12 @@ export async function _activate(context: ExtensionContext): Promise { ); if (choice === update) { - commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/')); + commands.executeCommand('vscode.open', Uri.parse('https://aka.ms/vscode-download-git')); } else if (choice === neverShowAgain) { await config.update('ignoreLegacyWarning', true, true); } @@ -261,7 +266,7 @@ async function checkGitWindows(info: IGit): Promise { ); if (choice === update) { - commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/')); + commands.executeCommand('vscode.open', Uri.parse('https://aka.ms/vscode-download-git')); } else if (choice === neverShowAgain) { await config.update('ignoreWindowsGit27Warning', true, true); } diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 9cf79aadf8248..a6e835043a866 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -4,9 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitter, QuickPickItem, Disposable, SourceControl, SourceControlResourceGroup, TextEditor, Memento, OutputChannel, commands } from 'vscode'; +import TelemetryReporter from '@vscode/extension-telemetry'; import { Repository, RepositoryState } from './repository'; import { memoize, sequentialize, debounce } from './decorators'; -import { dispose, anyEvent, filterEvent, isDescendant, pathEquals, toDisposable, eventToPromise } from './util'; +import { dispose, anyEvent, filterEvent, isDescendant, pathEquals, toDisposable, eventToPromise, logTimestamp } from './util'; import { Git } from './git'; import * as path from 'path'; import * as fs from 'fs'; @@ -17,6 +18,7 @@ import { Askpass } from './askpass'; import { IPushErrorHandlerRegistry } from './pushError'; import { ApiRepository } from './api/api1'; import { IRemoteSourcePublisherRegistry } from './remotePublisher'; +import { Log, LogLevel } from './log'; const localize = nls.loadMessageBundle(); @@ -107,7 +109,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR private disposables: Disposable[] = []; - constructor(readonly git: Git, private readonly askpass: Askpass, private globalState: Memento, private outputChannel: OutputChannel) { + constructor(readonly git: Git, private readonly askpass: Askpass, private globalState: Memento, private outputChannel: OutputChannel, private telemetryReporter: TelemetryReporter) { workspace.onDidChangeWorkspaceFolders(this.onDidChangeWorkspaceFolders, this, this.disposables); window.onDidChangeVisibleTextEditors(this.onDidChangeVisibleTextEditors, this, this.disposables); workspace.onDidChangeConfiguration(this.onDidChangeConfiguration, this, this.disposables); @@ -133,25 +135,36 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR } /** - * Scans the first level of each workspace folder, looking - * for git repositories. + * Scans each workspace folder, looking for git repositories. By + * default it scans one level deep but that can be changed using + * the git.repositoryScanMaxDepth setting. */ private async scanWorkspaceFolders(): Promise { const config = workspace.getConfiguration('git'); const autoRepositoryDetection = config.get('autoRepositoryDetection'); + // Log repository scan settings + if (Log.logLevel <= LogLevel.Trace) { + this.outputChannel.appendLine(`${logTimestamp()} Trace: autoRepositoryDetection="${autoRepositoryDetection}"`); + } + if (autoRepositoryDetection !== true && autoRepositoryDetection !== 'subFolders') { return; } await Promise.all((workspace.workspaceFolders || []).map(async folder => { const root = folder.uri.fsPath; - const children = await new Promise((c, e) => fs.readdir(root, (err, r) => err ? e(err) : c(r))); - const subfolders = new Set(children.filter(child => child !== '.git').map(child => path.join(root, child))); + // Workspace folder children + const repositoryScanMaxDepth = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanMaxDepth', 1); + const repositoryScanIgnoredFolders = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanIgnoredFolders', []); + + const subfolders = new Set(await this.traverseWorkspaceFolder(root, repositoryScanMaxDepth, repositoryScanIgnoredFolders)); + + // Repository scan folders const scanPaths = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('scanRepositories') || []; for (const scanPath of scanPaths) { - if (scanPath !== '.git') { + if (scanPath === '.git') { continue; } @@ -167,6 +180,31 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR })); } + private async traverseWorkspaceFolder(workspaceFolder: string, maxDepth: number, repositoryScanIgnoredFolders: string[]): Promise { + const result: string[] = []; + const foldersToTravers = [{ path: workspaceFolder, depth: 0 }]; + + while (foldersToTravers.length > 0) { + const currentFolder = foldersToTravers.shift()!; + + if (currentFolder.depth < maxDepth || maxDepth === -1) { + const children = await fs.promises.readdir(currentFolder.path, { withFileTypes: true }); + const childrenFolders = children + .filter(dirent => + dirent.isDirectory() && dirent.name !== '.git' && + !repositoryScanIgnoredFolders.find(f => pathEquals(dirent.name, f))) + .map(dirent => path.join(currentFolder.path, dirent.name)); + + result.push(...childrenFolders); + foldersToTravers.push(...childrenFolders.map(folder => { + return { path: folder, depth: currentFolder.depth + 1 }; + })); + } + } + + return result; + } + private onPossibleGitRepositoryChange(uri: Uri): void { const config = workspace.getConfiguration('git'); const autoRepositoryDetection = config.get('autoRepositoryDetection'); @@ -297,13 +335,15 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR } const dotGit = await this.git.getRepositoryDotGit(repositoryRoot); - const repository = new Repository(this.git.open(repositoryRoot, dotGit), this, this, this.globalState, this.outputChannel); + const repository = new Repository(this.git.open(repositoryRoot, dotGit), this, this, this.globalState, this.outputChannel, this.telemetryReporter); this.open(repository); repository.status(); // do not await this, we want SCM to know about the repo asap } catch (ex) { // noop - this.outputChannel.appendLine(`Opening repository for path='${repoPath}' failed; ex=${ex}`); + if (Log.logLevel <= LogLevel.Trace) { + this.outputChannel.appendLine(`${logTimestamp()} Trace: Opening repository for path='${repoPath}' failed; ex=${ex}`); + } } } @@ -329,7 +369,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR } private open(repository: Repository): void { - this.outputChannel.appendLine(`Open repository: ${repository.root}`); + this.outputChannel.appendLine(`${logTimestamp()} Open repository: ${repository.root}`); const onDidDisappearRepository = filterEvent(repository.onDidChangeState, state => state === RepositoryState.Disposed); const disappearListener = onDidDisappearRepository(() => dispose()); @@ -386,7 +426,7 @@ export class Model implements IRemoteSourcePublisherRegistry, IPushErrorHandlerR return; } - this.outputChannel.appendLine(`Close repository: ${repository.root}`); + this.outputChannel.appendLine(`${logTimestamp()} Close repository: ${repository.root}`); openRepository.dispose(); } diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 27d730b1165d8..6b2205ddcb724 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -5,7 +5,8 @@ import * as fs from 'fs'; import * as path from 'path'; -import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, OutputChannel, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration, commands } from 'vscode'; +import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, OutputChannel, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration, commands, SourceControlActionButton } from 'vscode'; +import TelemetryReporter from '@vscode/extension-telemetry'; import * as nls from 'vscode-nls'; import { Branch, Change, ForcePushMode, GitErrorCodes, LogOptions, Ref, RefType, Remote, Status, CommitOptions, BranchQuery, FetchOptions } from './api/git'; import { AutoFetcher } from './autofetch'; @@ -13,7 +14,7 @@ import { debounce, memoize, throttle } from './decorators'; import { Commit, GitError, Repository as BaseRepository, Stash, Submodule, LogFileOptions } from './git'; import { StatusBarCommands } from './statusbar'; import { toGitUri } from './uri'; -import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, onceEvent } from './util'; +import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, IDisposable, isDescendant, logTimestamp, onceEvent, relativePath } from './util'; import { IFileWatcher, watch } from './watch'; import { Log, LogLevel } from './log'; import { IPushErrorHandlerRegistry } from './pushError'; @@ -516,8 +517,8 @@ class FileEventLogger { } this.eventDisposable = combinedDisposable([ - this.onWorkspaceWorkingTreeFileChange(uri => this.outputChannel.appendLine(`[debug] [wt] Change: ${uri.fsPath}`)), - this.onDotGitFileChange(uri => this.outputChannel.appendLine(`[debug] [.git] Change: ${uri.fsPath}`)) + this.onWorkspaceWorkingTreeFileChange(uri => this.outputChannel.appendLine(`${logTimestamp()} [debug] [wt] Change: ${uri.fsPath}`)), + this.onDotGitFileChange(uri => this.outputChannel.appendLine(`${logTimestamp()} [debug] [.git] Change: ${uri.fsPath}`)) ]); } @@ -567,7 +568,7 @@ class DotGitWatcher implements IFileWatcher { upstreamWatcher.event(this.emitter.fire, this.emitter, this.transientDisposables); } catch (err) { if (Log.logLevel <= LogLevel.Error) { - this.outputChannel.appendLine(`Warning: Failed to watch ref '${upstreamPath}', is most likely packed.`); + this.outputChannel.appendLine(`${logTimestamp()} Warning: Failed to watch ref '${upstreamPath}', is most likely packed.`); } } } @@ -664,7 +665,7 @@ class ResourceCommandResolver { case Status.MODIFIED: case Status.UNTRACKED: case Status.IGNORED: - case Status.INTENT_TO_ADD: + case Status.INTENT_TO_ADD: { const uriString = resource.resourceUri.toString(); const [indexStatus] = this.repository.indexGroup.resourceStates.filter(r => r.resourceUri.toString() === uriString); @@ -673,7 +674,7 @@ class ResourceCommandResolver { } return resource.resourceUri; - + } case Status.BOTH_ADDED: case Status.BOTH_MODIFIED: return resource.resourceUri; @@ -853,7 +854,8 @@ export class Repository implements Disposable { private pushErrorHandlerRegistry: IPushErrorHandlerRegistry, remoteSourcePublisherRegistry: IRemoteSourcePublisherRegistry, globalState: Memento, - outputChannel: OutputChannel + outputChannel: OutputChannel, + private telemetryReporter: TelemetryReporter ) { const workspaceWatcher = workspace.createFileSystemWatcher('**'); this.disposables.push(workspaceWatcher); @@ -870,7 +872,7 @@ export class Repository implements Disposable { this.disposables.push(dotGitFileWatcher); } catch (err) { if (Log.logLevel <= LogLevel.Error) { - outputChannel.appendLine(`Failed to watch '${this.dotGit}', reverting to legacy API file watched. Some events might be lost.\n${err.stack || err}`); + outputChannel.appendLine(`${logTimestamp()} Failed to watch '${this.dotGit}', reverting to legacy API file watched. Some events might be lost.\n${err.stack || err}`); } onDotGitFileChange = filterEvent(onWorkspaceRepositoryFileChange, uri => /\/\.git($|\/)/.test(uri.path)); @@ -1159,8 +1161,8 @@ export class Repository implements Disposable { } async stage(resource: Uri, contents: string): Promise { - const relativePath = path.relative(this.repository.root, resource.fsPath).replace(/\\/g, '/'); - await this.run(Operation.Stage, () => this.repository.stage(relativePath, contents)); + const path = relativePath(this.repository.root, resource.fsPath).replace(/\\/g, '/'); + await this.run(Operation.Stage, () => this.repository.stage(path, contents)); this._onDidChangeOriginalResource.fire(resource); } @@ -1543,16 +1545,16 @@ export class Repository implements Disposable { async show(ref: string, filePath: string): Promise { return await this.run(Operation.Show, async () => { - const relativePath = path.relative(this.repository.root, filePath).replace(/\\/g, '/'); + 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'); try { - return await this.repository.bufferString(`${ref}:${relativePath}`, defaultEncoding, autoGuessEncoding); + return await this.repository.bufferString(`${ref}:${path}`, defaultEncoding, autoGuessEncoding); } catch (err) { if (err.gitErrorCode === GitErrorCodes.WrongCase) { - const gitRelativePath = await this.repository.getGitRelativePath(ref, relativePath); + const gitRelativePath = await this.repository.getGitRelativePath(ref, path); return await this.repository.bufferString(`${ref}:${gitRelativePath}`, defaultEncoding, autoGuessEncoding); } @@ -1563,8 +1565,8 @@ export class Repository implements Disposable { async buffer(ref: string, filePath: string): Promise { return this.run(Operation.Show, () => { - const relativePath = path.relative(this.repository.root, filePath).replace(/\\/g, '/'); - return this.repository.buffer(`${ref}:${relativePath}`); + const path = relativePath(this.repository.root, filePath).replace(/\\/g, '/'); + return this.repository.buffer(`${ref}:${path}`); }); } @@ -1608,7 +1610,7 @@ export class Repository implements Disposable { return await this.run(Operation.Ignore, async () => { const ignoreFile = `${this.repository.root}${path.sep}.gitignore`; const textToAppend = files - .map(uri => path.relative(this.repository.root, uri.fsPath).replace(/\\/g, '/')) + .map(uri => relativePath(this.repository.root, uri.fsPath).replace(/\\/g, '/')) .join('\n'); const document = await new Promise(c => fs.exists(ignoreFile, c)) @@ -1800,9 +1802,20 @@ export class Repository implements Disposable { const scopedConfig = workspace.getConfiguration('git', Uri.file(this.repository.root)); const ignoreSubmodules = scopedConfig.get('ignoreSubmodules'); - const limit = scopedConfig.get('statusLimit', 5000); + const limit = scopedConfig.get('statusLimit', 10000); + + const { status, statusLength, didHitLimit } = await this.repository.getStatus({ limit, ignoreSubmodules }); - const { status, didHitLimit } = await this.repository.getStatus({ limit, ignoreSubmodules }); + if (didHitLimit) { + /* __GDPR__ + "statusLimit" : { + "ignoreSubmodules": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "limit": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "statusLength": { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + this.telemetryReporter.sendTelemetryEvent('statusLimit', { ignoreSubmodules: String(ignoreSubmodules) }, { limit, statusLength }); + } const config = workspace.getConfiguration('git'); const shouldIgnore = config.get('ignoreLimitWarning') === true; @@ -1922,7 +1935,7 @@ export class Repository implements Disposable { return undefined; }); - let actionButton: SourceControl['actionButton']; + let actionButton: SourceControlActionButton | undefined; if (HEAD !== undefined) { const config = workspace.getConfiguration('git', Uri.file(this.repository.root)); const showActionButton = config.get('showUnpublishedCommitsButton', 'whenEmpty'); @@ -1935,18 +1948,23 @@ export class Repository implements Disposable { const rebaseWhenSync = config.get('rebaseWhenSync'); actionButton = { - command: rebaseWhenSync ? 'git.syncRebase' : 'git.sync', - title: localize('scm button sync title', '$(sync) Sync Changes {0}{1}', HEAD.behind ? `${HEAD.behind}$(arrow-down) ` : '', `${HEAD.ahead}$(arrow-up)`), - tooltip: this.syncTooltip, - arguments: [this._sourceControl], + command: { + command: rebaseWhenSync ? 'git.syncRebase' : 'git.sync', + title: localize('scm button sync title', "$(sync) {0}{1}", HEAD.behind ? `${HEAD.behind}$(arrow-down) ` : '', `${HEAD.ahead}$(arrow-up)`), + tooltip: this.syncTooltip, + arguments: [this._sourceControl], + }, + description: localize('scm button sync description', "$(sync) Sync Changes {0}{1}", HEAD.behind ? `${HEAD.behind}$(arrow-down) ` : '', `${HEAD.ahead}$(arrow-up)`) }; } } else { actionButton = { - command: 'git.publish', - title: localize('scm button publish title', "$(cloud-upload) Publish Branch"), - tooltip: localize('scm button publish tooltip', "Publish Branch"), - arguments: [this._sourceControl], + command: { + command: 'git.publish', + title: localize('scm button publish title', "$(cloud-upload) Publish Branch"), + tooltip: localize('scm button publish tooltip', "Publish Branch"), + arguments: [this._sourceControl], + } }; } } diff --git a/extensions/git/src/test/smoke.test.ts b/extensions/git/src/test/smoke.test.ts index 9005bb4956325..ed654eae7fb43 100644 --- a/extensions/git/src/test/smoke.test.ts +++ b/extensions/git/src/test/smoke.test.ts @@ -42,13 +42,12 @@ suite('git smoke test', function () { suiteSetup(async function () { fs.writeFileSync(file('app.js'), 'hello', 'utf8'); fs.writeFileSync(file('index.pug'), 'hello', 'utf8'); - cp.execSync('git init', { cwd }); + cp.execSync('git init -b main', { cwd }); cp.execSync('git config user.name testuser', { cwd }); cp.execSync('git config user.email monacotools@microsoft.com', { cwd }); cp.execSync('git config commit.gpgsign false', { cwd }); cp.execSync('git add .', { cwd }); cp.execSync('git commit -m "initial commit"', { cwd }); - cp.execSync('git branch -m main', { cwd }); // make sure git is activated const ext = extensions.getExtension('vscode.git'); diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index b5a46554ca18f..a8ae8fff98ad8 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -4,15 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import { Event, Disposable, EventEmitter } from 'vscode'; -import { dirname, sep } from 'path'; +import { dirname, sep, relative } from 'path'; import { Readable } from 'stream'; import { promises as fs, createReadStream } from 'fs'; import * as byline from 'byline'; +export const isMacintosh = process.platform === 'darwin'; +export const isWindows = process.platform === 'win32'; + export function log(...args: any[]): void { console.log.apply(console, ['git:', ...args]); } +export function logTimestamp(): string { + return `[${new Date().toISOString()}]`; +} + export interface IDisposable { dispose(): void; } @@ -280,8 +287,14 @@ export function detectUnicodeEncoding(buffer: Buffer): Encoding | null { return null; } -function isWindowsPath(path: string): boolean { - return /^[a-zA-Z]:\\/.test(path); +function normalizePath(path: string): string { + // Windows & Mac are currently being handled + // as case insensitive file systems in VS Code. + if (isWindows || isMacintosh) { + return path.toLowerCase(); + } + + return path; } export function isDescendant(parent: string, descendant: string): boolean { @@ -293,23 +306,26 @@ export function isDescendant(parent: string, descendant: string): boolean { parent += sep; } - // Windows is case insensitive - if (isWindowsPath(parent)) { - parent = parent.toLowerCase(); - descendant = descendant.toLowerCase(); - } - - return descendant.startsWith(parent); + return normalizePath(descendant).startsWith(normalizePath(parent)); } export function pathEquals(a: string, b: string): boolean { - // Windows is case insensitive - if (isWindowsPath(a)) { - a = a.toLowerCase(); - b = b.toLowerCase(); + return normalizePath(a) === normalizePath(b); +} + +/** + * Given the `repository.root` compute the relative path while trying to preserve + * the casing of the resource URI. The `repository.root` segment of the path can + * have a casing mismatch if the folder/workspace is being opened with incorrect + * casing. + */ +export function relativePath(from: string, to: string): string { + if (isDescendant(from, to) && from.length < to.length) { + return to.substring(from.length + 1); } - return a === b; + // Fallback to `path.relative` + return relative(from, to); } export function* splitInChunks(array: string[], maxChunkLength: number): IterableIterator { diff --git a/extensions/git/src/watch.ts b/extensions/git/src/watch.ts index c6670cfa8847b..a9d99e56a2225 100644 --- a/extensions/git/src/watch.ts +++ b/extensions/git/src/watch.ts @@ -3,23 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, EventEmitter, Uri } from 'vscode'; -import { join } from 'path'; -import * as fs from 'fs'; -import { IDisposable } from './util'; +import { Event, RelativePattern, Uri, workspace } from 'vscode'; +import { IDisposable, anyEvent } from './util'; export interface IFileWatcher extends IDisposable { readonly event: Event; } export function watch(location: string): IFileWatcher { - const dotGitWatcher = fs.watch(location); - const onDotGitFileChangeEmitter = new EventEmitter(); - dotGitWatcher.on('change', (_, e) => onDotGitFileChangeEmitter.fire(Uri.file(join(location, e as string)))); - dotGitWatcher.on('error', err => console.error(err)); + const watcher = workspace.createFileSystemWatcher(new RelativePattern(location, '*')); return new class implements IFileWatcher { - event = onDotGitFileChangeEmitter.event; - dispose() { dotGitWatcher.close(); } + event = anyEvent(watcher.onDidCreate, watcher.onDidChange, watcher.onDidDelete); + dispose() { + watcher.dispose(); + } }; } diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index ff535a1ee3ab1..8c832b81988a7 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -36,6 +36,16 @@ resolved "https://registry.yarnpkg.com/@types/which/-/which-1.0.28.tgz#016e387629b8817bed653fe32eab5d11279c8df6" integrity sha1-AW44dim4gXvtZT/jLqtdESecjfY= +"@vscode/extension-telemetry@0.4.6": + version "0.4.6" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.4.6.tgz#2f4c5bf81adf6b2e4ddba54759355e1559c5476b" + integrity sha512-bDXwHoNXIR1Rc8xdphJ4B3rWdzAGm+FUPk4mJl6/oyZmfEX+QdlDLxnCwlv/vxHU1p11ThHSB8kRhsWZ1CzOqw== + +"@vscode/iconv-lite-umd@0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz#d2f1e0664ee6036408f9743fee264ea0699b0e48" + integrity sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg== + byline@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" @@ -46,11 +56,6 @@ file-type@^7.2.0: resolved "https://registry.yarnpkg.com/file-type/-/file-type-7.2.0.tgz#113cfed52e1d6959ab80248906e2f25a8cdccb74" integrity sha1-ETz+1S4daVmrgCSJBuLyWozcy3Q= -iconv-lite-umd@0.6.10: - version "0.6.10" - resolved "https://registry.yarnpkg.com/iconv-lite-umd/-/iconv-lite-umd-0.6.10.tgz#faec47521e095b8e3a7175ae08e1b4ae0359a735" - integrity sha512-8NtgTa/m1jVq7vdywmD5+SqIlZsB59wtsjaylQuExyCojMq1tHVQxmHjeqVSYwKwnmQbH4mZ1Dxx1eqDkPgaqA== - isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -61,11 +66,6 @@ jschardet@3.0.0: resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882" integrity sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ== -vscode-extension-telemetry@0.4.3: - version "0.4.3" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.4.3.tgz#ea389b3d14b65d4fd5a6cf3760b3155e930193f2" - integrity sha512-opiIFOaAwyfACYMXByDqFMAlJ2iFMJR65/vIogJ960aLZWp9zaMdwY9CsY02EOYjHxPpjI7QeOQM3sYCb3xtJg== - vscode-nls@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.0.0.tgz#4001c8a6caba5cedb23a9c5ce1090395c0e44002" diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index 2dbf469fd2650..a3e43f3c02013 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -58,9 +58,9 @@ "vscode:prepublish": "npm run compile" }, "dependencies": { - "node-fetch": "2.6.1", + "node-fetch": "2.6.7", "uuid": "8.1.0", - "vscode-extension-telemetry": "0.4.3", + "@vscode/extension-telemetry": "0.4.6", "vscode-nls": "^5.0.0", "vscode-tas-client": "^0.1.22" }, diff --git a/extensions/github-authentication/src/common/env.ts b/extensions/github-authentication/src/common/env.ts new file mode 100644 index 0000000000000..b40c249d40a27 --- /dev/null +++ b/extensions/github-authentication/src/common/env.ts @@ -0,0 +1,18 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { Uri } from 'vscode'; + +const VALID_DESKTOP_CALLBACK_SCHEMES = [ + 'vscode', + 'vscode-insiders', + 'code-oss', + 'vscode-wsl', + 'vscode-exploration' +]; + +// This comes from the GitHub Authentication server +export function isSupportedEnvironment(url: Uri): boolean { + return VALID_DESKTOP_CALLBACK_SCHEMES.includes(url.scheme) || url.authority.endsWith('vscode.dev') || url.authority.endsWith('github.dev'); +} diff --git a/extensions/github-authentication/src/common/utils.ts b/extensions/github-authentication/src/common/utils.ts index e2af2e2d3c949..9ffbddec0b8c1 100644 --- a/extensions/github-authentication/src/common/utils.ts +++ b/extensions/github-authentication/src/common/utils.ts @@ -54,7 +54,7 @@ export function promiseFromEvent( let cancel = new EventEmitter(); return { promise: new Promise((resolve, reject) => { - cancel.event(_ => reject()); + cancel.event(_ => reject('Cancelled')); subscription = event((value: T) => { try { Promise.resolve(adapter(value, resolve, reject)) diff --git a/extensions/github-authentication/src/experimentationService.ts b/extensions/github-authentication/src/experimentationService.ts index ccfc19424077c..e3297ddd50ebf 100644 --- a/extensions/github-authentication/src/experimentationService.ts +++ b/extensions/github-authentication/src/experimentationService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import TelemetryReporter from 'vscode-extension-telemetry'; +import TelemetryReporter from '@vscode/extension-telemetry'; import { getExperimentationService, IExperimentationService, IExperimentationTelemetry, TargetPopulation } from 'vscode-tas-client'; export class ExperimentationTelemetry implements IExperimentationTelemetry { diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index 7cee96962f42c..90d3803fffa53 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -9,7 +9,7 @@ import { Keychain } from './common/keychain'; import { GitHubEnterpriseServer, GitHubServer, IGitHubServer } from './githubServer'; import { arrayEquals } from './common/utils'; import { ExperimentationTelemetry } from './experimentationService'; -import TelemetryReporter from 'vscode-extension-telemetry'; +import TelemetryReporter from '@vscode/extension-telemetry'; import { Log } from './common/logger'; interface SessionData { @@ -43,7 +43,11 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid this._telemetryReporter = new ExperimentationTelemetry(context, new TelemetryReporter(name, version, aiKey)); if (this.type === AuthProviderType.github) { - this._githubServer = new GitHubServer(this._logger, this._telemetryReporter); + this._githubServer = new GitHubServer( + // We only can use the Device Code flow when we are running with a remote extension host. + context.extension.extensionKind === vscode.ExtensionKind.Workspace, + this._logger, + this._telemetryReporter); } else { this._githubServer = new GitHubEnterpriseServer(this._logger, this._telemetryReporter); } @@ -68,13 +72,15 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid } async getSessions(scopes?: string[]): Promise { - this._logger.info(`Getting sessions for ${scopes?.join(',') || 'all scopes'}...`); + // For GitHub scope list, order doesn't matter so we immediately sort the scopes + const sortedScopes = scopes?.sort() || []; + this._logger.info(`Getting sessions for ${sortedScopes.length ? sortedScopes.join(',') : 'all scopes'}...`); const sessions = await this._sessionsPromise; - const finalSessions = scopes - ? sessions.filter(session => arrayEquals([...session.scopes].sort(), scopes.sort())) + const finalSessions = sortedScopes.length + ? sessions.filter(session => arrayEquals([...session.scopes].sort(), sortedScopes)) : sessions; - this._logger.info(`Got ${finalSessions.length} sessions for ${scopes?.join(',') || 'all scopes'}...`); + this._logger.info(`Got ${finalSessions.length} sessions for ${sortedScopes?.join(',') ?? 'all scopes'}...`); return finalSessions; } @@ -134,12 +140,20 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid return []; } + // TODO: eventually remove this Set because we should only have one session per set of scopes. + const scopesSeen = new Set(); const sessionPromises = sessionData.map(async (session: SessionData) => { + // For GitHub scope list, order doesn't matter so we immediately sort the scopes + const sortedScopes = session.scopes.sort(); + const scopesStr = sortedScopes.join(' '); + if (scopesSeen.has(scopesStr)) { + return undefined; + } let userInfo: { id: string, accountName: string } | undefined; if (!session.account) { try { userInfo = await this._githubServer.getUserInfo(session.accessToken); - this._logger.info(`Verified session with the following scopes: ${session.scopes}`); + this._logger.info(`Verified session with the following scopes: ${scopesStr}`); } catch (e) { // Remove sessions that return unauthorized response if (e.message === 'Unauthorized') { @@ -150,7 +164,8 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid setTimeout(() => this.afterTokenLoad(session.accessToken), 1000); - this._logger.trace(`Read the following session from the keychain with the following scopes: ${session.scopes}`); + this._logger.trace(`Read the following session from the keychain with the following scopes: ${scopesStr}`); + scopesSeen.add(scopesStr); return { id: session.id, account: { @@ -159,7 +174,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid : userInfo?.accountName ?? '', id: session.account?.id ?? userInfo?.id ?? '' }, - scopes: session.scopes, + scopes: sortedScopes, accessToken: session.accessToken }; }); @@ -186,22 +201,26 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid public async createSession(scopes: string[]): Promise { try { + // For GitHub scope list, order doesn't matter so we immediately sort the scopes + const sortedScopes = scopes.sort(); + /* __GDPR__ "login" : { "scopes": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } } */ this._telemetryReporter?.sendTelemetryEvent('login', { - scopes: JSON.stringify(scopes), + scopes: JSON.stringify(sortedScopes), }); - const scopeString = scopes.join(' '); + + const scopeString = sortedScopes.join(' '); const token = await this._githubServer.login(scopeString); this.afterTokenLoad(token); - const session = await this.tokenToSession(token, scopes); + const session = await this.tokenToSession(token, sortedScopes); const sessions = await this._sessionsPromise; - const sessionIndex = sessions.findIndex(s => s.id === session.id || s.scopes.join(' ') === scopeString); + const sessionIndex = sessions.findIndex(s => s.id === session.id || arrayEquals([...s.scopes].sort(), sortedScopes)); if (sessionIndex > -1) { sessions.splice(sessionIndex, 1, session); } else { @@ -216,7 +235,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid return session; } catch (e) { // If login was cancelled, do not notify user. - if (e === 'Cancelled') { + if (e === 'Cancelled' || e.message === 'Cancelled') { /* __GDPR__ "loginCancelled" : { } */ diff --git a/extensions/github-authentication/src/githubServer.ts b/extensions/github-authentication/src/githubServer.ts index bc7e864d1141c..085e285c690ed 100644 --- a/extensions/github-authentication/src/githubServer.ts +++ b/extensions/github-authentication/src/githubServer.ts @@ -11,8 +11,10 @@ import { PromiseAdapter, promiseFromEvent } from './common/utils'; import { ExperimentationTelemetry } from './experimentationService'; import { AuthProviderType } from './github'; import { Log } from './common/logger'; +import { isSupportedEnvironment } from './common/env'; const localize = nls.loadMessageBundle(); +const CLIENT_ID = '01ab8ac9400c4e429b23'; const NETWORK_ERROR = 'network error'; /** @@ -50,6 +52,13 @@ export interface IGitHubServer extends vscode.Disposable { type: AuthProviderType; } +interface IGitHubDeviceCodeResponse { + device_code: string; + user_code: string; + verification_uri: string; + interval: number; +} + async function getScopes(token: string, serverUri: vscode.Uri, logger: Log): Promise { try { logger.info('Getting token scopes...'); @@ -93,8 +102,18 @@ async function getUserInfo(token: string, serverUri: vscode.Uri, logger: Log): P logger.info('Got account info!'); return { id: json.id, accountName: json.login }; } else { - logger.error(`Getting account info failed: ${result.statusText}`); - throw new Error(result.statusText); + // either display the response message or the http status text + let errorMessage = result.statusText; + try { + const json = await result.json(); + if (json.message) { + errorMessage = json.message; + } + } catch (err) { + // noop + } + logger.error(`Getting account info failed: ${errorMessage}`); + throw new Error(errorMessage); } } @@ -110,7 +129,7 @@ export class GitHubServer implements IGitHubServer { private _disposable: vscode.Disposable; private _uriHandler = new UriEventHandler(this._logger); - constructor(private readonly _logger: Log, private readonly _telemetryReporter: ExperimentationTelemetry) { + constructor(private readonly _supportDeviceCodeFlow: boolean, private readonly _logger: Log, private readonly _telemetryReporter: ExperimentationTelemetry) { this._disposable = vscode.Disposable.from( vscode.commands.registerCommand(this._statusBarCommandId, () => this.manuallyProvideUri()), vscode.window.registerUriHandler(this._uriHandler)); @@ -120,10 +139,6 @@ export class GitHubServer implements IGitHubServer { this._disposable.dispose(); } - private isTestEnvironment(url: vscode.Uri): boolean { - return /\.azurewebsites\.net$/.test(url.authority) || url.authority.startsWith('localhost:'); - } - // TODO@joaomoreno TODO@TylerLeonhardt private async isNoCorsEnvironment(): Promise { const uri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/dummy`)); @@ -135,9 +150,12 @@ export class GitHubServer implements IGitHubServer { const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate`)); - if (this.isTestEnvironment(callbackUri)) { - const token = await vscode.window.showInputBox({ prompt: 'GitHub Personal Access Token', ignoreFocusOut: true }); - if (!token) { throw new Error('Sign in failed: No token provided'); } + if (!isSupportedEnvironment(callbackUri)) { + const token = this._supportDeviceCodeFlow + ? await this.doDeviceCodeFlow(scopes) + : await vscode.window.showInputBox({ prompt: 'GitHub Personal Access Token', ignoreFocusOut: true }); + + if (!token) { throw new Error('No token provided'); } const tokenScopes = await getScopes(token, this.getServerUri('/'), this._logger); // Example: ['repo', 'user'] const scopesList = scopes.split(' '); // Example: 'read:user repo user:email' @@ -192,6 +210,97 @@ export class GitHubServer implements IGitHubServer { }); } + private async doDeviceCodeFlow(scopes: string): Promise { + // Get initial device code + const uri = `https://github.com/login/device/code?client_id=${CLIENT_ID}&scope=${scopes}`; + const result = await fetch(uri, { + method: 'POST', + headers: { + Accept: 'application/json' + } + }); + if (!result.ok) { + throw new Error(`Failed to get one-time code: ${await result.text()}`); + } + + const json = await result.json() as IGitHubDeviceCodeResponse; + + + const modalResult = await vscode.window.showInformationMessage( + localize('code.title', "Your Code: {0}", json.user_code), + { + modal: true, + detail: localize('code.detail', "To finish authenticating, navigate to GitHub and paste in the above one-time code.") + }, 'Copy & Continue to GitHub'); + + if (modalResult !== 'Copy & Continue to GitHub') { + throw new Error('Cancelled'); + } + + await vscode.env.clipboard.writeText(json.user_code); + + const uriToOpen = await vscode.env.asExternalUri(vscode.Uri.parse(json.verification_uri)); + await vscode.env.openExternal(uriToOpen); + + return await vscode.window.withProgress({ + location: vscode.ProgressLocation.Notification, + cancellable: true, + title: localize( + 'progress', + "Open [{0}]({0}) in a new tab and paste your one-time code: {1}", + json.verification_uri, + json.user_code) + }, async (_, token) => { + return await this.waitForDeviceCodeAccessToken(json, token); + }); + } + + private async waitForDeviceCodeAccessToken( + json: IGitHubDeviceCodeResponse, + token: vscode.CancellationToken + ): Promise { + + const refreshTokenUri = `https://github.com/login/oauth/access_token?client_id=${CLIENT_ID}&device_code=${json.device_code}&grant_type=urn:ietf:params:oauth:grant-type:device_code`; + + // Try for 2 minutes + const attempts = 120 / json.interval; + for (let i = 0; i < attempts; i++) { + await new Promise(resolve => setTimeout(resolve, json.interval * 1000)); + if (token.isCancellationRequested) { + throw new Error('Cancelled'); + } + let accessTokenResult; + try { + accessTokenResult = await fetch(refreshTokenUri, { + method: 'POST', + headers: { + Accept: 'application/json' + } + }); + } catch { + continue; + } + + if (!accessTokenResult.ok) { + continue; + } + + const accessTokenJson = await accessTokenResult.json(); + + if (accessTokenJson.error === 'authorization_pending') { + continue; + } + + if (accessTokenJson.error) { + throw new Error(accessTokenJson.error_description); + } + + return accessTokenJson.access_token; + } + + throw new Error('Cancelled'); + } + private exchangeCodeForToken: (scopes: string) => PromiseAdapter = (scopes) => async (uri, resolve, reject) => { const query = parseQuery(uri); diff --git a/extensions/github-authentication/yarn.lock b/extensions/github-authentication/yarn.lock index 3628d956fc84e..f9602e4369f2b 100644 --- a/extensions/github-authentication/yarn.lock +++ b/extensions/github-authentication/yarn.lock @@ -25,6 +25,11 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.0.0.tgz#165aae4819ad2174a17476dbe66feebd549556c0" integrity sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw== +"@vscode/extension-telemetry@0.4.6": + version "0.4.6" + resolved "https://registry.yarnpkg.com/@vscode/extension-telemetry/-/extension-telemetry-0.4.6.tgz#2f4c5bf81adf6b2e4ddba54759355e1559c5476b" + integrity sha512-bDXwHoNXIR1Rc8xdphJ4B3rWdzAGm+FUPk4mJl6/oyZmfEX+QdlDLxnCwlv/vxHU1p11ThHSB8kRhsWZ1CzOqw== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -50,9 +55,9 @@ delayed-stream@~1.0.0: integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= follow-redirects@^1.14.0: - version "1.14.5" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.5.tgz#f09a5848981d3c772b5392309778523f8d85c381" - integrity sha512-wtphSXy7d4/OR+MvIFbCVBDzZ5520qV8XfPklSN5QtxuMUJZ+b0Wnst1e1lCDocfzuCkHqj8k0FpZqO+UIaKNA== + version "1.14.7" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" + integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== form-data@^3.0.0: version "3.0.0" @@ -75,10 +80,12 @@ mime-types@^2.1.12: dependencies: mime-db "1.44.0" -node-fetch@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== +node-fetch@2.6.7: + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" tas-client@0.1.21: version "0.1.21" @@ -87,16 +94,16 @@ tas-client@0.1.21: dependencies: axios "^0.21.1" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + uuid@8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.1.0.tgz#6f1536eb43249f473abc6bd58ff983da1ca30d8d" integrity sha512-CI18flHDznR0lq54xBycOVmphdCYnQLKn8abKn7PXUiKUGdEd+/l9LWNJmugXel4hXq7S+RMNl34ecyC9TntWg== -vscode-extension-telemetry@0.4.3: - version "0.4.3" - resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.4.3.tgz#ea389b3d14b65d4fd5a6cf3760b3155e930193f2" - integrity sha512-opiIFOaAwyfACYMXByDqFMAlJ2iFMJR65/vIogJ960aLZWp9zaMdwY9CsY02EOYjHxPpjI7QeOQM3sYCb3xtJg== - vscode-nls@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" @@ -108,3 +115,16 @@ vscode-tas-client@^0.1.22: integrity sha512-1sYH73nhiSRVQgfZkLQNJW7VzhKM9qNbCe8QyXgiKkLhH4GflDXRPAK4yy4P41jUgula+Fc9G7i5imj1dlKfaw== dependencies: tas-client "0.1.21" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" diff --git a/extensions/github/markdown.css b/extensions/github/markdown.css new file mode 100644 index 0000000000000..e6b118cb8d92f --- /dev/null +++ b/extensions/github/markdown.css @@ -0,0 +1,4 @@ +.vscode-dark img[src$=\#gh-light-mode-only], +.vscode-light img[src$=\#gh-dark-mode-only] { + display: none; +} diff --git a/extensions/github/package.json b/extensions/github/package.json index e21350c32869a..7c0013fd7a8a4 100644 --- a/extensions/github/package.json +++ b/extensions/github/package.json @@ -64,6 +64,9 @@ "contents": "%welcome.publishWorkspaceFolder%", "when": "config.git.enabled && git.state == initialized && workbenchState == workspace && workspaceFolderCount != 0" } + ], + "markdown.previewStyles": [ + "./markdown.css" ] }, "scripts": { diff --git a/extensions/github/src/commands.ts b/extensions/github/src/commands.ts index dad2982533c1c..42ed8198287b0 100644 --- a/extensions/github/src/commands.ts +++ b/extensions/github/src/commands.ts @@ -6,12 +6,12 @@ import * as vscode from 'vscode'; import { API as GitAPI } from './typings/git'; import { publishRepository } from './publish'; -import { combinedDisposable } from './util'; +import { DisposableStore } from './util'; export function registerCommands(gitAPI: GitAPI): vscode.Disposable { - const disposables: vscode.Disposable[] = []; + const disposables = new DisposableStore(); - disposables.push(vscode.commands.registerCommand('github.publish', async () => { + disposables.add(vscode.commands.registerCommand('github.publish', async () => { try { publishRepository(gitAPI); } catch (err) { @@ -19,5 +19,5 @@ export function registerCommands(gitAPI: GitAPI): vscode.Disposable { } })); - return combinedDisposable(disposables); + return disposables; } diff --git a/extensions/github/src/extension.ts b/extensions/github/src/extension.ts index 4e25bad1313e8..2fbe1d597fd76 100644 --- a/extensions/github/src/extension.ts +++ b/extensions/github/src/extension.ts @@ -8,7 +8,7 @@ import { GithubRemoteSourceProvider } from './remoteSourceProvider'; import { GitExtension } from './typings/git'; import { registerCommands } from './commands'; import { GithubCredentialProviderManager } from './credentialProvider'; -import { dispose, combinedDisposable } from './util'; +import { DisposableStore } from './util'; import { GithubPushErrorHandler } from './pushErrorHandler'; import { GitBaseExtension } from './typings/git-base'; import { GithubRemoteSourcePublisher } from './remoteSourcePublisher'; @@ -19,7 +19,7 @@ export function activate(context: ExtensionContext): void { } function initializeGitBaseExtension(): Disposable { - const disposables = new Set(); + const disposables = new DisposableStore(); const initialize = () => { try { @@ -35,8 +35,7 @@ function initializeGitBaseExtension(): Disposable { const onDidChangeGitBaseExtensionEnablement = (enabled: boolean) => { if (!enabled) { - dispose(disposables); - disposables.clear(); + disposables.dispose(); } else { initialize(); } @@ -46,11 +45,11 @@ function initializeGitBaseExtension(): Disposable { disposables.add(gitBaseExtension.onDidChangeEnablement(onDidChangeGitBaseExtensionEnablement)); onDidChangeGitBaseExtensionEnablement(gitBaseExtension.enabled); - return combinedDisposable(disposables); + return disposables; } function initializeGitExtension(): Disposable { - const disposables = new Set(); + const disposables = new DisposableStore(); let gitExtension = extensions.getExtension('vscode.git'); @@ -68,8 +67,7 @@ function initializeGitExtension(): Disposable { commands.executeCommand('setContext', 'git-base.gitEnabled', true); } else { - dispose(disposables); - disposables.clear(); + disposables.dispose(); } }; @@ -81,16 +79,15 @@ function initializeGitExtension(): Disposable { if (gitExtension) { initialize(); } else { - const disposable = extensions.onDidChange(() => { + const listener = extensions.onDidChange(() => { if (!gitExtension && extensions.getExtension('vscode.git')) { gitExtension = extensions.getExtension('vscode.git'); initialize(); - - dispose(disposable); + listener.dispose(); } }); - disposables.add(disposable); + disposables.add(listener); } - return combinedDisposable(disposables); + return disposables; } diff --git a/extensions/github/src/util.ts b/extensions/github/src/util.ts index 60badd28c84b1..c7d23a8230108 100644 --- a/extensions/github/src/util.ts +++ b/extensions/github/src/util.ts @@ -5,20 +5,19 @@ import * as vscode from 'vscode'; -export function dispose(arg: vscode.Disposable | Iterable): void { - if (arg instanceof vscode.Disposable) { - arg.dispose(); - } else { - for (const disposable of arg) { - disposable.dispose(); - } +export class DisposableStore { + + private disposables = new Set(); + + add(disposable: vscode.Disposable): void { + this.disposables.add(disposable); } -} -export function combinedDisposable(disposables: Iterable): vscode.Disposable { - return { - dispose() { - dispose(disposables); + dispose(): void { + for (const disposable of this.disposables) { + disposable.dispose(); } - }; + + this.disposables.clear(); + } } diff --git a/extensions/github/yarn.lock b/extensions/github/yarn.lock index 94f7a26da9d9b..fabc2469c44cc 100644 --- a/extensions/github/yarn.lock +++ b/extensions/github/yarn.lock @@ -125,9 +125,11 @@ is-plain-object@^3.0.0: integrity sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g== node-fetch@^2.3.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== + version "2.6.7" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" + integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== + dependencies: + whatwg-url "^5.0.0" once@^1.4.0: version "1.4.0" @@ -136,6 +138,11 @@ once@^1.4.0: dependencies: wrappy "1" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= + tunnel@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" @@ -151,6 +158,19 @@ vscode-nls@^4.1.2: resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167" integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw== +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" diff --git a/extensions/grunt/package.json b/extensions/grunt/package.json index c784b1ed83a11..7e5c605ec00ff 100644 --- a/extensions/grunt/package.json +++ b/extensions/grunt/package.json @@ -24,7 +24,7 @@ }, "main": "./out/main", "activationEvents": [ - "onCommand:workbench.action.tasks.runTask" + "onTaskType:grunt" ], "capabilities": { "virtualWorkspaces": false, diff --git a/extensions/gulp/package.json b/extensions/gulp/package.json index 21800e96cfb60..995da7fbe5d3a 100644 --- a/extensions/gulp/package.json +++ b/extensions/gulp/package.json @@ -24,7 +24,7 @@ }, "main": "./out/main", "activationEvents": [ - "onCommand:workbench.action.tasks.runTask" + "onTaskType:gulp" ], "capabilities": { "virtualWorkspaces": false, diff --git a/extensions/html-language-features/client/src/tagClosing.ts b/extensions/html-language-features/client/src/autoInsertion.ts similarity index 63% rename from extensions/html-language-features/client/src/tagClosing.ts rename to extensions/html-language-features/client/src/autoInsertion.ts index 0d0ef10f80419..170afa46c0280 100644 --- a/extensions/html-language-features/client/src/tagClosing.ts +++ b/extensions/html-language-features/client/src/autoInsertion.ts @@ -3,15 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { window, workspace, Disposable, TextDocument, Position, SnippetString, TextDocumentChangeEvent, TextDocumentChangeReason } from 'vscode'; +import { window, workspace, Disposable, TextDocument, Position, SnippetString, TextDocumentChangeEvent, TextDocumentChangeReason, TextDocumentContentChangeEvent } from 'vscode'; import { Runtime } from './htmlClient'; -export function activateTagClosing(tagProvider: (document: TextDocument, position: Position) => Thenable, supportedLanguages: { [id: string]: boolean }, configName: string, runtime: Runtime): Disposable { - +export function activateAutoInsertion(provider: (kind: 'autoQuote' | 'autoClose', document: TextDocument, position: Position) => Thenable, supportedLanguages: { [id: string]: boolean }, runtime: Runtime): Disposable { const disposables: Disposable[] = []; workspace.onDidChangeTextDocument(onDidChangeTextDocument, null, disposables); - let isEnabled = false; + let anyIsEnabled = false; + const isEnabled = { + 'autoQuote': false, + 'autoClose': false + }; updateEnabledState(); window.onDidChangeActiveTextEditor(updateEnabledState, null, disposables); @@ -24,7 +27,7 @@ export function activateTagClosing(tagProvider: (document: TextDocument, positio }); function updateEnabledState() { - isEnabled = false; + anyIsEnabled = false; const editor = window.activeTextEditor; if (!editor) { return; @@ -33,14 +36,14 @@ export function activateTagClosing(tagProvider: (document: TextDocument, positio if (!supportedLanguages[document.languageId]) { return; } - if (!workspace.getConfiguration(undefined, document.uri).get(configName)) { - return; - } - isEnabled = true; + const configurations = workspace.getConfiguration(undefined, document.uri); + isEnabled['autoQuote'] = configurations.get('html.autoCreateQuotes') ?? false; + isEnabled['autoClose'] = configurations.get('html.autoClosingTags') ?? false; + anyIsEnabled = isEnabled['autoQuote'] || isEnabled['autoClose']; } function onDidChangeTextDocument({ document, contentChanges, reason }: TextDocumentChangeEvent) { - if (!isEnabled || contentChanges.length === 0 || reason === TextDocumentChangeReason.Undo || reason === TextDocumentChangeReason.Redo) { + if (!anyIsEnabled || contentChanges.length === 0 || reason === TextDocumentChangeReason.Undo || reason === TextDocumentChangeReason.Redo) { return; } const activeDocument = window.activeTextEditor && window.activeTextEditor.document; @@ -53,15 +56,20 @@ export function activateTagClosing(tagProvider: (document: TextDocument, positio const lastChange = contentChanges[contentChanges.length - 1]; const lastCharacter = lastChange.text[lastChange.text.length - 1]; - if (lastChange.rangeLength > 0 || lastCharacter !== '>' && lastCharacter !== '/') { - return; + if (isEnabled['autoQuote'] && lastChange.rangeLength === 0 && lastCharacter === '=') { + doAutoInsert('autoQuote', document, lastChange); + } else if (isEnabled['autoClose'] && lastChange.rangeLength === 0 && (lastCharacter === '>' || lastCharacter === '/')) { + doAutoInsert('autoClose', document, lastChange); } + } + + function doAutoInsert(kind: 'autoQuote' | 'autoClose', document: TextDocument, lastChange: TextDocumentContentChangeEvent) { const rangeStart = lastChange.range.start; const version = document.version; timeout = runtime.timer.setTimeout(() => { const position = new Position(rangeStart.line, rangeStart.character + lastChange.text.length); - tagProvider(document, position).then(text => { - if (text && isEnabled) { + provider(kind, document, position).then(text => { + if (text && isEnabled[kind]) { const activeEditor = window.activeTextEditor; if (activeEditor) { const activeDocument = activeEditor.document; diff --git a/extensions/html-language-features/client/src/customData.ts b/extensions/html-language-features/client/src/customData.ts index ecf964056e541..80e5f2f04d981 100644 --- a/extensions/html-language-features/client/src/customData.ts +++ b/extensions/html-language-features/client/src/customData.ts @@ -4,53 +4,103 @@ *--------------------------------------------------------------------------------------------*/ import { workspace, extensions, Uri, EventEmitter, Disposable } from 'vscode'; -import { resolvePath, joinPath } from './requests'; +import { Runtime } from './htmlClient'; +import { Utils } from 'vscode-uri'; -export function getCustomDataSource(toDispose: Disposable[]) { - let pathsInWorkspace = getCustomDataPathsInAllWorkspaces(); - let pathsInExtensions = getCustomDataPathsFromAllExtensions(); + +export function getCustomDataSource(runtime: Runtime, toDispose: Disposable[]) { + let localExtensionUris = new Set(); + let externalExtensionUris = new Set(); + const workspaceUris = new Set(); + + collectInWorkspaces(workspaceUris); + collectInExtensions(localExtensionUris, externalExtensionUris); const onChange = new EventEmitter(); toDispose.push(extensions.onDidChange(_ => { - const newPathsInExtensions = getCustomDataPathsFromAllExtensions(); - if (newPathsInExtensions.length !== pathsInExtensions.length || !newPathsInExtensions.every((val, idx) => val === pathsInExtensions[idx])) { - pathsInExtensions = newPathsInExtensions; + const newLocalExtensionUris = new Set(); + const newExternalExtensionUris = new Set(); + collectInExtensions(newLocalExtensionUris, newExternalExtensionUris); + if (hasChanges(newLocalExtensionUris, localExtensionUris) || hasChanges(newExternalExtensionUris, externalExtensionUris)) { + localExtensionUris = newLocalExtensionUris; + externalExtensionUris = newExternalExtensionUris; onChange.fire(); } })); toDispose.push(workspace.onDidChangeConfiguration(e => { if (e.affectsConfiguration('html.customData')) { - pathsInWorkspace = getCustomDataPathsInAllWorkspaces(); + workspaceUris.clear(); + collectInWorkspaces(workspaceUris); + onChange.fire(); + } + })); + + toDispose.push(workspace.onDidChangeTextDocument(e => { + const path = e.document.uri.toString(); + if (externalExtensionUris.has(path) || workspaceUris.has(path)) { onChange.fire(); } })); return { get uris() { - return pathsInWorkspace.concat(pathsInExtensions); + return [...localExtensionUris].concat([...externalExtensionUris], [...workspaceUris]); }, get onDidChange() { return onChange.event; + }, + getContent(uriString: string): Thenable { + const uri = Uri.parse(uriString); + if (localExtensionUris.has(uriString)) { + return workspace.fs.readFile(uri).then(buffer => { + return new runtime.TextDecoder().decode(buffer); + }); + } + return workspace.openTextDocument(uri).then(doc => { + return doc.getText(); + }); } }; } +function hasChanges(s1: Set, s2: Set) { + if (s1.size !== s2.size) { + return true; + } + for (const uri of s1) { + if (!s2.has(uri)) { + return true; + } + } + return false; +} + +function isURI(uriOrPath: string) { + return /^(?\w[\w\d+.-]*):/.test(uriOrPath); +} -function getCustomDataPathsInAllWorkspaces(): string[] { + +function collectInWorkspaces(workspaceUris: Set): Set { const workspaceFolders = workspace.workspaceFolders; - const dataPaths: string[] = []; + const dataPaths = new Set(); if (!workspaceFolders) { return dataPaths; } - const collect = (paths: string[] | undefined, rootFolder: Uri) => { - if (Array.isArray(paths)) { - for (const path of paths) { - if (typeof path === 'string') { - dataPaths.push(resolvePath(rootFolder, path).toString()); + const collect = (uriOrPaths: string[] | undefined, rootFolder: Uri) => { + if (Array.isArray(uriOrPaths)) { + for (const uriOrPath of uriOrPaths) { + if (typeof uriOrPath === 'string') { + if (!isURI(uriOrPath)) { + // path in the workspace + workspaceUris.add(Utils.resolvePath(rootFolder, uriOrPath).toString()); + } else { + // external uri + workspaceUris.add(uriOrPath); + } } } } @@ -74,15 +124,20 @@ function getCustomDataPathsInAllWorkspaces(): string[] { return dataPaths; } -function getCustomDataPathsFromAllExtensions(): string[] { - const dataPaths: string[] = []; +function collectInExtensions(localExtensionUris: Set, externalUris: Set): void { for (const extension of extensions.all) { const customData = extension.packageJSON?.contributes?.html?.customData; if (Array.isArray(customData)) { - for (const rp of customData) { - dataPaths.push(joinPath(extension.extensionUri, rp).toString()); + for (const uriOrPath of customData) { + if (!isURI(uriOrPath)) { + // relative path in an extension + localExtensionUris.add(Uri.joinPath(extension.extensionUri, uriOrPath).toString()); + } else { + // external uri + externalUris.add(uriOrPath); + } + } } } - return dataPaths; } diff --git a/extensions/html-language-features/client/src/htmlClient.ts b/extensions/html-language-features/client/src/htmlClient.ts index 42e75a297e5b9..d24c0ec9205be 100644 --- a/extensions/html-language-features/client/src/htmlClient.ts +++ b/extensions/html-language-features/client/src/htmlClient.ts @@ -12,20 +12,40 @@ import { DocumentSemanticTokensProvider, DocumentRangeSemanticTokensProvider, SemanticTokens, window, commands } from 'vscode'; import { - LanguageClientOptions, RequestType, TextDocumentPositionParams, DocumentRangeFormattingParams, - DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, TextDocumentIdentifier, RequestType0, Range as LspRange, NotificationType, CommonLanguageClient + LanguageClientOptions, RequestType, DocumentRangeFormattingParams, + DocumentRangeFormattingRequest, ProvideCompletionItemsSignature, TextDocumentIdentifier, RequestType0, Range as LspRange, Position as LspPosition, NotificationType, CommonLanguageClient } from 'vscode-languageclient'; -import { activateTagClosing } from './tagClosing'; -import { RequestService } from './requests'; +import { FileSystemProvider, serveFileSystemRequests } from './requests'; import { getCustomDataSource } from './customData'; +import { activateAutoInsertion } from './autoInsertion'; namespace CustomDataChangedNotification { export const type: NotificationType = new NotificationType('html/customDataChanged'); } -namespace TagCloseRequest { - export const type: RequestType = new RequestType('html/tag'); +namespace CustomDataContent { + export const type: RequestType = new RequestType('html/customDataContent'); } + +interface AutoInsertParams { + /** + * The auto insert kind + */ + kind: 'autoQuote' | 'autoClose'; + /** + * The text document. + */ + textDocument: TextDocumentIdentifier; + /** + * The position inside the text document. + */ + position: LspPosition; +} + +namespace AutoInsertRequest { + export const type: RequestType = new RequestType('html/autoInsert'); +} + // experimental: semantic tokens interface SemanticTokenParams { textDocument: TextDocumentIdentifier; @@ -56,7 +76,7 @@ export type LanguageClientConstructor = (name: string, description: string, clie export interface Runtime { TextDecoder: { new(encoding?: string): { decode(buffer: ArrayBuffer): string; } }; - fs?: RequestService; + fileFs?: FileSystemProvider; telemetry?: TelemetryReporter; readonly timer: { setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): Disposable; @@ -73,8 +93,6 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua let rangeFormatting: Disposable | undefined = undefined; - const customDataSource = getCustomDataSource(context.subscriptions); - // Options to control the language client let clientOptions: LanguageClientOptions = { documentSelector, @@ -120,16 +138,26 @@ export function startClient(context: ExtensionContext, newLanguageClient: Langua toDispose.push(disposable); client.onReady().then(() => { + toDispose.push(serveFileSystemRequests(client, runtime)); + + const customDataSource = getCustomDataSource(runtime, context.subscriptions); + client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris); customDataSource.onDidChange(() => { client.sendNotification(CustomDataChangedNotification.type, customDataSource.uris); }); + client.onRequest(CustomDataContent.type, customDataSource.getContent); + - let tagRequestor = (document: TextDocument, position: Position) => { - let param = client.code2ProtocolConverter.asTextDocumentPositionParams(document, position); - return client.sendRequest(TagCloseRequest.type, param); + const insertRequestor = (kind: 'autoQuote' | 'autoClose', document: TextDocument, position: Position): Promise => { + let param: AutoInsertParams = { + kind, + textDocument: client.code2ProtocolConverter.asTextDocumentIdentifier(document), + position: client.code2ProtocolConverter.asPosition(position) + }; + return client.sendRequest(AutoInsertRequest.type, param); }; - disposable = activateTagClosing(tagRequestor, { html: true, handlebars: true }, 'html.autoClosingTags', runtime); + let disposable = activateAutoInsertion(insertRequestor, { html: true, handlebars: true }, runtime); toDispose.push(disposable); disposable = client.onTelemetry(e => { diff --git a/extensions/html-language-features/client/src/node/htmlClientMain.ts b/extensions/html-language-features/client/src/node/htmlClientMain.ts index d402ee31e7954..4c7d24e397c2c 100644 --- a/extensions/html-language-features/client/src/node/htmlClientMain.ts +++ b/extensions/html-language-features/client/src/node/htmlClientMain.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getNodeFSRequestService } from './nodeFs'; +import { getNodeFileFS } from './nodeFs'; import { Disposable, ExtensionContext } from 'vscode'; import { startClient, LanguageClientConstructor } from '../htmlClient'; import { ServerOptions, TransportKind, LanguageClientOptions, LanguageClient } from 'vscode-languageclient/node'; import { TextDecoder } from 'util'; import * as fs from 'fs'; -import TelemetryReporter from 'vscode-extension-telemetry'; +import TelemetryReporter from '@vscode/extension-telemetry'; let telemetry: TelemetryReporter | undefined; @@ -44,7 +44,7 @@ export function activate(context: ExtensionContext) { } }; - startClient(context, newLanguageClient, { fs: getNodeFSRequestService(), TextDecoder, telemetry, timer }); + startClient(context, newLanguageClient, { fileFs: getNodeFileFS(), TextDecoder, telemetry, timer }); } interface IPackageInfo { diff --git a/extensions/html-language-features/client/src/node/nodeFs.ts b/extensions/html-language-features/client/src/node/nodeFs.ts index c13ef2e1c08d5..46a3aeb9de459 100644 --- a/extensions/html-language-features/client/src/node/nodeFs.ts +++ b/extensions/html-language-features/client/src/node/nodeFs.ts @@ -5,28 +5,15 @@ import * as fs from 'fs'; import { Uri } from 'vscode'; -import { getScheme, RequestService, FileType } from '../requests'; +import { FileSystemProvider, FileType } from '../requests'; -export function getNodeFSRequestService(): RequestService { +export function getNodeFileFS(): FileSystemProvider { function ensureFileUri(location: string) { - if (getScheme(location) !== 'file') { + if (!location.startsWith('file:')) { throw new Error('fileRequestService can only handle file URLs'); } } return { - getContent(location: string, encoding?: string) { - ensureFileUri(location); - return new Promise((c, e) => { - const uri = Uri.parse(location); - fs.readFile(uri.fsPath, encoding, (err, buf) => { - if (err) { - return e(err); - } - c(buf.toString()); - - }); - }); - }, stat(location: string) { ensureFileUri(location); return new Promise((c, e) => { diff --git a/extensions/html-language-features/client/src/requests.ts b/extensions/html-language-features/client/src/requests.ts index f127c88562ff2..ba124e28cd7cf 100644 --- a/extensions/html-language-features/client/src/requests.ts +++ b/extensions/html-language-features/client/src/requests.ts @@ -3,13 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Uri, workspace } from 'vscode'; +import { Uri, workspace, Disposable } from 'vscode'; import { RequestType, CommonLanguageClient } from 'vscode-languageclient'; import { Runtime } from './htmlClient'; -export namespace FsContentRequest { - export const type: RequestType<{ uri: string; encoding?: string; }, string, any> = new RequestType('fs/content'); -} export namespace FsStatRequest { export const type: RequestType = new RequestType('fs/stat'); } @@ -18,30 +15,23 @@ export namespace FsReadDirRequest { export const type: RequestType = new RequestType('fs/readDir'); } -export function serveFileSystemRequests(client: CommonLanguageClient, runtime: Runtime) { - client.onRequest(FsContentRequest.type, (param: { uri: string; encoding?: string; }) => { - const uri = Uri.parse(param.uri); - if (uri.scheme === 'file' && runtime.fs) { - return runtime.fs.getContent(param.uri); - } - return workspace.fs.readFile(uri).then(buffer => { - return new runtime.TextDecoder(param.encoding).decode(buffer); - }); - }); - client.onRequest(FsReadDirRequest.type, (uriString: string) => { +export function serveFileSystemRequests(client: CommonLanguageClient, runtime: Runtime): Disposable { + const disposables = []; + disposables.push(client.onRequest(FsReadDirRequest.type, (uriString: string) => { const uri = Uri.parse(uriString); - if (uri.scheme === 'file' && runtime.fs) { - return runtime.fs.readDirectory(uriString); + if (uri.scheme === 'file' && runtime.fileFs) { + return runtime.fileFs.readDirectory(uriString); } return workspace.fs.readDirectory(uri); - }); - client.onRequest(FsStatRequest.type, (uriString: string) => { + })); + disposables.push(client.onRequest(FsStatRequest.type, (uriString: string) => { const uri = Uri.parse(uriString); - if (uri.scheme === 'file' && runtime.fs) { - return runtime.fs.stat(uriString); + if (uri.scheme === 'file' && runtime.fileFs) { + return runtime.fileFs.stat(uriString); } return workspace.fs.stat(uri); - }); + })); + return Disposable.from(...disposables); } export enum FileType { @@ -82,67 +72,7 @@ export interface FileStat { size: number; } -export interface RequestService { - getContent(uri: string, encoding?: string): Promise; - +export interface FileSystemProvider { stat(uri: string): Promise; readDirectory(uri: string): Promise<[string, FileType][]>; } - -export function getScheme(uri: string) { - return uri.substr(0, uri.indexOf(':')); -} - -export function dirname(uri: string) { - const lastIndexOfSlash = uri.lastIndexOf('/'); - return lastIndexOfSlash !== -1 ? uri.substr(0, lastIndexOfSlash) : ''; -} - -export function basename(uri: string) { - const lastIndexOfSlash = uri.lastIndexOf('/'); - return uri.substr(lastIndexOfSlash + 1); -} - -const Slash = '/'.charCodeAt(0); -const Dot = '.'.charCodeAt(0); - -export function isAbsolutePath(path: string) { - return path.charCodeAt(0) === Slash; -} - -export function resolvePath(uri: Uri, path: string): Uri { - if (isAbsolutePath(path)) { - return uri.with({ path: normalizePath(path.split('/')) }); - } - return joinPath(uri, path); -} - -export function normalizePath(parts: string[]): string { - const newParts: string[] = []; - for (const part of parts) { - if (part.length === 0 || part.length === 1 && part.charCodeAt(0) === Dot) { - // ignore - } else if (part.length === 2 && part.charCodeAt(0) === Dot && part.charCodeAt(1) === Dot) { - newParts.pop(); - } else { - newParts.push(part); - } - } - if (parts.length > 1 && parts[parts.length - 1].length === 0) { - newParts.push(''); - } - let res = newParts.join('/'); - if (parts[0].length === 0) { - res = '/' + res; - } - return res; -} - - -export function joinPath(uri: Uri, ...paths: string[]): Uri { - const parts = uri.path.split('/'); - for (let path of paths) { - parts.push(...path.split('/')); - } - return uri.with({ path: normalizePath(parts) }); -} diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 07bbfe29e9abd..5f348e1128661 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -197,6 +197,12 @@ "default": true, "description": "%html.validate.styles%" }, + "html.autoCreateQuotes": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%html.autoCreateQuotes%" + }, "html.autoClosingTags": { "type": "boolean", "scope": "resource", @@ -255,9 +261,10 @@ ] }, "dependencies": { - "vscode-extension-telemetry": "0.4.3", + "@vscode/extension-telemetry": "0.4.6", "vscode-languageclient": "^7.0.0", - "vscode-nls": "^5.0.0" + "vscode-nls": "^5.0.0", + "vscode-uri": "^3.0.3" }, "devDependencies": { "@types/node": "14.x" diff --git a/extensions/html-language-features/package.nls.json b/extensions/html-language-features/package.nls.json index 00a218de43ed2..702ba05054148 100644 --- a/extensions/html-language-features/package.nls.json +++ b/extensions/html-language-features/package.nls.json @@ -27,6 +27,7 @@ "html.trace.server.desc": "Traces the communication between VS Code and the HTML language server.", "html.validate.scripts": "Controls whether the built-in HTML language support validates embedded scripts.", "html.validate.styles": "Controls whether the built-in HTML language support validates embedded styles.", + "html.autoCreateQuotes": "Enable/disable auto creation of quotes for HTML attribute assignment.", "html.autoClosingTags": "Enable/disable autoclosing of HTML tags.", "html.completion.attributeDefaultValue": "Controls the default value for attributes when completion is accepted.", "html.completion.attributeDefaultValue.doublequotes": "Attribute value is set to \"\".", diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 48f4c0f6259d9..35aedc3f1041d 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -9,12 +9,12 @@ }, "main": "./out/node/htmlServerMain", "dependencies": { - "vscode-css-languageservice": "^5.1.8", - "vscode-html-languageservice": "^4.1.1", + "vscode-css-languageservice": "^5.1.12", + "vscode-html-languageservice": "^4.2.1", "vscode-languageserver": "^7.0.0", - "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-textdocument": "^1.0.3", "vscode-nls": "^5.0.0", - "vscode-uri": "^3.0.2" + "vscode-uri": "^3.0.3" }, "devDependencies": { "@types/mocha": "^8.2.0", diff --git a/extensions/html-language-features/server/src/customData.ts b/extensions/html-language-features/server/src/customData.ts index 3ef347a23d2ef..08e40bf2db3ee 100644 --- a/extensions/html-language-features/server/src/customData.ts +++ b/extensions/html-language-features/server/src/customData.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { IHTMLDataProvider, newHTMLDataProvider } from 'vscode-html-languageservice'; -import { RequestService } from './requests'; +import { CustomDataRequestService } from './htmlServer'; -export function fetchHTMLDataProviders(dataPaths: string[], requestService: RequestService): Promise { +export function fetchHTMLDataProviders(dataPaths: string[], requestService: CustomDataRequestService): Promise { const providers = dataPaths.map(async p => { try { const content = await requestService.getContent(p); diff --git a/extensions/html-language-features/server/src/htmlServer.ts b/extensions/html-language-features/server/src/htmlServer.ts index 425e73c806aa2..033ed22c5645f 100644 --- a/extensions/html-language-features/server/src/htmlServer.ts +++ b/extensions/html-language-features/server/src/htmlServer.ts @@ -5,7 +5,7 @@ import { Connection, TextDocuments, InitializeParams, InitializeResult, RequestType, - DocumentRangeFormattingRequest, Disposable, TextDocumentPositionParams, ServerCapabilities, + DocumentRangeFormattingRequest, Disposable, ServerCapabilities, ConfigurationRequest, ConfigurationParams, DidChangeWorkspaceFoldersNotification, DocumentColorRequest, ColorPresentationRequest, TextDocumentSyncKind, NotificationType, RequestType0, DocumentFormattingRequest, FormattingOptions, TextEdit } from 'vscode-languageserver'; @@ -24,14 +24,33 @@ import { getFoldingRanges } from './modes/htmlFolding'; import { fetchHTMLDataProviders } from './customData'; import { getSelectionRanges } from './modes/selectionRanges'; import { SemanticTokenProvider, newSemanticTokenProvider } from './modes/semanticTokens'; -import { RequestService, getRequestService } from './requests'; +import { FileSystemProvider, getFileSystemProvider } from './requests'; namespace CustomDataChangedNotification { export const type: NotificationType = new NotificationType('html/customDataChanged'); } -namespace TagCloseRequest { - export const type: RequestType = new RequestType('html/tag'); +namespace CustomDataContent { + export const type: RequestType = new RequestType('html/customDataContent'); +} + +interface AutoInsertParams { + /** + * The auto insert kind + */ + kind: 'autoQuote' | 'autoClose'; + /** + * The text document. + */ + textDocument: TextDocumentIdentifier; + /** + * The position inside the text document. + */ + position: Position; +} + +namespace AutoInsertRequest { + export const type: RequestType = new RequestType('html/autoInsert'); } // experimental: semantic tokens @@ -47,8 +66,7 @@ namespace SemanticTokenLegendRequest { } export interface RuntimeEnvironment { - file?: RequestService; - http?: RequestService + fileFs?: FileSystemProvider; configureHttpRequests?(proxy: string, strictSSL: boolean): void; readonly timer: { setImmediate(callback: (...args: any[]) => void, ...args: any[]): Disposable; @@ -56,6 +74,12 @@ export interface RuntimeEnvironment { } } + +export interface CustomDataRequestService { + getContent(uri: string): Promise; +} + + export function startServer(connection: Connection, runtime: RuntimeEnvironment) { // Create a text document manager. @@ -74,10 +98,11 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) let workspaceFoldersSupport = false; let foldingRangeLimit = Number.MAX_VALUE; - const notReady = () => Promise.reject('Not Ready'); - let requestService: RequestService = { getContent: notReady, stat: notReady, readDirectory: notReady }; - - + const customDataRequestService: CustomDataRequestService = { + getContent(uri: string) { + return connection.sendRequest(CustomDataContent.type, uri); + } + }; let globalSettings: Settings = {}; let documentSettings: { [key: string]: Thenable } = {}; @@ -113,17 +138,19 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) } } - requestService = getRequestService(initializationOptions?.handledSchemas || ['file'], connection, runtime); + const handledSchemas = initializationOptions?.handledSchemas as string[] ?? ['file']; + + const fileSystemProvider = getFileSystemProvider(handledSchemas, connection, runtime); const workspace = { get settings() { return globalSettings; }, get folders() { return workspaceFolders; } }; - languageModes = getLanguageModes(initializationOptions?.embeddedLanguages || { css: true, javascript: true }, workspace, params.capabilities, requestService); + languageModes = getLanguageModes(initializationOptions?.embeddedLanguages || { css: true, javascript: true }, workspace, params.capabilities, fileSystemProvider); const dataPaths: string[] = initializationOptions?.dataPaths || []; - fetchHTMLDataProviders(dataPaths, requestService).then(dataProviders => { + fetchHTMLDataProviders(dataPaths, customDataRequestService).then(dataProviders => { languageModes.updateDataProviders(dataProviders); }); @@ -471,20 +498,20 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }, [], `Error while computing color presentations for ${params.textDocument.uri}`, token); }); - connection.onRequest(TagCloseRequest.type, (params, token) => { + connection.onRequest(AutoInsertRequest.type, (params, token) => { return runSafe(runtime, async () => { const document = documents.get(params.textDocument.uri); if (document) { const pos = params.position; if (pos.character > 0) { const mode = languageModes.getModeAtPosition(document, Position.create(pos.line, pos.character - 1)); - if (mode && mode.doAutoClose) { - return mode.doAutoClose(document, pos); + if (mode && mode.doAutoInsert) { + return mode.doAutoInsert(document, pos, params.kind); } } } return null; - }, null, `Error while computing tag close actions for ${params.textDocument.uri}`, token); + }, null, `Error while computing auto insert actions for ${params.textDocument.uri}`, token); }); connection.onFoldingRanges((params, token) => { @@ -567,7 +594,7 @@ export function startServer(connection: Connection, runtime: RuntimeEnvironment) }); connection.onNotification(CustomDataChangedNotification.type, dataPaths => { - fetchHTMLDataProviders(dataPaths, requestService).then(dataProviders => { + fetchHTMLDataProviders(dataPaths, customDataRequestService).then(dataProviders => { languageModes.updateDataProviders(dataProviders); }); }); diff --git a/extensions/html-language-features/server/src/modes/htmlMode.ts b/extensions/html-language-features/server/src/modes/htmlMode.ts index 9c75531b3d07c..04c37a2b491dc 100644 --- a/extensions/html-language-features/server/src/modes/htmlMode.ts +++ b/extensions/html-language-features/server/src/modes/htmlMode.ts @@ -55,11 +55,21 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: async getFoldingRanges(document: TextDocument): Promise { return htmlLanguageService.getFoldingRanges(document); }, - async doAutoClose(document: TextDocument, position: Position) { + async doAutoInsert(document: TextDocument, position: Position, kind: 'autoQuote' | 'autoClose', settings = workspace.settings) { const offset = document.offsetAt(position); const text = document.getText(); - if (offset > 0 && text.charAt(offset - 1).match(/[>\/]/g)) { - return htmlLanguageService.doTagComplete(document, position, htmlDocuments.get(document)); + if (kind === 'autoQuote') { + if (offset > 0 && text.charAt(offset - 1) === '=') { + const htmlSettings = settings?.html; + const options = merge(htmlSettings?.suggest, {}); + options.attributeDefaultValue = htmlSettings?.completion?.attributeDefaultValue ?? 'doublequotes'; + + return htmlLanguageService.doQuoteComplete(document, position, htmlDocuments.get(document), options); + } + } else if (kind === 'autoClose') { + if (offset > 0 && text.charAt(offset - 1).match(/[>\/]/g)) { + return htmlLanguageService.doTagComplete(document, position, htmlDocuments.get(document)); + } } return null; }, diff --git a/extensions/html-language-features/server/src/modes/javascriptMode.ts b/extensions/html-language-features/server/src/modes/javascriptMode.ts index e6571c5bac0ae..85b9a8ae2e058 100644 --- a/extensions/html-language-features/server/src/modes/javascriptMode.ts +++ b/extensions/html-language-features/server/src/modes/javascriptMode.ts @@ -387,83 +387,152 @@ function convertRange(document: TextDocument, span: { start: number | undefined, function convertKind(kind: string): CompletionItemKind { switch (kind) { - case 'primitive type': - case 'keyword': + case Kind.primitiveType: + case Kind.keyword: return CompletionItemKind.Keyword; - case 'var': - case 'local var': + + case Kind.const: + case Kind.let: + case Kind.variable: + case Kind.localVariable: + case Kind.alias: + case Kind.parameter: return CompletionItemKind.Variable; - case 'property': - case 'getter': - case 'setter': + + case Kind.memberVariable: + case Kind.memberGetAccessor: + case Kind.memberSetAccessor: return CompletionItemKind.Field; - case 'function': - case 'method': - case 'construct': - case 'call': - case 'index': + + case Kind.function: + case Kind.localFunction: return CompletionItemKind.Function; - case 'enum': + + case Kind.method: + case Kind.constructSignature: + case Kind.callSignature: + case Kind.indexSignature: + return CompletionItemKind.Method; + + case Kind.enum: return CompletionItemKind.Enum; - case 'module': + + case Kind.enumMember: + return CompletionItemKind.EnumMember; + + case Kind.module: + case Kind.externalModuleName: return CompletionItemKind.Module; - case 'class': + + case Kind.class: + case Kind.type: return CompletionItemKind.Class; - case 'interface': + + case Kind.interface: return CompletionItemKind.Interface; - case 'warning': + + case Kind.warning: + return CompletionItemKind.Text; + + case Kind.script: return CompletionItemKind.File; - } - return CompletionItemKind.Property; + case Kind.directory: + return CompletionItemKind.Folder; + + case Kind.string: + return CompletionItemKind.Constant; + + default: + return CompletionItemKind.Property; + } +} +const enum Kind { + alias = 'alias', + callSignature = 'call', + class = 'class', + const = 'const', + constructorImplementation = 'constructor', + constructSignature = 'construct', + directory = 'directory', + enum = 'enum', + enumMember = 'enum member', + externalModuleName = 'external module name', + function = 'function', + indexSignature = 'index', + interface = 'interface', + keyword = 'keyword', + let = 'let', + localFunction = 'local function', + localVariable = 'local var', + method = 'method', + memberGetAccessor = 'getter', + memberSetAccessor = 'setter', + memberVariable = 'property', + module = 'module', + primitiveType = 'primitive type', + script = 'script', + type = 'type', + variable = 'var', + warning = 'warning', + string = 'string', + parameter = 'parameter', + typeParameter = 'type parameter' } function convertSymbolKind(kind: string): SymbolKind { switch (kind) { - case 'var': - case 'local var': - case 'const': - return SymbolKind.Variable; - case 'function': - case 'local function': - return SymbolKind.Function; - case 'enum': - return SymbolKind.Enum; - case 'module': - return SymbolKind.Module; - case 'class': - return SymbolKind.Class; - case 'interface': - return SymbolKind.Interface; - case 'method': - return SymbolKind.Method; - case 'property': - case 'getter': - case 'setter': - return SymbolKind.Property; + case Kind.module: return SymbolKind.Module; + case Kind.class: return SymbolKind.Class; + case Kind.enum: return SymbolKind.Enum; + case Kind.enumMember: return SymbolKind.EnumMember; + case Kind.interface: return SymbolKind.Interface; + case Kind.indexSignature: return SymbolKind.Method; + case Kind.callSignature: return SymbolKind.Method; + case Kind.method: return SymbolKind.Method; + case Kind.memberVariable: return SymbolKind.Property; + case Kind.memberGetAccessor: return SymbolKind.Property; + case Kind.memberSetAccessor: return SymbolKind.Property; + case Kind.variable: return SymbolKind.Variable; + case Kind.let: return SymbolKind.Variable; + case Kind.const: return SymbolKind.Variable; + case Kind.localVariable: return SymbolKind.Variable; + case Kind.alias: return SymbolKind.Variable; + case Kind.function: return SymbolKind.Function; + case Kind.localFunction: return SymbolKind.Function; + case Kind.constructSignature: return SymbolKind.Constructor; + case Kind.constructorImplementation: return SymbolKind.Constructor; + case Kind.typeParameter: return SymbolKind.TypeParameter; + case Kind.string: return SymbolKind.String; + default: return SymbolKind.Variable; } - return SymbolKind.Variable; } -function convertOptions(options: FormattingOptions, formatSettings: any, initialIndentLevel: number): ts.FormatCodeOptions { +function convertOptions(options: FormattingOptions, formatSettings: any, initialIndentLevel: number): ts.FormatCodeSettings { return { - ConvertTabsToSpaces: options.insertSpaces, - TabSize: options.tabSize, - IndentSize: options.tabSize, - IndentStyle: ts.IndentStyle.Smart, - NewLineCharacter: '\n', - BaseIndentSize: options.tabSize * initialIndentLevel, - InsertSpaceAfterCommaDelimiter: Boolean(!formatSettings || formatSettings.insertSpaceAfterCommaDelimiter), - InsertSpaceAfterSemicolonInForStatements: Boolean(!formatSettings || formatSettings.insertSpaceAfterSemicolonInForStatements), - InsertSpaceBeforeAndAfterBinaryOperators: Boolean(!formatSettings || formatSettings.insertSpaceBeforeAndAfterBinaryOperators), - InsertSpaceAfterKeywordsInControlFlowStatements: Boolean(!formatSettings || formatSettings.insertSpaceAfterKeywordsInControlFlowStatements), - InsertSpaceAfterFunctionKeywordForAnonymousFunctions: Boolean(!formatSettings || formatSettings.insertSpaceAfterFunctionKeywordForAnonymousFunctions), - InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: Boolean(formatSettings && formatSettings.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis), - InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: Boolean(formatSettings && formatSettings.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets), - InsertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: Boolean(formatSettings && formatSettings.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces), - InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: Boolean(formatSettings && formatSettings.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces), - PlaceOpenBraceOnNewLineForControlBlocks: Boolean(formatSettings && formatSettings.placeOpenBraceOnNewLineForFunctions), - PlaceOpenBraceOnNewLineForFunctions: Boolean(formatSettings && formatSettings.placeOpenBraceOnNewLineForControlBlocks) + convertTabsToSpaces: options.insertSpaces, + tabSize: options.tabSize, + indentSize: options.tabSize, + indentStyle: ts.IndentStyle.Smart, + newLineCharacter: '\n', + baseIndentSize: options.tabSize * initialIndentLevel, + insertSpaceAfterCommaDelimiter: Boolean(!formatSettings || formatSettings.insertSpaceAfterCommaDelimiter), + insertSpaceAfterConstructor: Boolean(formatSettings && formatSettings.insertSpaceAfterConstructor), + insertSpaceAfterSemicolonInForStatements: Boolean(!formatSettings || formatSettings.insertSpaceAfterSemicolonInForStatements), + insertSpaceBeforeAndAfterBinaryOperators: Boolean(!formatSettings || formatSettings.insertSpaceBeforeAndAfterBinaryOperators), + insertSpaceAfterKeywordsInControlFlowStatements: Boolean(!formatSettings || formatSettings.insertSpaceAfterKeywordsInControlFlowStatements), + insertSpaceAfterFunctionKeywordForAnonymousFunctions: Boolean(!formatSettings || formatSettings.insertSpaceAfterFunctionKeywordForAnonymousFunctions), + insertSpaceBeforeFunctionParenthesis: Boolean(formatSettings && formatSettings.insertSpaceBeforeFunctionParenthesis), + insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: Boolean(formatSettings && formatSettings.insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis), + insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: Boolean(formatSettings && formatSettings.insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets), + insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: Boolean(formatSettings && formatSettings.insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces), + insertSpaceAfterOpeningAndBeforeClosingEmptyBraces: Boolean(!formatSettings || formatSettings.insertSpaceAfterOpeningAndBeforeClosingEmptyBraces), + insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: Boolean(formatSettings && formatSettings.insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces), + insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: Boolean(formatSettings && formatSettings.insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces), + insertSpaceAfterTypeAssertion: Boolean(formatSettings && formatSettings.insertSpaceAfterTypeAssertion), + placeOpenBraceOnNewLineForControlBlocks: Boolean(formatSettings && formatSettings.placeOpenBraceOnNewLineForFunctions), + placeOpenBraceOnNewLineForFunctions: Boolean(formatSettings && formatSettings.placeOpenBraceOnNewLineForControlBlocks), + semicolons: formatSettings?.semicolons }; } diff --git a/extensions/html-language-features/server/src/modes/languageModes.ts b/extensions/html-language-features/server/src/modes/languageModes.ts index eda0dd8376081..7709256121ce8 100644 --- a/extensions/html-language-features/server/src/modes/languageModes.ts +++ b/extensions/html-language-features/server/src/modes/languageModes.ts @@ -21,7 +21,7 @@ import { getCSSMode } from './cssMode'; import { getDocumentRegions, HTMLDocumentRegions } from './embeddedSupport'; import { getHTMLMode } from './htmlMode'; import { getJavaScriptMode } from './javascriptMode'; -import { RequestService } from '../requests'; +import { FileSystemProvider } from '../requests'; export { WorkspaceFolder, CompletionItem, CompletionList, CompletionItemKind, Definition, Diagnostic, DocumentHighlight, DocumentHighlightKind, @@ -72,7 +72,7 @@ export interface LanguageMode { format?: (document: TextDocument, range: Range, options: FormattingOptions, settings?: Settings) => Promise; findDocumentColors?: (document: TextDocument) => Promise; getColorPresentations?: (document: TextDocument, color: Color, range: Range) => Promise; - doAutoClose?: (document: TextDocument, position: Position) => Promise; + doAutoInsert?: (document: TextDocument, position: Position, kind: 'autoClose' | 'autoQuote') => Promise; findMatchingTagPosition?: (document: TextDocument, position: Position) => Promise; getFoldingRanges?: (document: TextDocument) => Promise; onDocumentRemoved(document: TextDocument): void; @@ -97,7 +97,7 @@ export interface LanguageModeRange extends Range { attributeValue?: boolean; } -export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }, workspace: Workspace, clientCapabilities: ClientCapabilities, requestService: RequestService): LanguageModes { +export function getLanguageModes(supportedLanguages: { [languageId: string]: boolean; }, workspace: Workspace, clientCapabilities: ClientCapabilities, requestService: FileSystemProvider): LanguageModes { const htmlLanguageService = getHTMLLanguageService({ clientCapabilities, fileSystemProvider: requestService }); const cssLanguageService = getCSSLanguageService({ clientCapabilities, fileSystemProvider: requestService }); diff --git a/extensions/html-language-features/server/src/node/htmlServerMain.ts b/extensions/html-language-features/server/src/node/htmlServerMain.ts index faf82b3c40c13..0367e11a2209f 100644 --- a/extensions/html-language-features/server/src/node/htmlServerMain.ts +++ b/extensions/html-language-features/server/src/node/htmlServerMain.ts @@ -6,7 +6,7 @@ import { createConnection, Connection, Disposable } from 'vscode-languageserver/node'; import { formatError } from '../utils/runner'; import { RuntimeEnvironment, startServer } from '../htmlServer'; -import { getNodeFSRequestService } from './nodeFs'; +import { getNodeFileFS } from './nodeFs'; // Create a connection for the server. @@ -30,7 +30,7 @@ const runtime: RuntimeEnvironment = { return { dispose: () => clearTimeout(handle) }; } }, - file: getNodeFSRequestService() + fileFs: getNodeFileFS() }; startServer(connection, runtime); diff --git a/extensions/html-language-features/server/src/node/nodeFs.ts b/extensions/html-language-features/server/src/node/nodeFs.ts index c7b1301296d4a..9bab4cf2913fd 100644 --- a/extensions/html-language-features/server/src/node/nodeFs.ts +++ b/extensions/html-language-features/server/src/node/nodeFs.ts @@ -3,32 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { RequestService, getScheme } from '../requests'; +import { FileSystemProvider, getScheme } from '../requests'; import { URI as Uri } from 'vscode-uri'; import * as fs from 'fs'; import { FileType } from 'vscode-css-languageservice'; -export function getNodeFSRequestService(): RequestService { +export function getNodeFileFS(): FileSystemProvider { function ensureFileUri(location: string) { if (getScheme(location) !== 'file') { - throw new Error('fileRequestService can only handle file URLs'); + throw new Error('fileSystemProvider can only handle file URLs'); } } return { - getContent(location: string, encoding?: string) { - ensureFileUri(location); - return new Promise((c, e) => { - const uri = Uri.parse(location); - fs.readFile(uri.fsPath, encoding, (err, buf) => { - if (err) { - return e(err); - } - c(buf.toString()); - - }); - }); - }, stat(location: string) { ensureFileUri(location); return new Promise((c, e) => { diff --git a/extensions/html-language-features/server/src/requests.ts b/extensions/html-language-features/server/src/requests.ts index 8d741faebf373..9ece22ac66e96 100644 --- a/extensions/html-language-features/server/src/requests.ts +++ b/extensions/html-language-features/server/src/requests.ts @@ -7,9 +7,6 @@ import { URI } from 'vscode-uri'; import { RequestType, Connection } from 'vscode-languageserver'; import { RuntimeEnvironment } from './htmlServer'; -export namespace FsContentRequest { - export const type: RequestType<{ uri: string; encoding?: string; }, string, any> = new RequestType('fs/content'); -} export namespace FsStatRequest { export const type: RequestType = new RequestType('fs/stat'); } @@ -56,45 +53,27 @@ export interface FileStat { size: number; } -export interface RequestService { - getContent(uri: string, encoding?: string): Promise; - +export interface FileSystemProvider { stat(uri: string): Promise; readDirectory(uri: string): Promise<[string, FileType][]>; } -export function getRequestService(handledSchemas: string[], connection: Connection, runtime: RuntimeEnvironment): RequestService { - const builtInHandlers: { [protocol: string]: RequestService | undefined } = {}; - for (let protocol of handledSchemas) { - if (protocol === 'file') { - builtInHandlers[protocol] = runtime.file; - } else if (protocol === 'http' || protocol === 'https') { - builtInHandlers[protocol] = runtime.http; - } - } +export function getFileSystemProvider(handledSchemas: string[], connection: Connection, runtime: RuntimeEnvironment): FileSystemProvider { + const fileFs = runtime.fileFs && handledSchemas.indexOf('file') !== -1 ? runtime.fileFs : undefined; return { async stat(uri: string): Promise { - const handler = builtInHandlers[getScheme(uri)]; - if (handler) { - return handler.stat(uri); + if (fileFs && uri.startsWith('file:')) { + return fileFs.stat(uri); } const res = await connection.sendRequest(FsStatRequest.type, uri.toString()); return res; }, readDirectory(uri: string): Promise<[string, FileType][]> { - const handler = builtInHandlers[getScheme(uri)]; - if (handler) { - return handler.readDirectory(uri); + if (fileFs && uri.startsWith('file:')) { + return fileFs.readDirectory(uri); } return connection.sendRequest(FsReadDirRequest.type, uri.toString()); - }, - getContent(uri: string, encoding?: string): Promise { - const handler = builtInHandlers[getScheme(uri)]; - if (handler) { - return handler.getContent(uri, encoding); - } - return connection.sendRequest(FsContentRequest.type, { uri: uri.toString(), encoding }); } }; } diff --git a/extensions/html-language-features/server/src/test/completions.test.ts b/extensions/html-language-features/server/src/test/completions.test.ts index aaf3cb5ec6b84..b64d7dee8604c 100644 --- a/extensions/html-language-features/server/src/test/completions.test.ts +++ b/extensions/html-language-features/server/src/test/completions.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import * as path from 'path'; import { URI } from 'vscode-uri'; import { getLanguageModes, WorkspaceFolder, TextDocument, CompletionList, CompletionItemKind, ClientCapabilities, TextEdit } from '../modes/languageModes'; -import { getNodeFSRequestService } from '../node/nodeFs'; +import { getNodeFileFS } from '../node/nodeFs'; import { getDocumentContext } from '../utils/documentContext'; export interface ItemDescription { label: string; @@ -59,7 +59,7 @@ export async function testCompletionFor(value: string, expected: { count?: numbe let position = document.positionAt(offset); const context = getDocumentContext(uri, workspace.folders); - const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFSRequestService()); + const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFileFS()); const mode = languageModes.getModeAtPosition(document, position)!; let list = await mode.doComplete!(document, position, context); diff --git a/extensions/html-language-features/server/src/test/folding.test.ts b/extensions/html-language-features/server/src/test/folding.test.ts index 7bf90bb10d544..44aaea9026cf5 100644 --- a/extensions/html-language-features/server/src/test/folding.test.ts +++ b/extensions/html-language-features/server/src/test/folding.test.ts @@ -8,7 +8,7 @@ import * as assert from 'assert'; import { getFoldingRanges } from '../modes/htmlFolding'; import { TextDocument, getLanguageModes } from '../modes/languageModes'; import { ClientCapabilities } from 'vscode-css-languageservice'; -import { getNodeFSRequestService } from '../node/nodeFs'; +import { getNodeFileFS } from '../node/nodeFs'; interface ExpectedIndentRange { startLine: number; @@ -22,7 +22,7 @@ async function assertRanges(lines: string[], expected: ExpectedIndentRange[], me settings: {}, folders: [{ name: 'foo', uri: 'test://foo' }] }; - const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFSRequestService()); + const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFileFS()); const actual = await getFoldingRanges(languageModes, document, nRanges, null); let actualRanges = []; diff --git a/extensions/html-language-features/server/src/test/formatting.test.ts b/extensions/html-language-features/server/src/test/formatting.test.ts index 107001c7c007c..140942868e443 100644 --- a/extensions/html-language-features/server/src/test/formatting.test.ts +++ b/extensions/html-language-features/server/src/test/formatting.test.ts @@ -10,7 +10,7 @@ import * as assert from 'assert'; import { getLanguageModes, TextDocument, Range, FormattingOptions, ClientCapabilities } from '../modes/languageModes'; import { format } from '../modes/formatting'; -import { getNodeFSRequestService } from '../node/nodeFs'; +import { getNodeFileFS } from '../node/nodeFs'; suite('HTML Embedded Formatting', () => { @@ -19,7 +19,7 @@ suite('HTML Embedded Formatting', () => { settings: options, folders: [{ name: 'foo', uri: 'test://foo' }] }; - const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFSRequestService()); + const languageModes = getLanguageModes({ css: true, javascript: true }, workspace, ClientCapabilities.LATEST, getNodeFileFS()); let rangeStartOffset = value.indexOf('|'); let rangeEndOffset; @@ -77,7 +77,7 @@ suite('HTML Embedded Formatting', () => { }); test('HTML & Multiple Scripts', async () => { - await assertFormat('\n', '\n\n\n \n \n\n\n'); + await assertFormat('\n', '\n\n\n \n \n\n\n'); }); test('HTML & Styles', async () => { @@ -120,7 +120,7 @@ suite('HTML Embedded Formatting', () => { '', '', ' + - +
    diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index 4f97f5b635da7..03dcf66b9f68c 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -1,6 +1,6 @@ - + - - + + diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index a64d5e36de1e8..4d1fe2c6a0499 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -4,46 +4,21 @@ *--------------------------------------------------------------------------------------------*/ import { isStandalone } from 'vs/base/browser/browser'; -import { streamToBuffer } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { parse } from 'vs/base/common/marshalling'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { isEqual } from 'vs/base/common/resources'; -import { encodePath, URI, UriComponents } from 'vs/base/common/uri'; -import { generateUuid } from 'vs/base/common/uuid'; +import { URI, UriComponents } from 'vs/base/common/uri'; import { request } from 'vs/base/parts/request/browser/request'; -import { localize } from 'vs/nls'; -import { parseLogLevel } from 'vs/platform/log/common/log'; import product from 'vs/platform/product/common/product'; import { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/windows/common/windows'; -import { create, ICredentialsProvider, IHomeIndicator, IProductQualityChangeHandler, ISettingsSyncOptions, IURLCallbackProvider, IWelcomeBanner, IWindowIndicator, IWorkbenchConstructionOptions, IWorkspace, IWorkspaceProvider } from 'vs/workbench/workbench.web.api'; +import { posix } from 'vs/base/common/path'; +import { ltrim } from 'vs/base/common/strings'; +import { create, ICredentialsProvider, IURLCallbackProvider, IWorkbenchConstructionOptions, IWorkspace, IWorkspaceProvider } from 'vs/workbench/workbench.web.main'; import { equals as arrayEquals } from 'vs/base/common/arrays'; -function doCreateUri(path: string, queryValues: Map): URI { - let query: string | undefined = undefined; - - if (queryValues) { - let index = 0; - queryValues.forEach((value, key) => { - if (!query) { - query = ''; - } - - const prefix = (index++ === 0) ? '' : '&'; - query += `${prefix}${key}=${encodeURIComponent(value)}`; - }); - } - - /** - * Preserve the current path so it works with reverse proxies serving behind a - * sub-path. - * @author coder - */ - path = (window.location.pathname + "/" + path).replace(/\/\/+/g, "/") - return URI.parse(window.location.href).with({ path, query }); -} - interface ICredential { service: string; account: string; @@ -60,7 +35,7 @@ interface IToken { class LocalStorageCredentialsProvider implements ICredentialsProvider { - static readonly CREDENTIALS_OPENED_KEY = 'credentials.provider'; + private static readonly CREDENTIALS_STORAGE_KEY = 'credentials.provider'; private readonly authService: string | undefined; @@ -147,7 +122,7 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider { private get credentials(): ICredential[] { if (!this._credentials) { try { - const serializedCredentials = window.localStorage.getItem(LocalStorageCredentialsProvider.CREDENTIALS_OPENED_KEY); + const serializedCredentials = window.localStorage.getItem(LocalStorageCredentialsProvider.CREDENTIALS_STORAGE_KEY); if (serializedCredentials) { this._credentials = JSON.parse(serializedCredentials); } @@ -164,7 +139,7 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider { } private save(): void { - window.localStorage.setItem(LocalStorageCredentialsProvider.CREDENTIALS_OPENED_KEY, JSON.stringify(this.credentials)); + window.localStorage.setItem(LocalStorageCredentialsProvider.CREDENTIALS_STORAGE_KEY, JSON.stringify(this.credentials)); } async getPassword(service: string, account: string): Promise { @@ -257,104 +232,230 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider { } async clear(): Promise { - window.localStorage.removeItem(LocalStorageCredentialsProvider.CREDENTIALS_OPENED_KEY); + window.localStorage.removeItem(LocalStorageCredentialsProvider.CREDENTIALS_STORAGE_KEY); } } -class PollingURLCallbackProvider extends Disposable implements IURLCallbackProvider { +class LocalStorageURLCallbackProvider extends Disposable implements IURLCallbackProvider { - static readonly FETCH_INTERVAL = 500; // fetch every 500ms - static readonly FETCH_TIMEOUT = 5 * 60 * 1000; // ...but stop after 5min + private static REQUEST_ID = 0; - static readonly QUERY_KEYS = { - REQUEST_ID: 'vscode-requestId', - SCHEME: 'vscode-scheme', - AUTHORITY: 'vscode-authority', - PATH: 'vscode-path', - QUERY: 'vscode-query', - FRAGMENT: 'vscode-fragment' - }; + private static QUERY_KEYS: ('scheme' | 'authority' | 'path' | 'query' | 'fragment')[] = [ + 'scheme', + 'authority', + 'path', + 'query', + 'fragment' + ]; private readonly _onCallback = this._register(new Emitter()); readonly onCallback = this._onCallback.event; - create(options?: Partial): URI { - const queryValues: Map = new Map(); + private pendingCallbacks = new Set(); + private lastTimeChecked = Date.now(); + private checkCallbacksTimeout: unknown | undefined = undefined; + private onDidChangeLocalStorageDisposable: IDisposable | undefined; - const requestId = generateUuid(); - queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.REQUEST_ID, requestId); + create(options: Partial = {}): URI { + const id = ++LocalStorageURLCallbackProvider.REQUEST_ID; + const queryParams: string[] = [`vscode-reqid=${id}`]; - const { scheme, authority, path, query, fragment } = options ? options : { scheme: undefined, authority: undefined, path: undefined, query: undefined, fragment: undefined }; + for (const key of LocalStorageURLCallbackProvider.QUERY_KEYS) { + const value = options[key]; - if (scheme) { - queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.SCHEME, scheme); + if (value) { + queryParams.push(`vscode-${key}=${encodeURIComponent(value)}`); + } } - if (authority) { - queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.AUTHORITY, authority); - } + // TODO@joao remove eventually + // https://github.com/microsoft/vscode-dev/issues/62 + // https://github.com/microsoft/vscode/blob/159479eb5ae451a66b5dac3c12d564f32f454796/extensions/github-authentication/src/githubServer.ts#L50-L50 + if (!(options.authority === 'vscode.github-authentication' && options.path === '/dummy')) { + const key = `vscode-web.url-callbacks[${id}]`; + window.localStorage.removeItem(key); - if (path) { - queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.PATH, path); + this.pendingCallbacks.add(id); + this.startListening(); } - if (query) { - queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.QUERY, query); - } + return URI.parse(window.location.href).with({ path: '/callback', query: queryParams.join('&') }); + } - if (fragment) { - queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.FRAGMENT, fragment); + private startListening(): void { + if (this.onDidChangeLocalStorageDisposable) { + return; } - // Start to poll on the callback being fired - this.periodicFetchCallback(requestId, Date.now()); + const fn = () => this.onDidChangeLocalStorage(); + window.addEventListener('storage', fn); + this.onDidChangeLocalStorageDisposable = { dispose: () => window.removeEventListener('storage', fn) }; + } - return doCreateUri('/callback', queryValues); + private stopListening(): void { + this.onDidChangeLocalStorageDisposable?.dispose(); + this.onDidChangeLocalStorageDisposable = undefined; } - private async periodicFetchCallback(requestId: string, startTime: number): Promise { + // this fires every time local storage changes, but we + // don't want to check more often than once a second + private async onDidChangeLocalStorage(): Promise { + const ellapsed = Date.now() - this.lastTimeChecked; + + if (ellapsed > 1000) { + this.checkCallbacks(); + } else if (this.checkCallbacksTimeout === undefined) { + this.checkCallbacksTimeout = setTimeout(() => { + this.checkCallbacksTimeout = undefined; + this.checkCallbacks(); + }, 1000 - ellapsed); + } + } - // Ask server for callback results - const queryValues: Map = new Map(); - queryValues.set(PollingURLCallbackProvider.QUERY_KEYS.REQUEST_ID, requestId); + private checkCallbacks(): void { + let pendingCallbacks: Set | undefined; - const result = await request({ - url: doCreateUri('/fetch-callback', queryValues).toString(true) - }, CancellationToken.None); + for (const id of this.pendingCallbacks) { + const key = `vscode-web.url-callbacks[${id}]`; + const result = window.localStorage.getItem(key); - // Check for callback results - const content = await streamToBuffer(result.stream); - if (content.byteLength > 0) { - try { - this._onCallback.fire(URI.revive(JSON.parse(content.toString()))); - } catch (error) { - console.error(error); - } + if (result !== null) { + try { + this._onCallback.fire(URI.revive(JSON.parse(result))); + } catch (error) { + console.error(error); + } - return; // done + pendingCallbacks = pendingCallbacks ?? new Set(this.pendingCallbacks); + pendingCallbacks.delete(id); + window.localStorage.removeItem(key); + } } - // Continue fetching unless we hit the timeout - if (Date.now() - startTime < PollingURLCallbackProvider.FETCH_TIMEOUT) { - setTimeout(() => this.periodicFetchCallback(requestId, startTime), PollingURLCallbackProvider.FETCH_INTERVAL); + if (pendingCallbacks) { + this.pendingCallbacks = pendingCallbacks; + + if (this.pendingCallbacks.size === 0) { + this.stopListening(); + } } + + this.lastTimeChecked = Date.now(); } } class WorkspaceProvider implements IWorkspaceProvider { - static QUERY_PARAM_EMPTY_WINDOW = 'ew'; - static QUERY_PARAM_FOLDER = 'folder'; - static QUERY_PARAM_WORKSPACE = 'workspace'; + private static readonly LAST_WORKSPACE_STORAGE_KEY = 'workspaces.lastOpened'; + + private static QUERY_PARAM_EMPTY_WINDOW = 'ew'; + private static QUERY_PARAM_FOLDER = 'folder'; + private static QUERY_PARAM_WORKSPACE = 'workspace'; + + private static QUERY_PARAM_PAYLOAD = 'payload'; + + static create(config: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents }) { + let foundWorkspace = false; + let workspace: IWorkspace; + let payload = Object.create(null); + + const query = new URL(document.location.href).searchParams; + query.forEach((value, key) => { + switch (key) { + + // Folder + case WorkspaceProvider.QUERY_PARAM_FOLDER: + console.log("do we have a query param folder?") + if (config.remoteAuthority && value.startsWith(posix.sep)) { + console.log("do we get in the remote authority block here") + // when connected to a remote and having a value + // that is a path (begins with a `/`), assume this + // is a vscode-remote resource as simplified URL. + workspace = { folderUri: URI.from({ scheme: Schemas.vscodeRemote, path: value, authority: config.remoteAuthority }) }; + } else { + workspace = { folderUri: URI.parse(value) }; + } + foundWorkspace = true; + break; + + // Workspace + case WorkspaceProvider.QUERY_PARAM_WORKSPACE: + if (config.remoteAuthority && value.startsWith(posix.sep)) { + // when connected to a remote and having a value + // that is a path (begins with a `/`), assume this + // is a vscode-remote resource as simplified URL. + workspace = { workspaceUri: URI.from({ scheme: Schemas.vscodeRemote, path: value, authority: config.remoteAuthority }) }; + } else { + workspace = { workspaceUri: URI.parse(value) }; + } + foundWorkspace = true; + break; + + // Empty + case WorkspaceProvider.QUERY_PARAM_EMPTY_WINDOW: + workspace = undefined; + foundWorkspace = true; + break; + + // Payload + case WorkspaceProvider.QUERY_PARAM_PAYLOAD: + try { + payload = parse(value); // use marshalling#parse() to revive potential URIs + } catch (error) { + console.error(error); // possible invalid JSON + } + break; + } + }); + + // NOTE@coder we have our own logic to determine last open workspace + // This code will never run since we have our redirect logic before + // getting here. + // If we do want to use this, we need to add a flag check. + // -jsjoeio + const DISABLE_LAST_OPENED = true + + if (!foundWorkspace) { + if (config.folderUri) { + console.log("no workspace found") + workspace = { folderUri: URI.revive(config.folderUri) }; + console.log("what is workspace", workspace) + } else if (config.workspaceUri) { + workspace = { workspaceUri: URI.revive(config.workspaceUri) }; + } else if (!DISABLE_LAST_OPENED) { + workspace = (() => { + const lastWorkspaceRaw = window.localStorage.getItem(WorkspaceProvider.LAST_WORKSPACE_STORAGE_KEY); + if (lastWorkspaceRaw) { + try { + return parse(lastWorkspaceRaw); // use marshalling#parse() to revive potential URIs + } catch (error) { + // Ignore + } + } - static QUERY_PARAM_PAYLOAD = 'payload'; + return undefined; + })(); + } + } + + // Keep this as last opened workspace in storage + if (workspace) { + window.localStorage.setItem(WorkspaceProvider.LAST_WORKSPACE_STORAGE_KEY, JSON.stringify(workspace)); + } else { + window.localStorage.removeItem(WorkspaceProvider.LAST_WORKSPACE_STORAGE_KEY); + } + + return new WorkspaceProvider(workspace, payload, config); + } readonly trusted = true; - constructor( + private constructor( readonly workspace: IWorkspace, - readonly payload: object - ) { } + readonly payload: object, + private readonly config: IWorkbenchConstructionOptions + ) { + } async open(workspace: IWorkspace, options?: { reuse?: boolean, payload?: object }): Promise { if (options?.reuse && !options.payload && this.isSame(this.workspace, workspace)) { @@ -388,28 +489,36 @@ class WorkspaceProvider implements IWorkspaceProvider { targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_EMPTY_WINDOW}=true`; } - // Folder - /** - * Modified to print as a human-readable string for file paths. - * @author coder - */ else if (isFolderToOpen(workspace)) { - const target = workspace.folderUri.scheme === Schemas.vscodeRemote - ? encodePath(workspace.folderUri.path) - : encodeURIComponent(workspace.folderUri.toString()); - targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_FOLDER}=${target}`; + let queryParamFolder: string; + if (this.config.remoteAuthority && workspace.folderUri.scheme === Schemas.vscodeRemote) { + // when connected to a remote and having a folder + // for that remote, only use the path as query + // value to form shorter, nicer URLs. + // ensure paths are absolute (begin with `/`) + // clipboard: ltrim(workspace.folderUri.path, posix.sep) + queryParamFolder = `${posix.sep}${ltrim(workspace.folderUri.path, posix.sep)}`; + } else { + queryParamFolder = encodeURIComponent(workspace.folderUri.toString(true)); + } + + targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_FOLDER}=${queryParamFolder}`; } // Workspace - /** - * Modified to print as a human-readable string for file paths. - * @author coder - */ else if (isWorkspaceToOpen(workspace)) { - const target = workspace.workspaceUri.scheme === Schemas.vscodeRemote - ? encodePath(workspace.workspaceUri.path) - : encodeURIComponent(workspace.workspaceUri.toString()); - targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_WORKSPACE}=${target}`; + let queryParamWorkspace: string; + if (this.config.remoteAuthority && workspace.workspaceUri.scheme === Schemas.vscodeRemote) { + // when connected to a remote and having a workspace + // for that remote, only use the path as query + // value to form shorter, nicer URLs. + // ensure paths are absolute (begin with `/`) + queryParamWorkspace = `${posix.sep}${ltrim(workspace.workspaceUri.path, posix.sep)}`; + } else { + queryParamWorkspace = encodeURIComponent(workspace.workspaceUri.toString(true)); + } + + targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_WORKSPACE}=${queryParamWorkspace}`; } // Append payload if any @@ -451,43 +560,28 @@ class WorkspaceProvider implements IWorkspaceProvider { } } -class WindowIndicator implements IWindowIndicator { - - readonly onDidChange = Event.None; - - readonly label: string; - readonly tooltip: string; - readonly command: string | undefined; - - constructor(workspace: IWorkspace) { - let repositoryOwner: string | undefined = undefined; - let repositoryName: string | undefined = undefined; - - if (workspace) { - let uri: URI | undefined = undefined; - if (isFolderToOpen(workspace)) { - uri = workspace.folderUri; - } else if (isWorkspaceToOpen(workspace)) { - uri = workspace.workspaceUri; - } +function doCreateUri(path: string, queryValues: Map): URI { + let query: string | undefined = undefined; - if (uri?.scheme === 'github' || uri?.scheme === 'codespace') { - [repositoryOwner, repositoryName] = uri.authority.split('+'); + if (queryValues) { + let index = 0; + queryValues.forEach((value, key) => { + if (!query) { + query = ''; } - } - // Repo - if (repositoryName && repositoryOwner) { - this.label = localize('playgroundLabelRepository', "$(remote) Visual Studio Code Playground: {0}/{1}", repositoryOwner, repositoryName); - this.tooltip = localize('playgroundRepositoryTooltip', "Visual Studio Code Playground: {0}/{1}", repositoryOwner, repositoryName); - } - - // No Repo - else { - this.label = localize('playgroundLabel', "$(remote) Visual Studio Code Playground"); - this.tooltip = localize('playgroundTooltip', "Visual Studio Code Playground"); - } + const prefix = (index++ === 0) ? '' : '&'; + query += `${prefix}${key}=${encodeURIComponent(value)}`; + }); } + + /** + * Preserve the current path so it works with reverse proxies serving behind a + * sub-path. + * @author coder + */ + path = (window.location.pathname + "/" + path).replace(/\/\/+/g, "/") + return URI.parse(window.location.href).with({ path, query }); } (function () { @@ -498,15 +592,6 @@ class WindowIndicator implements IWindowIndicator { if (!configElement || !configElementAttribute) { throw new Error('Missing web configuration element'); } - - const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents } = JSON.parse(configElementAttribute); - - // Find workspace to open and payload - let foundWorkspace = false; - let workspace: IWorkspace; - let payload = Object.create(null); - let logLevel: string | undefined = undefined; - /** * If the value begins with a slash assume it is a file path and convert it to * use the vscode-remote scheme. @@ -517,129 +602,11 @@ class WindowIndicator implements IWindowIndicator { * * @author coder */ - const remoteAuthority = location.host - const toRemote = (value: string): string => { - if (value.startsWith('/')) { - return 'vscode-remote://' + remoteAuthority + value; - } - return value; - }; - - const query = new URL(document.location.href).searchParams; - query.forEach((value, key) => { - switch (key) { - // Folder - case WorkspaceProvider.QUERY_PARAM_FOLDER: - /** - * Handle URIs that we previously left unencoded and de-schemed. - * - * @author coder - */ - value = toRemote(value); - workspace = { folderUri: URI.parse(value) }; - foundWorkspace = true; - break; - - // Workspace - case WorkspaceProvider.QUERY_PARAM_WORKSPACE: - /** - * Handle URIs that we previously left unencoded and de-schemed. - * - * @author coder - */ - value = toRemote(value); - workspace = { workspaceUri: URI.parse(value) }; - foundWorkspace = true; - break; - - // Empty - case WorkspaceProvider.QUERY_PARAM_EMPTY_WINDOW: - workspace = undefined; - foundWorkspace = true; - break; - - // Payload - case WorkspaceProvider.QUERY_PARAM_PAYLOAD: - try { - payload = JSON.parse(value); - } catch (error) { - console.error(error); // possible invalid JSON - } - break; - - // Log level - case 'logLevel': - logLevel = value; - break; - } - }); - - // If no workspace is provided through the URL, check for config attribute from server - if (!foundWorkspace) { - if (config.folderUri) { - workspace = { folderUri: URI.revive(config.folderUri) }; - } else if (config.workspaceUri) { - workspace = { workspaceUri: URI.revive(config.workspaceUri) }; - } else { - workspace = undefined; - } - } - - // Workspace Provider - const workspaceProvider = new WorkspaceProvider(workspace, payload); - - // Home Indicator - const homeIndicator: IHomeIndicator = { - href: 'https://github.com/microsoft/vscode', - icon: 'code', - title: localize('home', "Home") - }; - - - // Welcome Banner - const welcomeBanner: undefined | IWelcomeBanner = undefined; - // message: localize('welcomeBannerMessage', "{0} Web. Browser based playground for testing.", product.nameShort), - // actions: [{ - // href: 'https://github.com/microsoft/vscode', - // label: localize('learnMore', "Learn More") - // }] - // }; - - // Window indicator (unless connected to a remote) - let windowIndicator: WindowIndicator | undefined = undefined; - if (!workspaceProvider.hasRemote()) { - windowIndicator = new WindowIndicator(workspace); - } - - // Product Quality Change Handler - const productQualityChangeHandler: IProductQualityChangeHandler = (quality) => { - let queryString = `quality=${quality}`; - - // Save all other query params we might have - const query = new URL(document.location.href).searchParams; - query.forEach((value, key) => { - if (key !== 'quality') { - queryString += `&${key}=${value}`; - } - }); - - window.location.href = `${window.location.origin}?${queryString}`; - }; - - // settings sync options - const settingsSyncOptions: ISettingsSyncOptions | undefined = config.settingsSyncOptions ? { - enabled: config.settingsSyncOptions.enabled, - } : undefined; + const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents } = { ...JSON.parse(configElementAttribute), remoteAuthority: location.host } // Finally create workbench create(document.body, { ...config, - /** - * Ensure the remote authority points to the current address since we cannot - * determine this reliably on the backend. - * @author coder - */ - remoteAuthority, /** * Override relative URLs in the product configuration against the window * location as necessary. Only paths that must be absolute need to be @@ -659,17 +626,11 @@ class WindowIndicator implements IWindowIndicator { ).toString(), ), }, - developmentOptions: { - logLevel: logLevel ? parseLogLevel(logLevel) : undefined, - ...config.developmentOptions - }, - settingsSyncOptions, - homeIndicator, - windowIndicator, - welcomeBanner, - productQualityChangeHandler, - workspaceProvider, - urlCallbackProvider: new PollingURLCallbackProvider(), - credentialsProvider: new LocalStorageCredentialsProvider() + settingsSyncOptions: config.settingsSyncOptions ? { + enabled: config.settingsSyncOptions.enabled, + } : undefined, + workspaceProvider: WorkspaceProvider.create(config), + urlCallbackProvider: new LocalStorageURLCallbackProvider(), + credentialsProvider: config.remoteAuthority ? undefined : new LocalStorageCredentialsProvider() // with a remote, we don't use a local credentials provider }); })(); diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/deprecatedExtensionsCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/deprecatedExtensionsCleaner.ts deleted file mode 100644 index f02aeb652053f..0000000000000 --- a/src/vs/code/electron-browser/sharedProcess/contrib/deprecatedExtensionsCleaner.ts +++ /dev/null @@ -1,25 +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 { Disposable } from 'vs/base/common/lifecycle'; -import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; - -export class DeprecatedExtensionsCleaner extends Disposable { - - constructor( - @IExtensionManagementService private readonly extensionManagementService: ExtensionManagementService - ) { - super(); - - this._register(extensionManagementService); // TODO@sandy081 this seems fishy - - this.cleanUpDeprecatedExtensions(); - } - - private cleanUpDeprecatedExtensions(): void { - this.extensionManagementService.removeDeprecatedExtensions(); - } -} diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/extensionsCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/extensionsCleaner.ts new file mode 100644 index 0000000000000..f64b77fc74920 --- /dev/null +++ b/src/vs/code/electron-browser/sharedProcess/contrib/extensionsCleaner.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { IExtensionGalleryService, IExtensionManagementService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage'; +import { migrateUnsupportedExtensions } from 'vs/platform/extensionManagement/common/unsupportedExtensionsMigration'; +import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; +import { ILogService } from 'vs/platform/log/common/log'; + +export class ExtensionsCleaner extends Disposable { + + constructor( + @IExtensionManagementService extensionManagementService: ExtensionManagementService, + @IExtensionGalleryService extensionGalleryService: IExtensionGalleryService, + @IExtensionStorageService extensionStorageService: IExtensionStorageService, + @IGlobalExtensionEnablementService extensionEnablementService: IGlobalExtensionEnablementService, + @ILogService logService: ILogService, + ) { + super(); + extensionManagementService.removeDeprecatedExtensions(); + migrateUnsupportedExtensions(extensionManagementService, extensionGalleryService, extensionStorageService, extensionEnablementService, logService); + } +} diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index e3077d66510c5..7581b0bcdbdcf 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -14,7 +14,7 @@ import { URI } from 'vs/base/common/uri'; import { ProxyChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc'; import { Server as MessagePortServer } from 'vs/base/parts/ipc/electron-browser/ipc.mp'; import { CodeCacheCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/codeCacheCleaner'; -import { DeprecatedExtensionsCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/deprecatedExtensionsCleaner'; +import { ExtensionsCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/extensionsCleaner'; import { LanguagePackCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner'; import { LocalizationsUpdater } from 'vs/code/electron-browser/sharedProcess/contrib/localizationsUpdater'; import { LogsDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/logsDataCleaner'; @@ -28,7 +28,7 @@ import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsServ import { IDownloadService } from 'vs/platform/download/common/download'; import { DownloadService } from 'vs/platform/download/common/downloadService'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; -import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { SharedProcessEnvironmentService } from 'vs/platform/sharedProcess/node/sharedProcessEnvironmentService'; import { GlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionEnablementService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IExtensionGalleryService, IExtensionManagementService, IExtensionTipsService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -69,16 +69,15 @@ import { CustomEndpointTelemetryService } from 'vs/platform/telemetry/node/custo import { LocalReconnectConstants, TerminalIpcChannels, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal'; import { PtyHostService } from 'vs/platform/terminal/node/ptyHostService'; -import { ExtensionsStorageSyncService, IExtensionsStorageSyncService } from 'vs/platform/userDataSync/common/extensionsStorageSync'; +import { ExtensionStorageService, IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage'; import { IgnoredExtensionsManagementService, IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions'; -import { UserDataAutoSyncEnablementService } from 'vs/platform/userDataSync/common/userDataAutoSyncService'; -import { IUserDataAutoSyncEnablementService, IUserDataSyncBackupStoreService, IUserDataSyncLogService, IUserDataSyncResourceEnablementService, IUserDataSyncService, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, IUserDataSyncUtilService, registerConfiguration as registerUserDataSyncConfiguration } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncBackupStoreService, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncService, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, IUserDataSyncUtilService, registerConfiguration as registerUserDataSyncConfiguration } from 'vs/platform/userDataSync/common/userDataSync'; import { IUserDataSyncAccountService, UserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount'; import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService'; import { UserDataAutoSyncChannel, UserDataSyncAccountServiceChannel, UserDataSyncMachinesServiceChannel, UserDataSyncStoreManagementServiceChannel, UserDataSyncUtilServiceClient } from 'vs/platform/userDataSync/common/userDataSyncIpc'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; import { IUserDataSyncMachinesService, UserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines'; -import { UserDataSyncResourceEnablementService } from 'vs/platform/userDataSync/common/userDataSyncResourceEnablementService'; +import { UserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSyncEnablementService'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { UserDataSyncChannel } from 'vs/platform/userDataSync/common/userDataSyncServiceIpc'; import { UserDataSyncStoreManagementService, UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; @@ -86,16 +85,20 @@ import { UserDataAutoSyncService } from 'vs/platform/userDataSync/electron-sandb import { ActiveWindowManager } from 'vs/platform/windows/node/windowTracker'; import { ISignService } from 'vs/platform/sign/common/sign'; import { SignService } from 'vs/platform/sign/node/signService'; -import { ISharedTunnelsService } from 'vs/platform/remote/common/tunnel'; -import { SharedTunnelsService } from 'vs/platform/remote/node/tunnelService'; +import { ISharedTunnelsService } from 'vs/platform/tunnel/common/tunnel'; +import { SharedTunnelsService } from 'vs/platform/tunnel/node/tunnelService'; import { ipcSharedProcessTunnelChannelName, ISharedProcessTunnelService } from 'vs/platform/remote/common/sharedProcessTunnelService'; -import { SharedProcessTunnelService } from 'vs/platform/remote/node/sharedProcessTunnelService'; +import { SharedProcessTunnelService } from 'vs/platform/tunnel/node/sharedProcessTunnelService'; import { ipcSharedProcessWorkerChannelName, ISharedProcessWorkerConfiguration, ISharedProcessWorkerService } from 'vs/platform/sharedProcess/common/sharedProcessWorkerService'; import { SharedProcessWorkerService } from 'vs/platform/sharedProcess/electron-browser/sharedProcessWorkerService'; -import { IUserConfigurationFileService, UserConfigurationFileServiceId } from 'vs/platform/configuration/common/userConfigurationFileService'; import { AssignmentService } from 'vs/platform/assignment/common/assignmentService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; +import { isLinux } from 'vs/base/common/platform'; +import { FileUserDataProvider } from 'vs/platform/userData/common/fileUserDataProvider'; +import { DiskFileSystemProviderClient, LOCAL_FILE_SYSTEM_CHANNEL_NAME } from 'vs/platform/files/common/diskFileSystemProviderClient'; +import { InspectProfilingService as V8InspectProfilingService } from 'vs/platform/profiling/node/profilingService'; +import { IV8InspectProfilingService } from 'vs/platform/profiling/common/profiling'; class SharedProcessMain extends Disposable { @@ -160,7 +163,7 @@ class SharedProcessMain extends Disposable { instantiationService.createInstance(StorageDataCleaner, this.configuration.backupWorkspacesPath), instantiationService.createInstance(LogsDataCleaner), instantiationService.createInstance(LocalizationsUpdater), - instantiationService.createInstance(DeprecatedExtensionsCleaner) + instantiationService.createInstance(ExtensionsCleaner) )); } @@ -177,7 +180,7 @@ class SharedProcessMain extends Disposable { services.set(IMainProcessService, mainProcessService); // Environment - const environmentService = new NativeEnvironmentService(this.configuration.args, productService); + const environmentService = new SharedProcessEnvironmentService(this.configuration.args, productService); services.set(INativeEnvironmentService, environmentService); // Logger @@ -205,6 +208,18 @@ class SharedProcessMain extends Disposable { const diskFileSystemProvider = this._register(new DiskFileSystemProvider(logService)); fileService.registerProvider(Schemas.file, diskFileSystemProvider); + const userDataFileSystemProvider = this._register(new FileUserDataProvider( + Schemas.file, + // Specifically for user data, use the disk file system provider + // from the main process to enable atomic read/write operations. + // Since user data can change very frequently across multiple + // processes, we want a single process handling these operations. + this._register(new DiskFileSystemProviderClient(mainProcessService.getChannel(LOCAL_FILE_SYSTEM_CHANNEL_NAME), { pathCaseSensitive: isLinux })), + Schemas.userData, + logService + )); + fileService.registerProvider(Schemas.userData, userDataFileSystemProvider); + // Configuration const configurationService = this._register(new ConfigurationService(environmentService.settingsResource, fileService)); services.set(IConfigurationService, configurationService); @@ -220,9 +235,6 @@ class SharedProcessMain extends Disposable { storageService.initialize() ]); - // User Configuration File - services.set(IUserConfigurationFileService, ProxyChannel.toService(mainProcessService.getChannel(UserConfigurationFileServiceId))); - // URI Identity services.set(IUriIdentityService, new UriIdentityService(fileService)); @@ -232,6 +244,9 @@ class SharedProcessMain extends Disposable { // Checksum services.set(IChecksumService, new SyncDescriptor(ChecksumService)); + // V8 Inspect profiler + services.set(IV8InspectProfilingService, new SyncDescriptor(V8InspectProfilingService)); + // Native Host const nativeHostService = ProxyChannel.toService(mainProcessService.getChannel('nativeHost'), { context: this.configuration.windowId }); services.set(INativeHostService, nativeHostService); @@ -311,13 +326,12 @@ class SharedProcessMain extends Disposable { services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(this.server.getChannel('userDataSyncUtil', client => client.ctx !== 'main'))); services.set(IGlobalExtensionEnablementService, new SyncDescriptor(GlobalExtensionEnablementService)); services.set(IIgnoredExtensionsManagementService, new SyncDescriptor(IgnoredExtensionsManagementService)); - services.set(IExtensionsStorageSyncService, new SyncDescriptor(ExtensionsStorageSyncService)); + services.set(IExtensionStorageService, new SyncDescriptor(ExtensionStorageService)); services.set(IUserDataSyncStoreManagementService, new SyncDescriptor(UserDataSyncStoreManagementService)); services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService)); services.set(IUserDataSyncMachinesService, new SyncDescriptor(UserDataSyncMachinesService)); services.set(IUserDataSyncBackupStoreService, new SyncDescriptor(UserDataSyncBackupStoreService)); - services.set(IUserDataAutoSyncEnablementService, new SyncDescriptor(UserDataAutoSyncEnablementService)); - services.set(IUserDataSyncResourceEnablementService, new SyncDescriptor(UserDataSyncResourceEnablementService)); + services.set(IUserDataSyncEnablementService, new SyncDescriptor(UserDataSyncEnablementService)); services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService)); const ptyHostService = new PtyHostService({ @@ -367,6 +381,10 @@ class SharedProcessMain extends Disposable { const checksumChannel = ProxyChannel.fromService(accessor.get(IChecksumService)); this.server.registerChannel('checksum', checksumChannel); + // Profiling + const profilingChannel = ProxyChannel.fromService(accessor.get(IV8InspectProfilingService)); + this.server.registerChannel('v8InspectProfiling', profilingChannel); + // Settings Sync const userDataSyncMachineChannel = new UserDataSyncMachinesServiceChannel(accessor.get(IUserDataSyncMachinesService)); this.server.registerChannel('userDataSyncMachines', userDataSyncMachineChannel); diff --git a/src/vs/code/electron-browser/workbench/workbench.html b/src/vs/code/electron-browser/workbench/workbench.html index 006d06ac4a9ea..47066f520be18 100644 --- a/src/vs/code/electron-browser/workbench/workbench.html +++ b/src/vs/code/electron-browser/workbench/workbench.html @@ -3,8 +3,8 @@ - - + + diff --git a/src/vs/code/electron-browser/workbench/workbench.js b/src/vs/code/electron-browser/workbench/workbench.js index f0235cf89a1f9..d83fb9abcd32c 100644 --- a/src/vs/code/electron-browser/workbench/workbench.js +++ b/src/vs/code/electron-browser/workbench/workbench.js @@ -29,7 +29,7 @@ performance.mark('code/didLoadWorkbenchMain'); // @ts-ignore - return require('vs/workbench/electron-browser/desktop.main').main(configuration); + return require('vs/workbench/electron-sandbox/desktop.main').main(configuration); }, { configureDeveloperSettings: function (windowConfig) { diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 4baf8158a5928..efa47fecfbeba 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { app, BrowserWindow, contentTracing, dialog, ipcMain, protocol, session, Session, systemPreferences } from 'electron'; +import { app, BrowserWindow, contentTracing, dialog, ipcMain, protocol, session, Session, systemPreferences, WebFrameMain } from 'electron'; import { statSync } from 'fs'; import { hostname, release } from 'os'; import { VSBuffer } from 'vs/base/common/buffer'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; -import { isEqualOrParent } from 'vs/base/common/extpath'; +import { isEqualOrParent, randomPath } from 'vs/base/common/extpath'; import { once } from 'vs/base/common/functional'; import { stripComments } from 'vs/base/common/json'; import { getPathLabel, mnemonicButtonLabel } from 'vs/base/common/labels'; @@ -17,7 +17,6 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { isAbsolute, join, posix } from 'vs/base/common/path'; import { IProcessEnvironment, isLinux, isLinuxSnap, isMacintosh, isWindows } from 'vs/base/common/platform'; -import { joinPath } from 'vs/base/common/resources'; import { assertType, withNullAsUndefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; @@ -32,24 +31,28 @@ import { localize } from 'vs/nls'; import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { UserConfigurationFileService, UserConfigurationFileServiceId } from 'vs/platform/configuration/common/userConfigurationFileService'; +import { ICredentialsMainService } from 'vs/platform/credentials/common/credentials'; +import { CredentialsMainService } from 'vs/platform/credentials/node/credentialsMainService'; import { ElectronExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/electron-main/extensionHostDebugIpc'; import { IDiagnosticsService } from 'vs/platform/diagnostics/common/diagnostics'; +import { DiagnosticsMainService, IDiagnosticsMainService } from 'vs/platform/diagnostics/electron-main/diagnosticsMainService'; import { DialogMainService, IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogMainService'; import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; -import { EncryptionMainService, IEncryptionMainService } from 'vs/platform/encryption/electron-main/encryptionMainService'; +import { IEncryptionMainService } from 'vs/platform/encryption/common/encryptionService'; +import { EncryptionMainService } from 'vs/platform/encryption/node/encryptionMainService'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { isLaunchedFromCli } from 'vs/platform/environment/node/argvHelper'; -import { resolveShellEnv } from 'vs/platform/environment/node/shellEnv'; +import { getResolvedShellEnv } from 'vs/platform/environment/node/shellEnv'; import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust'; import { ExtensionUrlTrustService } from 'vs/platform/extensionManagement/node/extensionUrlTrustService'; import { IExtensionHostStarter, ipcExtensionHostStarterChannelName } from 'vs/platform/extensions/common/extensionHostStarter'; import { WorkerMainProcessExtensionHostStarter } from 'vs/platform/extensions/electron-main/workerMainProcessExtensionHostStarter'; import { IExternalTerminalMainService } from 'vs/platform/externalTerminal/common/externalTerminal'; import { LinuxExternalTerminalService, MacExternalTerminalService, WindowsExternalTerminalService } from 'vs/platform/externalTerminal/node/externalTerminalService'; +import { LOCAL_FILE_SYSTEM_CHANNEL_NAME } from 'vs/platform/files/common/diskFileSystemProviderClient'; import { IFileService } from 'vs/platform/files/common/files'; -import { DiskFileSystemProviderChannel } from 'vs/platform/files/electron-main/diskFileSystemProviderIpc'; +import { DiskFileSystemProviderChannel } from 'vs/platform/files/electron-main/diskFileSystemProviderServer'; import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -68,7 +71,7 @@ import { SharedProcess } from 'vs/platform/sharedProcess/electron-main/sharedPro import { ISignService } from 'vs/platform/sign/common/sign'; import { IStateMainService } from 'vs/platform/state/electron-main/state'; import { StorageDatabaseChannel } from 'vs/platform/storage/electron-main/storageIpc'; -import { IStorageMainService, StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; +import { GlobalStorageMainService, IGlobalStorageMainService, IStorageMainService, StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { resolveCommonProperties } from 'vs/platform/telemetry/common/commonProperties'; import { ITelemetryService, machineIdKey, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; import { TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc'; @@ -154,6 +157,90 @@ export class CodeApplication extends Disposable { //#endregion + //#region Request filtering + + // Block all SVG requests from unsupported origins + const supportedSvgSchemes = new Set([Schemas.file, Schemas.vscodeFileResource, Schemas.vscodeRemoteResource, 'devtools']); + + // But allow them if the are made from inside an webview + const isSafeFrame = (requestFrame: WebFrameMain | undefined): boolean => { + for (let frame: WebFrameMain | null | undefined = requestFrame; frame; frame = frame.parent) { + if (frame.url.startsWith(`${Schemas.vscodeWebview}://`)) { + return true; + } + } + return false; + }; + + const isSvgRequestFromSafeContext = (details: Electron.OnBeforeRequestListenerDetails | Electron.OnHeadersReceivedListenerDetails): boolean => { + return details.resourceType === 'xhr' || isSafeFrame(details.frame); + }; + + const isAllowedVsCodeFileRequest = (details: Electron.OnBeforeRequestListenerDetails) => { + const frame = details.frame; + if (!frame || !this.windowsMainService) { + return false; + } + + // Check to see if the request comes from one of the main windows (or shared process) and not from embedded content + const windows = BrowserWindow.getAllWindows(); + for (const window of windows) { + if (frame.processId === window.webContents.mainFrame.processId) { + return true; + } + } + + return false; + }; + + session.defaultSession.webRequest.onBeforeRequest((details, callback) => { + const uri = URI.parse(details.url); + + if (uri.scheme === Schemas.vscodeFileResource) { + if (!isAllowedVsCodeFileRequest(details)) { + this.logService.error('Blocked vscode-file request', details.url); + return callback({ cancel: true }); + } + } + + // Block most svgs + if (uri.path.endsWith('.svg')) { + const isSafeResourceUrl = supportedSvgSchemes.has(uri.scheme); + if (!isSafeResourceUrl) { + return callback({ cancel: !isSvgRequestFromSafeContext(details) }); + } + } + + return callback({ cancel: false }); + }); + + // Configure SVG header content type properly + // https://github.com/microsoft/vscode/issues/97564 + session.defaultSession.webRequest.onHeadersReceived((details, callback) => { + const responseHeaders = details.responseHeaders as Record; + const contentTypes = (responseHeaders['content-type'] || responseHeaders['Content-Type']); + + if (contentTypes && Array.isArray(contentTypes)) { + const uri = URI.parse(details.url); + if (uri.path.endsWith('.svg')) { + if (supportedSvgSchemes.has(uri.scheme)) { + responseHeaders['Content-Type'] = ['image/svg+xml']; + + return callback({ cancel: false, responseHeaders }); + } + } + + // remote extension schemes have the following format + // http://127.0.0.1:/vscode-remote-resource?path= + if (!uri.path.includes(Schemas.vscodeRemoteResource) && contentTypes.some(contentType => contentType.toLowerCase().includes('image/svg'))) { + return callback({ cancel: !isSvgRequestFromSafeContext(details) }); + } + } + + return callback({ cancel: false }); + }); + + //#endregion //#region Code Cache @@ -488,6 +575,7 @@ export class CodeApplication extends Disposable { services.set(ILaunchMainService, new SyncDescriptor(LaunchMainService)); // Diagnostics + services.set(IDiagnosticsMainService, new SyncDescriptor(DiagnosticsMainService)); services.set(IDiagnosticsService, ProxyChannel.toService(getDelayedChannel(sharedProcessReady.then(client => client.getChannel('diagnostics'))))); // Issues @@ -502,6 +590,9 @@ export class CodeApplication extends Disposable { // Native Host services.set(INativeHostMainService, new SyncDescriptor(NativeHostMainService, [sharedProcess])); + // Credentials + services.set(ICredentialsMainService, new SyncDescriptor(CredentialsMainService, [false])); + // Webview Manager services.set(IWebviewManagerService, new SyncDescriptor(WebviewMainService)); @@ -521,6 +612,7 @@ export class CodeApplication extends Disposable { // Storage services.set(IStorageMainService, new SyncDescriptor(StorageMainService)); + services.set(IGlobalStorageMainService, new SyncDescriptor(GlobalStorageMainService)); // External terminal if (isWindows) { @@ -559,23 +651,23 @@ export class CodeApplication extends Disposable { private initChannels(accessor: ServicesAccessor, mainProcessElectronServer: ElectronIPCServer, sharedProcessClient: Promise): void { - // Launch: this one is explicitly registered to the node.js - // server because when a second instance starts up, that is - // the only possible connection between the first and the - // second instance. Electron IPC does not work across apps. + // Channels registered to node.js are exposed to second instances + // launching because that is the only way the second instance + // can talk to the first instance. Electron IPC does not work + // across apps until `requestSingleInstance` APIs are adopted. + const launchChannel = ProxyChannel.fromService(accessor.get(ILaunchMainService), { disableMarshalling: true }); this.mainProcessNodeIpcServer.registerChannel('launch', launchChannel); + const diagnosticsChannel = ProxyChannel.fromService(accessor.get(IDiagnosticsMainService), { disableMarshalling: true }); + this.mainProcessNodeIpcServer.registerChannel('diagnostics', diagnosticsChannel); + // Local Files const diskFileSystemProvider = this.fileService.getProvider(Schemas.file); assertType(diskFileSystemProvider instanceof DiskFileSystemProvider); - const fileSystemProviderChannel = new DiskFileSystemProviderChannel(diskFileSystemProvider, this.logService); - mainProcessElectronServer.registerChannel('localFilesystem', fileSystemProviderChannel); - - // User Configuration File - const userConfigurationFileService = new UserConfigurationFileService(this.environmentMainService, this.fileService, this.logService); - mainProcessElectronServer.registerChannel(UserConfigurationFileServiceId, ProxyChannel.fromService(userConfigurationFileService)); - sharedProcessClient.then(client => client.registerChannel(UserConfigurationFileServiceId, ProxyChannel.fromService(userConfigurationFileService))); + const fileSystemProviderChannel = new DiskFileSystemProviderChannel(diskFileSystemProvider, this.logService, this.environmentMainService); + mainProcessElectronServer.registerChannel(LOCAL_FILE_SYSTEM_CHANNEL_NAME, fileSystemProviderChannel); + sharedProcessClient.then(client => client.registerChannel(LOCAL_FILE_SYSTEM_CHANNEL_NAME, fileSystemProviderChannel)); // Update const updateChannel = new UpdateChannel(accessor.get(IUpdateService)); @@ -589,6 +681,10 @@ export class CodeApplication extends Disposable { const encryptionChannel = ProxyChannel.fromService(accessor.get(IEncryptionMainService)); mainProcessElectronServer.registerChannel('encryption', encryptionChannel); + // Credentials + const credentialsChannel = ProxyChannel.fromService(accessor.get(ICredentialsMainService)); + mainProcessElectronServer.registerChannel('credentials', credentialsChannel); + // Signing const signChannel = ProxyChannel.fromService(accessor.get(ISignService)); mainProcessElectronServer.registerChannel('sign', signChannel); @@ -1033,7 +1129,7 @@ export class CodeApplication extends Disposable { private async resolveShellEnvironment(args: NativeParsedArgs, env: IProcessEnvironment, notifyOnError: boolean): Promise { try { - return await resolveShellEnv(this.logService, args, env); + return await getResolvedShellEnv(this.logService, args, env); } catch (error) { const errorMessage = toErrorMessage(error); if (notifyOnError) { @@ -1047,7 +1143,7 @@ export class CodeApplication extends Disposable { } private stopTracingEventually(accessor: ServicesAccessor, windows: ICodeWindow[]): void { - this.logService.info(`Tracing: waiting for windows to get ready...`); + this.logService.info('Tracing: waiting for windows to get ready...'); const dialogMainService = accessor.get(IDialogMainService); @@ -1059,7 +1155,7 @@ export class CodeApplication extends Disposable { recordingStopped = true; // only once - const path = await contentTracing.stopRecording(joinPath(this.environmentMainService.userHome, `${this.productService.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`).fsPath); + const path = await contentTracing.stopRecording(`${randomPath(this.environmentMainService.userHome.fsPath, this.productService.applicationName)}.trace.txt`); if (!timeout) { dialogMainService.showMessageBox({ diff --git a/src/vs/code/electron-main/auth.ts b/src/vs/code/electron-main/auth.ts index 859dc950f8c6c..9e56bcfa902f0 100644 --- a/src/vs/code/electron-main/auth.ts +++ b/src/vs/code/electron-main/auth.ts @@ -9,9 +9,9 @@ import { Event } from 'vs/base/common/event'; import { hash } from 'vs/base/common/hash'; import { Disposable } from 'vs/base/common/lifecycle'; import { generateUuid } from 'vs/base/common/uuid'; -import { IEncryptionMainService } from 'vs/platform/encryption/electron-main/encryptionMainService'; +import { ICredentialsMainService } from 'vs/platform/credentials/common/credentials'; +import { IEncryptionMainService } from 'vs/platform/encryption/common/encryptionService'; import { ILogService } from 'vs/platform/log/common/log'; -import { INativeHostMainService } from 'vs/platform/native/electron-main/nativeHostMainService'; import { IProductService } from 'vs/platform/product/common/productService'; import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; @@ -67,7 +67,7 @@ export class ProxyAuthHandler extends Disposable { constructor( @ILogService private readonly logService: ILogService, @IWindowsMainService private readonly windowsMainService: IWindowsMainService, - @INativeHostMainService private readonly nativeHostMainService: INativeHostMainService, + @ICredentialsMainService private readonly credentialsService: ICredentialsMainService, @IEncryptionMainService private readonly encryptionMainService: IEncryptionMainService, @IProductService private readonly productService: IProductService ) { @@ -154,7 +154,7 @@ export class ProxyAuthHandler extends Disposable { let storedUsername: string | undefined = undefined; let storedPassword: string | undefined = undefined; try { - const encryptedSerializedProxyCredentials = await this.nativeHostMainService.getPassword(undefined, this.PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash); + const encryptedSerializedProxyCredentials = await this.credentialsService.getPassword(this.PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash); if (encryptedSerializedProxyCredentials) { const credentials: Credentials = JSON.parse(await this.encryptionMainService.decrypt(encryptedSerializedProxyCredentials)); @@ -212,9 +212,9 @@ export class ProxyAuthHandler extends Disposable { try { if (reply.remember) { const encryptedSerializedCredentials = await this.encryptionMainService.encrypt(JSON.stringify(credentials)); - await this.nativeHostMainService.setPassword(undefined, this.PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash, encryptedSerializedCredentials); + await this.credentialsService.setPassword(this.PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash, encryptedSerializedCredentials); } else { - await this.nativeHostMainService.deletePassword(undefined, this.PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash); + await this.credentialsService.deletePassword(this.PROXY_CREDENTIALS_SERVICE_KEY, authInfoHash); } } catch (error) { this.logService.error(error); // handle gracefully diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 6340ed52eabfc..4ba46359de9d0 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -26,6 +26,7 @@ import { CodeApplication } from 'vs/code/electron-main/app'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; +import { IDiagnosticsMainService } from 'vs/platform/diagnostics/electron-main/diagnosticsMainService'; import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { EnvironmentMainService, IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; @@ -48,8 +49,8 @@ import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; import { IProtocolMainService } from 'vs/platform/protocol/electron-main/protocol'; import { ProtocolMainService } from 'vs/platform/protocol/electron-main/protocolMainService'; -import { ITunnelService } from 'vs/platform/remote/common/tunnel'; -import { TunnelService } from 'vs/platform/remote/node/tunnelService'; +import { ITunnelService } from 'vs/platform/tunnel/common/tunnel'; +import { TunnelService } from 'vs/platform/tunnel/node/tunnelService'; import { IRequestService } from 'vs/platform/request/common/request'; import { RequestMainService } from 'vs/platform/request/electron-main/requestMainService'; import { ISignService } from 'vs/platform/sign/common/sign'; @@ -310,14 +311,15 @@ class CodeMain { }, 10000); } - const launchService = ProxyChannel.toService(client.getChannel('launch'), { disableMarshalling: true }); + const otherInstanceLaunchMainService = ProxyChannel.toService(client.getChannel('launch'), { disableMarshalling: true }); + const otherInstanceDiagnosticsMainService = ProxyChannel.toService(client.getChannel('diagnostics'), { disableMarshalling: true }); // Process Info if (environmentMainService.args.status) { return instantiationService.invokeFunction(async () => { const diagnosticsService = new DiagnosticsService(NullTelemetryService, productService); - const mainProcessInfo = await launchService.getMainProcessInfo(); - const remoteDiagnostics = await launchService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true }); + const mainProcessInfo = await otherInstanceLaunchMainService.getMainProcessInfo(); + const remoteDiagnostics = await otherInstanceDiagnosticsMainService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true }); const diagnostics = await diagnosticsService.getDiagnostics(mainProcessInfo, remoteDiagnostics); console.log(diagnostics); @@ -327,12 +329,12 @@ class CodeMain { // Windows: allow to set foreground if (isWindows) { - await this.windowsAllowSetForegroundWindow(launchService, logService); + await this.windowsAllowSetForegroundWindow(otherInstanceLaunchMainService, logService); } // Send environment over... logService.trace('Sending env to running instance...'); - await launchService.start(environmentMainService.args, process.env as IProcessEnvironment); + await otherInstanceLaunchMainService.start(environmentMainService.args, process.env as IProcessEnvironment); // Cleanup client.dispose(); diff --git a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts index fcd44854ede91..4fb10a1b5f3cf 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts @@ -3,9 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./media/issueReporter'; +import 'vs/base/browser/ui/codicons/codiconStyles'; // make sure codicon css is loaded +import { localize } from 'vs/nls'; import { $, reset, safeInnerHtml, windowOpenNoOpener } from 'vs/base/browser/dom'; import { Button } from 'vs/base/browser/ui/button/button'; -import 'vs/base/browser/ui/codicons/codiconStyles'; // make sure codicon css is loaded import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { Delayer } from 'vs/base/common/async'; import { Codicon } from 'vs/base/common/codicons'; @@ -17,12 +19,9 @@ import { escape } from 'vs/base/common/strings'; import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IssueReporterData as IssueReporterModelData, IssueReporterModel } from 'vs/code/electron-sandbox/issue/issueReporterModel'; import BaseHtml from 'vs/code/electron-sandbox/issue/issueReporterPage'; -import 'vs/css!./media/issueReporter'; -import { localize } from 'vs/nls'; import { isRemoteDiagnosticError, SystemInfo } from 'vs/platform/diagnostics/common/diagnostics'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ElectronIPCMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; -import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; + import { IssueReporterData, IssueReporterExtensionData, IssueReporterStyles, IssueReporterWindowConfiguration, IssueType } from 'vs/platform/issue/common/issue'; import { normalizeGitHubUrl } from 'vs/platform/issue/common/issueReporterUtil'; import { INativeHostService } from 'vs/platform/native/electron-sandbox/native'; @@ -70,7 +69,8 @@ export class IssueReporter extends Disposable { constructor(private readonly configuration: IssueReporterWindowConfiguration) { super(); - this.initServices(configuration); + const mainProcessService = new ElectronIPCMainProcessService(configuration.windowId); + this.nativeHostService = new NativeHostService(configuration.windowId, mainProcessService) as INativeHostService; const targetExtension = configuration.data.extensionId ? configuration.data.enabledExtensions.find(extension => extension.id === configuration.data.extensionId) : undefined; this.issueReporterModel = new IssueReporterModel({ @@ -255,15 +255,6 @@ export class IssueReporter extends Disposable { this.updateExtensionSelector(installedExtensions); } - private initServices(configuration: IssueReporterWindowConfiguration): void { - const serviceCollection = new ServiceCollection(); - const mainProcessService = new ElectronIPCMainProcessService(configuration.windowId); - serviceCollection.set(IMainProcessService, mainProcessService); - - this.nativeHostService = new NativeHostService(configuration.windowId, mainProcessService) as INativeHostService; - serviceCollection.set(INativeHostService, this.nativeHostService); - } - private setEventHandlers(): void { this.addEventListener('issue-type', 'change', (event: Event) => { const issueType = parseInt((event.target).value); diff --git a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts index 5f2040266cfc2..fad45e924746c 100644 --- a/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-sandbox/processExplorer/processExplorerMain.ts @@ -3,8 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { $, append, createStyleSheet } from 'vs/base/browser/dom'; +import 'vs/css!./media/processExplorer'; import 'vs/base/browser/ui/codicons/codiconStyles'; // make sure codicon css is loaded +import { localize } from 'vs/nls'; +import { $, append, createStyleSheet } from 'vs/base/browser/dom'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { DataTree } from 'vs/base/browser/ui/tree/dataTree'; import { IDataSource, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; @@ -13,8 +15,6 @@ import { ProcessItem } from 'vs/base/common/processes'; import { IContextMenuItem } from 'vs/base/parts/contextmenu/common/contextmenu'; import { popup } from 'vs/base/parts/contextmenu/electron-sandbox/contextmenu'; import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; -import 'vs/css!./media/processExplorer'; -import { localize } from 'vs/nls'; import { IRemoteDiagnosticError, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; import { ByteSize } from 'vs/platform/files/common/files'; import { ElectronIPCMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService'; @@ -265,8 +265,6 @@ class ProcessExplorer { this.lastRequestTime = Date.now(); ipcRenderer.send('vscode:windowsInfoRequest'); ipcRenderer.send('vscode:listProcesses'); - - } private setEventHandlers(data: ProcessExplorerData): void { @@ -452,9 +450,23 @@ class ProcessExplorer { items.push({ label: localize('copy', "Copy"), click: () => { - const row = document.getElementById(`pid-${pid}`); - if (row) { - this.nativeHostService.writeClipboardText(row.innerText); + // Collect the selected pids + const selectionPids = this.tree?.getSelection()?.map(e => { + if (!e || !('pid' in e)) { + return undefined; + } + return e.pid; + }).filter(e => !!e) as number[]; + // If the selection does not contain the right clicked item, copy the right clicked + // item only. + if (!selectionPids?.includes(pid)) { + selectionPids.length = 0; + selectionPids.push(pid); + } + const rows = selectionPids?.map(e => document.getElementById(`pid-${e}`)).filter(e => !!e) as HTMLElement[]; + if (rows) { + const text = rows.map(e => e.innerText).filter(e => !!e) as string[]; + this.nativeHostService.writeClipboardText(text.join('\n')); } } }); @@ -506,7 +518,7 @@ function createCodiconStyleSheet() { const codiconStyleSheet = createStyleSheet(); codiconStyleSheet.id = 'codiconStyles'; - const iconsStyleSheet = getIconsStyleSheet(); + const iconsStyleSheet = getIconsStyleSheet(undefined); function updateAll() { codiconStyleSheet.textContent = iconsStyleSheet.getCSS(); } diff --git a/src/vs/code/electron-sandbox/workbench/workbench.html b/src/vs/code/electron-sandbox/workbench/workbench.html index f76e077595219..47066f520be18 100644 --- a/src/vs/code/electron-sandbox/workbench/workbench.html +++ b/src/vs/code/electron-sandbox/workbench/workbench.html @@ -3,8 +3,8 @@ - - + + diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 6ece06fd6e3cd..8aac55005d43c 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -8,13 +8,13 @@ import { chmodSync, existsSync, readFileSync, statSync, truncateSync, unlinkSync import { homedir, release, tmpdir } from 'os'; import type { ProfilingSession, Target } from 'v8-inspect-profiler'; import { Event } from 'vs/base/common/event'; -import { isAbsolute, join, resolve } from 'vs/base/common/path'; +import { isAbsolute, resolve } from 'vs/base/common/path'; import { IProcessEnvironment, isMacintosh, isWindows } from 'vs/base/common/platform'; import { randomPort } from 'vs/base/common/ports'; import { isString } from 'vs/base/common/types'; import { whenDeleted, writeFileSync } from 'vs/base/node/pfs'; import { findFreePort } from 'vs/base/node/ports'; -import { watchFileContents } from 'vs/base/node/watcher'; +import { watchFileContents } from 'vs/platform/files/node/watcher/nodejs/nodejsWatcherLib'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { buildHelpMessage, buildVersionMessage, OPTIONS } from 'vs/platform/environment/node/argv'; import { addArg, parseCLIProcessArgv } from 'vs/platform/environment/node/argvHelper'; @@ -22,6 +22,8 @@ import { getStdinFilePath, hasStdinWithoutTty, readFromStdin, stdinDataListener import { createWaitMarkerFile } from 'vs/platform/environment/node/wait'; import product from 'vs/platform/product/common/product'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { randomPath } from 'vs/base/common/extpath'; +import { Utils } from 'vs/platform/profiling/common/profiling'; function shouldSpawnCliProcess(argv: NativeParsedArgs): boolean { return !!argv['install-source'] @@ -32,10 +34,6 @@ function shouldSpawnCliProcess(argv: NativeParsedArgs): boolean { || !!argv['telemetry']; } -function createFileName(dir: string, prefix: string): string { - return join(dir, `${prefix}-${Math.random().toString(16).slice(-4)}`); -} - interface IMainCli { main: (argv: NativeParsedArgs) => Promise; } @@ -259,7 +257,7 @@ export async function main(argv: string[]): Promise { throw new Error('Failed to find free ports for profiler. Make sure to shutdown all instances of the editor first.'); } - const filenamePrefix = createFileName(homedir(), 'prof'); + const filenamePrefix = randomPath(homedir(), 'prof'); addArg(argv, `--inspect-brk=${portMain}`); addArg(argv, `--remote-debugging-port=${portRenderer}`); @@ -288,17 +286,17 @@ export async function main(argv: string[]): Promise { return; } let suffix = ''; - let profile = await session.stop(); + let result = await session.stop(); if (!process.env['VSCODE_DEV']) { // when running from a not-development-build we remove // absolute filenames because we don't want to reveal anything // about users. We also append the `.txt` suffix to make it // easier to attach these files to GH issues - profile = profiler.rewriteAbsolutePaths(profile, 'piiRemoved'); + result.profile = Utils.rewriteAbsolutePaths(result.profile, 'piiRemoved'); suffix = '.txt'; } - await profiler.writeProfile(profile, `${filenamePrefix}.${name}.cpuprofile${suffix}`); + writeFileSync(`${filenamePrefix}.${name}.cpuprofile${suffix}`, JSON.stringify(result.profile, undefined, 4)); } }; } @@ -393,7 +391,7 @@ export async function main(argv: string[]): Promise { for (const outputType of ['stdout', 'stderr']) { // Tmp file to target output to - const tmpName = createFileName(tmpdir(), `code-${outputType}`); + const tmpName = randomPath(tmpdir(), `code-${outputType}`); writeFileSync(tmpName, ''); spawnArgs.push(`--${outputType}`, tmpName); @@ -403,8 +401,12 @@ export async function main(argv: string[]): Promise { const stream = outputType === 'stdout' ? process.stdout : process.stderr; const cts = new CancellationTokenSource(); - child.on('close', () => cts.dispose(true)); - await watchFileContents(tmpName, chunk => stream.write(chunk), cts.token); + child.on('close', () => { + // We must dispose the token to stop watching, + // but the watcher might still be reading data. + setTimeout(() => cts.dispose(true), 200); + }); + await watchFileContents(tmpName, chunk => stream.write(chunk), () => { /* ignore */ }, cts.token); } finally { unlinkSync(tmpName); } diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index fdee012d7ea10..f5a699ea72e87 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -22,7 +22,7 @@ import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { ExtensionGalleryServiceWithNoStorageService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; -import { IExtensionGalleryService, IExtensionManagementCLIService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionGalleryService, IExtensionManagementCLIService, IExtensionManagementService, InstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementCLIService } from 'vs/platform/extensionManagement/common/extensionManagementCLIService'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { IFileService } from 'vs/platform/files/common/files'; @@ -217,7 +217,8 @@ class CliMain extends Disposable { // Install Extension else if (this.argv['install-extension'] || this.argv['install-builtin-extension']) { - return extensionManagementCLIService.installExtensions(this.asExtensionIdOrVSIX(this.argv['install-extension'] || []), this.argv['install-builtin-extension'] || [], !!this.argv['do-not-sync'], !!this.argv['force']); + const installOptions: InstallOptions = { isMachineScoped: !!this.argv['do-not-sync'], installPreReleaseVersion: !!this.argv['pre-release'] }; + return extensionManagementCLIService.installExtensions(this.asExtensionIdOrVSIX(this.argv['install-extension'] || []), this.argv['install-builtin-extension'] || [], installOptions, !!this.argv['force']); } // Uninstall Extension diff --git a/src/vs/code/electron-sandbox/issue/test/testReporterModel.test.ts b/src/vs/code/test/electron-sandbox/issue/testReporterModel.test.ts similarity index 100% rename from src/vs/code/electron-sandbox/issue/test/testReporterModel.test.ts rename to src/vs/code/test/electron-sandbox/issue/testReporterModel.test.ts diff --git a/src/vs/css.build.js b/src/vs/css.build.js index 5ad45ba6a0d44..c8875859684aa 100644 --- a/src/vs/css.build.js +++ b/src/vs/css.build.js @@ -18,10 +18,6 @@ var _cssPluginGlobal = this; var CSSBuildLoaderPlugin; (function (CSSBuildLoaderPlugin) { var global = (_cssPluginGlobal || {}); - /** - * Known issue: - * - In IE there is no way to know if the CSS file loaded successfully or not. - */ var BrowserCSSLoader = /** @class */ (function () { function BrowserCSSLoader() { this._pendingLoads = 0; @@ -328,6 +324,7 @@ var CSSBuildLoaderPlugin; // .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris var newText = fileContents.toString() .replace(/"/g, '\'') + .replace(/%/g, '%25') .replace(//g, '%3E') .replace(/&/g, '%26') diff --git a/src/vs/editor/browser/config/charWidthReader.ts b/src/vs/editor/browser/config/charWidthReader.ts index 0493c5cddb0c0..16b6d7d1c48d4 100644 --- a/src/vs/editor/browser/config/charWidthReader.ts +++ b/src/vs/editor/browser/config/charWidthReader.ts @@ -3,8 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isSafari } from 'vs/base/browser/browser'; -import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; +import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; export const enum CharWidthRequestType { @@ -64,38 +63,22 @@ class DomCharWidthReader { } private _createDomElements(): void { - const fontFamily = this._bareFontInfo.getMassagedFontFamily(isSafari ? EDITOR_FONT_DEFAULTS.fontFamily : null); - const container = document.createElement('div'); container.style.position = 'absolute'; container.style.top = '-50000px'; container.style.width = '50000px'; const regularDomNode = document.createElement('div'); - regularDomNode.style.fontFamily = fontFamily; - regularDomNode.style.fontWeight = this._bareFontInfo.fontWeight; - regularDomNode.style.fontSize = this._bareFontInfo.fontSize + 'px'; - regularDomNode.style.fontFeatureSettings = this._bareFontInfo.fontFeatureSettings; - regularDomNode.style.lineHeight = this._bareFontInfo.lineHeight + 'px'; - regularDomNode.style.letterSpacing = this._bareFontInfo.letterSpacing + 'px'; + applyFontInfo(regularDomNode, this._bareFontInfo); container.appendChild(regularDomNode); const boldDomNode = document.createElement('div'); - boldDomNode.style.fontFamily = fontFamily; + applyFontInfo(boldDomNode, this._bareFontInfo); boldDomNode.style.fontWeight = 'bold'; - boldDomNode.style.fontSize = this._bareFontInfo.fontSize + 'px'; - boldDomNode.style.fontFeatureSettings = this._bareFontInfo.fontFeatureSettings; - boldDomNode.style.lineHeight = this._bareFontInfo.lineHeight + 'px'; - boldDomNode.style.letterSpacing = this._bareFontInfo.letterSpacing + 'px'; container.appendChild(boldDomNode); const italicDomNode = document.createElement('div'); - italicDomNode.style.fontFamily = fontFamily; - italicDomNode.style.fontWeight = this._bareFontInfo.fontWeight; - italicDomNode.style.fontSize = this._bareFontInfo.fontSize + 'px'; - italicDomNode.style.fontFeatureSettings = this._bareFontInfo.fontFeatureSettings; - italicDomNode.style.lineHeight = this._bareFontInfo.lineHeight + 'px'; - italicDomNode.style.letterSpacing = this._bareFontInfo.letterSpacing + 'px'; + applyFontInfo(italicDomNode, this._bareFontInfo); italicDomNode.style.fontStyle = 'italic'; container.appendChild(italicDomNode); diff --git a/src/vs/editor/browser/config/configuration.ts b/src/vs/editor/browser/config/configuration.ts deleted file mode 100644 index f4175c0bf63a5..0000000000000 --- a/src/vs/editor/browser/config/configuration.ts +++ /dev/null @@ -1,383 +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 browser from 'vs/base/browser/browser'; -import { FastDomNode } from 'vs/base/browser/fastDomNode'; -import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable } from 'vs/base/common/lifecycle'; -import * as platform from 'vs/base/common/platform'; -import { CharWidthRequest, CharWidthRequestType, readCharWidths } from 'vs/editor/browser/config/charWidthReader'; -import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; -import { CommonEditorConfiguration, IEnvConfiguration } from 'vs/editor/common/config/commonEditorConfig'; -import { EditorOption, EditorFontLigatures, EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; -import { BareFontInfo, FontInfo, SERIALIZED_FONT_INFO_VERSION } from 'vs/editor/common/config/fontInfo'; -import { IDimension } from 'vs/editor/common/editorCommon'; -import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; -import { IEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; - -class CSSBasedConfigurationCache { - - private readonly _keys: { [key: string]: BareFontInfo; }; - private readonly _values: { [key: string]: FontInfo; }; - - constructor() { - this._keys = Object.create(null); - this._values = Object.create(null); - } - - public has(item: BareFontInfo): boolean { - const itemId = item.getId(); - return !!this._values[itemId]; - } - - public get(item: BareFontInfo): FontInfo { - const itemId = item.getId(); - return this._values[itemId]; - } - - public put(item: BareFontInfo, value: FontInfo): void { - const itemId = item.getId(); - this._keys[itemId] = item; - this._values[itemId] = value; - } - - public remove(item: BareFontInfo): void { - const itemId = item.getId(); - delete this._keys[itemId]; - delete this._values[itemId]; - } - - public getValues(): FontInfo[] { - return Object.keys(this._keys).map(id => this._values[id]); - } -} - -export function clearAllFontInfos(): void { - CSSBasedConfiguration.INSTANCE.clearCache(); -} - -export function readFontInfo(bareFontInfo: BareFontInfo): FontInfo { - return CSSBasedConfiguration.INSTANCE.readConfiguration(bareFontInfo); -} - -export function restoreFontInfo(fontInfo: ISerializedFontInfo[]): void { - CSSBasedConfiguration.INSTANCE.restoreFontInfo(fontInfo); -} - -export function serializeFontInfo(): ISerializedFontInfo[] | null { - const fontInfo = CSSBasedConfiguration.INSTANCE.saveFontInfo(); - if (fontInfo.length > 0) { - return fontInfo; - } - - return null; -} - -export interface ISerializedFontInfo { - readonly version: number; - readonly zoomLevel: number; - readonly pixelRatio: number; - readonly fontFamily: string; - readonly fontWeight: string; - readonly fontSize: number; - readonly fontFeatureSettings: string; - readonly lineHeight: number; - readonly letterSpacing: number; - readonly isMonospace: boolean; - readonly typicalHalfwidthCharacterWidth: number; - readonly typicalFullwidthCharacterWidth: number; - readonly canUseHalfwidthRightwardsArrow: boolean; - readonly spaceWidth: number; - readonly middotWidth: number; - readonly wsmiddotWidth: number; - readonly maxDigitWidth: number; -} - -class CSSBasedConfiguration extends Disposable { - - public static readonly INSTANCE = new CSSBasedConfiguration(); - - private _cache: CSSBasedConfigurationCache; - private _evictUntrustedReadingsTimeout: any; - - private _onDidChange = this._register(new Emitter()); - public readonly onDidChange: Event = this._onDidChange.event; - - constructor() { - super(); - - this._cache = new CSSBasedConfigurationCache(); - this._evictUntrustedReadingsTimeout = -1; - } - - public override dispose(): void { - if (this._evictUntrustedReadingsTimeout !== -1) { - clearTimeout(this._evictUntrustedReadingsTimeout); - this._evictUntrustedReadingsTimeout = -1; - } - super.dispose(); - } - - public clearCache(): void { - this._cache = new CSSBasedConfigurationCache(); - this._onDidChange.fire(); - } - - private _writeToCache(item: BareFontInfo, value: FontInfo): void { - this._cache.put(item, value); - - if (!value.isTrusted && this._evictUntrustedReadingsTimeout === -1) { - // Try reading again after some time - this._evictUntrustedReadingsTimeout = setTimeout(() => { - this._evictUntrustedReadingsTimeout = -1; - this._evictUntrustedReadings(); - }, 5000); - } - } - - private _evictUntrustedReadings(): void { - const values = this._cache.getValues(); - let somethingRemoved = false; - for (const item of values) { - if (!item.isTrusted) { - somethingRemoved = true; - this._cache.remove(item); - } - } - if (somethingRemoved) { - this._onDidChange.fire(); - } - } - - public saveFontInfo(): ISerializedFontInfo[] { - // Only save trusted font info (that has been measured in this running instance) - return this._cache.getValues().filter(item => item.isTrusted); - } - - public restoreFontInfo(savedFontInfos: ISerializedFontInfo[]): void { - // Take all the saved font info and insert them in the cache without the trusted flag. - // The reason for this is that a font might have been installed on the OS in the meantime. - for (const savedFontInfo of savedFontInfos) { - if (savedFontInfo.version !== SERIALIZED_FONT_INFO_VERSION) { - // cannot use older version - continue; - } - const fontInfo = new FontInfo(savedFontInfo, false); - this._writeToCache(fontInfo, fontInfo); - } - } - - public readConfiguration(bareFontInfo: BareFontInfo): FontInfo { - if (!this._cache.has(bareFontInfo)) { - let readConfig = CSSBasedConfiguration._actualReadConfiguration(bareFontInfo); - - if (readConfig.typicalHalfwidthCharacterWidth <= 2 || readConfig.typicalFullwidthCharacterWidth <= 2 || readConfig.spaceWidth <= 2 || readConfig.maxDigitWidth <= 2) { - // Hey, it's Bug 14341 ... we couldn't read - readConfig = new FontInfo({ - zoomLevel: browser.getZoomLevel(), - pixelRatio: browser.getPixelRatio(), - fontFamily: readConfig.fontFamily, - fontWeight: readConfig.fontWeight, - fontSize: readConfig.fontSize, - fontFeatureSettings: readConfig.fontFeatureSettings, - lineHeight: readConfig.lineHeight, - letterSpacing: readConfig.letterSpacing, - isMonospace: readConfig.isMonospace, - typicalHalfwidthCharacterWidth: Math.max(readConfig.typicalHalfwidthCharacterWidth, 5), - typicalFullwidthCharacterWidth: Math.max(readConfig.typicalFullwidthCharacterWidth, 5), - canUseHalfwidthRightwardsArrow: readConfig.canUseHalfwidthRightwardsArrow, - spaceWidth: Math.max(readConfig.spaceWidth, 5), - middotWidth: Math.max(readConfig.middotWidth, 5), - wsmiddotWidth: Math.max(readConfig.wsmiddotWidth, 5), - maxDigitWidth: Math.max(readConfig.maxDigitWidth, 5), - }, false); - } - - this._writeToCache(bareFontInfo, readConfig); - } - return this._cache.get(bareFontInfo); - } - - private static createRequest(chr: string, type: CharWidthRequestType, all: CharWidthRequest[], monospace: CharWidthRequest[] | null): CharWidthRequest { - const result = new CharWidthRequest(chr, type); - all.push(result); - if (monospace) { - monospace.push(result); - } - return result; - } - - private static _actualReadConfiguration(bareFontInfo: BareFontInfo): FontInfo { - const all: CharWidthRequest[] = []; - const monospace: CharWidthRequest[] = []; - - const typicalHalfwidthCharacter = this.createRequest('n', CharWidthRequestType.Regular, all, monospace); - const typicalFullwidthCharacter = this.createRequest('\uff4d', CharWidthRequestType.Regular, all, null); - const space = this.createRequest(' ', CharWidthRequestType.Regular, all, monospace); - const digit0 = this.createRequest('0', CharWidthRequestType.Regular, all, monospace); - const digit1 = this.createRequest('1', CharWidthRequestType.Regular, all, monospace); - const digit2 = this.createRequest('2', CharWidthRequestType.Regular, all, monospace); - const digit3 = this.createRequest('3', CharWidthRequestType.Regular, all, monospace); - const digit4 = this.createRequest('4', CharWidthRequestType.Regular, all, monospace); - const digit5 = this.createRequest('5', CharWidthRequestType.Regular, all, monospace); - const digit6 = this.createRequest('6', CharWidthRequestType.Regular, all, monospace); - const digit7 = this.createRequest('7', CharWidthRequestType.Regular, all, monospace); - const digit8 = this.createRequest('8', CharWidthRequestType.Regular, all, monospace); - const digit9 = this.createRequest('9', CharWidthRequestType.Regular, all, monospace); - - // monospace test: used for whitespace rendering - const rightwardsArrow = this.createRequest('→', CharWidthRequestType.Regular, all, monospace); - const halfwidthRightwardsArrow = this.createRequest('→', CharWidthRequestType.Regular, all, null); - - // U+00B7 - MIDDLE DOT - const middot = this.createRequest('·', CharWidthRequestType.Regular, all, monospace); - - // U+2E31 - WORD SEPARATOR MIDDLE DOT - const wsmiddotWidth = this.createRequest(String.fromCharCode(0x2E31), CharWidthRequestType.Regular, all, null); - - // monospace test: some characters - const monospaceTestChars = '|/-_ilm%'; - for (let i = 0, len = monospaceTestChars.length; i < len; i++) { - this.createRequest(monospaceTestChars.charAt(i), CharWidthRequestType.Regular, all, monospace); - this.createRequest(monospaceTestChars.charAt(i), CharWidthRequestType.Italic, all, monospace); - this.createRequest(monospaceTestChars.charAt(i), CharWidthRequestType.Bold, all, monospace); - - } - - readCharWidths(bareFontInfo, all); - - const maxDigitWidth = Math.max(digit0.width, digit1.width, digit2.width, digit3.width, digit4.width, digit5.width, digit6.width, digit7.width, digit8.width, digit9.width); - - let isMonospace = (bareFontInfo.fontFeatureSettings === EditorFontLigatures.OFF); - const referenceWidth = monospace[0].width; - for (let i = 1, len = monospace.length; isMonospace && i < len; i++) { - const diff = referenceWidth - monospace[i].width; - if (diff < -0.001 || diff > 0.001) { - isMonospace = false; - break; - } - } - - let canUseHalfwidthRightwardsArrow = true; - if (isMonospace && halfwidthRightwardsArrow.width !== referenceWidth) { - // using a halfwidth rightwards arrow would break monospace... - canUseHalfwidthRightwardsArrow = false; - } - if (halfwidthRightwardsArrow.width > rightwardsArrow.width) { - // using a halfwidth rightwards arrow would paint a larger arrow than a regular rightwards arrow - canUseHalfwidthRightwardsArrow = false; - } - - // let's trust the zoom level only 2s after it was changed. - const canTrustBrowserZoomLevel = (browser.getTimeSinceLastZoomLevelChanged() > 2000); - return new FontInfo({ - zoomLevel: browser.getZoomLevel(), - pixelRatio: browser.getPixelRatio(), - fontFamily: bareFontInfo.fontFamily, - fontWeight: bareFontInfo.fontWeight, - fontSize: bareFontInfo.fontSize, - fontFeatureSettings: bareFontInfo.fontFeatureSettings, - lineHeight: bareFontInfo.lineHeight, - letterSpacing: bareFontInfo.letterSpacing, - isMonospace: isMonospace, - typicalHalfwidthCharacterWidth: typicalHalfwidthCharacter.width, - typicalFullwidthCharacterWidth: typicalFullwidthCharacter.width, - canUseHalfwidthRightwardsArrow: canUseHalfwidthRightwardsArrow, - spaceWidth: space.width, - middotWidth: middot.width, - wsmiddotWidth: wsmiddotWidth.width, - maxDigitWidth: maxDigitWidth - }, canTrustBrowserZoomLevel); - } -} - -export class Configuration extends CommonEditorConfiguration { - - public static applyFontInfoSlow(domNode: HTMLElement, fontInfo: BareFontInfo): void { - domNode.style.fontFamily = fontInfo.getMassagedFontFamily(browser.isSafari ? EDITOR_FONT_DEFAULTS.fontFamily : null); - domNode.style.fontWeight = fontInfo.fontWeight; - domNode.style.fontSize = fontInfo.fontSize + 'px'; - domNode.style.fontFeatureSettings = fontInfo.fontFeatureSettings; - domNode.style.lineHeight = fontInfo.lineHeight + 'px'; - domNode.style.letterSpacing = fontInfo.letterSpacing + 'px'; - } - - public static applyFontInfo(domNode: FastDomNode, fontInfo: BareFontInfo): void { - domNode.setFontFamily(fontInfo.getMassagedFontFamily(browser.isSafari ? EDITOR_FONT_DEFAULTS.fontFamily : null)); - domNode.setFontWeight(fontInfo.fontWeight); - domNode.setFontSize(fontInfo.fontSize); - domNode.setFontFeatureSettings(fontInfo.fontFeatureSettings); - domNode.setLineHeight(fontInfo.lineHeight); - domNode.setLetterSpacing(fontInfo.letterSpacing); - } - - private readonly _elementSizeObserver: ElementSizeObserver; - - constructor( - isSimpleWidget: boolean, - options: Readonly, - referenceDomElement: HTMLElement | null = null, - private readonly accessibilityService: IAccessibilityService - ) { - super(isSimpleWidget, options); - - this._elementSizeObserver = this._register(new ElementSizeObserver(referenceDomElement, options.dimension, () => this._recomputeOptions())); - - this._register(CSSBasedConfiguration.INSTANCE.onDidChange(() => this._recomputeOptions())); - - if (this._validatedOptions.get(EditorOption.automaticLayout)) { - this._elementSizeObserver.startObserving(); - } - - this._register(browser.onDidChangeZoomLevel(_ => this._recomputeOptions())); - this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => this._recomputeOptions())); - - this._recomputeOptions(); - } - - public override observeReferenceElement(dimension?: IDimension): void { - this._elementSizeObserver.observe(dimension); - } - - public override updatePixelRatio(): void { - this._recomputeOptions(); - } - - private static _getExtraEditorClassName(): string { - let extra = ''; - if (!browser.isSafari && !browser.isWebkitWebView) { - // Use user-select: none in all browsers except Safari and native macOS WebView - extra += 'no-user-select '; - } - if (browser.isSafari) { - // See https://github.com/microsoft/vscode/issues/108822 - extra += 'no-minimap-shadow '; - } - if (platform.isMacintosh) { - extra += 'mac '; - } - return extra; - } - - protected _getEnvConfiguration(): IEnvConfiguration { - return { - extraEditorClassName: Configuration._getExtraEditorClassName(), - outerWidth: this._elementSizeObserver.getWidth(), - outerHeight: this._elementSizeObserver.getHeight(), - emptySelectionClipboard: browser.isWebKit || browser.isFirefox, - pixelRatio: browser.getPixelRatio(), - zoomLevel: browser.getZoomLevel(), - accessibilitySupport: ( - this.accessibilityService.isScreenReaderOptimized() - ? AccessibilitySupport.Enabled - : this.accessibilityService.getAccessibilitySupport() - ) - }; - } - - protected readConfiguration(bareFontInfo: BareFontInfo): FontInfo { - return CSSBasedConfiguration.INSTANCE.readConfiguration(bareFontInfo); - } -} diff --git a/src/vs/editor/browser/config/domFontInfo.ts b/src/vs/editor/browser/config/domFontInfo.ts new file mode 100644 index 0000000000000..96c7021e53b82 --- /dev/null +++ b/src/vs/editor/browser/config/domFontInfo.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 * as browser from 'vs/base/browser/browser'; +import { FastDomNode } from 'vs/base/browser/fastDomNode'; +import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; +import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; + +export function applyFontInfo(domNode: FastDomNode | HTMLElement, fontInfo: BareFontInfo): void { + if (domNode instanceof FastDomNode) { + domNode.setFontFamily(fontInfo.getMassagedFontFamily(browser.isSafari ? EDITOR_FONT_DEFAULTS.fontFamily : null)); + domNode.setFontWeight(fontInfo.fontWeight); + domNode.setFontSize(fontInfo.fontSize); + domNode.setFontFeatureSettings(fontInfo.fontFeatureSettings); + domNode.setLineHeight(fontInfo.lineHeight); + domNode.setLetterSpacing(fontInfo.letterSpacing); + } else { + domNode.style.fontFamily = fontInfo.getMassagedFontFamily(browser.isSafari ? EDITOR_FONT_DEFAULTS.fontFamily : null); + domNode.style.fontWeight = fontInfo.fontWeight; + domNode.style.fontSize = fontInfo.fontSize + 'px'; + domNode.style.fontFeatureSettings = fontInfo.fontFeatureSettings; + domNode.style.lineHeight = fontInfo.lineHeight + 'px'; + domNode.style.letterSpacing = fontInfo.letterSpacing + 'px'; + } +} diff --git a/src/vs/editor/browser/config/editorConfiguration.ts b/src/vs/editor/browser/config/editorConfiguration.ts new file mode 100644 index 0000000000000..5af58c15fd4d8 --- /dev/null +++ b/src/vs/editor/browser/config/editorConfiguration.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 * as browser from 'vs/base/browser/browser'; +import * as arrays from 'vs/base/common/arrays'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as objects from 'vs/base/common/objects'; +import * as platform from 'vs/base/common/platform'; +import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; +import { FontMeasurements } from 'vs/editor/browser/config/fontMeasurements'; +import { migrateOptions } from 'vs/editor/browser/config/migrateOptions'; +import { TabFocus } from 'vs/editor/browser/config/tabFocus'; +import { ComputeOptionsMemory, ConfigurationChangedEvent, EditorOption, editorOptionsRegistry, FindComputedEditorOptionValueById, IComputedEditorOptions, IEditorOptions, IEnvironmentalOptions } from 'vs/editor/common/config/editorOptions'; +import { EditorZoom } from 'vs/editor/common/config/editorZoom'; +import { BareFontInfo, FontInfo, IValidatedEditorOptions } from 'vs/editor/common/config/fontInfo'; +import { IDimension } from 'vs/editor/common/core/dimension'; +import { IEditorConfiguration } from 'vs/editor/common/config/editorConfiguration'; +import { AccessibilitySupport, IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; + +export interface IEditorConstructionOptions extends IEditorOptions { + /** + * The initial editor dimension (to avoid measuring the container). + */ + dimension?: IDimension; + /** + * Place overflow widgets inside an external DOM node. + * Defaults to an internal DOM node. + */ + overflowWidgetsDomNode?: HTMLElement; +} + +export class EditorConfiguration extends Disposable implements IEditorConfiguration { + + private _onDidChange = this._register(new Emitter()); + public readonly onDidChange: Event = this._onDidChange.event; + + private _onDidChangeFast = this._register(new Emitter()); + public readonly onDidChangeFast: Event = this._onDidChangeFast.event; + + public readonly isSimpleWidget: boolean; + private readonly _containerObserver: ElementSizeObserver; + + private _isDominatedByLongLines: boolean = false; + private _viewLineCount: number = 1; + private _lineNumbersDigitCount: number = 1; + private _reservedHeight: number = 0; + + private readonly _computeOptionsMemory: ComputeOptionsMemory = new ComputeOptionsMemory(); + /** + * Raw options as they were passed in and merged with all calls to `updateOptions`. + */ + private readonly _rawOptions: IEditorOptions; + /** + * Validated version of `_rawOptions`. + */ + private _validatedOptions: ValidatedEditorOptions; + /** + * Complete options which are a combination of passed in options and env values. + */ + public options: ComputedEditorOptions; + + constructor( + isSimpleWidget: boolean, + options: Readonly, + container: HTMLElement | null, + @IAccessibilityService private readonly _accessibilityService: IAccessibilityService + ) { + super(); + this.isSimpleWidget = isSimpleWidget; + this._containerObserver = this._register(new ElementSizeObserver(container, options.dimension)); + + this._rawOptions = deepCloneAndMigrateOptions(options); + this._validatedOptions = EditorOptionsUtil.validateOptions(this._rawOptions); + this.options = this._computeOptions(); + + if (this.options.get(EditorOption.automaticLayout)) { + this._containerObserver.startObserving(); + } + + this._register(EditorZoom.onDidChangeZoomLevel(() => this._recomputeOptions())); + this._register(TabFocus.onDidChangeTabFocus(() => this._recomputeOptions())); + this._register(this._containerObserver.onDidChange(() => this._recomputeOptions())); + this._register(FontMeasurements.onDidChange(() => this._recomputeOptions())); + this._register(browser.PixelRatio.onDidChange(() => this._recomputeOptions())); + this._register(this._accessibilityService.onDidChangeScreenReaderOptimized(() => this._recomputeOptions())); + } + + private _recomputeOptions(): void { + const newOptions = this._computeOptions(); + const changeEvent = EditorOptionsUtil.checkEquals(this.options, newOptions); + if (changeEvent === null) { + // nothing changed! + return; + } + + this.options = newOptions; + this._onDidChangeFast.fire(changeEvent); + this._onDidChange.fire(changeEvent); + } + + private _computeOptions(): ComputedEditorOptions { + const partialEnv = this._readEnvConfiguration(); + const bareFontInfo = BareFontInfo.createFromValidatedSettings(this._validatedOptions, partialEnv.pixelRatio, this.isSimpleWidget); + const fontInfo = this._readFontInfo(bareFontInfo); + const env: IEnvironmentalOptions = { + memory: this._computeOptionsMemory, + outerWidth: partialEnv.outerWidth, + outerHeight: partialEnv.outerHeight - this._reservedHeight, + fontInfo: fontInfo, + extraEditorClassName: partialEnv.extraEditorClassName, + isDominatedByLongLines: this._isDominatedByLongLines, + viewLineCount: this._viewLineCount, + lineNumbersDigitCount: this._lineNumbersDigitCount, + emptySelectionClipboard: partialEnv.emptySelectionClipboard, + pixelRatio: partialEnv.pixelRatio, + tabFocusMode: TabFocus.getTabFocusMode(), + accessibilitySupport: partialEnv.accessibilitySupport + }; + return EditorOptionsUtil.computeOptions(this._validatedOptions, env); + } + + protected _readEnvConfiguration(): IEnvConfiguration { + return { + extraEditorClassName: getExtraEditorClassName(), + outerWidth: this._containerObserver.getWidth(), + outerHeight: this._containerObserver.getHeight(), + emptySelectionClipboard: browser.isWebKit || browser.isFirefox, + pixelRatio: browser.PixelRatio.value, + accessibilitySupport: ( + this._accessibilityService.isScreenReaderOptimized() + ? AccessibilitySupport.Enabled + : this._accessibilityService.getAccessibilitySupport() + ) + }; + } + + protected _readFontInfo(bareFontInfo: BareFontInfo): FontInfo { + return FontMeasurements.readFontInfo(bareFontInfo); + } + + public getRawOptions(): IEditorOptions { + return this._rawOptions; + } + + public updateOptions(_newOptions: Readonly): void { + const newOptions = deepCloneAndMigrateOptions(_newOptions); + + const didChange = EditorOptionsUtil.applyUpdate(this._rawOptions, newOptions); + if (!didChange) { + return; + } + + this._validatedOptions = EditorOptionsUtil.validateOptions(this._rawOptions); + this._recomputeOptions(); + } + + public observeContainer(dimension?: IDimension): void { + this._containerObserver.observe(dimension); + } + + public setIsDominatedByLongLines(isDominatedByLongLines: boolean): void { + if (this._isDominatedByLongLines === isDominatedByLongLines) { + return; + } + this._isDominatedByLongLines = isDominatedByLongLines; + this._recomputeOptions(); + } + + public setModelLineCount(modelLineCount: number): void { + const lineNumbersDigitCount = digitCount(modelLineCount); + if (this._lineNumbersDigitCount === lineNumbersDigitCount) { + return; + } + this._lineNumbersDigitCount = lineNumbersDigitCount; + this._recomputeOptions(); + } + + public setViewLineCount(viewLineCount: number): void { + if (this._viewLineCount === viewLineCount) { + return; + } + this._viewLineCount = viewLineCount; + this._recomputeOptions(); + } + + public setReservedHeight(reservedHeight: number) { + if (this._reservedHeight === reservedHeight) { + return; + } + this._reservedHeight = reservedHeight; + this._recomputeOptions(); + } +} + +function digitCount(n: number): number { + let r = 0; + while (n) { + n = Math.floor(n / 10); + r++; + } + return r ? r : 1; +} + +function getExtraEditorClassName(): string { + let extra = ''; + if (!browser.isSafari && !browser.isWebkitWebView) { + // Use user-select: none in all browsers except Safari and native macOS WebView + extra += 'no-user-select '; + } + if (browser.isSafari) { + // See https://github.com/microsoft/vscode/issues/108822 + extra += 'no-minimap-shadow '; + } + if (platform.isMacintosh) { + extra += 'mac '; + } + return extra; +} + +export interface IEnvConfiguration { + extraEditorClassName: string; + outerWidth: number; + outerHeight: number; + emptySelectionClipboard: boolean; + pixelRatio: number; + accessibilitySupport: AccessibilitySupport; +} + +class ValidatedEditorOptions implements IValidatedEditorOptions { + private readonly _values: any[] = []; + public _read(option: EditorOption): T { + return this._values[option]; + } + public get(id: T): FindComputedEditorOptionValueById { + return this._values[id]; + } + public _write(option: EditorOption, value: T): void { + this._values[option] = value; + } +} + +export class ComputedEditorOptions implements IComputedEditorOptions { + private readonly _values: any[] = []; + public _read(id: EditorOption): T { + if (id >= this._values.length) { + throw new Error('Cannot read uninitialized value'); + } + return this._values[id]; + } + public get(id: T): FindComputedEditorOptionValueById { + return this._read(id); + } + public _write(id: EditorOption, value: T): void { + this._values[id] = value; + } +} + +class EditorOptionsUtil { + + public static validateOptions(options: IEditorOptions): ValidatedEditorOptions { + const result = new ValidatedEditorOptions(); + for (const editorOption of editorOptionsRegistry) { + const value = (editorOption.name === '_never_' ? undefined : (options as any)[editorOption.name]); + result._write(editorOption.id, editorOption.validate(value)); + } + return result; + } + + public static computeOptions(options: ValidatedEditorOptions, env: IEnvironmentalOptions): ComputedEditorOptions { + const result = new ComputedEditorOptions(); + for (const editorOption of editorOptionsRegistry) { + result._write(editorOption.id, editorOption.compute(env, result, options._read(editorOption.id))); + } + return result; + } + + private static _deepEquals(a: T, b: T): boolean { + if (typeof a !== 'object' || typeof b !== 'object' || !a || !b) { + return a === b; + } + if (Array.isArray(a) || Array.isArray(b)) { + return (Array.isArray(a) && Array.isArray(b) ? arrays.equals(a, b) : false); + } + if (Object.keys(a).length !== Object.keys(b).length) { + return false; + } + for (const key in a) { + if (!EditorOptionsUtil._deepEquals(a[key], b[key])) { + return false; + } + } + return true; + } + + public static checkEquals(a: ComputedEditorOptions, b: ComputedEditorOptions): ConfigurationChangedEvent | null { + const result: boolean[] = []; + let somethingChanged = false; + for (const editorOption of editorOptionsRegistry) { + const changed = !EditorOptionsUtil._deepEquals(a._read(editorOption.id), b._read(editorOption.id)); + result[editorOption.id] = changed; + if (changed) { + somethingChanged = true; + } + } + return (somethingChanged ? new ConfigurationChangedEvent(result) : null); + } + + /** + * Returns true if something changed. + * Modifies `options`. + */ + public static applyUpdate(options: IEditorOptions, update: Readonly): boolean { + let changed = false; + for (const editorOption of editorOptionsRegistry) { + if (update.hasOwnProperty(editorOption.name)) { + const result = editorOption.applyUpdate((options as any)[editorOption.name], (update as any)[editorOption.name]); + (options as any)[editorOption.name] = result.newValue; + changed = changed || result.didChange; + } + } + return changed; + } +} + +function deepCloneAndMigrateOptions(_options: Readonly): IEditorOptions { + const options = objects.deepClone(_options); + migrateOptions(options); + return options; +} diff --git a/src/vs/editor/browser/config/elementSizeObserver.ts b/src/vs/editor/browser/config/elementSizeObserver.ts index cf42a075793b7..3529822e209c2 100644 --- a/src/vs/editor/browser/config/elementSizeObserver.ts +++ b/src/vs/editor/browser/config/elementSizeObserver.ts @@ -4,51 +4,25 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vs/base/common/lifecycle'; -import { IDimension } from 'vs/editor/common/editorCommon'; - -interface ResizeObserver { - observe(target: Element): void; - unobserve(target: Element): void; - disconnect(): void; -} - -interface ResizeObserverSize { - inlineSize: number; - blockSize: number; -} - -interface ResizeObserverEntry { - readonly target: Element; - readonly contentRect: DOMRectReadOnly; - readonly borderBoxSize: ResizeObserverSize; - readonly contentBoxSize: ResizeObserverSize; -} - -type ResizeObserverCallback = (entries: ReadonlyArray, observer: ResizeObserver) => void; - -declare const ResizeObserver: { - prototype: ResizeObserver; - new(callback: ResizeObserverCallback): ResizeObserver; -}; - +import { IDimension } from 'vs/editor/common/core/dimension'; +import { Emitter, Event } from 'vs/base/common/event'; export class ElementSizeObserver extends Disposable { - private readonly referenceDomElement: HTMLElement | null; - private readonly changeCallback: () => void; - private width: number; - private height: number; - private resizeObserver: ResizeObserver | null; - private measureReferenceDomElementToken: number; + private _onDidChange = this._register(new Emitter()); + public readonly onDidChange: Event = this._onDidChange.event; - constructor(referenceDomElement: HTMLElement | null, dimension: IDimension | undefined, changeCallback: () => void) { + private readonly _referenceDomElement: HTMLElement | null; + private _width: number; + private _height: number; + private _resizeObserver: ResizeObserver | null; + + constructor(referenceDomElement: HTMLElement | null, dimension: IDimension | undefined) { super(); - this.referenceDomElement = referenceDomElement; - this.changeCallback = changeCallback; - this.width = -1; - this.height = -1; - this.resizeObserver = null; - this.measureReferenceDomElementToken = -1; + this._referenceDomElement = referenceDomElement; + this._width = -1; + this._height = -1; + this._resizeObserver = null; this.measureReferenceDomElement(false, dimension); } @@ -58,41 +32,30 @@ export class ElementSizeObserver extends Disposable { } public getWidth(): number { - return this.width; + return this._width; } public getHeight(): number { - return this.height; + return this._height; } public startObserving(): void { - if (typeof ResizeObserver !== 'undefined') { - if (!this.resizeObserver && this.referenceDomElement) { - this.resizeObserver = new ResizeObserver((entries) => { - if (entries && entries[0] && entries[0].contentRect) { - this.observe({ width: entries[0].contentRect.width, height: entries[0].contentRect.height }); - } else { - this.observe(); - } - }); - this.resizeObserver.observe(this.referenceDomElement); - } - } else { - if (this.measureReferenceDomElementToken === -1) { - // setInterval type defaults to NodeJS.Timeout instead of number, so specify it as a number - this.measureReferenceDomElementToken = setInterval(() => this.observe(), 100); - } + if (!this._resizeObserver && this._referenceDomElement) { + this._resizeObserver = new ResizeObserver((entries) => { + if (entries && entries[0] && entries[0].contentRect) { + this.observe({ width: entries[0].contentRect.width, height: entries[0].contentRect.height }); + } else { + this.observe(); + } + }); + this._resizeObserver.observe(this._referenceDomElement); } } public stopObserving(): void { - if (this.resizeObserver) { - this.resizeObserver.disconnect(); - this.resizeObserver = null; - } - if (this.measureReferenceDomElementToken !== -1) { - clearInterval(this.measureReferenceDomElementToken); - this.measureReferenceDomElementToken = -1; + if (this._resizeObserver) { + this._resizeObserver.disconnect(); + this._resizeObserver = null; } } @@ -100,25 +63,24 @@ export class ElementSizeObserver extends Disposable { this.measureReferenceDomElement(true, dimension); } - private measureReferenceDomElement(callChangeCallback: boolean, dimension?: IDimension): void { + private measureReferenceDomElement(emitEvent: boolean, dimension?: IDimension): void { let observedWidth = 0; let observedHeight = 0; if (dimension) { observedWidth = dimension.width; observedHeight = dimension.height; - } else if (this.referenceDomElement) { - observedWidth = this.referenceDomElement.clientWidth; - observedHeight = this.referenceDomElement.clientHeight; + } else if (this._referenceDomElement) { + observedWidth = this._referenceDomElement.clientWidth; + observedHeight = this._referenceDomElement.clientHeight; } observedWidth = Math.max(5, observedWidth); observedHeight = Math.max(5, observedHeight); - if (this.width !== observedWidth || this.height !== observedHeight) { - this.width = observedWidth; - this.height = observedHeight; - if (callChangeCallback) { - this.changeCallback(); + if (this._width !== observedWidth || this._height !== observedHeight) { + this._width = observedWidth; + this._height = observedHeight; + if (emitEvent) { + this._onDidChange.fire(); } } } - } diff --git a/src/vs/editor/browser/config/fontMeasurements.ts b/src/vs/editor/browser/config/fontMeasurements.ts new file mode 100644 index 0000000000000..61fc14448a76d --- /dev/null +++ b/src/vs/editor/browser/config/fontMeasurements.ts @@ -0,0 +1,275 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as browser from 'vs/base/browser/browser'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { CharWidthRequest, CharWidthRequestType, readCharWidths } from 'vs/editor/browser/config/charWidthReader'; +import { EditorFontLigatures } from 'vs/editor/common/config/editorOptions'; +import { BareFontInfo, FontInfo, SERIALIZED_FONT_INFO_VERSION } from 'vs/editor/common/config/fontInfo'; + +/** + * Serializable font information. + */ +export interface ISerializedFontInfo { + readonly version: number; + readonly pixelRatio: number; + readonly fontFamily: string; + readonly fontWeight: string; + readonly fontSize: number; + readonly fontFeatureSettings: string; + readonly lineHeight: number; + readonly letterSpacing: number; + readonly isMonospace: boolean; + readonly typicalHalfwidthCharacterWidth: number; + readonly typicalFullwidthCharacterWidth: number; + readonly canUseHalfwidthRightwardsArrow: boolean; + readonly spaceWidth: number; + readonly middotWidth: number; + readonly wsmiddotWidth: number; + readonly maxDigitWidth: number; +} + +class FontMeasurementsImpl extends Disposable { + + private _cache: FontMeasurementsCache; + private _evictUntrustedReadingsTimeout: number; + + private readonly _onDidChange = this._register(new Emitter()); + public readonly onDidChange: Event = this._onDidChange.event; + + constructor() { + super(); + + this._cache = new FontMeasurementsCache(); + this._evictUntrustedReadingsTimeout = -1; + } + + public override dispose(): void { + if (this._evictUntrustedReadingsTimeout !== -1) { + window.clearTimeout(this._evictUntrustedReadingsTimeout); + this._evictUntrustedReadingsTimeout = -1; + } + super.dispose(); + } + + /** + * Clear all cached font information and trigger a change event. + */ + public clearAllFontInfos(): void { + this._cache = new FontMeasurementsCache(); + this._onDidChange.fire(); + } + + private _writeToCache(item: BareFontInfo, value: FontInfo): void { + this._cache.put(item, value); + + if (!value.isTrusted && this._evictUntrustedReadingsTimeout === -1) { + // Try reading again after some time + this._evictUntrustedReadingsTimeout = window.setTimeout(() => { + this._evictUntrustedReadingsTimeout = -1; + this._evictUntrustedReadings(); + }, 5000); + } + } + + private _evictUntrustedReadings(): void { + const values = this._cache.getValues(); + let somethingRemoved = false; + for (const item of values) { + if (!item.isTrusted) { + somethingRemoved = true; + this._cache.remove(item); + } + } + if (somethingRemoved) { + this._onDidChange.fire(); + } + } + + /** + * Serialized currently cached font information. + */ + public serializeFontInfo(): ISerializedFontInfo[] { + // Only save trusted font info (that has been measured in this running instance) + return this._cache.getValues().filter(item => item.isTrusted); + } + + /** + * Restore previously serialized font informations. + */ + public restoreFontInfo(savedFontInfos: ISerializedFontInfo[]): void { + // Take all the saved font info and insert them in the cache without the trusted flag. + // The reason for this is that a font might have been installed on the OS in the meantime. + for (const savedFontInfo of savedFontInfos) { + if (savedFontInfo.version !== SERIALIZED_FONT_INFO_VERSION) { + // cannot use older version + continue; + } + const fontInfo = new FontInfo(savedFontInfo, false); + this._writeToCache(fontInfo, fontInfo); + } + } + + /** + * Read font information. + */ + public readFontInfo(bareFontInfo: BareFontInfo): FontInfo { + if (!this._cache.has(bareFontInfo)) { + let readConfig = this._actualReadFontInfo(bareFontInfo); + + if (readConfig.typicalHalfwidthCharacterWidth <= 2 || readConfig.typicalFullwidthCharacterWidth <= 2 || readConfig.spaceWidth <= 2 || readConfig.maxDigitWidth <= 2) { + // Hey, it's Bug 14341 ... we couldn't read + readConfig = new FontInfo({ + pixelRatio: browser.PixelRatio.value, + fontFamily: readConfig.fontFamily, + fontWeight: readConfig.fontWeight, + fontSize: readConfig.fontSize, + fontFeatureSettings: readConfig.fontFeatureSettings, + lineHeight: readConfig.lineHeight, + letterSpacing: readConfig.letterSpacing, + isMonospace: readConfig.isMonospace, + typicalHalfwidthCharacterWidth: Math.max(readConfig.typicalHalfwidthCharacterWidth, 5), + typicalFullwidthCharacterWidth: Math.max(readConfig.typicalFullwidthCharacterWidth, 5), + canUseHalfwidthRightwardsArrow: readConfig.canUseHalfwidthRightwardsArrow, + spaceWidth: Math.max(readConfig.spaceWidth, 5), + middotWidth: Math.max(readConfig.middotWidth, 5), + wsmiddotWidth: Math.max(readConfig.wsmiddotWidth, 5), + maxDigitWidth: Math.max(readConfig.maxDigitWidth, 5), + }, false); + } + + this._writeToCache(bareFontInfo, readConfig); + } + return this._cache.get(bareFontInfo); + } + + private _createRequest(chr: string, type: CharWidthRequestType, all: CharWidthRequest[], monospace: CharWidthRequest[] | null): CharWidthRequest { + const result = new CharWidthRequest(chr, type); + all.push(result); + if (monospace) { + monospace.push(result); + } + return result; + } + + private _actualReadFontInfo(bareFontInfo: BareFontInfo): FontInfo { + const all: CharWidthRequest[] = []; + const monospace: CharWidthRequest[] = []; + + const typicalHalfwidthCharacter = this._createRequest('n', CharWidthRequestType.Regular, all, monospace); + const typicalFullwidthCharacter = this._createRequest('\uff4d', CharWidthRequestType.Regular, all, null); + const space = this._createRequest(' ', CharWidthRequestType.Regular, all, monospace); + const digit0 = this._createRequest('0', CharWidthRequestType.Regular, all, monospace); + const digit1 = this._createRequest('1', CharWidthRequestType.Regular, all, monospace); + const digit2 = this._createRequest('2', CharWidthRequestType.Regular, all, monospace); + const digit3 = this._createRequest('3', CharWidthRequestType.Regular, all, monospace); + const digit4 = this._createRequest('4', CharWidthRequestType.Regular, all, monospace); + const digit5 = this._createRequest('5', CharWidthRequestType.Regular, all, monospace); + const digit6 = this._createRequest('6', CharWidthRequestType.Regular, all, monospace); + const digit7 = this._createRequest('7', CharWidthRequestType.Regular, all, monospace); + const digit8 = this._createRequest('8', CharWidthRequestType.Regular, all, monospace); + const digit9 = this._createRequest('9', CharWidthRequestType.Regular, all, monospace); + + // monospace test: used for whitespace rendering + const rightwardsArrow = this._createRequest('→', CharWidthRequestType.Regular, all, monospace); + const halfwidthRightwardsArrow = this._createRequest('→', CharWidthRequestType.Regular, all, null); + + // U+00B7 - MIDDLE DOT + const middot = this._createRequest('·', CharWidthRequestType.Regular, all, monospace); + + // U+2E31 - WORD SEPARATOR MIDDLE DOT + const wsmiddotWidth = this._createRequest(String.fromCharCode(0x2E31), CharWidthRequestType.Regular, all, null); + + // monospace test: some characters + const monospaceTestChars = '|/-_ilm%'; + for (let i = 0, len = monospaceTestChars.length; i < len; i++) { + this._createRequest(monospaceTestChars.charAt(i), CharWidthRequestType.Regular, all, monospace); + this._createRequest(monospaceTestChars.charAt(i), CharWidthRequestType.Italic, all, monospace); + this._createRequest(monospaceTestChars.charAt(i), CharWidthRequestType.Bold, all, monospace); + } + + readCharWidths(bareFontInfo, all); + + const maxDigitWidth = Math.max(digit0.width, digit1.width, digit2.width, digit3.width, digit4.width, digit5.width, digit6.width, digit7.width, digit8.width, digit9.width); + + let isMonospace = (bareFontInfo.fontFeatureSettings === EditorFontLigatures.OFF); + const referenceWidth = monospace[0].width; + for (let i = 1, len = monospace.length; isMonospace && i < len; i++) { + const diff = referenceWidth - monospace[i].width; + if (diff < -0.001 || diff > 0.001) { + isMonospace = false; + break; + } + } + + let canUseHalfwidthRightwardsArrow = true; + if (isMonospace && halfwidthRightwardsArrow.width !== referenceWidth) { + // using a halfwidth rightwards arrow would break monospace... + canUseHalfwidthRightwardsArrow = false; + } + if (halfwidthRightwardsArrow.width > rightwardsArrow.width) { + // using a halfwidth rightwards arrow would paint a larger arrow than a regular rightwards arrow + canUseHalfwidthRightwardsArrow = false; + } + + return new FontInfo({ + pixelRatio: browser.PixelRatio.value, + fontFamily: bareFontInfo.fontFamily, + fontWeight: bareFontInfo.fontWeight, + fontSize: bareFontInfo.fontSize, + fontFeatureSettings: bareFontInfo.fontFeatureSettings, + lineHeight: bareFontInfo.lineHeight, + letterSpacing: bareFontInfo.letterSpacing, + isMonospace: isMonospace, + typicalHalfwidthCharacterWidth: typicalHalfwidthCharacter.width, + typicalFullwidthCharacterWidth: typicalFullwidthCharacter.width, + canUseHalfwidthRightwardsArrow: canUseHalfwidthRightwardsArrow, + spaceWidth: space.width, + middotWidth: middot.width, + wsmiddotWidth: wsmiddotWidth.width, + maxDigitWidth: maxDigitWidth + }, true); + } +} + +class FontMeasurementsCache { + + private readonly _keys: { [key: string]: BareFontInfo; }; + private readonly _values: { [key: string]: FontInfo; }; + + constructor() { + this._keys = Object.create(null); + this._values = Object.create(null); + } + + public has(item: BareFontInfo): boolean { + const itemId = item.getId(); + return !!this._values[itemId]; + } + + public get(item: BareFontInfo): FontInfo { + const itemId = item.getId(); + return this._values[itemId]; + } + + public put(item: BareFontInfo, value: FontInfo): void { + const itemId = item.getId(); + this._keys[itemId] = item; + this._values[itemId] = value; + } + + public remove(item: BareFontInfo): void { + const itemId = item.getId(); + delete this._keys[itemId]; + delete this._values[itemId]; + } + + public getValues(): FontInfo[] { + return Object.keys(this._keys).map(id => this._values[id]); + } +} + +export const FontMeasurements = new FontMeasurementsImpl(); diff --git a/src/vs/editor/browser/config/migrateOptions.ts b/src/vs/editor/browser/config/migrateOptions.ts new file mode 100644 index 0000000000000..713fd69f4bdda --- /dev/null +++ b/src/vs/editor/browser/config/migrateOptions.ts @@ -0,0 +1,155 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { forEach } from 'vs/base/common/collections'; +import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; + +/** + * Compatibility with old options + */ +export function migrateOptions(options: IEditorOptions): void { + const wordWrap = options.wordWrap; + if (wordWrap === true) { + options.wordWrap = 'on'; + } else if (wordWrap === false) { + options.wordWrap = 'off'; + } + + const lineNumbers = options.lineNumbers; + if (lineNumbers === true) { + options.lineNumbers = 'on'; + } else if (lineNumbers === false) { + options.lineNumbers = 'off'; + } + + const autoClosingBrackets = options.autoClosingBrackets; + if (autoClosingBrackets === false) { + options.autoClosingBrackets = 'never'; + options.autoClosingQuotes = 'never'; + options.autoSurround = 'never'; + } + + const cursorBlinking = options.cursorBlinking; + if (cursorBlinking === 'visible') { + options.cursorBlinking = 'solid'; + } + + const renderWhitespace = options.renderWhitespace; + if (renderWhitespace === true) { + options.renderWhitespace = 'boundary'; + } else if (renderWhitespace === false) { + options.renderWhitespace = 'none'; + } + + const renderLineHighlight = options.renderLineHighlight; + if (renderLineHighlight === true) { + options.renderLineHighlight = 'line'; + } else if (renderLineHighlight === false) { + options.renderLineHighlight = 'none'; + } + + const acceptSuggestionOnEnter = options.acceptSuggestionOnEnter; + if (acceptSuggestionOnEnter === true) { + options.acceptSuggestionOnEnter = 'on'; + } else if (acceptSuggestionOnEnter === false) { + options.acceptSuggestionOnEnter = 'off'; + } + + const tabCompletion = options.tabCompletion; + if (tabCompletion === false) { + options.tabCompletion = 'off'; + } else if (tabCompletion === true) { + options.tabCompletion = 'onlySnippets'; + } + + const suggest = options.suggest; + if (suggest && typeof (suggest).filteredTypes === 'object' && (suggest).filteredTypes) { + const mapping: Record = {}; + mapping['method'] = 'showMethods'; + mapping['function'] = 'showFunctions'; + mapping['constructor'] = 'showConstructors'; + mapping['deprecated'] = 'showDeprecated'; + mapping['field'] = 'showFields'; + mapping['variable'] = 'showVariables'; + mapping['class'] = 'showClasses'; + mapping['struct'] = 'showStructs'; + mapping['interface'] = 'showInterfaces'; + mapping['module'] = 'showModules'; + mapping['property'] = 'showProperties'; + mapping['event'] = 'showEvents'; + mapping['operator'] = 'showOperators'; + mapping['unit'] = 'showUnits'; + mapping['value'] = 'showValues'; + mapping['constant'] = 'showConstants'; + mapping['enum'] = 'showEnums'; + mapping['enumMember'] = 'showEnumMembers'; + mapping['keyword'] = 'showKeywords'; + mapping['text'] = 'showWords'; + mapping['color'] = 'showColors'; + mapping['file'] = 'showFiles'; + mapping['reference'] = 'showReferences'; + mapping['folder'] = 'showFolders'; + mapping['typeParameter'] = 'showTypeParameters'; + mapping['snippet'] = 'showSnippets'; + forEach(mapping, entry => { + const value = (suggest).filteredTypes[entry.key]; + if (value === false) { + (suggest)[entry.value] = value; + } + }); + // delete (suggest).filteredTypes; + } + + const hover = options.hover; + if (hover === true) { + options.hover = { + enabled: true + }; + } else if (hover === false) { + options.hover = { + enabled: false + }; + } + + const parameterHints = options.parameterHints; + if (parameterHints === true) { + options.parameterHints = { + enabled: true + }; + } else if (parameterHints === false) { + options.parameterHints = { + enabled: false + }; + } + + const autoIndent = options.autoIndent; + if (autoIndent === true) { + options.autoIndent = 'full'; + } else if (autoIndent === false) { + options.autoIndent = 'advanced'; + } + + const matchBrackets = options.matchBrackets; + if (matchBrackets === true) { + options.matchBrackets = 'always'; + } else if (matchBrackets === false) { + options.matchBrackets = 'never'; + } + + const { renderIndentGuides, highlightActiveIndentGuide } = options as any as { + renderIndentGuides: boolean; + highlightActiveIndentGuide: boolean; + }; + if (!options.guides) { + options.guides = {}; + } + + if (renderIndentGuides !== undefined) { + options.guides.indentation = !!renderIndentGuides; + } + if (highlightActiveIndentGuide !== undefined) { + options.guides.highlightActiveIndentation = !!highlightActiveIndentGuide; + } +} diff --git a/src/vs/editor/browser/config/tabFocus.ts b/src/vs/editor/browser/config/tabFocus.ts new file mode 100644 index 0000000000000..70f8bb9205643 --- /dev/null +++ b/src/vs/editor/browser/config/tabFocus.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter, Event } from 'vs/base/common/event'; + +class TabFocusImpl { + private _tabFocus: boolean = false; + + private readonly _onDidChangeTabFocus = new Emitter(); + public readonly onDidChangeTabFocus: Event = this._onDidChangeTabFocus.event; + + public getTabFocusMode(): boolean { + return this._tabFocus; + } + + public setTabFocusMode(tabFocusMode: boolean): void { + if (this._tabFocus === tabFocusMode) { + return; + } + + this._tabFocus = tabFocusMode; + this._onDidChangeTabFocus.fire(this._tabFocus); + } +} + +/** + * Control what pressing Tab does. + * If it is false, pressing Tab or Shift-Tab will be handled by the editor. + * If it is true, pressing Tab or Shift-Tab will move the browser focus. + * Defaults to false. + */ +export const TabFocus = new TabFocusImpl(); diff --git a/src/vs/editor/browser/controller/coreCommands.ts b/src/vs/editor/browser/controller/coreCommands.ts index b5a9ad8ad9ad6..04f0b7359ee4b 100644 --- a/src/vs/editor/browser/controller/coreCommands.ts +++ b/src/vs/editor/browser/controller/coreCommands.ts @@ -11,17 +11,17 @@ import { status } from 'vs/base/browser/ui/aria/aria'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Command, EditorCommand, ICommandOptions, registerEditorCommand, MultiCommand, UndoCommand, RedoCommand, SelectAllCommand } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { ColumnSelection, IColumnSelectResult } from 'vs/editor/common/controller/cursorColumnSelection'; -import { CursorState, EditOperationType, IColumnSelectData, PartialCursorState } from 'vs/editor/common/controller/cursorCommon'; -import { DeleteOperations } from 'vs/editor/common/controller/cursorDeleteOperations'; -import { CursorChangeReason } from 'vs/editor/common/controller/cursorEvents'; -import { CursorMove as CursorMove_, CursorMoveCommands } from 'vs/editor/common/controller/cursorMoveCommands'; -import { TypeOperations } from 'vs/editor/common/controller/cursorTypeOperations'; +import { ColumnSelection, IColumnSelectResult } from 'vs/editor/common/cursor/cursorColumnSelection'; +import { CursorState, EditOperationType, IColumnSelectData, PartialCursorState } from 'vs/editor/common/cursor/cursorCommon'; +import { DeleteOperations } from 'vs/editor/common/cursor/cursorDeleteOperations'; +import { CursorChangeReason } from 'vs/editor/common/cursor/cursorEvents'; +import { CursorMove as CursorMove_, CursorMoveCommands } from 'vs/editor/common/cursor/cursorMoveCommands'; +import { TypeOperations } from 'vs/editor/common/cursor/cursorTypeOperations'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Handler, ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { VerticalRevealType } from 'vs/editor/common/view/viewEvents'; +import { VerticalRevealType } from 'vs/editor/common/viewModel/viewEvents'; import { ICommandHandlerDescription } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -329,34 +329,40 @@ export namespace CoreNavigationCommands { class BaseMoveToCommand extends CoreEditorCommand { + private readonly _minimalReveal: boolean; private readonly _inSelectionMode: boolean; - constructor(opts: ICommandOptions & { inSelectionMode: boolean; }) { + constructor(opts: ICommandOptions & { minimalReveal: boolean; inSelectionMode: boolean; }) { super(opts); + this._minimalReveal = opts.minimalReveal; this._inSelectionMode = opts.inSelectionMode; } public runCoreEditorCommand(viewModel: IViewModel, args: any): void { viewModel.model.pushStackElement(); - viewModel.setCursorStates( + const cursorStateChanged = viewModel.setCursorStates( args.source, CursorChangeReason.Explicit, [ CursorMoveCommands.moveTo(viewModel, viewModel.getPrimaryCursorState(), this._inSelectionMode, args.position, args.viewPosition) ] ); - viewModel.revealPrimaryCursor(args.source, true); + if (cursorStateChanged) { + viewModel.revealPrimaryCursor(args.source, true, this._minimalReveal); + } } } export const MoveTo: CoreEditorCommand = registerEditorCommand(new BaseMoveToCommand({ id: '_moveTo', + minimalReveal: true, inSelectionMode: false, precondition: undefined })); export const MoveToSelect: CoreEditorCommand = registerEditorCommand(new BaseMoveToCommand({ id: '_moveToSelect', + minimalReveal: false, inSelectionMode: true, precondition: undefined })); @@ -398,8 +404,8 @@ export namespace CoreNavigationCommands { const validatedPosition = viewModel.model.validatePosition(args.position); const validatedViewPosition = viewModel.coordinatesConverter.validateViewPosition(new Position(args.viewPosition.lineNumber, args.viewPosition.column), validatedPosition); - let fromViewLineNumber = args.doColumnSelect ? prevColumnSelectData.fromViewLineNumber : validatedViewPosition.lineNumber; - let fromViewVisualColumn = args.doColumnSelect ? prevColumnSelectData.fromViewVisualColumn : args.mouseColumn - 1; + const fromViewLineNumber = args.doColumnSelect ? prevColumnSelectData.fromViewLineNumber : validatedViewPosition.lineNumber; + const fromViewVisualColumn = args.doColumnSelect ? prevColumnSelectData.fromViewVisualColumn : args.mouseColumn - 1; return ColumnSelection.columnSelect(viewModel.cursorConfig, viewModel, fromViewLineNumber, fromViewVisualColumn, validatedViewPosition.lineNumber, args.mouseColumn - 1); } }); @@ -598,7 +604,7 @@ export namespace CoreNavigationCommands { direction: this._staticArgs.direction, unit: this._staticArgs.unit, select: this._staticArgs.select, - value: viewModel.cursorConfig.pageSize + value: dynamicArgs.pageSize || viewModel.cursorConfig.pageSize }; } diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index 6743bb2cba23d..8a2279b483072 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -8,16 +8,16 @@ import { StandardWheelEvent, IMouseWheelEvent } from 'vs/base/browser/mouseEvent import { TimeoutTimer } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; -import { HitTestContext, IViewZoneData, MouseTarget, MouseTargetFactory, PointerHandlerLastRenderData } from 'vs/editor/browser/controller/mouseTarget'; -import { IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; -import { ClientCoordinates, EditorMouseEvent, EditorMouseEventFactory, GlobalEditorMouseMoveMonitor, createEditorPagePosition } from 'vs/editor/browser/editorDom'; +import { HitTestContext, MouseTarget, MouseTargetFactory, PointerHandlerLastRenderData } from 'vs/editor/browser/controller/mouseTarget'; +import { IMouseTarget, IMouseTargetViewZoneData, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { ClientCoordinates, EditorMouseEvent, EditorMouseEventFactory, GlobalEditorMouseMoveMonitor, createEditorPagePosition, createCoordinatesRelativeToEditor } from 'vs/editor/browser/editorDom'; import { ViewController } from 'vs/editor/browser/view/viewController'; import { EditorZoom } from 'vs/editor/common/config/editorZoom'; import { Position } from 'vs/editor/common/core/position'; import { Selection } from 'vs/editor/common/core/selection'; -import { HorizontalPosition } from 'vs/editor/common/view/renderingContext'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; -import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { HorizontalPosition } from 'vs/editor/browser/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/viewModel/viewContext'; +import * as viewEvents from 'vs/editor/common/viewModel/viewEvents'; import { ViewEventHandler } from 'vs/editor/common/viewModel/viewEventHandler'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -172,7 +172,8 @@ export class MouseHandler extends ViewEventHandler { return null; } - return this.mouseTargetFactory.createMouseTarget(this.viewHelper.getLastRenderData(), editorPos, pos, null); + const relativePos = createCoordinatesRelativeToEditor(this.viewHelper.viewDomNode, editorPos, pos); + return this.mouseTargetFactory.createMouseTarget(this.viewHelper.getLastRenderData(), editorPos, pos, relativePos, null); } protected _createMouseTarget(e: EditorMouseEvent, testEventTarget: boolean): IMouseTarget { @@ -185,11 +186,11 @@ export class MouseHandler extends ViewEventHandler { ); } } - return this.mouseTargetFactory.createMouseTarget(this.viewHelper.getLastRenderData(), e.editorPos, e.pos, testEventTarget ? target : null); + return this.mouseTargetFactory.createMouseTarget(this.viewHelper.getLastRenderData(), e.editorPos, e.pos, e.relativePos, testEventTarget ? target : null); } private _getMouseColumn(e: EditorMouseEvent): number { - return this.mouseTargetFactory.getMouseColumn(e.editorPos, e.pos); + return this.mouseTargetFactory.getMouseColumn(e.relativePos); } protected _onContextMenu(e: EditorMouseEvent, testEventTarget: boolean): void { @@ -259,7 +260,7 @@ export class MouseHandler extends ViewEventHandler { // Do not steal focus e.preventDefault(); } else if (targetIsViewZone) { - const viewZoneData = t.detail; + const viewZoneData = t.detail; if (this.viewHelper.shouldSuppressMouseDownOnViewZone(viewZoneData.viewZoneId)) { focus(); this._mouseDownOperation.start(t.type, e); @@ -454,7 +455,7 @@ class MouseDownOperation extends Disposable { this._currentSelection = e.selections[0]; } - private _getPositionOutsideEditor(e: EditorMouseEvent): MouseTarget | null { + private _getPositionOutsideEditor(e: EditorMouseEvent): IMouseTarget | null { const editorContent = e.editorPos; const model = this._context.model; const viewLayout = this._context.viewLayout; @@ -467,42 +468,42 @@ class MouseDownOperation extends Disposable { if (viewZoneData) { const newPosition = this._helpPositionJumpOverViewZone(viewZoneData); if (newPosition) { - return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, newPosition); + return MouseTarget.createOutsideEditor(mouseColumn, newPosition); } } const aboveLineNumber = viewLayout.getLineNumberAtVerticalOffset(verticalOffset); - return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(aboveLineNumber, 1)); + return MouseTarget.createOutsideEditor(mouseColumn, new Position(aboveLineNumber, 1)); } if (e.posy > editorContent.y + editorContent.height) { - const verticalOffset = viewLayout.getCurrentScrollTop() + (e.posy - editorContent.y); + const verticalOffset = viewLayout.getCurrentScrollTop() + e.relativePos.y; const viewZoneData = HitTestContext.getZoneAtCoord(this._context, verticalOffset); if (viewZoneData) { const newPosition = this._helpPositionJumpOverViewZone(viewZoneData); if (newPosition) { - return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, newPosition); + return MouseTarget.createOutsideEditor(mouseColumn, newPosition); } } const belowLineNumber = viewLayout.getLineNumberAtVerticalOffset(verticalOffset); - return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(belowLineNumber, model.getLineMaxColumn(belowLineNumber))); + return MouseTarget.createOutsideEditor(mouseColumn, new Position(belowLineNumber, model.getLineMaxColumn(belowLineNumber))); } - const possibleLineNumber = viewLayout.getLineNumberAtVerticalOffset(viewLayout.getCurrentScrollTop() + (e.posy - editorContent.y)); + const possibleLineNumber = viewLayout.getLineNumberAtVerticalOffset(viewLayout.getCurrentScrollTop() + e.relativePos.y); if (e.posx < editorContent.x) { - return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(possibleLineNumber, 1)); + return MouseTarget.createOutsideEditor(mouseColumn, new Position(possibleLineNumber, 1)); } if (e.posx > editorContent.x + editorContent.width) { - return new MouseTarget(null, MouseTargetType.OUTSIDE_EDITOR, mouseColumn, new Position(possibleLineNumber, model.getLineMaxColumn(possibleLineNumber))); + return MouseTarget.createOutsideEditor(mouseColumn, new Position(possibleLineNumber, model.getLineMaxColumn(possibleLineNumber))); } return null; } - private _findMousePosition(e: EditorMouseEvent, testEventTarget: boolean): MouseTarget | null { + private _findMousePosition(e: EditorMouseEvent, testEventTarget: boolean): IMouseTarget | null { const positionOutsideEditor = this._getPositionOutsideEditor(e); if (positionOutsideEditor) { return positionOutsideEditor; @@ -515,16 +516,16 @@ class MouseDownOperation extends Disposable { } if (t.type === MouseTargetType.CONTENT_VIEW_ZONE || t.type === MouseTargetType.GUTTER_VIEW_ZONE) { - const newPosition = this._helpPositionJumpOverViewZone(t.detail); + const newPosition = this._helpPositionJumpOverViewZone(t.detail); if (newPosition) { - return new MouseTarget(t.element, t.type, t.mouseColumn, newPosition, null, t.detail); + return MouseTarget.createViewZone(t.type, t.element, t.mouseColumn, newPosition, t.detail); } } return t; } - private _helpPositionJumpOverViewZone(viewZoneData: IViewZoneData): Position | null { + private _helpPositionJumpOverViewZone(viewZoneData: IMouseTargetViewZoneData): Position | null { // Force position on view zones to go above or below depending on where selection started from const selectionStart = new Position(this._currentSelection.selectionStartLineNumber, this._currentSelection.selectionStartColumn); const positionBefore = viewZoneData.positionBefore; @@ -540,7 +541,7 @@ class MouseDownOperation extends Disposable { return null; } - private _dispatchMouse(position: MouseTarget, inSelectionMode: boolean): void { + private _dispatchMouse(position: IMouseTarget, inSelectionMode: boolean): void { if (!position.position) { return; } diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index e6dc5ca034a30..fe7424ed899d4 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -4,51 +4,26 @@ *--------------------------------------------------------------------------------------------*/ import { IPointerHandlerHelper } from 'vs/editor/browser/controller/mouseHandler'; -import { IMouseTarget, MouseTargetType } from 'vs/editor/browser/editorBrowser'; -import { ClientCoordinates, EditorMouseEvent, EditorPagePosition, PageCoordinates } from 'vs/editor/browser/editorDom'; +import { IMouseTargetContentEmptyData, IMouseTargetMarginData, IMouseTarget, IMouseTargetContentEmpty, IMouseTargetContentText, IMouseTargetContentWidget, IMouseTargetMargin, IMouseTargetOutsideEditor, IMouseTargetOverlayWidget, IMouseTargetScrollbar, IMouseTargetTextarea, IMouseTargetUnknown, IMouseTargetViewZone, IMouseTargetContentTextData, IMouseTargetViewZoneData, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { ClientCoordinates, EditorMouseEvent, EditorPagePosition, PageCoordinates, CoordinatesRelativeToEditor } from 'vs/editor/browser/editorDom'; import { PartFingerprint, PartFingerprints } from 'vs/editor/browser/view/viewPart'; import { ViewLine } from 'vs/editor/browser/viewParts/lines/viewLine'; import { IViewCursorRenderData } from 'vs/editor/browser/viewParts/viewCursors/viewCursor'; import { EditorLayoutInfo, EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Range as EditorRange } from 'vs/editor/common/core/range'; -import { HorizontalPosition } from 'vs/editor/common/view/renderingContext'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { HorizontalPosition } from 'vs/editor/browser/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/viewModel/viewContext'; import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; -import { CursorColumns } from 'vs/editor/common/controller/cursorCommon'; +import { CursorColumns } from 'vs/editor/common/core/cursorColumns'; import * as dom from 'vs/base/browser/dom'; -import { AtomicTabMoveOperations, Direction } from 'vs/editor/common/controller/cursorAtomicMoveOperations'; +import { AtomicTabMoveOperations, Direction } from 'vs/editor/common/cursor/cursorAtomicMoveOperations'; import { PositionAffinity } from 'vs/editor/common/model'; import { InjectedText } from 'vs/editor/common/viewModel/modelLineProjectionData'; -export interface IViewZoneData { - viewZoneId: string; - positionBefore: Position | null; - positionAfter: Position | null; - position: Position; - afterLineNumber: number; -} - -export interface IMarginData { - isAfterLines: boolean; - glyphMarginLeft: number; - glyphMarginWidth: number; - lineNumbersWidth: number; - offsetX: number; -} - -export interface IEmptyContentData { - isAfterLines: boolean; - horizontalDistanceToText?: number; -} - -export interface ITextContentData { - mightBeForeignElement: boolean; -} - const enum HitTestResultType { - Unknown = 0, - Content = 1, + Unknown, + Content, } class UnknownHitTestResult { @@ -86,25 +61,46 @@ export class PointerHandlerLastRenderData { ) { } } -export class MouseTarget implements IMouseTarget { - - public readonly element: Element | null; - public readonly type: MouseTargetType; - public readonly mouseColumn: number; - public readonly position: Position | null; - public readonly range: EditorRange | null; - public readonly detail: any; +export class MouseTarget { - constructor(element: Element | null, type: MouseTargetType, mouseColumn: number = 0, position: Position | null = null, range: EditorRange | null = null, detail: any = null) { - this.element = element; - this.type = type; - this.mouseColumn = mouseColumn; - this.position = position; + private static _deduceRage(position: Position): EditorRange; + private static _deduceRage(position: Position, range: EditorRange | null): EditorRange; + private static _deduceRage(position: Position | null): EditorRange | null; + private static _deduceRage(position: Position | null, range: EditorRange | null = null): EditorRange | null { if (!range && position) { - range = new EditorRange(position.lineNumber, position.column, position.lineNumber, position.column); + return new EditorRange(position.lineNumber, position.column, position.lineNumber, position.column); } - this.range = range; - this.detail = detail; + return range ?? null; + } + public static createUnknown(element: Element | null, mouseColumn: number, position: Position | null): IMouseTargetUnknown { + return { type: MouseTargetType.UNKNOWN, element, mouseColumn, position, range: this._deduceRage(position) }; + } + public static createTextarea(element: Element | null, mouseColumn: number): IMouseTargetTextarea { + return { type: MouseTargetType.TEXTAREA, element, mouseColumn, position: null, range: null }; + } + public static createMargin(type: MouseTargetType.GUTTER_GLYPH_MARGIN | MouseTargetType.GUTTER_LINE_NUMBERS | MouseTargetType.GUTTER_LINE_DECORATIONS, element: Element | null, mouseColumn: number, position: Position, range: EditorRange, detail: IMouseTargetMarginData): IMouseTargetMargin { + return { type, element, mouseColumn, position, range, detail }; + } + public static createViewZone(type: MouseTargetType.GUTTER_VIEW_ZONE | MouseTargetType.CONTENT_VIEW_ZONE, element: Element | null, mouseColumn: number, position: Position, detail: IMouseTargetViewZoneData): IMouseTargetViewZone { + return { type, element, mouseColumn, position, range: this._deduceRage(position), detail }; + } + public static createContentText(element: Element | null, mouseColumn: number, position: Position, range: EditorRange | null, detail: IMouseTargetContentTextData): IMouseTargetContentText { + return { type: MouseTargetType.CONTENT_TEXT, element, mouseColumn, position, range: this._deduceRage(position, range), detail }; + } + public static createContentEmpty(element: Element | null, mouseColumn: number, position: Position, detail: IMouseTargetContentEmptyData): IMouseTargetContentEmpty { + return { type: MouseTargetType.CONTENT_EMPTY, element, mouseColumn, position, range: this._deduceRage(position), detail }; + } + public static createContentWidget(element: Element | null, mouseColumn: number, detail: string): IMouseTargetContentWidget { + return { type: MouseTargetType.CONTENT_WIDGET, element, mouseColumn, position: null, range: null, detail }; + } + public static createScrollbar(element: Element | null, mouseColumn: number, position: Position): IMouseTargetScrollbar { + return { type: MouseTargetType.SCROLLBAR, element, mouseColumn, position, range: this._deduceRage(position) }; + } + public static createOverlayWidget(element: Element | null, mouseColumn: number, detail: string): IMouseTargetOverlayWidget { + return { type: MouseTargetType.OVERLAY_WIDGET, element, mouseColumn, position: null, range: null, detail }; + } + public static createOutsideEditor(mouseColumn: number, position: Position): IMouseTargetOutsideEditor { + return { type: MouseTargetType.OUTSIDE_EDITOR, element: null, mouseColumn, position, range: this._deduceRage(position) }; } private static _typeToString(type: MouseTargetType): string { @@ -148,11 +144,7 @@ export class MouseTarget implements IMouseTarget { } public static toString(target: IMouseTarget): string { - return this._typeToString(target.type) + ': ' + target.position + ' - ' + target.range + ' - ' + target.detail; - } - - public toString(): string { - return MouseTarget.toString(this); + return this._typeToString(target.type) + ': ' + target.position + ' - ' + target.range + ' - ' + JSON.stringify((target).detail); } } @@ -248,11 +240,11 @@ export class HitTestContext { this._viewHelper = viewHelper; } - public getZoneAtCoord(mouseVerticalOffset: number): IViewZoneData | null { + public getZoneAtCoord(mouseVerticalOffset: number): IMouseTargetViewZoneData | null { return HitTestContext.getZoneAtCoord(this._context, mouseVerticalOffset); } - public static getZoneAtCoord(context: ViewContext, mouseVerticalOffset: number): IViewZoneData | null { + public static getZoneAtCoord(context: ViewContext, mouseVerticalOffset: number): IMouseTargetViewZoneData | null { // The target is either a view zone or the empty space after the last view-line const viewZoneWhitespace = context.viewLayout.getWhitespaceAtVerticalOffset(mouseVerticalOffset); @@ -374,6 +366,7 @@ abstract class BareHitTestRequest { public readonly editorPos: EditorPagePosition; public readonly pos: PageCoordinates; + public readonly relativePos: CoordinatesRelativeToEditor; public readonly mouseVerticalOffset: number; public readonly isInMarginArea: boolean; public readonly isInContentArea: boolean; @@ -381,13 +374,14 @@ abstract class BareHitTestRequest { protected readonly mouseColumn: number; - constructor(ctx: HitTestContext, editorPos: EditorPagePosition, pos: PageCoordinates) { + constructor(ctx: HitTestContext, editorPos: EditorPagePosition, pos: PageCoordinates, relativePos: CoordinatesRelativeToEditor) { this.editorPos = editorPos; this.pos = pos; + this.relativePos = relativePos; - this.mouseVerticalOffset = Math.max(0, ctx.getCurrentScrollTop() + pos.y - editorPos.y); - this.mouseContentHorizontalOffset = ctx.getCurrentScrollLeft() + pos.x - editorPos.x - ctx.layoutInfo.contentLeft; - this.isInMarginArea = (pos.x - editorPos.x < ctx.layoutInfo.contentLeft && pos.x - editorPos.x >= ctx.layoutInfo.glyphMarginLeft); + this.mouseVerticalOffset = Math.max(0, ctx.getCurrentScrollTop() + this.relativePos.y); + this.mouseContentHorizontalOffset = ctx.getCurrentScrollLeft() + this.relativePos.x - ctx.layoutInfo.contentLeft; + this.isInMarginArea = (this.relativePos.x < ctx.layoutInfo.contentLeft && this.relativePos.x >= ctx.layoutInfo.glyphMarginLeft); this.isInContentArea = !this.isInMarginArea; this.mouseColumn = Math.max(0, MouseTargetFactory._getMouseColumn(this.mouseContentHorizontalOffset, ctx.typicalHalfwidthCharacterWidth)); } @@ -398,8 +392,8 @@ class HitTestRequest extends BareHitTestRequest { public readonly target: Element | null; public readonly targetPath: Uint8Array; - constructor(ctx: HitTestContext, editorPos: EditorPagePosition, pos: PageCoordinates, target: Element | null) { - super(ctx, editorPos, pos); + constructor(ctx: HitTestContext, editorPos: EditorPagePosition, pos: PageCoordinates, relativePos: CoordinatesRelativeToEditor, target: Element | null) { + super(ctx, editorPos, pos, relativePos); this._ctx = ctx; if (target) { @@ -412,31 +406,47 @@ class HitTestRequest extends BareHitTestRequest { } public override toString(): string { - return `pos(${this.pos.x},${this.pos.y}), editorPos(${this.editorPos.x},${this.editorPos.y}), mouseVerticalOffset: ${this.mouseVerticalOffset}, mouseContentHorizontalOffset: ${this.mouseContentHorizontalOffset}\n\ttarget: ${this.target ? (this.target).outerHTML : null}`; - } - - public fulfill(type: MouseTargetType.UNKNOWN, position?: Position | null, range?: EditorRange | null): MouseTarget; - public fulfill(type: MouseTargetType.TEXTAREA, position: Position | null): MouseTarget; - public fulfill(type: MouseTargetType.GUTTER_GLYPH_MARGIN | MouseTargetType.GUTTER_LINE_NUMBERS | MouseTargetType.GUTTER_LINE_DECORATIONS, position: Position, range: EditorRange, detail: IMarginData): MouseTarget; - public fulfill(type: MouseTargetType.GUTTER_VIEW_ZONE | MouseTargetType.CONTENT_VIEW_ZONE, position: Position, range: null, detail: IViewZoneData): MouseTarget; - public fulfill(type: MouseTargetType.CONTENT_TEXT, position: Position | null, range: EditorRange | null, detail: ITextContentData): MouseTarget; - public fulfill(type: MouseTargetType.CONTENT_EMPTY, position: Position | null, range: EditorRange | null, detail: IEmptyContentData): MouseTarget; - public fulfill(type: MouseTargetType.CONTENT_WIDGET, position: null, range: null, detail: string): MouseTarget; - public fulfill(type: MouseTargetType.SCROLLBAR, position: Position): MouseTarget; - public fulfill(type: MouseTargetType.OVERLAY_WIDGET, position: null, range: null, detail: string): MouseTarget; - // public fulfill(type: MouseTargetType.OVERVIEW_RULER, position?: Position | null, range?: EditorRange | null, detail?: any): MouseTarget; - // public fulfill(type: MouseTargetType.OUTSIDE_EDITOR, position?: Position | null, range?: EditorRange | null, detail?: any): MouseTarget; - public fulfill(type: MouseTargetType, position: Position | null = null, range: EditorRange | null = null, detail: any = null): MouseTarget { - let mouseColumn = this.mouseColumn; + return `pos(${this.pos.x},${this.pos.y}), editorPos(${this.editorPos.x},${this.editorPos.y}), relativePos(${this.relativePos.x},${this.relativePos.y}), mouseVerticalOffset: ${this.mouseVerticalOffset}, mouseContentHorizontalOffset: ${this.mouseContentHorizontalOffset}\n\ttarget: ${this.target ? (this.target).outerHTML : null}`; + } + + private _getMouseColumn(position: Position | null = null): number { if (position && position.column < this._ctx.model.getLineMaxColumn(position.lineNumber)) { // Most likely, the line contains foreign decorations... - mouseColumn = CursorColumns.visibleColumnFromColumn(this._ctx.model.getLineContent(position.lineNumber), position.column, this._ctx.model.getTextModelOptions().tabSize) + 1; + return CursorColumns.visibleColumnFromColumn(this._ctx.model.getLineContent(position.lineNumber), position.column, this._ctx.model.getTextModelOptions().tabSize) + 1; } - return new MouseTarget(this.target, type, mouseColumn, position, range, detail); + return this.mouseColumn; + } + + public fulfillUnknown(position: Position | null = null): IMouseTargetUnknown { + return MouseTarget.createUnknown(this.target, this._getMouseColumn(position), position); + } + public fulfillTextarea(): IMouseTargetTextarea { + return MouseTarget.createTextarea(this.target, this._getMouseColumn()); + } + public fulfillMargin(type: MouseTargetType.GUTTER_GLYPH_MARGIN | MouseTargetType.GUTTER_LINE_NUMBERS | MouseTargetType.GUTTER_LINE_DECORATIONS, position: Position, range: EditorRange, detail: IMouseTargetMarginData): IMouseTargetMargin { + return MouseTarget.createMargin(type, this.target, this._getMouseColumn(position), position, range, detail); + } + public fulfillViewZone(type: MouseTargetType.GUTTER_VIEW_ZONE | MouseTargetType.CONTENT_VIEW_ZONE, position: Position, detail: IMouseTargetViewZoneData): IMouseTargetViewZone { + return MouseTarget.createViewZone(type, this.target, this._getMouseColumn(position), position, detail); + } + public fulfillContentText(position: Position, range: EditorRange | null, detail: IMouseTargetContentTextData): IMouseTargetContentText { + return MouseTarget.createContentText(this.target, this._getMouseColumn(position), position, range, detail); + } + public fulfillContentEmpty(position: Position, detail: IMouseTargetContentEmptyData): IMouseTargetContentEmpty { + return MouseTarget.createContentEmpty(this.target, this._getMouseColumn(position), position, detail); + } + public fulfillContentWidget(detail: string): IMouseTargetContentWidget { + return MouseTarget.createContentWidget(this.target, this._getMouseColumn(), detail); + } + public fulfillScrollbar(position: Position): IMouseTargetScrollbar { + return MouseTarget.createScrollbar(this.target, this._getMouseColumn(position), position); + } + public fulfillOverlayWidget(detail: string): IMouseTargetOverlayWidget { + return MouseTarget.createOverlayWidget(this.target, this._getMouseColumn(), detail); } public withTarget(target: Element | null): HitTestRequest { - return new HitTestRequest(this._ctx, this.editorPos, this.pos, target); + return new HitTestRequest(this._ctx, this.editorPos, this.pos, this.relativePos, target); } } @@ -444,9 +454,9 @@ interface ResolvedHitTestRequest extends HitTestRequest { readonly target: Element; } -const EMPTY_CONTENT_AFTER_LINES: IEmptyContentData = { isAfterLines: true }; +const EMPTY_CONTENT_AFTER_LINES: IMouseTargetContentEmptyData = { isAfterLines: true }; -function createEmptyContentDataInLines(horizontalDistanceToText: number): IEmptyContentData { +function createEmptyContentDataInLines(horizontalDistanceToText: number): IMouseTargetContentEmptyData { return { isAfterLines: false, horizontalDistanceToText: horizontalDistanceToText @@ -480,20 +490,20 @@ export class MouseTargetFactory { return false; } - public createMouseTarget(lastRenderData: PointerHandlerLastRenderData, editorPos: EditorPagePosition, pos: PageCoordinates, target: HTMLElement | null): IMouseTarget { + public createMouseTarget(lastRenderData: PointerHandlerLastRenderData, editorPos: EditorPagePosition, pos: PageCoordinates, relativePos: CoordinatesRelativeToEditor, target: HTMLElement | null): IMouseTarget { const ctx = new HitTestContext(this._context, this._viewHelper, lastRenderData); - const request = new HitTestRequest(ctx, editorPos, pos, target); + const request = new HitTestRequest(ctx, editorPos, pos, relativePos, target); try { const r = MouseTargetFactory._createMouseTarget(ctx, request, false); - // console.log(r.toString()); + // console.log(MouseTarget.toString(r)); return r; } catch (err) { // console.log(err); - return request.fulfill(MouseTargetType.UNKNOWN); + return request.fulfillUnknown(); } } - private static _createMouseTarget(ctx: HitTestContext, request: HitTestRequest, domHitTestExecuted: boolean): MouseTarget { + private static _createMouseTarget(ctx: HitTestContext, request: HitTestRequest, domHitTestExecuted: boolean): IMouseTarget { // console.log(`${domHitTestExecuted ? '=>' : ''}CAME IN REQUEST: ${request}`); @@ -501,7 +511,7 @@ export class MouseTargetFactory { if (request.target === null) { if (domHitTestExecuted) { // Still no target... and we have already executed hit test... - return request.fulfill(MouseTargetType.UNKNOWN); + return request.fulfillUnknown(); } const hitTestResult = MouseTargetFactory._doHitTest(ctx, request); @@ -516,7 +526,7 @@ export class MouseTargetFactory { // we know for a fact that request.target is not null const resolvedRequest = request; - let result: MouseTarget | null = null; + let result: IMouseTarget | null = null; result = result || MouseTargetFactory._hitTestContentWidget(ctx, resolvedRequest); result = result || MouseTargetFactory._hitTestOverlayWidget(ctx, resolvedRequest); @@ -529,36 +539,36 @@ export class MouseTargetFactory { result = result || MouseTargetFactory._hitTestViewLines(ctx, resolvedRequest, domHitTestExecuted); result = result || MouseTargetFactory._hitTestScrollbar(ctx, resolvedRequest); - return (result || request.fulfill(MouseTargetType.UNKNOWN)); + return (result || request.fulfillUnknown()); } - private static _hitTestContentWidget(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestContentWidget(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { // Is it a content widget? if (ElementPath.isChildOfContentWidgets(request.targetPath) || ElementPath.isChildOfOverflowingContentWidgets(request.targetPath)) { const widgetId = ctx.findAttribute(request.target, 'widgetId'); if (widgetId) { - return request.fulfill(MouseTargetType.CONTENT_WIDGET, null, null, widgetId); + return request.fulfillContentWidget(widgetId); } else { - return request.fulfill(MouseTargetType.UNKNOWN); + return request.fulfillUnknown(); } } return null; } - private static _hitTestOverlayWidget(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestOverlayWidget(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { // Is it an overlay widget? if (ElementPath.isChildOfOverlayWidgets(request.targetPath)) { const widgetId = ctx.findAttribute(request.target, 'widgetId'); if (widgetId) { - return request.fulfill(MouseTargetType.OVERLAY_WIDGET, null, null, widgetId); + return request.fulfillOverlayWidget(widgetId); } else { - return request.fulfill(MouseTargetType.UNKNOWN); + return request.fulfillUnknown(); } } return null; } - private static _hitTestViewCursor(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestViewCursor(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { if (request.target) { // Check if we've hit a painted cursor @@ -567,7 +577,7 @@ export class MouseTargetFactory { for (const d of lastViewCursorsRenderData) { if (request.target === d.domNode) { - return request.fulfill(MouseTargetType.CONTENT_TEXT, d.position, null, { mightBeForeignElement: false }); + return request.fulfillContentText(d.position, null, { mightBeForeignElement: false, injectedText: null }); } } } @@ -599,7 +609,7 @@ export class MouseTargetFactory { cursorVerticalOffset <= mouseVerticalOffset && mouseVerticalOffset <= cursorVerticalOffset + d.height ) { - return request.fulfill(MouseTargetType.CONTENT_TEXT, d.position, null, { mightBeForeignElement: false }); + return request.fulfillContentText(d.position, null, { mightBeForeignElement: false, injectedText: null }); } } } @@ -607,33 +617,33 @@ export class MouseTargetFactory { return null; } - private static _hitTestViewZone(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestViewZone(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { const viewZoneData = ctx.getZoneAtCoord(request.mouseVerticalOffset); if (viewZoneData) { const mouseTargetType = (request.isInContentArea ? MouseTargetType.CONTENT_VIEW_ZONE : MouseTargetType.GUTTER_VIEW_ZONE); - return request.fulfill(mouseTargetType, viewZoneData.position, null, viewZoneData); + return request.fulfillViewZone(mouseTargetType, viewZoneData.position, viewZoneData); } return null; } - private static _hitTestTextArea(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestTextArea(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { // Is it the textarea? if (ElementPath.isTextArea(request.targetPath)) { if (ctx.lastRenderData.lastTextareaPosition) { - return request.fulfill(MouseTargetType.CONTENT_TEXT, ctx.lastRenderData.lastTextareaPosition, null, { mightBeForeignElement: false }); + return request.fulfillContentText(ctx.lastRenderData.lastTextareaPosition, null, { mightBeForeignElement: false, injectedText: null }); } - return request.fulfill(MouseTargetType.TEXTAREA, ctx.lastRenderData.lastTextareaPosition); + return request.fulfillTextarea(); } return null; } - private static _hitTestMargin(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestMargin(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { if (request.isInMarginArea) { const res = ctx.getFullLineRangeAtCoord(request.mouseVerticalOffset); const pos = res.range.getStartPosition(); - let offset = Math.abs(request.pos.x - request.editorPos.x); - const detail: IMarginData = { + let offset = Math.abs(request.relativePos.x); + const detail: IMouseTargetMarginData = { isAfterLines: res.isAfterLines, glyphMarginLeft: ctx.layoutInfo.glyphMarginLeft, glyphMarginWidth: ctx.layoutInfo.glyphMarginWidth, @@ -645,29 +655,29 @@ export class MouseTargetFactory { if (offset <= ctx.layoutInfo.glyphMarginWidth) { // On the glyph margin - return request.fulfill(MouseTargetType.GUTTER_GLYPH_MARGIN, pos, res.range, detail); + return request.fulfillMargin(MouseTargetType.GUTTER_GLYPH_MARGIN, pos, res.range, detail); } offset -= ctx.layoutInfo.glyphMarginWidth; if (offset <= ctx.layoutInfo.lineNumbersWidth) { // On the line numbers - return request.fulfill(MouseTargetType.GUTTER_LINE_NUMBERS, pos, res.range, detail); + return request.fulfillMargin(MouseTargetType.GUTTER_LINE_NUMBERS, pos, res.range, detail); } offset -= ctx.layoutInfo.lineNumbersWidth; // On the line decorations - return request.fulfill(MouseTargetType.GUTTER_LINE_DECORATIONS, pos, res.range, detail); + return request.fulfillMargin(MouseTargetType.GUTTER_LINE_DECORATIONS, pos, res.range, detail); } return null; } - private static _hitTestViewLines(ctx: HitTestContext, request: ResolvedHitTestRequest, domHitTestExecuted: boolean): MouseTarget | null { + private static _hitTestViewLines(ctx: HitTestContext, request: ResolvedHitTestRequest, domHitTestExecuted: boolean): IMouseTarget | null { if (!ElementPath.isChildOfViewLines(request.targetPath)) { return null; } if (ctx.isInTopPadding(request.mouseVerticalOffset)) { - return request.fulfill(MouseTargetType.CONTENT_EMPTY, new Position(1, 1), null, EMPTY_CONTENT_AFTER_LINES); + return request.fulfillContentEmpty(new Position(1, 1), EMPTY_CONTENT_AFTER_LINES); } // Check if it is below any lines and any view zones @@ -675,7 +685,7 @@ export class MouseTargetFactory { // This most likely indicates it happened after the last view-line const lineCount = ctx.model.getLineCount(); const maxLineColumn = ctx.model.getLineMaxColumn(lineCount); - return request.fulfill(MouseTargetType.CONTENT_EMPTY, new Position(lineCount, maxLineColumn), null, EMPTY_CONTENT_AFTER_LINES); + return request.fulfillContentEmpty(new Position(lineCount, maxLineColumn), EMPTY_CONTENT_AFTER_LINES); } if (domHitTestExecuted) { @@ -686,19 +696,19 @@ export class MouseTargetFactory { if (ctx.model.getLineLength(lineNumber) === 0) { const lineWidth = ctx.getLineWidth(lineNumber); const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth); - return request.fulfill(MouseTargetType.CONTENT_EMPTY, new Position(lineNumber, 1), null, detail); + return request.fulfillContentEmpty(new Position(lineNumber, 1), detail); } const lineWidth = ctx.getLineWidth(lineNumber); if (request.mouseContentHorizontalOffset >= lineWidth) { const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth); const pos = new Position(lineNumber, ctx.model.getLineMaxColumn(lineNumber)); - return request.fulfill(MouseTargetType.CONTENT_EMPTY, pos, null, detail); + return request.fulfillContentEmpty(pos, detail); } } // We have already executed hit test... - return request.fulfill(MouseTargetType.UNKNOWN); + return request.fulfillUnknown(); } const hitTestResult = MouseTargetFactory._doHitTest(ctx, request); @@ -710,45 +720,45 @@ export class MouseTargetFactory { return this._createMouseTarget(ctx, request.withTarget(hitTestResult.hitTarget), true); } - private static _hitTestMinimap(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestMinimap(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { if (ElementPath.isChildOfMinimap(request.targetPath)) { const possibleLineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset); const maxColumn = ctx.model.getLineMaxColumn(possibleLineNumber); - return request.fulfill(MouseTargetType.SCROLLBAR, new Position(possibleLineNumber, maxColumn)); + return request.fulfillScrollbar(new Position(possibleLineNumber, maxColumn)); } return null; } - private static _hitTestScrollbarSlider(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestScrollbarSlider(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { if (ElementPath.isChildOfScrollableElement(request.targetPath)) { if (request.target && request.target.nodeType === 1) { const className = request.target.className; if (className && /\b(slider|scrollbar)\b/.test(className)) { const possibleLineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset); const maxColumn = ctx.model.getLineMaxColumn(possibleLineNumber); - return request.fulfill(MouseTargetType.SCROLLBAR, new Position(possibleLineNumber, maxColumn)); + return request.fulfillScrollbar(new Position(possibleLineNumber, maxColumn)); } } } return null; } - private static _hitTestScrollbar(ctx: HitTestContext, request: ResolvedHitTestRequest): MouseTarget | null { + private static _hitTestScrollbar(ctx: HitTestContext, request: ResolvedHitTestRequest): IMouseTarget | null { // Is it the overview ruler? // Is it a child of the scrollable element? if (ElementPath.isChildOfScrollableElement(request.targetPath)) { const possibleLineNumber = ctx.getLineNumberAtVerticalOffset(request.mouseVerticalOffset); const maxColumn = ctx.model.getLineMaxColumn(possibleLineNumber); - return request.fulfill(MouseTargetType.SCROLLBAR, new Position(possibleLineNumber, maxColumn)); + return request.fulfillScrollbar(new Position(possibleLineNumber, maxColumn)); } return null; } - public getMouseColumn(editorPos: EditorPagePosition, pos: PageCoordinates): number { + public getMouseColumn(relativePos: CoordinatesRelativeToEditor): number { const options = this._context.configuration.options; const layoutInfo = options.get(EditorOption.layoutInfo); - const mouseContentHorizontalOffset = this._context.viewLayout.getCurrentScrollLeft() + pos.x - editorPos.x - layoutInfo.contentLeft; + const mouseContentHorizontalOffset = this._context.viewLayout.getCurrentScrollLeft() + relativePos.x - layoutInfo.contentLeft; return MouseTargetFactory._getMouseColumn(mouseContentHorizontalOffset, options.get(EditorOption.fontInfo).typicalHalfwidthCharacterWidth); } @@ -760,7 +770,7 @@ export class MouseTargetFactory { return (chars + 1); } - private static createMouseTargetFromHitTestPosition(ctx: HitTestContext, request: HitTestRequest, spanNode: HTMLElement, pos: Position, injectedText: InjectedText | null): MouseTarget { + private static createMouseTargetFromHitTestPosition(ctx: HitTestContext, request: HitTestRequest, spanNode: HTMLElement, pos: Position, injectedText: InjectedText | null): IMouseTarget { const lineNumber = pos.lineNumber; const column = pos.column; @@ -768,19 +778,19 @@ export class MouseTargetFactory { if (request.mouseContentHorizontalOffset > lineWidth) { const detail = createEmptyContentDataInLines(request.mouseContentHorizontalOffset - lineWidth); - return request.fulfill(MouseTargetType.CONTENT_EMPTY, pos, null, detail); + return request.fulfillContentEmpty(pos, detail); } const visibleRange = ctx.visibleRangeForPosition(lineNumber, column); if (!visibleRange) { - return request.fulfill(MouseTargetType.UNKNOWN, pos); + return request.fulfillUnknown(pos); } const columnHorizontalOffset = visibleRange.left; if (request.mouseContentHorizontalOffset === columnHorizontalOffset) { - return request.fulfill(MouseTargetType.CONTENT_TEXT, pos, null, { mightBeForeignElement: !!injectedText }); + return request.fulfillContentText(pos, null, { mightBeForeignElement: !!injectedText, injectedText }); } // Let's define a, b, c and check if the offset is in between them... @@ -813,10 +823,10 @@ export class MouseTargetFactory { const curr = points[i]; if (prev.offset <= request.mouseContentHorizontalOffset && request.mouseContentHorizontalOffset <= curr.offset) { const rng = new EditorRange(lineNumber, prev.column, lineNumber, curr.column); - return request.fulfill(MouseTargetType.CONTENT_TEXT, pos, rng, { mightBeForeignElement: !mouseIsOverSpanNode || !!injectedText }); + return request.fulfillContentText(pos, rng, { mightBeForeignElement: !mouseIsOverSpanNode || !!injectedText, injectedText }); } } - return request.fulfill(MouseTargetType.CONTENT_TEXT, pos, null, { mightBeForeignElement: !mouseIsOverSpanNode || !!injectedText }); + return request.fulfillContentText(pos, null, { mightBeForeignElement: !mouseIsOverSpanNode || !!injectedText, injectedText }); } /** @@ -834,8 +844,8 @@ export class MouseTargetFactory { if (adjustedPageY <= request.editorPos.y) { adjustedPageY = request.editorPos.y + 1; } - if (adjustedPageY >= request.editorPos.y + ctx.layoutInfo.height) { - adjustedPageY = request.editorPos.y + ctx.layoutInfo.height - 1; + if (adjustedPageY >= request.editorPos.y + request.editorPos.height) { + adjustedPageY = request.editorPos.y + request.editorPos.height - 1; } const adjustedPage = new PageCoordinates(request.pos.x, adjustedPageY); diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index 5c4df88ff3b17..0fb7a0d6788d4 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -11,7 +11,7 @@ import { IPointerHandlerHelper, MouseHandler, createMouseMoveEventMerger } from import { IMouseTarget } from 'vs/editor/browser/editorBrowser'; import { EditorMouseEvent, EditorPointerEventFactory } from 'vs/editor/browser/editorDom'; import { ViewController } from 'vs/editor/browser/view/viewController'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; +import { ViewContext } from 'vs/editor/common/viewModel/viewContext'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { TextAreaSyntethicEvents } from 'vs/editor/browser/controller/textAreaInput'; diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 323517cac2d05..beee2daef1ba4 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -10,8 +10,8 @@ import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import * as platform from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; -import { Configuration } from 'vs/editor/browser/config/configuration'; -import { CopyOptions, ICompositionData, IPasteData, ITextAreaInputHost, TextAreaInput, ClipboardDataToCopy } from 'vs/editor/browser/controller/textAreaInput'; +import { applyFontInfo } from 'vs/editor/browser/config/domFontInfo'; +import { CopyOptions, ICompositionData, IPasteData, ITextAreaInputHost, TextAreaInput, ClipboardDataToCopy, TextAreaWrapper } from 'vs/editor/browser/controller/textAreaInput'; import { ISimpleModel, ITypeData, PagedScreenReaderStrategy, TextAreaState, _debugComposition } from 'vs/editor/browser/controller/textAreaState'; import { ViewController } from 'vs/editor/browser/view/viewController'; import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart'; @@ -19,38 +19,58 @@ import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/line import { Margin } from 'vs/editor/browser/viewParts/margin/margin'; import { RenderLineNumbersType, EditorOption, IComputedEditorOptions, EditorOptions } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; -import { WordCharacterClass, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier'; +import { WordCharacterClass, getMapForWordSeparators } from 'vs/editor/common/core/wordCharacterClassifier'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { EndOfLinePreference } from 'vs/editor/common/model'; -import { RenderingContext, RestrictedRenderingContext, HorizontalPosition } from 'vs/editor/common/view/renderingContext'; -import { ViewContext } from 'vs/editor/common/view/viewContext'; -import * as viewEvents from 'vs/editor/common/view/viewEvents'; +import { RenderingContext, RestrictedRenderingContext, HorizontalPosition } from 'vs/editor/browser/view/renderingContext'; +import { ViewContext } from 'vs/editor/common/viewModel/viewContext'; +import * as viewEvents from 'vs/editor/common/viewModel/viewEvents'; import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { IEditorAriaOptions } from 'vs/editor/browser/editorBrowser'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from 'vs/base/browser/ui/mouseCursor/mouseCursor'; +import { ColorId, ITokenPresentation, TokenizationRegistry } from 'vs/editor/common/languages'; +import { Color } from 'vs/base/common/color'; -export interface ITextAreaHandlerHelper { - visibleRangeForPositionRelativeToEditor(lineNumber: number, column: number): HorizontalPosition | null; +export interface IVisibleRangeProvider { + visibleRangeForPosition(position: Position): HorizontalPosition | null; } class VisibleTextAreaData { _visibleTextAreaBrand: void = undefined; - public readonly top: number; - public readonly left: number; - public readonly width: number; + public startPosition: Position | null = null; + public endPosition: Position | null = null; - constructor(top: number, left: number, width: number) { - this.top = top; - this.left = left; - this.width = width; + public visibleTextareaStart: HorizontalPosition | null = null; + public visibleTextareaEnd: HorizontalPosition | null = null; + + constructor( + private readonly _context: ViewContext, + public readonly modelLineNumber: number, + public readonly distanceToModelLineStart: number, + public readonly widthOfHiddenLineTextBefore: number, + public readonly distanceToModelLineEnd: number, + ) { } - public setWidth(width: number): VisibleTextAreaData { - return new VisibleTextAreaData(this.top, this.left, width); + prepareRender(visibleRangeProvider: IVisibleRangeProvider): void { + const startModelPosition = new Position(this.modelLineNumber, this.distanceToModelLineStart + 1); + const endModelPosition = new Position(this.modelLineNumber, this._context.model.getModelLineMaxColumn(this.modelLineNumber) - this.distanceToModelLineEnd); + + this.startPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(startModelPosition); + this.endPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(endModelPosition); + + if (this.startPosition.lineNumber === this.endPosition.lineNumber) { + this.visibleTextareaStart = visibleRangeProvider.visibleRangeForPosition(this.startPosition); + this.visibleTextareaEnd = visibleRangeProvider.visibleRangeForPosition(this.endPosition); + } else { + // TODO: what if the view positions are not on the same line? + this.visibleTextareaStart = null; + this.visibleTextareaEnd = null; + } } } @@ -59,7 +79,7 @@ const canUseZeroSizeTextarea = (browser.isFirefox); export class TextAreaHandler extends ViewPart { private readonly _viewController: ViewController; - private readonly _viewHelper: ITextAreaHandlerHelper; + private readonly _visibleRangeProvider: IVisibleRangeProvider; private _scrollLeft: number; private _scrollTop: number; @@ -90,11 +110,11 @@ export class TextAreaHandler extends ViewPart { public readonly textAreaCover: FastDomNode; private readonly _textAreaInput: TextAreaInput; - constructor(context: ViewContext, viewController: ViewController, viewHelper: ITextAreaHandlerHelper) { + constructor(context: ViewContext, viewController: ViewController, visibleRangeProvider: IVisibleRangeProvider) { super(context); this._viewController = viewController; - this._viewHelper = viewHelper; + this._visibleRangeProvider = visibleRangeProvider; this._scrollLeft = 0; this._scrollTop = 0; @@ -152,7 +172,7 @@ export class TextAreaHandler extends ViewPart { }; const textAreaInputHost: ITextAreaInputHost = { - getDataToCopy: (generateHTML: boolean): ClipboardDataToCopy => { + getDataToCopy: (): ClipboardDataToCopy => { const rawTextToCopy = this._context.model.getPlainTextToCopy(this._modelSelections, this._emptySelectionClipboard, platform.isWindows); const newLineCharacter = this._context.model.getEOL(); @@ -162,13 +182,11 @@ export class TextAreaHandler extends ViewPart { let html: string | null | undefined = undefined; let mode: string | null = null; - if (generateHTML) { - if (CopyOptions.forceCopyWithSyntaxHighlighting || (this._copyWithSyntaxHighlighting && text.length < 65536)) { - const richText = this._context.model.getRichTextToCopy(this._modelSelections, this._emptySelectionClipboard); - if (richText) { - html = richText.html; - mode = richText.mode; - } + if (CopyOptions.forceCopyWithSyntaxHighlighting || (this._copyWithSyntaxHighlighting && text.length < 65536)) { + const richText = this._context.model.getRichTextToCopy(this._modelSelections, this._emptySelectionClipboard); + if (richText) { + html = richText.html; + mode = richText.mode; } } return { @@ -226,7 +244,8 @@ export class TextAreaHandler extends ViewPart { } }; - this._textAreaInput = this._register(new TextAreaInput(textAreaInputHost, this.textArea)); + const textAreaWrapper = this._register(new TextAreaWrapper(this.textArea.domNode)); + this._textAreaInput = this._register(new TextAreaInput(textAreaInputHost, textAreaWrapper, platform.OS, browser)); this._register(this._textAreaInput.onKeyDown((e: IKeyboardEvent) => { this._viewController.emitKeyDown(e); @@ -272,28 +291,80 @@ export class TextAreaHandler extends ViewPart { })); this._register(this._textAreaInput.onCompositionStart((e) => { - const lineNumber = this._selections[0].startLineNumber; - const column = this._selections[0].startColumn + e.revealDeltaColumns; + // The textarea might contain some content when composition starts. + // + // When we make the textarea visible, it always has a height of 1 line, + // so we don't need to worry too much about content on lines above or below + // the selection. + // + // However, the text on the current line needs to be made visible because + // some IME methods allow to glyphs on the current line (by pressing arrow keys). + // + // (1) The textarea might contain only some parts of the current line, + // like the word before the selection. Also, the content inside the textarea + // can grow or shrink as composition occurs. We therefore anchor the textarea + // in terms of distance to a certain line start and line end. + // + // (2) Also, we should not make \t characters visible, because their rendering + // inside the